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:
Lukas Tönne
2023-05-10 17:56:23 +02:00
parent 939f7e8010
commit 3913d22757

View File

@@ -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);
}