Fix #107779: Support simulation zone remapping in node group operators
Adds a `remap_pairing` function for node group operators that ensures the simulation input nodes' `output_node_id` matches the new node are creating a group, ungrouping a node group, or separating from a group. Also fixes a crash in the "Group Separate" operator when group input/output nodes are included in the selection. Pull Request: https://projects.blender.org/blender/blender/pulls/107807
This commit is contained in:
@@ -137,6 +137,27 @@ static bNode *node_group_get_active(bContext *C, const char *node_idname)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Maps old to new identifiers for simulation input node pairing. */
|
||||
static void remap_pairing(bNodeTree &dst_tree,
|
||||
Span<bNode *> nodes,
|
||||
const Map<int32_t, int32_t> &identifier_map)
|
||||
{
|
||||
for (bNode *dst_node : nodes) {
|
||||
if (dst_node->type == GEO_NODE_SIMULATION_INPUT) {
|
||||
NodeGeometrySimulationInput *data = static_cast<NodeGeometrySimulationInput *>(
|
||||
dst_node->storage);
|
||||
if (data->output_node_id == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data->output_node_id = identifier_map.lookup_default(data->output_node_id, 0);
|
||||
if (data->output_node_id == 0) {
|
||||
blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -232,7 +253,10 @@ static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
|
||||
bNodeTree *wgroup = ntreeCopyTree(bmain, ngroup);
|
||||
|
||||
/* Add the nodes into the `ntree`. */
|
||||
Vector<bNode *> new_nodes;
|
||||
Map<int32_t, int32_t> node_identifier_map;
|
||||
LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
|
||||
new_nodes.append(node);
|
||||
/* Remove interface nodes.
|
||||
* This also removes remaining links to and from interface nodes.
|
||||
*/
|
||||
@@ -253,8 +277,10 @@ static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
|
||||
/* migrate node */
|
||||
BLI_remlink(&wgroup->nodes, node);
|
||||
BLI_addtail(&ntree->nodes, node);
|
||||
const int32_t old_identifier = node->identifier;
|
||||
nodeUniqueID(ntree, node);
|
||||
nodeUniqueName(ntree, node);
|
||||
node_identifier_map.add(old_identifier, node->identifier);
|
||||
|
||||
BKE_ntree_update_tag_node_new(ntree, node);
|
||||
|
||||
@@ -310,6 +336,8 @@ static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
|
||||
}
|
||||
}
|
||||
|
||||
remap_pairing(*ntree, new_nodes, node_identifier_map);
|
||||
|
||||
/* free the group tree (takes care of user count) */
|
||||
BKE_id_free(bmain, wgroup);
|
||||
|
||||
@@ -360,7 +388,8 @@ static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
|
||||
|
||||
/* find internal links to this output */
|
||||
for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next;
|
||||
tlink = tlink->next) {
|
||||
tlink = tlink->next)
|
||||
{
|
||||
/* only use active output node */
|
||||
if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) {
|
||||
if (STREQ(tlink->tosock->identifier, identifier)) {
|
||||
@@ -440,28 +469,6 @@ void NODE_OT_group_ungroup(wmOperatorType *ot)
|
||||
/** \name Separate Operator
|
||||
* \{ */
|
||||
|
||||
static void remap_pairing(bNodeTree &dst_tree, const Set<bNode *> &nodes)
|
||||
{
|
||||
for (bNode *dst_node : nodes) {
|
||||
if (dst_node->type == GEO_NODE_SIMULATION_INPUT) {
|
||||
NodeGeometrySimulationInput &data = *static_cast<NodeGeometrySimulationInput *>(
|
||||
dst_node->storage);
|
||||
/* XXX Technically this is not correct because the output_node_id is only valid
|
||||
* in the original node group tree and we'd have map old IDs to new nodes first.
|
||||
* The ungroup operator does not build a node map, it just expects node IDs to
|
||||
* remain unchanged, which is probably true most of the time because they are
|
||||
* mostly random numbers out of the uint32_t range. */
|
||||
if (const bNode *output_node = dst_tree.node_by_id(data.output_node_id)) {
|
||||
data.output_node_id = output_node->identifier;
|
||||
}
|
||||
else {
|
||||
data.output_node_id = 0;
|
||||
blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \return True if successful.
|
||||
*/
|
||||
@@ -472,28 +479,31 @@ static bool node_group_separate_selected(
|
||||
|
||||
ListBase anim_basepaths = {nullptr, nullptr};
|
||||
|
||||
Map<bNode *, bNode *> node_map;
|
||||
Map<const bNodeSocket *, bNodeSocket *> socket_map;
|
||||
Map<int32_t, int32_t> node_identifier_map;
|
||||
|
||||
/* Add selected nodes into the ntree, ignoring interface nodes. */
|
||||
VectorSet<bNode *> nodes_to_move = get_selected_nodes(ngroup);
|
||||
nodes_to_move.remove_if(
|
||||
[](const bNode *node) { return node->is_group_input() || node->is_group_output(); });
|
||||
|
||||
Set<bNode *> new_nodes;
|
||||
|
||||
for (bNode *node : nodes_to_move) {
|
||||
bNode *newnode;
|
||||
if (make_copy) {
|
||||
newnode = bke::node_copy_with_mapping(&ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
|
||||
new_nodes.add_new(newnode);
|
||||
node_identifier_map.add(node->identifier, newnode->identifier);
|
||||
}
|
||||
else {
|
||||
newnode = node;
|
||||
BLI_remlink(&ngroup.nodes, newnode);
|
||||
BLI_addtail(&ntree.nodes, newnode);
|
||||
const int32_t old_identifier = node->identifier;
|
||||
nodeUniqueID(&ntree, newnode);
|
||||
nodeUniqueName(&ntree, newnode);
|
||||
node_identifier_map.add(old_identifier, newnode->identifier);
|
||||
}
|
||||
node_map.add_new(node, newnode);
|
||||
|
||||
/* Keep track of this node's RNA "base" path (the part of the path identifying the node)
|
||||
* if the old node-tree has animation data which potentially covers this node. */
|
||||
@@ -525,16 +535,16 @@ static bool node_group_separate_selected(
|
||||
|
||||
/* add internal links to the ntree */
|
||||
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
|
||||
const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT));
|
||||
const bool toselect = (link->tonode && (link->tonode->flag & NODE_SELECT));
|
||||
const bool fromselect = (link->fromnode && nodes_to_move.contains(link->fromnode));
|
||||
const bool toselect = (link->tonode && nodes_to_move.contains(link->tonode));
|
||||
|
||||
if (make_copy) {
|
||||
/* make a copy of internal links */
|
||||
if (fromselect && toselect) {
|
||||
nodeAddLink(&ntree,
|
||||
ntree.node_by_id(link->fromnode->identifier),
|
||||
node_map.lookup(link->fromnode),
|
||||
socket_map.lookup(link->fromsock),
|
||||
ntree.node_by_id(link->tonode->identifier),
|
||||
node_map.lookup(link->tonode),
|
||||
socket_map.lookup(link->tosock));
|
||||
}
|
||||
}
|
||||
@@ -550,7 +560,9 @@ static bool node_group_separate_selected(
|
||||
}
|
||||
}
|
||||
|
||||
for (bNode *node : new_nodes) {
|
||||
remap_pairing(ntree, nodes_to_move, node_identifier_map);
|
||||
|
||||
for (bNode *node : node_map.values()) {
|
||||
nodeDeclarationEnsure(&ntree, node);
|
||||
}
|
||||
|
||||
@@ -566,8 +578,6 @@ static bool node_group_separate_selected(
|
||||
}
|
||||
}
|
||||
|
||||
remap_pairing(ntree, new_nodes);
|
||||
|
||||
BKE_ntree_update_tag_all(&ntree);
|
||||
if (!make_copy) {
|
||||
BKE_ntree_update_tag_all(&ngroup);
|
||||
@@ -696,7 +706,8 @@ static bool node_group_make_test_selected(bNodeTree &ntree,
|
||||
for (bNode *node : nodes_to_group) {
|
||||
const char *disabled_hint = nullptr;
|
||||
if (node->typeinfo->poll_instance &&
|
||||
!node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
|
||||
!node->typeinfo->poll_instance(node, ngroup, &disabled_hint))
|
||||
{
|
||||
if (disabled_hint) {
|
||||
BKE_reportf(&reports,
|
||||
RPT_WARNING,
|
||||
@@ -734,6 +745,36 @@ static bool node_group_make_test_selected(bNodeTree &ntree,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Check if simulation zone pairs are fully selected.
|
||||
* Simulation input or output nodes can only be grouped together with the paired node.
|
||||
*/
|
||||
for (bNode *input_node : ntree.nodes_by_type("GeometryNodeSimulationInput")) {
|
||||
const NodeGeometrySimulationInput &input_data =
|
||||
*static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
|
||||
|
||||
if (bNode *output_node = ntree.node_by_id(input_data.output_node_id)) {
|
||||
const bool input_selected = nodes_to_group.contains(input_node);
|
||||
const bool output_selected = nodes_to_group.contains(output_node);
|
||||
if (input_selected && !output_selected) {
|
||||
BKE_reportf(
|
||||
&reports,
|
||||
RPT_WARNING,
|
||||
"Can not add simulation input node '%s' to a group without its paired output '%s'",
|
||||
input_node->name,
|
||||
output_node->name);
|
||||
return false;
|
||||
}
|
||||
if (output_selected && !input_selected) {
|
||||
BKE_reportf(
|
||||
&reports,
|
||||
RPT_WARNING,
|
||||
"Can not add simulation output node '%s' to a group without its paired input '%s'",
|
||||
output_node->name,
|
||||
input_node->name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -863,6 +904,8 @@ static void node_group_make_insert_selected(const bContext &C,
|
||||
Vector<OutputLinkInfo> output_links;
|
||||
Set<bNodeLink *> internal_links_to_move;
|
||||
Set<bNodeLink *> links_to_remove;
|
||||
/* Map old to new node identifiers. */
|
||||
Map<int32_t, int32_t> node_identifier_map;
|
||||
|
||||
ntree.ensure_topology_cache();
|
||||
for (bNode *node : nodes_to_move) {
|
||||
@@ -959,11 +1002,15 @@ static void node_group_make_insert_selected(const bContext &C,
|
||||
|
||||
/* Move nodes into the group. */
|
||||
for (bNode *node : nodes_to_move) {
|
||||
const int32_t old_identifier = node->identifier;
|
||||
|
||||
BLI_remlink(&ntree.nodes, node);
|
||||
BLI_addtail(&group.nodes, node);
|
||||
nodeUniqueID(&group, node);
|
||||
nodeUniqueName(&group, node);
|
||||
|
||||
node_identifier_map.add(old_identifier, node->identifier);
|
||||
|
||||
BKE_ntree_update_tag_node_removed(&ntree);
|
||||
BKE_ntree_update_tag_node_new(&group, node);
|
||||
}
|
||||
@@ -1029,6 +1076,8 @@ static void node_group_make_insert_selected(const bContext &C,
|
||||
}
|
||||
}
|
||||
|
||||
remap_pairing(group, nodes_to_move, node_identifier_map);
|
||||
|
||||
if (group.type == NTREE_GEOMETRY) {
|
||||
bke::node_field_inferencing::update_field_inferencing(group);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user