feat(web): switch to implementation-first capability wave
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 01:22:03 +08:00
parent 5fb12cd6a5
commit 7549b9a15c
159 changed files with 14110 additions and 74 deletions

View File

@@ -1578,6 +1578,7 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
type == "unwrapUV" || type == "addMaterialSlot" || type == "removeMaterialSlot" ||
type == "assignMaterialFaces" || type == "setMaterialPrincipled" ||
type == "importImage" || type == "setMaterialImageNode" || type == "setShaderGraph" ||
type == "setGeometryNodeGraph" || type == "setPhysicsSimulation" ||
type == "insertObjectKeyframe" || type == "deleteObjectKeyframe" ||
type == "setFCurveInterpolation" || type == "setActiveAction" || type == "setNLAStack" ||
type == "setConstraint" || type == "setParent" || type == "createCollection" ||
@@ -1667,6 +1668,16 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
command.value("imageId", "").c_str(), command.value("usage", "").c_str(),
command.value("uvMap", "").c_str(), main_error);
}
else if (type == "setGeometryNodeGraph") {
const json graph = command.value("graph", json::object());
applied = web_engine_blend_main_set_geometry_node_graph(
engine->authoritative_main, command.value("meshId", "").c_str(), graph.dump().c_str(), main_error);
}
else if (type == "setPhysicsSimulation") {
const json manifest = command.value("manifest", json::object());
applied = web_engine_blend_main_set_physics_simulation(
engine->authoritative_main, manifest.dump().c_str(), main_error);
}
else if (type == "setShaderGraph") {
const json graph = command.value("graph", json::object());
const std::string material_id = command.value("materialId", "");

View File

@@ -96,6 +96,8 @@ struct WebBlendMainState {
blender::Main *main = nullptr;
blender::Scene *scene = nullptr;
blender::ViewLayer *view_layer = nullptr;
std::unordered_map<std::string, std::string> geometry_node_graph_overrides;
std::string physics_simulation_override;
};
namespace {
@@ -4296,6 +4298,21 @@ bool web_engine_blend_main_geometry_node_graphs_json(WebBlendMainState *state,
graph["graphHash"] = web_engine_sha256_hex(graph.dump());
graphs.push_back(std::move(graph));
}
for (const auto &[graph_id, graph_text] : state->geometry_node_graph_overrides) {
if (graphs.size() >= max_graphs) {
error = "GN_GRAPH_BUDGET_EXCEEDED: Geometry Node graph count exceeds 4096";
return false;
}
json graph = json::parse(graph_text);
bool already_present = false;
for (const json &existing : graphs) {
if (existing.value("id", "") == graph_id) {
already_present = true;
break;
}
}
if (!already_present) graphs.push_back(std::move(graph));
}
geometry_node_graphs_json = graphs.dump();
return true;
}
@@ -4305,6 +4322,37 @@ bool web_engine_blend_main_geometry_node_graphs_json(WebBlendMainState *state,
}
}
bool web_engine_blend_main_set_geometry_node_graph(WebBlendMainState *state,
const char *mesh_id,
const char *graph_json,
std::string &error)
{
if (state == nullptr || state->main == nullptr || mesh_id == nullptr || graph_json == nullptr ||
find_mesh_object(state->main, mesh_id) == nullptr)
{
error = "GN_INVALID_GRAPH: Geometry Node target mesh is unavailable";
return false;
}
try {
const json graph = json::parse(graph_json);
const std::string graph_id = graph.value("id", "");
if (graph.value("schemaVersion", 0) != 1 || graph_id.empty() ||
!graph.contains("nodes") || !graph["nodes"].is_array() ||
!graph.contains("links") || !graph["links"].is_array() ||
graph["nodes"].size() > 4096 || graph["links"].size() > 16384)
{
error = "GN_INVALID_GRAPH: Geometry Node graph schema or bounds are invalid";
return false;
}
state->geometry_node_graph_overrides[graph_id] = graph.dump();
return true;
}
catch (const std::exception &exception) {
error = std::string("GN_INVALID_GRAPH: Geometry Node graph JSON is invalid: ") + exception.what();
return false;
}
}
bool web_engine_blend_main_physics_simulation_json(WebBlendMainState *state,
std::string &physics_json,
std::string &error)
@@ -4313,6 +4361,10 @@ bool web_engine_blend_main_physics_simulation_json(WebBlendMainState *state,
error = "PHYSICS_MANIFEST_INVALID: authoritative Main is not open";
return false;
}
if (!state->physics_simulation_override.empty()) {
physics_json = state->physics_simulation_override;
return true;
}
#ifdef WITH_WEB
/* Legacy particle systems are metadata-only in the Web runtime. Their desktop modifier chain
* can contain file-space pointers that are unsafe for the physics manifest walker. */
@@ -4431,6 +4483,40 @@ bool web_engine_blend_main_physics_simulation_json(WebBlendMainState *state,
return true;
}
bool web_engine_blend_main_set_physics_simulation(WebBlendMainState *state,
const char *manifest_json,
std::string &error)
{
if (state == nullptr || state->main == nullptr || manifest_json == nullptr) {
error = "PHYSICS_MANIFEST_INVALID: authoritative Main is not open";
return false;
}
try {
const json manifest = json::parse(manifest_json);
if (manifest.value("schemaVersion", 0) != 1 || !manifest.contains("systems") ||
!manifest["systems"].is_array() || manifest["systems"].size() > 4096)
{
error = "PHYSICS_MANIFEST_INVALID: physics manifest schema or system budget is invalid";
return false;
}
for (const json &system : manifest["systems"]) {
const std::string owner_id = system.value("ownerObjectId", "");
if (!system.is_object() || owner_id.empty() || find_object(state->main, owner_id.c_str()) == nullptr ||
!system.contains("settings") || !system["settings"].is_object())
{
error = "PHYSICS_MANIFEST_INVALID: physics system owner or settings are invalid";
return false;
}
}
state->physics_simulation_override = manifest.dump();
return true;
}
catch (const std::exception &exception) {
error = std::string("PHYSICS_MANIFEST_INVALID: physics manifest JSON is invalid: ") + exception.what();
return false;
}
}
bool web_engine_blend_main_particle_settings_json(WebBlendMainState *state,
std::string &particle_settings_json,
std::string &error)

View File

@@ -380,9 +380,16 @@ bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state,
bool web_engine_blend_main_geometry_node_graphs_json(WebBlendMainState *state,
std::string &geometry_node_graphs_json,
std::string &error);
bool web_engine_blend_main_set_geometry_node_graph(WebBlendMainState *state,
const char *mesh_id,
const char *graph_json,
std::string &error);
bool web_engine_blend_main_physics_simulation_json(WebBlendMainState *state,
std::string &physics_json,
std::string &error);
bool web_engine_blend_main_set_physics_simulation(WebBlendMainState *state,
const char *manifest_json,
std::string &error);
bool web_engine_blend_main_particle_settings_json(WebBlendMainState *state,
std::string &particle_settings_json,
std::string &error);