Support for simulation zones in copy operators #106812

Merged
4 changed files with 100 additions and 14 deletions

View File

@ -3830,8 +3830,7 @@ bool nodeDeclarationEnsureOnOutdatedNode(bNodeTree *ntree, bNode *node)
if (node->typeinfo->declare_dynamic) {
BLI_assert(ntree != nullptr);
BLI_assert(node != nullptr);
node->runtime->declaration = new blender::nodes::NodeDeclaration();
blender::nodes::build_node_declaration_dynamic(*ntree, *node, *node->runtime->declaration);
blender::nodes::update_node_declaration_and_sockets(*ntree, *node);
return true;
}
if (node->typeinfo->declare) {

View File

@ -16,6 +16,8 @@
#include "ED_render.h"
#include "ED_screen.h"
#include "NOD_socket.h"
#include "RNA_access.h"
#include "RNA_define.h"
@ -182,6 +184,33 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot)
/** \name Paste
* \{ */
static void remap_pairing(bNodeTree &dst_tree, const Map<const bNode *, bNode *> &node_map)
{
/* We don't have the old tree for looking up output nodes by ID,
* so have to build a map first to find copied output nodes in the new tree. */
Map<int32_t, bNode *> dst_output_node_map;
LukasTonne marked this conversation as resolved Outdated

Use int32_t instead of uint32_t for node identifiers.

Use `int32_t` instead of `uint32_t` for node identifiers.

Oh, somehow thought the identifiers were uint32_t, my bad.

Oh, somehow thought the identifiers were `uint32_t`, my bad.
for (const auto &item : node_map.items()) {
if (item.key->type == GEO_NODE_SIMULATION_OUTPUT) {
dst_output_node_map.add_new(item.key->identifier, item.value);
}
}
for (bNode *dst_node : node_map.values()) {
if (dst_node->type == GEO_NODE_SIMULATION_INPUT) {
NodeGeometrySimulationInput *data = static_cast<NodeGeometrySimulationInput *>(
dst_node->storage);
const bNode *dst_output_node = dst_output_node_map.lookup_default(data->output_node_id, nullptr);
if (dst_output_node != nullptr) {
data->output_node_id = dst_output_node->identifier;
}
else {
data->output_node_id = 0;
blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node);
}
}
}
}
static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
SpaceNode &snode = *CTX_wm_space_node(C);
@ -237,10 +266,6 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
}
for (bNode *new_node : node_map.values()) {

Changing socket layout too early causes the socket_map to become invalid. So this section has been moved down beyond the link remapping section.

This section was originally in node_copy_with_mapping (see f36dd0660983). It was moved out of the primary node duplication loop so that the simulation input could build its declaration based on the copy of the output node.

Same applies in "Duplicate" and "Group Separate" operators below.

Changing socket layout too early causes the `socket_map` to become invalid. So this section has been moved down beyond the link remapping section. This section was originally in `node_copy_with_mapping` (see [f36dd0660983](https://projects.blender.org/blender/blender/commit/f36dd066098311c6ec77ca144e5bb1b2bca252c1)). It was moved out of the primary node duplication loop so that the simulation input could build its declaration based on the copy of the output node. Same applies in "Duplicate" and "Group Separate" operators below.
nodeDeclarationEnsure(&tree, new_node);
}
for (bNode *new_node : node_map.values()) {
nodeSetSelected(new_node, true);
@ -289,6 +314,12 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
}
for (bNode *new_node : node_map.values()) {
nodeDeclarationEnsure(&tree, new_node);
}
remap_pairing(tree, node_map);
tree.ensure_topology_cache();
for (bNode *new_node : node_map.values()) {
/* Update multi input socket indices in case all connected nodes weren't copied. */

View File

@ -65,6 +65,7 @@
#include "NOD_composite.h"
#include "NOD_geometry.h"
#include "NOD_shader.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
#include "node_intern.hh" /* own include */
@ -1248,6 +1249,33 @@ static void node_duplicate_reparent_recursive(bNodeTree *ntree,
}
}
static void remap_pairing(bNodeTree &dst_tree, const Map<bNode *, bNode *> &node_map)
{
/* We don't have the old tree for looking up output nodes by ID,
* so have to build a map first to find copied output nodes in the new tree. */
Map<uint32_t, bNode *> dst_output_node_map;
for (const auto &item : node_map.items()) {
if (item.key->type == GEO_NODE_SIMULATION_OUTPUT) {
dst_output_node_map.add_new(item.key->identifier, item.value);
}
}
for (bNode *dst_node : node_map.values()) {
if (dst_node->type == GEO_NODE_SIMULATION_INPUT) {
NodeGeometrySimulationInput *data = static_cast<NodeGeometrySimulationInput *>(
dst_node->storage);
const bNode *dst_output_node = dst_output_node_map.lookup_default(data->output_node_id, nullptr);
if (dst_output_node != nullptr) {
data->output_node_id = dst_output_node->identifier;
}
else {
data->output_node_id = 0;
blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node);
}
}
}
}
static int node_duplicate_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@ -1285,10 +1313,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
for (bNode *node : node_map.values()) {
nodeDeclarationEnsure(ntree, node);
}
/* Copy links between selected nodes. */
bNodeLink *lastlink = (bNodeLink *)ntree->links.last;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
@ -1324,6 +1348,10 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
}
for (bNode *node : node_map.values()) {
nodeDeclarationEnsure(ntree, node);
}
/* Clear flags for recursive depth-first iteration. */
for (bNode *node : ntree->all_nodes()) {
node->flag &= ~NODE_TEST;
@ -1335,6 +1363,8 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
}
remap_pairing(*ntree, node_map);
/* Deselect old nodes, select the copies instead. */
for (const auto item : node_map.items()) {
bNode *src_node = item.key;

View File

@ -436,6 +436,30 @@ 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
Review

Based on the comment it sounds like the proper solution would be to build the node map. Is there a reason for not just doing that instead of using a solution you already know might not work?

Based on the comment it sounds like the proper solution would be to build the node map. Is there a reason for not just doing that instead of using a solution you already know might not work?
Review

I'd consider that a separate task, to fix the group separate operator. Ideally would also unify all the other copy operators. It is extremely unlikely this would cause problems in practice because of the randomness of identifiers. Relying on randomness and expecting that the identifier method remains unchanged is bad of course.

I've added a new task for this #106852

I'd consider that a separate task, to fix the group separate operator. Ideally would also unify all the other copy operators. It is extremely unlikely this would cause problems in practice because of the randomness of identifiers. Relying on randomness and expecting that the identifier method remains unchanged is bad of course. I've added a new task for this #106852
* remain unchanged, which is probably true most of the time because they are
* mostly random numbers out of the uint32_t range.
*/
const bNode *dst_output_node = dst_tree.node_by_id(data->output_node_id);
if (dst_output_node != nullptr) {
data->output_node_id = dst_output_node->identifier;
}
else {
data->output_node_id = 0;
blender::nodes::update_node_declaration_and_sockets(dst_tree, *dst_node);
}
}
}
}
/**
* \return True if successful.
*/
@ -497,10 +521,6 @@ static bool node_group_separate_selected(
nodeRebuildIDVector(&ngroup);
}
for (bNode *node : new_nodes) {
nodeDeclarationEnsure(&ntree, node);
}
/* add internal links to the ntree */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT));
@ -528,6 +548,10 @@ static bool node_group_separate_selected(
}
}
for (bNode *node : new_nodes) {
nodeDeclarationEnsure(&ntree, node);
}
/* and copy across the animation,
* note that the animation data's action can be nullptr here */
if (ngroup.adt) {
@ -540,6 +564,8 @@ 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);