Files
multi_render_blender/src/main/BlenderProcess.js
2026-02-27 18:01:00 +01:00

147 lines
6.0 KiB
JavaScript

const { spawn } = require("child_process");
const PathResolver = require("./PathResolver.js");
const BlenderProcess = {
render_frame: (obj_params) => {
let str_blend_path = obj_params.str_blend_path;
let str_camera_name = obj_params.str_camera_name;
let nb_frame = obj_params.nb_frame;
let nb_resolution_x = obj_params.nb_resolution_x;
let nb_resolution_y = obj_params.nb_resolution_y;
let str_format = obj_params.str_format;
let str_output_path = obj_params.str_output_path;
let str_safe_name = str_camera_name.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
let list_python_lines = [
"import bpy",
"scene=bpy.context.scene",
"scene.camera=bpy.data.objects['" + str_safe_name + "']",
"scene.render.resolution_x=" + nb_resolution_x,
"scene.render.resolution_y=" + nb_resolution_y,
];
if (obj_params.obj_render_settings) {
let obj_rs = obj_params.obj_render_settings;
if (obj_rs.str_engine) {
list_python_lines.push("scene.render.engine='" + obj_rs.str_engine + "'");
}
if (obj_rs.nb_resolution_percentage !== undefined) {
list_python_lines.push("scene.render.resolution_percentage=" + obj_rs.nb_resolution_percentage);
}
if (obj_rs.is_film_transparent !== undefined) {
list_python_lines.push("scene.render.film_transparent=" + (obj_rs.is_film_transparent ? "True" : "False"));
}
if (obj_rs.str_engine === "CYCLES") {
if (obj_rs.nb_samples !== undefined) {
list_python_lines.push("scene.cycles.samples=" + obj_rs.nb_samples);
}
if (obj_rs.str_device) {
list_python_lines.push("scene.cycles.device='" + obj_rs.str_device + "'");
}
if (obj_rs.is_denoise !== undefined) {
list_python_lines.push("scene.cycles.use_denoising=" + (obj_rs.is_denoise ? "True" : "False"));
}
} else if (obj_rs.str_engine === "BLENDER_EEVEE" || obj_rs.str_engine === "BLENDER_EEVEE_NEXT") {
if (obj_rs.nb_samples !== undefined) {
list_python_lines.push("try:\n scene.eevee.taa_render_samples=" + obj_rs.nb_samples + "\nexcept:\n try:\n scene.eevee.samples=" + obj_rs.nb_samples + "\n except:pass");
}
}
}
let str_python_expr = list_python_lines.join(";");
if (obj_params.list_collections && obj_params.list_collections.length > 0) {
str_python_expr += "\ndef _slc(lc,n,v):"
+ "\n for c in lc.children:"
+ "\n if c.name==n:c.exclude=v;return"
+ "\n _slc(c,n,v)\n";
for (let obj_col of obj_params.list_collections) {
let str_col_safe = obj_col.str_name.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
let str_val = obj_col.is_hide_render ? "True" : "False";
str_python_expr += "bpy.data.collections['" + str_col_safe + "'].hide_render=" + str_val
+ ";_slc(bpy.context.view_layer.layer_collection,'" + str_col_safe + "'," + str_val + ")\n";
}
}
let list_args = [
"-b", str_blend_path,
"--python-expr", str_python_expr,
"-o", str_output_path,
"-F", str_format,
"-f", String(nb_frame),
];
return new Promise((resolve, reject) => {
let str_stdout = "";
let str_stderr = "";
let obj_process = spawn(PathResolver.get_blender_path(), list_args);
obj_process.stdout.on("data", (obj_data) => {
str_stdout += obj_data.toString();
if (obj_params.fn_on_stdout) {
obj_params.fn_on_stdout(obj_data.toString());
}
});
obj_process.stderr.on("data", (obj_data) => {
str_stderr += obj_data.toString();
});
obj_process.on("close", (nb_code) => {
if (nb_code !== 0) {
reject({
str_message: "Blender a quitte avec le code " + nb_code,
str_stderr: str_stderr,
str_stdout: str_stdout,
});
return;
}
let str_rendered_file = BlenderProcess._find_rendered_file(str_stdout);
resolve({
str_rendered_file: str_rendered_file,
str_stdout: str_stdout,
});
});
obj_process.on("error", (obj_err) => {
reject({
str_message: "Impossible de lancer Blender : " + obj_err.message,
str_stderr: "",
str_stdout: "",
});
});
// Store process reference for kill support
if (obj_params.fn_on_process) {
obj_params.fn_on_process(obj_process);
}
});
},
_find_rendered_file: (str_stdout) => {
let list_lines = str_stdout.split("\n");
for (let str_line of list_lines) {
let nb_index = str_line.indexOf("Saved: ");
if (nb_index !== -1) {
let str_path = str_line.substring(nb_index + 7).trim();
// Remove trailing info like " Time: 00:01.23"
let nb_time_index = str_path.indexOf(" Time:");
if (nb_time_index !== -1) {
str_path = str_path.substring(0, nb_time_index).trim();
}
// Blender 5.x wraps path in single quotes: Saved: '/path/file.png'
if (str_path.startsWith("'") && str_path.endsWith("'")) {
str_path = str_path.substring(1, str_path.length - 1);
}
return str_path;
}
}
return null;
},
};
module.exports = BlenderProcess;