[Bf-blender-cvs] [fa3ca9afdb6] master: Nodes: Rewrite group creation operator

Hans Goudey noreply at git.blender.org
Wed Dec 21 19:29:08 CET 2022


Commit: fa3ca9afdb6f1ea1b709226af3f085133e5ddc2f
Author: Hans Goudey
Date:   Wed Dec 21 12:26:09 2022 -0600
Branches: master
https://developer.blender.org/rBfa3ca9afdb6f1ea1b709226af3f085133e5ddc2f

Nodes: Rewrite group creation operator

Separate the "insert nodes into group" operation into more distinct
phases. This helps to clarify what is actually happening, to avoid
redundant updates to group nodes every time a new socket is discovered,
and to make use of the topology cache to avoid the "accidentally
quadratic" alrogithms that we have slowly been removing from node
editing.

The change is motivated by the desire to use dynamic node declarations
for group nodes and group input/output nodes, where it is helpful to
avoid updating the declaration and sockets multiple times.

===================================================================

M	source/blender/blenkernel/intern/node.cc
M	source/blender/editors/space_node/node_group.cc
M	source/blender/nodes/NOD_socket.h
M	source/blender/nodes/intern/node_socket.cc

===================================================================

diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 9488d3a40ef..9fc2cbde1d5 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -2369,6 +2369,8 @@ bNodeLink *nodeAddLink(
 {
   BLI_assert(fromnode);
   BLI_assert(tonode);
+  BLI_assert(ntree->all_nodes().contains(fromnode));
+  BLI_assert(ntree->all_nodes().contains(tonode));
 
   bNodeLink *link = nullptr;
   if (fromsock->in_out == SOCK_OUT && tosock->in_out == SOCK_IN) {
diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc
index 6fb8ff54988..7666502b447 100644
--- a/source/blender/editors/space_node/node_group.cc
+++ b/source/blender/editors/space_node/node_group.cc
@@ -16,6 +16,7 @@
 #include "BLI_listbase.h"
 #include "BLI_map.hh"
 #include "BLI_math_vec_types.hh"
+#include "BLI_set.hh"
 #include "BLI_string.h"
 #include "BLI_vector.hh"
 
@@ -726,71 +727,53 @@ static void get_min_max_of_nodes(const Span<bNode *> nodes,
 }
 
 /**
- * Redirect a link that are connecting a non-selected node to selected one.
- * Create new socket or reuse an existing one that was connected from the same input.
- * The output sockets of group nodes usually have consciously given names so they have
- * precedence over socket names the link points to.
- *
- * \param ntree: The node tree that the node group is being created from.
- * \param ngroup: The node tree of the new node group.
- * \param gnode: The new group node in the original tree.
- * \param input_node: The input node of the new node group.
- * \param link: The incoming link that needs to be altered.
- * \param reusable_sockets: Map for input socket interface lookup.
+ * Skip reroute nodes when finding the the socket to use as an example for a new group interface
+ * item. This moves "inward" into nodes selected for grouping to find properties like whether a
+ * connected socket has a hidden value. It only works in trivial situations-- a single line of
+ * connected reroutes with no branching.
  */
-static void node_group_make_redirect_incoming_link(
-    bNodeTree &ntree,
-    bNodeTree *ngroup,
-    bNode *gnode,
-    bNode *input_node,
-    bNodeLink *link,
-    Map<bNodeSocket *, bNodeSocket *> &reusable_sockets)
+static const bNodeSocket &find_socket_to_use_for_interface(const bNodeTree &node_tree,
+                                                           const bNodeSocket &socket)
 {
-  bNodeSocket *input_socket = reusable_sockets.lookup_default(link->fromsock, nullptr);
-  if (input_socket) {
-    /* The incoming link is from a socket that has already been linked to
-     * a socket interface of the input node.
-     * Change the source of the link to the previously created socket interface.
-     * Move the link into the node tree of the new group. */
-    link->fromnode = input_node;
-    link->fromsock = input_socket;
-    BLI_remlink(&ntree.links, link);
-    BLI_addtail(&ngroup->links, link);
+  if (node_tree.has_available_link_cycle()) {
+    return socket;
   }
-  else {
-    bNode *node_for_typeinfo = nullptr;
-    bNodeSocket *socket_for_typeinfo = nullptr;
-    /* Find a socket where typeinfo and name may come from. */
-    node_socket_skip_reroutes(
-        &ntree.links, link->tonode, link->tosock, &node_for_typeinfo, &socket_for_typeinfo);
-    bNodeSocket *socket_for_naming = socket_for_typeinfo;
-
-    /* Use the name of group node output sockets. */
-    if (ELEM(link->fromnode->type, NODE_GROUP_INPUT, NODE_GROUP, NODE_CUSTOM_GROUP)) {
-      socket_for_naming = link->fromsock;
-    }
-
-    bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocketWithName(ngroup,
-                                                                    node_for_typeinfo,
-                                                                    socket_for_typeinfo,
-                                                                    socket_for_naming->idname,
-                                                                    socket_for_naming->name);
-
-    /* Update the group node and interface sockets so the new interface socket can be linked. */
-    node_group_update(&ntree, gnode);
-    node_group_input_update(ngroup, input_node);
-
-    /* Create new internal link. */
-    bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
-    nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
+  const bNode &node = socket.owner_node();
+  if (!node.is_reroute()) {
+    return socket;
+  }
+  const bNodeSocket &other_socket = socket.in_out == SOCK_IN ? node.output_socket(0) :
+                                                               node.input_socket(0);
+  if (!other_socket.is_logically_linked()) {
+    return socket;
+  }
+  return *other_socket.logically_linked_sockets().first();
+}
 
-    /* Redirect external link. */
-    link->tonode = gnode;
-    link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
+/**
+ * The output sockets of group nodes usually have consciously given names so they have
+ * precedence over socket names the link points to.
+ */
+static bool prefer_node_for_interface_name(const bNode &node)
+{
+  return node.is_group() || node.is_group_input() || node.is_group_output();
+}
 
-    /* Remember which interface socket the link has been redirected to. */
-    reusable_sockets.add_new(link->fromsock, input_sock);
-  }
+static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree,
+                                              bNodeTree &tree_for_interface,
+                                              const bNodeSocket &socket)
+{
+  /* The "example socket" has to have the same `in_out` status as the new interface socket. */
+  const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket);
+  const bNode &node_for_io = socket_for_io.owner_node();
+  const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ?
+                                           socket :
+                                           socket_for_io;
+  return ntreeAddSocketInterfaceFromSocketWithName(&tree_for_interface,
+                                                   &node_for_io,
+                                                   &socket_for_io,
+                                                   socket_for_io.idname,
+                                                   socket_for_name.name);
 }
 
 static void node_group_make_insert_selected(const bContext &C,
@@ -799,170 +782,160 @@ static void node_group_make_insert_selected(const bContext &C,
                                             const VectorSet<bNode *> &nodes_to_move)
 {
   Main *bmain = CTX_data_main(&C);
-  bNodeTree *ngroup = (bNodeTree *)gnode->id;
+  bNodeTree &group = *reinterpret_cast<bNodeTree *>(gnode->id);
   BLI_assert(!nodes_to_move.contains(gnode));
 
-  /* XXX rough guess, not nice but we don't have access to UI constants here ... */
-  static const float offsetx = 200;
-  static const float offsety = 0.0f;
-
-  node_deselect_all(*ngroup);
+  node_deselect_all(group);
 
-  /* auto-add interface for "solo" nodes */
-  const bool expose_visible = nodes_to_move.size() == 1;
-
-  float2 center, min, max;
+  float2 min, max;
   get_min_max_of_nodes(nodes_to_move, false, min, max);
-  add_v2_v2v2(center, min, max);
-  mul_v2_fl(center, 0.5f);
+  const float2 center = math::midpoint(min, max);
 
   float2 real_min, real_max;
   get_min_max_of_nodes(nodes_to_move, true, real_min, real_max);
 
-  ListBase anim_basepaths = {nullptr, nullptr};
-
-  /* Detach unselected nodes inside frames when the frame is put into the group. Otherwise the
-   * `parent` pointer becomes dangling. */
-  for (bNode *node : ntree.all_nodes()) {
-    if (node->parent == nullptr) {
-      continue;
+  /* Reuse an existing output node or create a new one. */
+  group.ensure_topology_cache();
+  bNode *output_node = [&]() {
+    if (bNode *node = group.group_output_node()) {
+      return node;
     }
-    if (nodes_to_move.contains(node->parent) && !nodes_to_move.contains(node)) {
-      nodeDetachNode(&ntree, node);
-    }
-  }
+    bNode *output_node = nodeAddStaticNode(&C, &group, NODE_GROUP_OUTPUT);
+    output_node->locx = real_max[0] - center[0] + 50.0f;
+    return output_node;
+  }();
+
+  /* Create new group input node for easier organization of the new nodes inside the group. */
+  bNode *input_node = nodeAddStaticNode(&C, &group, NODE_GROUP_INPUT);
+  input_node->locx = real_min[0] - center[0] - 200.0f;
+
+  struct InputSocketInfo {
+    /* The unselected node the original link came from. */
+    bNode *from_node;
+    /* All the links that came from the socket on the unselected node. */
+    Vector<bNodeLink *> links;
+    const bNodeSocket *interface_socket;
+  };
+
+  struct OutputLinkInfo {
+    bNodeLink *link;
+    const bNodeSocket *interface_socket;
+  };
+
+  /* Map from single non-selected output sockets to potentially many selected input sockets. */
+  Map<bNodeSocket *, InputSocketInfo> input_links;
+  Vector<OutputLinkInfo> output_links;
+  Set<bNodeLink *> internal_links_to_move;
+  Set<bNodeLink *> links_to_remove;
 
-  /* move nodes over */
+  ntree.ensure_topology_cache();
   for (bNode *node : nodes_to_move) {
-    /* Keep track of this node's RNA "base" path (the part of the pat identifying the node)
-     * if the old node-tree has animation data which potentially covers this node. */
-    if (ntree.adt) {
-      PointerRNA ptr;
-      char *path;
-
-      RNA_pointer_create(&ntree.id, &RNA_Node, node, &ptr);
-      path = RNA_path_from_ID_to_struct(&ptr);
-
-      if (path) {
-        BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
+    for (bNodeSocket *input_socket : node->input_sockets()) {
+      for (bNodeLink *link : input_socket->directly_linked_links()) {
+        if (nodeLinkIsHidden(link)) {
+          links_to_remove.add(link);
+          continue;
+        }
+        if (nodes_to_move.contains(link->fromnode)) {
+          internal_links_to_move.add(link);
+        }
+        else {
+          InputSocketInfo &info = input_links.lookup_or_add_d

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list