feat(web): add scene image format authoring
This commit is contained in:
@@ -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 == "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 == "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")
|
||||
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")
|
||||
{
|
||||
const json properties = command.value("properties", json::object());
|
||||
if (type == "setSceneProperties") {
|
||||
@@ -2260,6 +2260,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 == "setSceneImageFormat") {
|
||||
applied = web_engine_blend_main_set_scene_image_format(
|
||||
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(),
|
||||
|
||||
@@ -5255,6 +5255,21 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
{"frameStart", frame_start},
|
||||
{"frameEnd", frame_end},
|
||||
{"outputPath", read_string(*blend.sdna, *render, "pic")}};
|
||||
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";
|
||||
if (image_type == R_IMF_IMTYPE_PNG) image_file_type = "PNG";
|
||||
else if (image_type == R_IMF_IMTYPE_JPEG90) image_file_type = "JPEG";
|
||||
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},
|
||||
{"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["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)},
|
||||
|
||||
@@ -6389,6 +6389,52 @@ bool web_engine_blend_main_set_scene_color_management(WebBlendMainState *state,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool web_engine_blend_main_set_scene_image_format(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 image-format 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 image-format settings must be a non-empty object"; return false; }
|
||||
const std::unordered_set<std::string> allowed = {"fileType", "colorMode", "depth", "quality", "compression", "exrCodec"};
|
||||
for (const auto &[key, value] : properties.items()) { (void)value; if (!allowed.contains(key)) { error = "RENDER_PROPERTY_INVALID: unsupported image-format property " + key; return false; } }
|
||||
if (properties.contains("fileType")) {
|
||||
if (!properties["fileType"].is_string()) { error = "RENDER_PROPERTY_INVALID: fileType must be a string"; return false; }
|
||||
const std::string file_type = properties["fileType"].get<std::string>();
|
||||
if (file_type == "PNG") scene->r.im_format.imtype = R_IMF_IMTYPE_PNG;
|
||||
else if (file_type == "JPEG") scene->r.im_format.imtype = R_IMF_IMTYPE_JPEG90;
|
||||
else if (file_type == "OPEN_EXR") scene->r.im_format.imtype = R_IMF_IMTYPE_OPENEXR;
|
||||
else if (file_type == "TIFF") scene->r.im_format.imtype = R_IMF_IMTYPE_TIFF;
|
||||
else if (file_type == "WEBP") scene->r.im_format.imtype = R_IMF_IMTYPE_WEBP;
|
||||
else { error = "RENDER_PROPERTY_INVALID: fileType is unsupported"; return false; }
|
||||
}
|
||||
if (properties.contains("colorMode")) {
|
||||
if (!properties["colorMode"].is_number_integer()) { error = "RENDER_PROPERTY_INVALID: colorMode must be an integer"; return false; }
|
||||
const int value = properties["colorMode"].get<int>();
|
||||
if (value != 8 && value != 16 && value != 24 && value != 32) { error = "RENDER_PROPERTY_INVALID: colorMode is unsupported"; return false; }
|
||||
scene->r.im_format.color_mode = ImColorMode(value);
|
||||
}
|
||||
if (properties.contains("depth")) {
|
||||
if (!properties["depth"].is_number_integer()) { error = "RENDER_PROPERTY_INVALID: depth must be an integer"; return false; }
|
||||
const int value = properties["depth"].get<int>();
|
||||
if (value != R_IMF_CHAN_DEPTH_8 && value != R_IMF_CHAN_DEPTH_10 && value != R_IMF_CHAN_DEPTH_12 && value != R_IMF_CHAN_DEPTH_16 && value != R_IMF_CHAN_DEPTH_32) { error = "RENDER_PROPERTY_INVALID: depth is unsupported"; return false; }
|
||||
scene->r.im_format.depth = eImageFormatDepth(value);
|
||||
}
|
||||
auto read_byte = [&](const char *key, int minimum, int maximum, char &target) {
|
||||
if (!properties.contains(key)) return true;
|
||||
if (!properties[key].is_number_integer()) { error = std::string("RENDER_PROPERTY_INVALID: ") + key + " must be an integer"; return false; }
|
||||
const int value = properties[key].get<int>();
|
||||
if (value < minimum || value > maximum) { error = std::string("RENDER_PROPERTY_INVALID: ") + key + " is outside the bounded range"; return false; }
|
||||
target = char(value); return true;
|
||||
};
|
||||
if (!read_byte("quality", 0, 100, scene->r.im_format.quality) || !read_byte("compression", 0, 100, scene->r.im_format.compress) || !read_byte("exrCodec", 0, 10, scene->r.im_format.exr_codec)) return false;
|
||||
scene->id.recalc |= ID_RECALC_PARAMETERS;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool web_engine_blend_main_write(WebBlendMainState *state,
|
||||
std::vector<uint8_t> &bytes,
|
||||
std::string &error)
|
||||
|
||||
@@ -567,6 +567,10 @@ bool web_engine_blend_main_set_scene_color_management(WebBlendMainState *state,
|
||||
const char *target_id,
|
||||
const char *properties_json,
|
||||
std::string &error);
|
||||
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_write(WebBlendMainState *state,
|
||||
std::vector<uint8_t> &bytes,
|
||||
std::string &error);
|
||||
|
||||
Reference in New Issue
Block a user