Nodes: Refactor to remove node and socket "new" pointers

These pointers point to the new nodes when duplicating,
and their even used to point to "original" nodes for
"localized" trees. They're just a bad design decision
that make code confusing and buggy.

Instead, node copy functions now optionally add to a map
of old to new socket pointers. The case where the compositor
abused these pointers as "original" pointers are handled
by looking up the string node names.

Differential Revision: https://developer.blender.org/D13518
This commit is contained in:
Hans Goudey 2021-12-22 08:47:46 -06:00
parent d6224db8f1
commit fdc4a1a590
Notes: blender-bot 2023-08-16 13:00:30 +02:00
Referenced by commit 81b3933abb, Fix T94357: Node Ungroup operator copies current node tree
Referenced by issue #94357, Unable to Ungroup group nodes
Referenced by issue #111166, Regression: Blender crashes when turning on a pass that has equally named AOV pass already
8 changed files with 191 additions and 286 deletions

View File

@ -34,6 +34,7 @@
#include "RNA_types.h"
#ifdef __cplusplus
# include "BLI_map.hh"
# include "BLI_string_ref.hh"
#endif
@ -518,8 +519,6 @@ void ntreeSetOutput(struct bNodeTree *ntree);
void ntreeFreeCache(struct bNodeTree *ntree);
bool ntreeNodeExists(const struct bNodeTree *ntree, const struct bNode *testnode);
bool ntreeOutputExists(const struct bNode *node, const struct bNodeSocket *testsock);
void ntreeNodeFlagSet(const bNodeTree *ntree, const int flag, const bool enable);
/**
* Returns localized tree for execution in threads.
@ -697,31 +696,27 @@ void nodeRemoveNode(struct Main *bmain,
struct bNode *node,
bool do_id_user);
/**
* \param ntree: is the target tree.
*
* \note keep socket list order identical, for copying links.
* \note `unique_name` needs to be true. It's only disabled for speed when doing GPUnodetrees.
*/
struct bNode *BKE_node_copy_ex(struct bNodeTree *ntree,
const struct bNode *node_src,
const int flag,
const bool unique_name);
#ifdef __cplusplus
namespace blender::bke {
/**
* Same as #BKE_node_copy_ex but stores pointers to a new node and its sockets in the source node.
*
* NOTE: DANGER ZONE!
*
* TODO(sergey): Maybe it's better to make BKE_node_copy_ex() return a mapping from old node and
* sockets to new one.
* \note keeps socket list order identical, for copying links.
* \note `unique_name` needs to be true. It's only disabled for speed when doing GPUnodetrees.
*/
struct bNode *BKE_node_copy_store_new_pointers(struct bNodeTree *ntree,
struct bNode *node_src,
const int flag);
struct bNodeTree *ntreeCopyTree_ex_new_pointers(const struct bNodeTree *ntree,
struct Main *bmain,
const bool do_id_user);
bNode *node_copy_with_mapping(bNodeTree *dst_tree,
const bNode &node_src,
int flag,
bool unique_name,
Map<const bNodeSocket *, bNodeSocket *> &new_socket_map);
bNode *node_copy(bNodeTree *dst_tree, const bNode &src_node, int flag, bool unique_name);
} // namespace blender::bke
#endif
bNode *BKE_node_copy(bNodeTree *dst_tree, const bNode *src_node, int flag, bool unique_name);
/**
* Also used via RNA API, so we check for proper input output direction.

View File

@ -99,6 +99,7 @@
#define NODE_DEFAULT_MAX_WIDTH 700
using blender::Array;
using blender::Map;
using blender::MutableSpan;
using blender::Set;
using blender::Span;
@ -152,62 +153,41 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
BLI_listbase_clear(&ntree_dst->nodes);
BLI_listbase_clear(&ntree_dst->links);
/* Since source nodes and sockets are unique pointers we can put everything in a single map. */
GHash *new_pointers = BLI_ghash_ptr_new(__func__);
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
LISTBASE_FOREACH (const bNode *, node_src, &ntree_src->nodes) {
bNode *new_node = BKE_node_copy_ex(ntree_dst, node_src, flag_subdata, true);
BLI_ghash_insert(new_pointers, (void *)node_src, new_node);
/* Store mapping to inputs. */
bNodeSocket *new_input_sock = (bNodeSocket *)new_node->inputs.first;
const bNodeSocket *input_sock_src = (const bNodeSocket *)node_src->inputs.first;
while (new_input_sock != nullptr) {
BLI_ghash_insert(new_pointers, (void *)input_sock_src, new_input_sock);
new_input_sock = new_input_sock->next;
input_sock_src = input_sock_src->next;
}
/* Store mapping to outputs. */
bNodeSocket *new_output_sock = (bNodeSocket *)new_node->outputs.first;
const bNodeSocket *output_sock_src = (const bNodeSocket *)node_src->outputs.first;
while (new_output_sock != nullptr) {
BLI_ghash_insert(new_pointers, (void *)output_sock_src, new_output_sock);
new_output_sock = new_output_sock->next;
output_sock_src = output_sock_src->next;
}
BLI_listbase_clear(&ntree_dst->nodes);
LISTBASE_FOREACH (const bNode *, src_node, &ntree_src->nodes) {
bNode *new_node = blender::bke::node_copy_with_mapping(
ntree_dst, *src_node, flag_subdata, true, socket_map);
node_map.add(src_node, new_node);
}
/* copy links */
BLI_duplicatelist(&ntree_dst->links, &ntree_src->links);
LISTBASE_FOREACH (bNodeLink *, link_dst, &ntree_dst->links) {
link_dst->fromnode = (bNode *)BLI_ghash_lookup_default(
new_pointers, link_dst->fromnode, nullptr);
link_dst->fromsock = (bNodeSocket *)BLI_ghash_lookup_default(
new_pointers, link_dst->fromsock, nullptr);
link_dst->tonode = (bNode *)BLI_ghash_lookup_default(new_pointers, link_dst->tonode, nullptr);
link_dst->tosock = (bNodeSocket *)BLI_ghash_lookup_default(
new_pointers, link_dst->tosock, nullptr);
/* update the link socket's pointer */
if (link_dst->tosock) {
link_dst->tosock->link = link_dst;
}
BLI_listbase_clear(&ntree_dst->links);
LISTBASE_FOREACH (const bNodeLink *, src_link, &ntree_src->links) {
bNodeLink *dst_link = (bNodeLink *)MEM_dupallocN(src_link);
dst_link->fromnode = node_map.lookup(src_link->fromnode);
dst_link->fromsock = socket_map.lookup(src_link->fromsock);
dst_link->tonode = node_map.lookup(src_link->tonode);
dst_link->tosock = socket_map.lookup(src_link->tosock);
BLI_assert(dst_link->tosock);
dst_link->tosock->link = dst_link;
BLI_addtail(&ntree_dst->links, dst_link);
}
/* copy interface sockets */
BLI_duplicatelist(&ntree_dst->inputs, &ntree_src->inputs);
bNodeSocket *sock_dst, *sock_src;
for (sock_dst = (bNodeSocket *)ntree_dst->inputs.first,
sock_src = (bNodeSocket *)ntree_src->inputs.first;
sock_dst != nullptr;
sock_dst = (bNodeSocket *)sock_dst->next, sock_src = (bNodeSocket *)sock_src->next) {
node_socket_copy(sock_dst, sock_src, flag_subdata);
BLI_listbase_clear(&ntree_dst->inputs);
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->inputs) {
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
node_socket_copy(dst_socket, src_socket, flag_subdata);
BLI_addtail(&ntree_dst->inputs, dst_socket);
}
BLI_duplicatelist(&ntree_dst->outputs, &ntree_src->outputs);
for (sock_dst = (bNodeSocket *)ntree_dst->outputs.first,
sock_src = (bNodeSocket *)ntree_src->outputs.first;
sock_dst != nullptr;
sock_dst = (bNodeSocket *)sock_dst->next, sock_src = (bNodeSocket *)sock_src->next) {
node_socket_copy(sock_dst, sock_src, flag_subdata);
BLI_listbase_clear(&ntree_dst->outputs);
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->outputs) {
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
node_socket_copy(dst_socket, src_socket, flag_subdata);
BLI_addtail(&ntree_dst->outputs, dst_socket);
}
/* copy preview hash */
@ -227,18 +207,11 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
}
/* update node->parent pointers */
for (bNode *node_dst = (bNode *)ntree_dst->nodes.first,
*node_src = (bNode *)ntree_src->nodes.first;
node_dst;
node_dst = (bNode *)node_dst->next, node_src = (bNode *)node_src->next) {
if (node_dst->parent) {
node_dst->parent = (bNode *)BLI_ghash_lookup_default(
new_pointers, node_dst->parent, nullptr);
LISTBASE_FOREACH (bNode *, new_node, &ntree_dst->nodes) {
if (new_node->parent) {
new_node->parent = node_map.lookup(new_node->parent);
}
}
BLI_ghash_free(new_pointers, nullptr, nullptr);
/* node tree will generate its own interface type */
ntree_dst->interface_type = nullptr;
@ -2238,136 +2211,100 @@ static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src,
sock_dst->cache = nullptr;
}
bNode *BKE_node_copy_ex(bNodeTree *ntree,
const bNode *node_src,
const int flag,
const bool unique_name)
{
bNode *node_dst = (bNode *)MEM_callocN(sizeof(bNode), "dupli node");
bNodeSocket *sock_dst, *sock_src;
bNodeLink *link_dst, *link_src;
namespace blender::bke {
*node_dst = *node_src;
bNode *node_copy_with_mapping(bNodeTree *dst_tree,
const bNode &node_src,
const int flag,
const bool unique_name,
Map<const bNodeSocket *, bNodeSocket *> &socket_map)
{
bNode *node_dst = (bNode *)MEM_mallocN(sizeof(bNode), __func__);
*node_dst = node_src;
/* Can be called for nodes outside a node tree (e.g. clipboard). */
if (ntree) {
if (dst_tree) {
if (unique_name) {
nodeUniqueName(ntree, node_dst);
nodeUniqueName(dst_tree, node_dst);
}
BLI_addtail(&ntree->nodes, node_dst);
BLI_addtail(&dst_tree->nodes, node_dst);
}
BLI_duplicatelist(&node_dst->inputs, &node_src->inputs);
for (sock_dst = (bNodeSocket *)node_dst->inputs.first,
sock_src = (bNodeSocket *)node_src->inputs.first;
sock_dst != nullptr;
sock_dst = (bNodeSocket *)sock_dst->next, sock_src = (bNodeSocket *)sock_src->next) {
node_socket_copy(sock_dst, sock_src, flag);
BLI_listbase_clear(&node_dst->inputs);
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &node_src.inputs) {
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
node_socket_copy(dst_socket, src_socket, flag);
BLI_addtail(&node_dst->inputs, dst_socket);
socket_map.add_new(src_socket, dst_socket);
}
BLI_duplicatelist(&node_dst->outputs, &node_src->outputs);
for (sock_dst = (bNodeSocket *)node_dst->outputs.first,
sock_src = (bNodeSocket *)node_src->outputs.first;
sock_dst != nullptr;
sock_dst = (bNodeSocket *)sock_dst->next, sock_src = (bNodeSocket *)sock_src->next) {
node_socket_copy(sock_dst, sock_src, flag);
BLI_listbase_clear(&node_dst->outputs);
LISTBASE_FOREACH (const bNodeSocket *, src_socket, &node_src.outputs) {
bNodeSocket *dst_socket = (bNodeSocket *)MEM_dupallocN(src_socket);
node_socket_copy(dst_socket, src_socket, flag);
BLI_addtail(&node_dst->outputs, dst_socket);
socket_map.add_new(src_socket, dst_socket);
}
if (node_src->prop) {
node_dst->prop = IDP_CopyProperty_ex(node_src->prop, flag);
if (node_src.prop) {
node_dst->prop = IDP_CopyProperty_ex(node_src.prop, flag);
}
BLI_duplicatelist(&node_dst->internal_links, &node_src->internal_links);
for (link_dst = (bNodeLink *)node_dst->internal_links.first,
link_src = (bNodeLink *)node_src->internal_links.first;
link_dst != nullptr;
link_dst = (bNodeLink *)link_dst->next, link_src = (bNodeLink *)link_src->next) {
/* This is a bit annoying to do index lookups in a list, but is likely to be faster than
* trying to create a hash-map. At least for usual nodes, which only have so much sockets
* and internal links. */
const int from_sock_index = BLI_findindex(&node_src->inputs, link_src->fromsock);
const int to_sock_index = BLI_findindex(&node_src->outputs, link_src->tosock);
BLI_assert(from_sock_index != -1);
BLI_assert(to_sock_index != -1);
link_dst->fromnode = node_dst;
link_dst->tonode = node_dst;
link_dst->fromsock = (bNodeSocket *)BLI_findlink(&node_dst->inputs, from_sock_index);
link_dst->tosock = (bNodeSocket *)BLI_findlink(&node_dst->outputs, to_sock_index);
BLI_listbase_clear(&node_dst->internal_links);
LISTBASE_FOREACH (const bNodeLink *, src_link, &node_src.internal_links) {
bNodeLink *dst_link = (bNodeLink *)MEM_dupallocN(src_link);
dst_link->fromnode = node_dst;
dst_link->tonode = node_dst;
dst_link->fromsock = socket_map.lookup(src_link->fromsock);
dst_link->tosock = socket_map.lookup(src_link->tosock);
BLI_addtail(&node_dst->internal_links, dst_link);
}
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus(node_dst->id);
}
if (node_src->typeinfo->copyfunc) {
node_src->typeinfo->copyfunc(ntree, node_dst, node_src);
if (node_src.typeinfo->copyfunc) {
node_src.typeinfo->copyfunc(dst_tree, node_dst, &node_src);
}
node_dst->new_node = nullptr;
/* Only call copy function when a copy is made for the main database, not
* for cases like the dependency graph and localization. */
if (node_dst->typeinfo->copyfunc_api && !(flag & LIB_ID_CREATE_NO_MAIN)) {
PointerRNA ptr;
RNA_pointer_create((ID *)ntree, &RNA_Node, node_dst, &ptr);
RNA_pointer_create((ID *)dst_tree, &RNA_Node, node_dst, &ptr);
node_dst->typeinfo->copyfunc_api(&ptr, node_src);
node_dst->typeinfo->copyfunc_api(&ptr, &node_src);
}
if (ntree) {
BKE_ntree_update_tag_node_new(ntree, node_dst);
if (dst_tree) {
BKE_ntree_update_tag_node_new(dst_tree, node_dst);
}
/* Reset the declaration of the new node. */
node_dst->declaration = nullptr;
nodeDeclarationEnsure(ntree, node_dst);
nodeDeclarationEnsure(dst_tree, node_dst);
return node_dst;
}
static void node_set_new_pointers(bNode *node_src, bNode *new_node)
bNode *node_copy(bNodeTree *dst_tree,
const bNode &src_node,
const int flag,
const bool unique_name)
{
/* Store mapping to the node itself. */
node_src->new_node = new_node;
/* Store mapping to inputs. */
bNodeSocket *new_input_sock = (bNodeSocket *)new_node->inputs.first;
bNodeSocket *input_sock_src = (bNodeSocket *)node_src->inputs.first;
while (new_input_sock != nullptr) {
input_sock_src->new_sock = new_input_sock;
new_input_sock = new_input_sock->next;
input_sock_src = input_sock_src->next;
}
/* Store mapping to outputs. */
bNodeSocket *new_output_sock = (bNodeSocket *)new_node->outputs.first;
bNodeSocket *output_sock_src = (bNodeSocket *)node_src->outputs.first;
while (new_output_sock != nullptr) {
output_sock_src->new_sock = new_output_sock;
new_output_sock = new_output_sock->next;
output_sock_src = output_sock_src->next;
}
Map<const bNodeSocket *, bNodeSocket *> socket_map;
return node_copy_with_mapping(dst_tree, src_node, flag, unique_name, socket_map);
}
bNode *BKE_node_copy_store_new_pointers(bNodeTree *ntree, bNode *node_src, const int flag)
{
bNode *new_node = BKE_node_copy_ex(ntree, node_src, flag, true);
node_set_new_pointers(node_src, new_node);
return new_node;
}
} // namespace blender::bke
bNodeTree *ntreeCopyTree_ex_new_pointers(const bNodeTree *ntree,
Main *bmain,
const bool do_id_user)
bNode *BKE_node_copy(bNodeTree *dst_tree,
const bNode *src_node,
const int flag,
const bool unique_name)
{
bNodeTree *new_ntree = ntreeCopyTree_ex(ntree, bmain, do_id_user);
bNode *new_node = (bNode *)new_ntree->nodes.first;
bNode *node_src = (bNode *)ntree->nodes.first;
while (new_node != nullptr) {
node_set_new_pointers(node_src, new_node);
new_node = new_node->next;
node_src = node_src->next;
}
return new_ntree;
return blender::bke::node_copy(dst_tree, *src_node, flag, unique_name);
}
static int node_count_links(const bNodeTree *ntree, const bNodeSocket *socket)
@ -3354,26 +3291,6 @@ bNodeTree *ntreeFromID(ID *id)
return (nodetree != nullptr) ? *nodetree : nullptr;
}
bool ntreeNodeExists(const bNodeTree *ntree, const bNode *testnode)
{
LISTBASE_FOREACH (const bNode *, node, &ntree->nodes) {
if (node == testnode) {
return true;
}
}
return false;
}
bool ntreeOutputExists(const bNode *node, const bNodeSocket *testsock)
{
LISTBASE_FOREACH (const bNodeSocket *, sock, &node->outputs) {
if (sock == testsock) {
return true;
}
}
return false;
}
void ntreeNodeFlagSet(const bNodeTree *ntree, const int flag, const bool enable)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {

View File

@ -580,7 +580,6 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup,
gsock->type = type;
gsock->next = gsock->prev = NULL;
gsock->new_sock = NULL;
gsock->link = NULL;
/* assign new unique index */
gsock->own_index = ngroup->cur_index++;

View File

@ -79,6 +79,7 @@
#define USE_ESC_COMPO
using blender::float2;
using blender::Map;
/* ***************** composite job manager ********************** */
@ -1249,7 +1250,8 @@ bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
/* ****************** Duplicate *********************** */
static void node_duplicate_reparent_recursive(bNode *node)
static void node_duplicate_reparent_recursive(const Map<const bNode *, bNode *> &node_map,
bNode *node)
{
bNode *parent;
@ -1259,15 +1261,15 @@ static void node_duplicate_reparent_recursive(bNode *node)
for (parent = node->parent; parent; parent = parent->parent) {
if (parent->flag & SELECT) {
if (!(parent->flag & NODE_TEST)) {
node_duplicate_reparent_recursive(parent);
node_duplicate_reparent_recursive(node_map, parent);
}
break;
}
}
/* reparent node copy to parent copy */
if (parent) {
nodeDetachNode(node->new_node);
nodeAttachNode(node->new_node, parent->new_node);
nodeDetachNode(node_map.lookup(node));
nodeAttachNode(node_map.lookup(node), node_map.lookup(parent));
}
}
@ -1280,10 +1282,15 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
bNode *lastnode = (bNode *)ntree->nodes.last;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
bNode *new_node = blender::bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
}
/* make sure we don't copy new nodes again! */
@ -1292,8 +1299,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
}
/* Copy links between selected nodes.
* NOTE: this depends on correct node->new_node and sock->new_sock pointers from above copy! */
/* Copy links between selected nodes. */
bNodeLink *lastlink = (bNodeLink *)ntree->links.last;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes.
@ -1303,11 +1309,11 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
(keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) {
bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
newlink->tonode = node_map.lookup(link->tonode);
newlink->tosock = socket_map.lookup(link->tosock);
if (link->fromnode && (link->fromnode->flag & NODE_SELECT)) {
newlink->fromnode = link->fromnode->new_node;
newlink->fromsock = link->fromsock->new_sock;
newlink->fromnode = node_map.lookup(link->fromnode);
newlink->fromsock = socket_map.lookup(link->fromsock);
}
else {
/* input node not copied, this keeps the original input linked */
@ -1331,7 +1337,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
/* reparent copied nodes */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) {
node_duplicate_reparent_recursive(node);
node_duplicate_reparent_recursive(node_map, node);
}
/* only has to check old nodes */
@ -1344,7 +1350,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
/* has been set during copy above */
bNode *newnode = node->new_node;
bNode *newnode = node_map.lookup(node);
nodeSetSelected(node, false);
node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
@ -2094,47 +2100,48 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
BKE_node_clipboard_clear();
BKE_node_clipboard_init(ntree);
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
bNode *new_node = BKE_node_copy_store_new_pointers(
nullptr, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN);
BKE_node_clipboard_add_node(new_node);
bNode *new_node = blender::bke::node_copy_with_mapping(nullptr,
*node,
LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_MAIN,
false,
socket_map);
node_map.add_new(node, new_node);
}
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
bNode *new_node = node->new_node;
for (bNode *new_node : node_map.values()) {
BKE_node_clipboard_add_node(new_node);
/* ensure valid pointers */
if (new_node->parent) {
/* parent pointer must be redirected to new node or detached if parent is
* not copied */
if (new_node->parent->flag & NODE_SELECT) {
new_node->parent = new_node->parent->new_node;
}
else {
nodeDetachNode(new_node);
}
/* Parent pointer must be redirected to new node or detached if parent is not copied. */
if (new_node->parent) {
if (node_map.contains(new_node->parent)) {
new_node->parent = node_map.lookup(new_node->parent);
}
else {
nodeDetachNode(new_node);
}
}
}
/* Copy links between selected nodes.
* NOTE: this depends on correct node->new_node and sock->new_sock pointers from above copy! */
/* Copy links between selected nodes. */
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes. */
if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode &&
(link->fromnode->flag & NODE_SELECT)) {
bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "bNodeLink");
BLI_assert(link->tonode);
BLI_assert(link->fromnode);
if (link->tonode->flag & NODE_SELECT && link->fromnode->flag & NODE_SELECT) {
bNodeLink *newlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), __func__);
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
newlink->fromnode = link->fromnode->new_node;
newlink->fromsock = link->fromsock->new_sock;
newlink->tonode = node_map.lookup(link->tonode);
newlink->tosock = socket_map.lookup(link->tosock);
newlink->fromnode = node_map.lookup(link->fromnode);
newlink->fromsock = socket_map.lookup(link->fromsock);
BKE_node_clipboard_add_link(newlink);
}
@ -2229,28 +2236,34 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
mul_v2_fl(center, 1.0 / num_nodes);
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
/* copy nodes from clipboard */
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
/* pasted nodes are selected */
nodeSetSelected(new_node, true);
bNode *new_node = blender::bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
}
/* reparent copied nodes */
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = node->new_node;
for (bNode *new_node : node_map.values()) {
/* pasted nodes are selected */
nodeSetSelected(new_node, true);
/* The parent pointer must be redirected to new node. */
if (new_node->parent) {
new_node->parent = new_node->parent->new_node;
if (node_map.contains(new_node->parent)) {
new_node->parent = node_map.lookup(new_node->parent);
}
}
}
LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) {
nodeAddLink(ntree,
link->fromnode->new_node,
link->fromsock->new_sock,
link->tonode->new_node,
link->tosock->new_sock);
node_map.lookup(link->fromnode),
socket_map.lookup(link->fromsock),
node_map.lookup(link->tonode),
socket_map.lookup(link->tosock));
}
Main *bmain = CTX_data_main(C);

View File

@ -62,6 +62,7 @@
#include "node_intern.hh" /* own include */
using blender::float2;
using blender::Map;
/* -------------------------------------------------------------------- */
/** \name Local Utilities
@ -220,21 +221,15 @@ static void animation_basepath_change_free(AnimationBasePathChange *basepath_cha
/* returns 1 if its OK */
static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
{
/* Clear new pointers, set in #ntreeCopyTree_ex_new_pointers. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->new_node = nullptr;
}
ListBase anim_basepaths = {nullptr, nullptr};
LinkNode *nodes_delayed_free = nullptr;
bNodeTree *ngroup = (bNodeTree *)gnode->id;
/* wgroup is a temporary copy of the NodeTree we're merging in
* - all of wgroup's nodes are copied across to their new home
* - ngroup (i.e. the source NodeTree) is left unscathed
* - temp copy. do change ID usercount for the copies
*/
bNodeTree *wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, true);
bNodeTree *wgroup = ntreeCopyTree(bmain, ntree);
/* Add the nodes into the ntree */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
@ -455,13 +450,11 @@ static bool node_group_separate_selected(
nodeSetSelected(node, false);
}
/* clear new pointers, set in BKE_node_copy_ex(). */
LISTBASE_FOREACH (bNode *, node, &ngroup.nodes) {
node->new_node = nullptr;
}
ListBase anim_basepaths = {nullptr, nullptr};
Map<const bNode *, bNode *> node_map;
Map<const bNodeSocket *, bNodeSocket *> socket_map;
/* add selected nodes into the ntree */
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup.nodes) {
if (!(node->flag & NODE_SELECT)) {
@ -477,7 +470,9 @@ static bool node_group_separate_selected(
bNode *newnode;
if (make_copy) {
/* make a copy */
newnode = BKE_node_copy_store_new_pointers(&ngroup, node, LIB_ID_COPY_DEFAULT);
newnode = blender::bke::node_copy_with_mapping(
&ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, newnode);
}
else {
/* use the existing node */
@ -526,10 +521,10 @@ static bool node_group_separate_selected(
/* make a copy of internal links */
if (fromselect && toselect) {
nodeAddLink(&ntree,
link->fromnode->new_node,
link->fromsock->new_sock,
link->tonode->new_node,
link->tosock->new_sock);
node_map.lookup(link->fromnode),
socket_map.lookup(link->fromsock),
node_map.lookup(link->tonode),
socket_map.lookup(link->tosock));
}
}
else {

View File

@ -95,7 +95,7 @@ typedef struct SocketDeclarationHandle SocketDeclarationHandle;
#endif
typedef struct bNodeSocket {
struct bNodeSocket *next, *prev, *new_sock;
struct bNodeSocket *next, *prev;
/** User-defined properties. */
IDProperty *prop;
@ -245,7 +245,7 @@ typedef enum eNodeSocketFlag {
/** TODO: Limit data in #bNode to what we want to see saved. */
typedef struct bNode {
struct bNode *next, *prev, *new_node;
struct bNode *next, *prev;
/** User-defined properties. */
IDProperty *prop;

View File

@ -118,19 +118,6 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree)
}
}
bNodeSocket *output_sock = (bNodeSocket *)node->outputs.first;
bNodeSocket *local_output_sock = (bNodeSocket *)local_node->outputs.first;
while (output_sock != nullptr) {
local_output_sock->cache = output_sock->cache;
output_sock->cache = nullptr;
/* This is actually link to original: someone was just lazy enough and tried to save few
* bytes in the cost of readability. */
local_output_sock->new_sock = output_sock;
output_sock = output_sock->next;
local_output_sock = local_output_sock->next;
}
node = node->next;
local_node = local_node->next;
}
@ -150,11 +137,11 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
BKE_node_preview_merge_tree(ntree, localtree, true);
for (lnode = (bNode *)localtree->nodes.first; lnode; lnode = lnode->next) {
if (ntreeNodeExists(ntree, lnode->new_node)) {
if (bNode *orig_node = nodeFindNodebyName(ntree, lnode->name)) {
if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) {
/* image_merge does sanity check for pointers */
BKE_image_merge(bmain, (Image *)lnode->new_node->id, (Image *)lnode->id);
BKE_image_merge(bmain, (Image *)orig_node->id, (Image *)lnode->id);
}
}
else if (lnode->type == CMP_NODE_MOVIEDISTORTION) {
@ -162,20 +149,19 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
* and to achieve much better performance on further calls this context should be
* copied back to original node */
if (lnode->storage) {
if (lnode->new_node->storage) {
BKE_tracking_distortion_free((MovieDistortion *)lnode->new_node->storage);
if (orig_node->storage) {
BKE_tracking_distortion_free((MovieDistortion *)orig_node->storage);
}
lnode->new_node->storage = BKE_tracking_distortion_copy(
(MovieDistortion *)lnode->storage);
orig_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)lnode->storage);
}
}
for (lsock = (bNodeSocket *)lnode->outputs.first; lsock; lsock = lsock->next) {
if (ntreeOutputExists(lnode->new_node, lsock->new_sock)) {
lsock->new_sock->cache = lsock->cache;
if (bNodeSocket *orig_socket = nodeFindSocket(orig_node, SOCK_OUT, lsock->identifier)) {
orig_socket->cache = lsock->cache;
lsock->cache = nullptr;
lsock->new_sock = nullptr;
orig_socket = nullptr;
}
}
}

View File

@ -659,7 +659,7 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree,
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->tmp_flag >= 0) {
int id = node->tmp_flag;
nodes_copy[id] = BKE_node_copy_ex(
nodes_copy[id] = BKE_node_copy(
ntree, node, LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN, false);
nodes_copy[id]->tmp_flag = -2; /* Copy */
/* Make sure to clear all sockets links as they are invalid. */