feat(web): materialize geometry nodes in Blender Main
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 11:01:05 +08:00
parent 5693c9ca24
commit e36d6c1ac2
34 changed files with 1892 additions and 29 deletions

View File

@@ -4637,9 +4637,9 @@ bool web_engine_blend_main_set_geometry_node_graph(WebBlendMainState *state,
error = "GN_INVALID_GRAPH: Geometry Node graph could not be persisted in Main";
return false;
}
/* Materialize a native GeometryNodeTree datablock as the durable owner of
* this graph. The bounded JSON remains the transport representation, while
* future node-family slices can populate the tree incrementally. */
/* Materialize the bounded graph into a native GeometryNodeTree. The JSON
* remains the lossless transport record, while Blender owns the editable
* node instances and links used by the modifier. */
bNodeTree *native_tree = nullptr;
for (bNodeTree &candidate : state->main->nodetrees) {
if (id_name(candidate.id) == graph_id) {
@@ -4656,6 +4656,60 @@ bool web_engine_blend_main_set_geometry_node_graph(WebBlendMainState *state,
error = "GN_INVALID_GRAPH: Blender could not create a GeometryNodeTree datablock";
return false;
}
while (native_tree->nodes.first != nullptr) {
bke::node_remove_node(state->main,
*native_tree,
*static_cast<bNode *>(native_tree->nodes.first),
true);
}
std::unordered_map<std::string, bNode *> native_nodes;
for (const json &node : graph["nodes"]) {
const std::string node_id = node.value("id", "");
const std::string node_type = node.value("type", "");
const std::string node_name = node.value("name", node_id);
if (node_id.empty() || node_type.empty() || node_name.empty()) {
error = "GN_INVALID_GRAPH: Geometry Node node identity is incomplete";
return false;
}
if (bke::node_type_find(UString(node_type)) == nullptr) {
error = "GN_NODE_UNSUPPORTED: Blender does not register the requested Geometry Node type";
return false;
}
bNode *native = bke::node_add_node(nullptr, *native_tree, UString(node_type));
if (native == nullptr) {
error = "GN_INVALID_GRAPH: Blender could not materialize a Geometry Node";
return false;
}
STRNCPY_UTF8(native->name, node_name.c_str());
STRNCPY_UTF8(native->label, node_name.c_str());
native_nodes.emplace(node_id, native);
}
const auto socket_for = [](bNode &node, const eNodeSocketInOut direction, const std::string &socket_id) -> bNodeSocket * {
const size_t separator = socket_id.rfind(':');
const std::string identifier = separator == std::string::npos ? socket_id : socket_id.substr(separator + 1);
ListBase *sockets = direction == SOCK_IN ? &node.inputs : &node.outputs;
for (bNodeSocket *socket = static_cast<bNodeSocket *>(sockets->first); socket != nullptr; socket = socket->next) {
if (identifier == socket->identifier || identifier == socket->name || socket_id == socket->identifier) return socket;
}
return nullptr;
};
for (const json &link : graph["links"]) {
const std::string from_id = link.value("fromNodeId", "");
const std::string to_id = link.value("toNodeId", "");
if (!native_nodes.contains(from_id) || !native_nodes.contains(to_id)) {
error = "GN_INVALID_GRAPH: Geometry Node link references a missing node";
return false;
}
bNodeSocket *from = socket_for(*native_nodes.at(from_id), SOCK_OUT, link.value("fromSocketId", ""));
bNodeSocket *to = socket_for(*native_nodes.at(to_id), SOCK_IN, link.value("toSocketId", ""));
if (from == nullptr || to == nullptr) {
error = "GN_INVALID_GRAPH: Geometry Node link references an unavailable socket";
return false;
}
bke::node_add_link(*native_tree, *native_nodes.at(from_id), *from, *native_nodes.at(to_id), *to);
}
BKE_ntree_update_tag_all(native_tree);
BKE_ntree_update_after_single_tree_change(*state->main, *native_tree);
Object *target_object = find_mesh_object(state->main, mesh_id);
NodesModifierData *nodes_modifier = nullptr;
if (target_object != nullptr) {