feat(web): add scene motion blur authoring
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-08-29 16:50:49 +08:00
parent d1635f5163
commit 05f9bf1433
47 changed files with 1918 additions and 40 deletions

View File

@@ -1592,7 +1592,7 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
type == "insertGreasePencilFrame" || type == "removeGreasePencilFrame" ||
type == "setGreasePencilStrokes" || type == "setVertexColors" ||
type == "setVertexWeights" || type == "setCameraProperties" || type == "setLightProperties" ||
type == "setWorldProperties" || type == "setSceneProperties" || type == "setSceneRenderSettings" || type == "setSceneAudioSettings" || type == "setSceneUnitSettings" || type == "setSceneTimelineSettings" || type == "setScenePipelineSettings" || type == "setSceneBindings" || type == "setSceneViewLayerSettings" || type == "setSceneCollectionSettings" || type == "setSceneBorderSettings" || type == "setSceneSimplifySettings" || type == "setSceneColorManagement" || type == "setSceneImageFormat" ||
type == "setWorldProperties" || type == "setSceneProperties" || type == "setSceneRenderSettings" || type == "setSceneAudioSettings" || type == "setSceneUnitSettings" || type == "setSceneTimelineSettings" || type == "setScenePipelineSettings" || type == "setSceneBindings" || type == "setSceneViewLayerSettings" || type == "setSceneCollectionSettings" || type == "setSceneBorderSettings" || type == "setSceneSimplifySettings" || type == "setSceneColorManagement" || type == "setSceneImageFormat" || type == "setSceneMotionBlur" ||
type == "setSculptMeshAttributes" ||
type == "sculptStroke") {
if (!engine->authoritative_commands || engine->authoritative_main == nullptr) {
@@ -2196,7 +2196,7 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
command.value("normalize", false), command.value("limit", 0u), command.value("mirror", false),
command.value("mirrorAxis", 0), command.value("mirrorTolerance", 1e-4f), main_error);
}
else if (type == "setCameraProperties" || type == "setLightProperties" || type == "setWorldProperties" || type == "setSceneProperties" || type == "setSceneRenderSettings" || type == "setSceneAudioSettings" || type == "setSceneUnitSettings" || type == "setSceneTimelineSettings" || type == "setScenePipelineSettings" || type == "setSceneBindings" || type == "setSceneViewLayerSettings" || type == "setSceneCollectionSettings" || type == "setSceneBorderSettings" || type == "setSceneSimplifySettings" || type == "setSceneColorManagement" || type == "setSceneImageFormat")
else if (type == "setCameraProperties" || type == "setLightProperties" || type == "setWorldProperties" || type == "setSceneProperties" || type == "setSceneRenderSettings" || type == "setSceneAudioSettings" || type == "setSceneUnitSettings" || type == "setSceneTimelineSettings" || type == "setScenePipelineSettings" || type == "setSceneBindings" || type == "setSceneViewLayerSettings" || type == "setSceneCollectionSettings" || type == "setSceneBorderSettings" || type == "setSceneSimplifySettings" || type == "setSceneColorManagement" || type == "setSceneImageFormat" || type == "setSceneMotionBlur")
{
const json properties = command.value("properties", json::object());
if (type == "setSceneProperties") {
@@ -2265,6 +2265,11 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
engine->authoritative_main, command.value("dataId", "").c_str(),
properties.dump().c_str(), main_error);
}
else if (type == "setSceneMotionBlur") {
applied = web_engine_blend_main_set_scene_motion_blur(
engine->authoritative_main, command.value("dataId", "").c_str(),
properties.dump().c_str(), main_error);
}
else {
applied = web_engine_blend_main_set_render_properties(
engine->authoritative_main, command.value("dataId", "").c_str(),

View File

@@ -5255,6 +5255,7 @@ json scene_ir_from_blend(const ParsedBlend &blend,
{"frameStart", frame_start},
{"frameEnd", frame_end},
{"outputPath", read_string(*blend.sdna, *render, "pic")}};
const int64_t render_mode = read_integer(*blend.sdna, *render, "mode").value_or(0);
if (const std::optional<ElementRef> image_format = embedded_element(*blend.sdna, *render, "im_format")) {
const int64_t image_type = read_integer(*blend.sdna, *image_format, "imtype").value_or(R_IMF_IMTYPE_PNG);
std::string image_file_type = "UNKNOWN";
@@ -5263,13 +5264,16 @@ json scene_ir_from_blend(const ParsedBlend &blend,
else if (image_type == R_IMF_IMTYPE_OPENEXR) image_file_type = "OPEN_EXR";
else if (image_type == R_IMF_IMTYPE_TIFF) image_file_type = "TIFF";
else if (image_type == R_IMF_IMTYPE_WEBP) image_file_type = "WEBP";
scene["imageFormatSettings"] = {{"fileType", image_file_type},
scene["imageFormatSettings"] = {{"fileType", image_file_type},
{"colorMode", read_integer(*blend.sdna, *image_format, "color_mode").value_or(32)},
{"depth", read_integer(*blend.sdna, *image_format, "depth").value_or(R_IMF_CHAN_DEPTH_8)},
{"quality", read_integer(*blend.sdna, *image_format, "quality").value_or(90)},
{"compression", read_integer(*blend.sdna, *image_format, "compress").value_or(15)},
{"exrCodec", read_integer(*blend.sdna, *image_format, "exr_codec").value_or(0)}};
}
scene["motionBlurSettings"] = {{"enabled", (render_mode & R_MBLUR) != 0},
{"shutter", read_float(*blend.sdna, *render, "motion_blur_shutter").value_or(0.5f)},
{"position", read_integer(*blend.sdna, *render, "motion_blur_position").value_or(SCE_MB_CENTER)}};
scene["timelineSettings"] = {{"fps", read_integer(*blend.sdna, *render, "frs_sec").value_or(24)},
{"fpsBase", read_float(*blend.sdna, *render, "frs_sec_base").value_or(1.0f)},
{"frameStep", read_integer(*blend.sdna, *render, "frame_step").value_or(1)},
@@ -5277,7 +5281,6 @@ json scene_ir_from_blend(const ParsedBlend &blend,
{"previewStart", read_integer(*blend.sdna, *render, "psfra").value_or(frame_start)},
{"previewEnd", read_integer(*blend.sdna, *render, "pefra").value_or(frame_end)}};
const int64_t scene_mode = read_integer(*blend.sdna, *render, "scemode").value_or(0);
const int64_t render_mode = read_integer(*blend.sdna, *render, "mode").value_or(0);
scene["pipelineSettings"] = {{"useCompositor", (scene_mode & R_DOCOMP) != 0},
{"useSequencer", (scene_mode & R_DOSEQ) != 0},
{"noOverwrite", (render_mode & R_NO_OVERWRITE) != 0},

View File

@@ -6435,6 +6435,38 @@ bool web_engine_blend_main_set_scene_image_format(WebBlendMainState *state,
return true;
}
bool web_engine_blend_main_set_scene_motion_blur(WebBlendMainState *state,
const char *target_id,
const char *properties_json,
std::string &error)
{
if (state == nullptr || state->main == nullptr || target_id == nullptr || properties_json == nullptr) { error = "RENDER_PROPERTY_INVALID: scene motion-blur target is missing"; return false; }
Scene *scene = find_scene(state->main, target_id);
if (scene == nullptr || scene->id.lib != nullptr) { error = "LINKED_DATA_MUTATION_BLOCKED: Scene is not editable"; return false; }
const json properties = json::parse(properties_json, nullptr, false);
if (!properties.is_object() || properties.empty()) { error = "RENDER_PROPERTY_INVALID: scene motion-blur settings must be a non-empty object"; return false; }
const std::unordered_set<std::string> allowed = {"enabled", "shutter", "position"};
for (const auto &[key, value] : properties.items()) { (void)value; if (!allowed.contains(key)) { error = "RENDER_PROPERTY_INVALID: unsupported motion-blur property " + key; return false; } }
if (properties.contains("shutter")) {
if (!properties["shutter"].is_number()) { error = "RENDER_PROPERTY_INVALID: shutter must be numeric"; return false; }
const float shutter = properties["shutter"].get<float>();
if (!std::isfinite(shutter) || shutter < 0.0f || shutter > 10.0f) { error = "RENDER_PROPERTY_INVALID: shutter is outside the bounded range"; return false; }
scene->r.motion_blur_shutter = shutter;
}
if (properties.contains("position")) {
if (!properties["position"].is_number_integer()) { error = "RENDER_PROPERTY_INVALID: position must be an integer"; return false; }
const int position = properties["position"].get<int>();
if (position < SCE_MB_CENTER || position > SCE_MB_END) { error = "RENDER_PROPERTY_INVALID: position is unsupported"; return false; }
scene->r.motion_blur_position = position;
}
if (properties.contains("enabled")) {
if (!properties["enabled"].is_boolean()) { error = "RENDER_PROPERTY_INVALID: enabled must be boolean"; return false; }
if (properties["enabled"].get<bool>()) scene->r.mode |= R_MBLUR; else scene->r.mode &= ~R_MBLUR;
}
scene->id.recalc |= ID_RECALC_PARAMETERS;
return true;
}
bool web_engine_blend_main_write(WebBlendMainState *state,
std::vector<uint8_t> &bytes,
std::string &error)

View File

@@ -571,6 +571,10 @@ bool web_engine_blend_main_set_scene_image_format(WebBlendMainState *state,
const char *target_id,
const char *properties_json,
std::string &error);
bool web_engine_blend_main_set_scene_motion_blur(WebBlendMainState *state,
const char *target_id,
const char *properties_json,
std::string &error);
bool web_engine_blend_main_write(WebBlendMainState *state,
std::vector<uint8_t> &bytes,
std::string &error);