Blender  V3.3
node_group.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2005 Blender Foundation. All rights reserved. */
3 
8 #include <cstdlib>
9 
10 #include "MEM_guardedalloc.h"
11 
12 #include "DNA_anim_types.h"
13 #include "DNA_node_types.h"
14 
15 #include "BLI_linklist.h"
16 #include "BLI_listbase.h"
17 #include "BLI_math_vec_types.hh"
18 #include "BLI_string.h"
19 #include "BLI_vector.hh"
20 
21 #include "BLT_translation.h"
22 
23 #include "BKE_action.h"
24 #include "BKE_animsys.h"
25 #include "BKE_context.h"
26 #include "BKE_lib_id.h"
27 #include "BKE_main.h"
28 #include "BKE_node_tree_update.h"
29 #include "BKE_report.h"
30 
31 #include "DEG_depsgraph_build.h"
32 
33 #include "ED_node.h" /* own include */
34 #include "ED_render.h"
35 #include "ED_screen.h"
36 
37 #include "RNA_access.h"
38 #include "RNA_define.h"
39 #include "RNA_path.h"
40 #include "RNA_prototypes.h"
41 
42 #include "WM_api.h"
43 #include "WM_types.h"
44 
45 #include "UI_resources.h"
46 
47 #include "NOD_common.h"
48 #include "NOD_socket.h"
49 #include "node_intern.hh" /* own include */
50 
51 namespace blender::ed::space_node {
52 
53 /* -------------------------------------------------------------------- */
58 {
60  SpaceNode *snode = CTX_wm_space_node(C);
61 
62  /* Group operators only defined for standard node tree types.
63  * Disabled otherwise to allow python-nodes define their own operators
64  * with same key-map. */
65  if (STR_ELEM(snode->tree_idname,
66  "ShaderNodeTree",
67  "CompositorNodeTree",
68  "TextureNodeTree",
69  "GeometryNodeTree")) {
70  return true;
71  }
72  }
73  return false;
74 }
75 
77 {
79  SpaceNode *snode = CTX_wm_space_node(C);
80 
81  /* Group operators only defined for standard node tree types.
82  * Disabled otherwise to allow python-nodes define their own operators
83  * with same key-map. */
84  if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
85  ED_node_is_geometry(snode)) {
86  return true;
87  }
88  }
89  return false;
90 }
91 
92 static const char *group_ntree_idname(bContext *C)
93 {
94  SpaceNode *snode = CTX_wm_space_node(C);
95  return snode->tree_idname;
96 }
97 
98 const char *node_group_idname(bContext *C)
99 {
100  SpaceNode *snode = CTX_wm_space_node(C);
101 
102  if (ED_node_is_shader(snode)) {
103  return "ShaderNodeGroup";
104  }
105  if (ED_node_is_compositor(snode)) {
106  return "CompositorNodeGroup";
107  }
108  if (ED_node_is_texture(snode)) {
109  return "TextureNodeGroup";
110  }
111  if (ED_node_is_geometry(snode)) {
112  return "GeometryNodeGroup";
113  }
114 
115  return "";
116 }
117 
118 static bNode *node_group_get_active(bContext *C, const char *node_idname)
119 {
120  SpaceNode *snode = CTX_wm_space_node(C);
121  bNode *node = nodeGetActive(snode->edittree);
122 
123  if (node && STREQ(node->idname, node_idname)) {
124  return node;
125  }
126  return nullptr;
127 }
128 
131 /* -------------------------------------------------------------------- */
136 {
137  SpaceNode *snode = CTX_wm_space_node(C);
138  const char *node_idname = node_group_idname(C);
139  const bool exit = RNA_boolean_get(op->ptr, "exit");
140 
142 
143  bNode *gnode = node_group_get_active(C, node_idname);
144 
145  if (gnode && !exit) {
146  bNodeTree *ngroup = (bNodeTree *)gnode->id;
147 
148  if (ngroup) {
149  ED_node_tree_push(snode, ngroup, gnode);
150  }
151  }
152  else {
153  ED_node_tree_pop(snode);
154  }
155 
157 
158  return OPERATOR_FINISHED;
159 }
160 
162 {
163  /* identifiers */
164  ot->name = "Edit Group";
165  ot->description = "Edit node group";
166  ot->idname = "NODE_OT_group_edit";
167 
168  /* api callbacks */
171 
172  /* flags */
174 
175  PropertyRNA *prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "");
177 }
178 
181 /* -------------------------------------------------------------------- */
190  const char *dst_basepath)
191 {
193  sizeof(*basepath_change), AT);
194  basepath_change->src_basepath = src_basepath;
195  basepath_change->dst_basepath = dst_basepath;
196  return basepath_change;
197 }
198 
200 {
201  if (basepath_change->src_basepath != basepath_change->dst_basepath) {
202  MEM_freeN((void *)basepath_change->src_basepath);
203  }
204  MEM_freeN((void *)basepath_change->dst_basepath);
205  MEM_freeN(basepath_change);
206 }
207 
211 static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
212 {
213  ListBase anim_basepaths = {nullptr, nullptr};
214  Vector<bNode *> nodes_delayed_free;
215  const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(gnode->id);
216 
217  /* `wgroup` is a temporary copy of the #NodeTree we're merging in
218  * - All of wgroup's nodes are copied across to their new home.
219  * - `ngroup` (i.e. the source NodeTree) is left unscathed.
220  * - Temp copy. do change ID user-count for the copies.
221  */
222  bNodeTree *wgroup = ntreeCopyTree(bmain, ngroup);
223 
224  /* Add the nodes into the `ntree`. */
225  LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
226  /* Remove interface nodes.
227  * This also removes remaining links to and from interface nodes.
228  */
230  /* We must delay removal since sockets will reference this node. see: T52092 */
231  nodes_delayed_free.append(node);
232  }
233 
234  /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
235  * if the old node-tree has animation data which potentially covers this node. */
236  const char *old_animation_basepath = nullptr;
237  if (wgroup->adt) {
238  PointerRNA ptr;
239  RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr);
240  old_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
241  }
242 
243  /* migrate node */
244  BLI_remlink(&wgroup->nodes, node);
247 
248  /* ensure unique node name in the node tree */
250 
251  if (wgroup->adt) {
252  PointerRNA ptr;
253  RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
254  const char *new_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
255  BLI_addtail(&anim_basepaths,
256  animation_basepath_change_new(old_animation_basepath, new_animation_basepath));
257  }
258 
259  if (!node->parent) {
260  node->locx += gnode->locx;
261  node->locy += gnode->locy;
262  }
263 
264  node->flag |= NODE_SELECT;
265  }
266 
267  bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
268 
269  /* Add internal links to the ntree */
270  LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
271  BLI_remlink(&wgroup->links, link);
272  BLI_addtail(&ntree->links, link);
274  }
275 
276  bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
277 
278  /* and copy across the animation,
279  * note that the animation data's action can be nullptr here */
280  if (wgroup->adt) {
281  bAction *waction;
282 
283  /* firstly, wgroup needs to temporary dummy action
284  * that can be destroyed, as it shares copies */
285  waction = wgroup->adt->action = (bAction *)BKE_id_copy(bmain, &wgroup->adt->action->id);
286 
287  /* now perform the moving */
288  BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths);
289 
290  /* paths + their wrappers need to be freed */
291  LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
292  animation_basepath_change_free(basepath_change);
293  }
294 
295  /* free temp action too */
296  if (waction) {
297  BKE_id_free(bmain, waction);
298  wgroup->adt->action = nullptr;
299  }
300  }
301 
302  /* free the group tree (takes care of user count) */
303  BKE_id_free(bmain, wgroup);
304 
305  /* restore external links to and from the gnode */
306 
307  /* input links */
308  if (glinks_first != nullptr) {
309  for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
310  if (link->fromnode->type == NODE_GROUP_INPUT) {
311  const char *identifier = link->fromsock->identifier;
312  int num_external_links = 0;
313 
314  /* find external links to this input */
315  for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
316  tlink = tlink->next) {
317  if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
318  nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
319  num_external_links++;
320  }
321  }
322 
323  /* if group output is not externally linked,
324  * convert the constant input value to ensure somewhat consistent behavior */
325  if (num_external_links == 0) {
326  /* TODO */
327 #if 0
328  bNodeSocket *sock = node_group_find_input_socket(gnode, identifier);
329  BLI_assert(sock);
330 
331  nodeSocketCopy(
332  ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode);
333 #endif
334  }
335  }
336  }
337 
338  /* Also iterate over new links to cover passthrough links. */
339  glinks_last = (bNodeLink *)ntree->links.last;
340 
341  /* output links */
342  for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
343  link = link->next) {
344  if (link->fromnode == gnode) {
345  const char *identifier = link->fromsock->identifier;
346  int num_internal_links = 0;
347 
348  /* find internal links to this output */
349  for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next;
350  tlink = tlink->next) {
351  /* only use active output node */
352  if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) {
353  if (STREQ(tlink->tosock->identifier, identifier)) {
354  nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode, link->tosock);
355  num_internal_links++;
356  }
357  }
358  }
359 
360  /* if group output is not internally linked,
361  * convert the constant output value to ensure somewhat consistent behavior */
362  if (num_internal_links == 0) {
363  /* TODO */
364 #if 0
365  bNodeSocket *sock = node_group_find_output_socket(gnode, identifier);
366  BLI_assert(sock);
367 
368  nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode);
369 #endif
370  }
371  }
372  }
373  }
374 
375  for (bNode *node : nodes_delayed_free) {
376  nodeRemoveNode(bmain, ntree, node, false);
377  }
378 
379  /* delete the group instance and dereference group tree */
380  nodeRemoveNode(bmain, ntree, gnode, true);
381 
382  return true;
383 }
384 
386 {
387  Main *bmain = CTX_data_main(C);
388  SpaceNode *snode = CTX_wm_space_node(C);
389  const char *node_idname = node_group_idname(C);
390 
392 
393  bNode *gnode = node_group_get_active(C, node_idname);
394  if (!gnode) {
395  return OPERATOR_CANCELLED;
396  }
397 
398  if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) {
400  }
401  else {
402  BKE_report(op->reports, RPT_WARNING, "Cannot ungroup");
403  return OPERATOR_CANCELLED;
404  }
405 
406  return OPERATOR_FINISHED;
407 }
408 
410 {
411  /* identifiers */
412  ot->name = "Ungroup";
413  ot->description = "Ungroup selected nodes";
414  ot->idname = "NODE_OT_group_ungroup";
415 
416  /* api callbacks */
419 
420  /* flags */
422 }
423 
426 /* -------------------------------------------------------------------- */
434  Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
435 {
436  /* deselect all nodes in the target tree */
438  nodeSetSelected(node, false);
439  }
440 
441  ListBase anim_basepaths = {nullptr, nullptr};
442 
445 
446  /* add selected nodes into the ntree */
447  LISTBASE_FOREACH_MUTABLE (bNode *, node, &ngroup.nodes) {
448  if (!(node->flag & NODE_SELECT)) {
449  continue;
450  }
451 
452  /* ignore interface nodes */
454  nodeSetSelected(node, false);
455  continue;
456  }
457 
458  bNode *newnode;
459  if (make_copy) {
460  /* make a copy */
462  &ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
463  node_map.add_new(node, newnode);
464  }
465  else {
466  /* use the existing node */
467  newnode = node;
468  }
469 
470  /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
471  * if the old node-tree has animation data which potentially covers this node. */
472  if (ngroup.adt) {
473  PointerRNA ptr;
474  char *path;
475 
476  RNA_pointer_create(&ngroup.id, &RNA_Node, newnode, &ptr);
478 
479  if (path) {
480  BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
481  }
482  }
483 
484  /* ensure valid parent pointers, detach if parent stays inside the group */
485  if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) {
486  nodeDetachNode(newnode);
487  }
488 
489  /* migrate node */
490  BLI_remlink(&ngroup.nodes, newnode);
491  BLI_addtail(&ntree.nodes, newnode);
492 
493  /* ensure unique node name in the node tree */
494  nodeUniqueName(&ntree, newnode);
495 
496  if (!newnode->parent) {
497  newnode->locx += offset.x;
498  newnode->locy += offset.y;
499  }
500  }
501 
502  /* add internal links to the ntree */
503  LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
504  const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT));
505  const bool toselect = (link->tonode && (link->tonode->flag & NODE_SELECT));
506 
507  if (make_copy) {
508  /* make a copy of internal links */
509  if (fromselect && toselect) {
511  node_map.lookup(link->fromnode),
512  socket_map.lookup(link->fromsock),
513  node_map.lookup(link->tonode),
514  socket_map.lookup(link->tosock));
515  }
516  }
517  else {
518  /* move valid links over, delete broken links */
519  if (fromselect && toselect) {
520  BLI_remlink(&ngroup.links, link);
521  BLI_addtail(&ntree.links, link);
522  }
523  else if (fromselect || toselect) {
524  nodeRemLink(&ngroup, link);
525  }
526  }
527  }
528 
529  /* and copy across the animation,
530  * note that the animation data's action can be nullptr here */
531  if (ngroup.adt) {
532  /* now perform the moving */
533  BKE_animdata_transfer_by_basepath(&bmain, &ngroup.id, &ntree.id, &anim_basepaths);
534 
535  /* paths + their wrappers need to be freed */
536  LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
537  animation_basepath_change_free(basepath_change);
538  }
539  }
540 
542  if (!make_copy) {
543  BKE_ntree_update_tag_all(&ngroup);
544  }
545 
546  return true;
547 }
548 
552 };
553 
554 /* Operator Property */
556  {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
557  {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
558  {0, nullptr, 0, nullptr, nullptr},
559 };
560 
562 {
563  Main *bmain = CTX_data_main(C);
564  SpaceNode *snode = CTX_wm_space_node(C);
565  int type = RNA_enum_get(op->ptr, "type");
566 
568 
569  /* are we inside of a group? */
570  bNodeTree *ngroup = snode->edittree;
571  bNodeTree *nparent = ED_node_tree_get(snode, 1);
572  if (!nparent) {
573  BKE_report(op->reports, RPT_WARNING, "Not inside node group");
574  return OPERATOR_CANCELLED;
575  }
576  /* get node tree offset */
577  const float2 offset = space_node_group_offset(*snode);
578 
579  switch (type) {
580  case NODE_GS_COPY:
581  if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, true)) {
582  BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
583  return OPERATOR_CANCELLED;
584  }
585  break;
586  case NODE_GS_MOVE:
587  if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, false)) {
588  BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
589  return OPERATOR_CANCELLED;
590  }
591  break;
592  }
593 
594  /* switch to parent tree */
595  ED_node_tree_pop(snode);
596 
598 
599  return OPERATOR_FINISHED;
600 }
601 
603  wmOperator *UNUSED(op),
604  const wmEvent *UNUSED(event))
605 {
607  C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
608  uiLayout *layout = UI_popup_menu_layout(pup);
609 
611  uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_COPY);
612  uiItemEnumO(layout, "NODE_OT_group_separate", nullptr, 0, "type", NODE_GS_MOVE);
613 
614  UI_popup_menu_end(C, pup);
615 
616  return OPERATOR_INTERFACE;
617 }
618 
620 {
621  /* identifiers */
622  ot->name = "Separate";
623  ot->description = "Separate selected nodes from the node group";
624  ot->idname = "NODE_OT_group_separate";
625 
626  /* api callbacks */
630 
631  /* flags */
633 
635 }
636 
639 /* -------------------------------------------------------------------- */
644 {
645  return (&node != gnode && !ELEM(node.type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT) &&
646  (node.flag & NODE_SELECT));
647 }
648 
650  bNode *gnode,
651  const char *ntree_idname,
652  struct ReportList &reports)
653 {
654  int ok = true;
655 
656  /* make a local pseudo node tree to pass to the node poll functions */
657  bNodeTree *ngroup = ntreeAddTree(nullptr, "Pseudo Node Group", ntree_idname);
658 
659  /* check poll functions for selected nodes */
661  if (node_group_make_use_node(*node, gnode)) {
662  const char *disabled_hint = nullptr;
663  if (node->typeinfo->poll_instance &&
664  !node->typeinfo->poll_instance(node, ngroup, &disabled_hint)) {
665  if (disabled_hint) {
666  BKE_reportf(&reports,
667  RPT_WARNING,
668  "Can not add node '%s' in a group:\n %s",
669  node->name,
670  disabled_hint);
671  }
672  else {
673  BKE_reportf(&reports, RPT_WARNING, "Can not add node '%s' in a group", node->name);
674  }
675  ok = false;
676  break;
677  }
678  }
679 
680  node->done = 0;
681  }
682 
683  /* free local pseudo node tree again */
684  ntreeFreeTree(ngroup);
685  MEM_freeN(ngroup);
686  if (!ok) {
687  return false;
688  }
689 
690  /* check if all connections are OK, no unselected node has both
691  * inputs and outputs to a selection */
692  LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
693  if (node_group_make_use_node(*link->fromnode, gnode)) {
694  link->tonode->done |= 1;
695  }
696  if (node_group_make_use_node(*link->tonode, gnode)) {
697  link->fromnode->done |= 2;
698  }
699  }
701  if (!(node->flag & NODE_SELECT) && node != gnode && node->done == 3) {
702  return false;
703  }
704  }
705  return true;
706 }
707 
709  bNodeTree &ntree, bNode *gnode, float2 &min, float2 &max, bool use_size)
710 {
711  int totselect = 0;
712 
713  INIT_MINMAX2(min, max);
715  if (node_group_make_use_node(*node, gnode)) {
716  float loc[2];
717  nodeToView(node, node->offsetx, node->offsety, &loc[0], &loc[1]);
718  minmax_v2v2_v2(min, max, loc);
719  if (use_size) {
720  loc[0] += node->width;
721  loc[1] -= node->height;
722  minmax_v2v2_v2(min, max, loc);
723  }
724  totselect++;
725  }
726  }
727 
728  /* sane min/max if no selected nodes */
729  if (totselect == 0) {
730  min[0] = min[1] = max[0] = max[1] = 0.0f;
731  }
732 
733  return totselect;
734 }
735 
737 {
738  Main *bmain = CTX_data_main(&C);
739  bNodeTree *ngroup = (bNodeTree *)gnode->id;
740  bool expose_visible = false;
741 
742  /* XXX rough guess, not nice but we don't have access to UI constants here ... */
743  static const float offsetx = 200;
744  static const float offsety = 0.0f;
745 
746  /* deselect all nodes in the target tree */
747  LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
748  nodeSetSelected(node, false);
749  }
750 
751  float2 center, min, max;
752  const int totselect = node_get_selected_minmax(ntree, gnode, min, max, false);
754  mul_v2_fl(center, 0.5f);
755 
756  float2 real_min, real_max;
757  node_get_selected_minmax(ntree, gnode, real_min, real_max, true);
758 
759  /* auto-add interface for "solo" nodes */
760  if (totselect == 1) {
761  expose_visible = true;
762  }
763 
764  ListBase anim_basepaths = {nullptr, nullptr};
765 
766  /* Detach unselected nodes inside frames when the frame is put into the group. Otherwise the
767  * `parent` pointer becomes dangling. */
769  if (node->parent == nullptr) {
770  continue;
771  }
772  if (node_group_make_use_node(*node->parent, gnode) &&
773  !node_group_make_use_node(*node, gnode)) {
775  }
776  }
777 
778  /* move nodes over */
780  if (node_group_make_use_node(*node, gnode)) {
781  /* Keep track of this node's RNA "base" path (the part of the pat identifying the node)
782  * if the old node-tree has animation data which potentially covers this node. */
783  if (ntree.adt) {
784  PointerRNA ptr;
785  char *path;
786 
787  RNA_pointer_create(&ntree.id, &RNA_Node, node, &ptr);
789 
790  if (path) {
791  BLI_addtail(&anim_basepaths, animation_basepath_change_new(path, path));
792  }
793  }
794 
795  /* ensure valid parent pointers, detach if parent stays outside the group */
796  if (node->parent && !(node->parent->flag & NODE_SELECT)) {
798  }
799 
800  /* change node-collection membership */
802  BLI_addtail(&ngroup->nodes, node);
805 
806  /* ensure unique node name in the ngroup */
807  nodeUniqueName(ngroup, node);
808  }
809  }
810 
811  /* move animation data over */
812  if (ntree.adt) {
813  BKE_animdata_transfer_by_basepath(bmain, &ntree.id, &ngroup->id, &anim_basepaths);
814 
815  /* paths + their wrappers need to be freed */
816  LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
817  animation_basepath_change_free(basepath_change);
818  }
819  }
820 
821  /* node groups don't use internal cached data */
822  ntreeFreeCache(ngroup);
823 
824  /* create input node */
825  bNode *input_node = nodeAddStaticNode(&C, ngroup, NODE_GROUP_INPUT);
826  input_node->locx = real_min[0] - center[0] - offsetx;
827  input_node->locy = -offsety;
828 
829  /* create output node */
830  bNode *output_node = nodeAddStaticNode(&C, ngroup, NODE_GROUP_OUTPUT);
831  output_node->locx = real_max[0] - center[0] + offsetx * 0.25f;
832  output_node->locy = -offsety;
833 
834  /* relink external sockets */
836  int fromselect = node_group_make_use_node(*link->fromnode, gnode);
837  int toselect = node_group_make_use_node(*link->tonode, gnode);
838 
839  if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) {
840  /* remove all links to/from the gnode.
841  * this can remove link information, but there's no general way to preserve it.
842  */
843  nodeRemLink(&ntree, link);
844  }
845  else if (toselect && !fromselect) {
846  /* Remove hidden links to not create unconnected sockets in the interface. */
847  if (nodeLinkIsHidden(link)) {
848  nodeRemLink(&ntree, link);
849  continue;
850  }
851 
852  bNodeSocket *link_sock;
853  bNode *link_node;
854  node_socket_skip_reroutes(&ntree.links, link->tonode, link->tosock, &link_node, &link_sock);
855  bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock);
856 
857  /* update the group node and interface node sockets,
858  * so the new interface socket can be linked.
859  */
860  node_group_update(&ntree, gnode);
861  node_group_input_update(ngroup, input_node);
862 
863  /* create new internal link */
864  bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
865  nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock);
866 
867  /* redirect external link */
868  link->tonode = gnode;
869  link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
870  }
871  else if (fromselect && !toselect) {
872  /* Remove hidden links to not create unconnected sockets in the interface. */
873  if (nodeLinkIsHidden(link)) {
874  nodeRemLink(&ntree, link);
875  continue;
876  }
877 
878  /* First check whether the source of this link is already connected to an output.
879  * If yes, reuse that output instead of duplicating it. */
880  bool connected = false;
881  LISTBASE_FOREACH (bNodeLink *, olink, &ngroup->links) {
882  if (olink->fromsock == link->fromsock && olink->tonode == output_node) {
883  bNodeSocket *output_sock = node_group_find_output_socket(gnode,
884  olink->tosock->identifier);
885  link->fromnode = gnode;
886  link->fromsock = output_sock;
887  connected = true;
888  }
889  }
890 
891  if (!connected) {
892  bNodeSocket *link_sock;
893  bNode *link_node;
895  &ntree.links, link->fromnode, link->fromsock, &link_node, &link_sock);
896  bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link_node, link_sock);
897 
898  /* update the group node and interface node sockets,
899  * so the new interface socket can be linked.
900  */
901  node_group_update(&ntree, gnode);
902  node_group_output_update(ngroup, output_node);
903 
904  /* create new internal link */
905  bNodeSocket *output_sock = node_group_output_find_socket(output_node, iosock->identifier);
906  nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock);
907 
908  /* redirect external link */
909  link->fromnode = gnode;
910  link->fromsock = node_group_find_output_socket(gnode, iosock->identifier);
911  }
912  }
913  }
914 
915  /* move internal links */
917  int fromselect = node_group_make_use_node(*link->fromnode, gnode);
918  int toselect = node_group_make_use_node(*link->tonode, gnode);
919 
920  if (fromselect && toselect) {
921  BLI_remlink(&ntree.links, link);
922  BLI_addtail(&ngroup->links, link);
923  }
924  }
925 
926  /* move nodes in the group to the center */
927  LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
928  if (node_group_make_use_node(*node, gnode) && !node->parent) {
929  node->locx -= center[0];
930  node->locy -= center[1];
931  }
932  }
933 
934  /* Expose all unlinked sockets too but only the visible ones. */
935  if (expose_visible) {
936  LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
937  if (node_group_make_use_node(*node, gnode)) {
938  LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
939  bool skip = false;
940  LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) {
941  if (link->tosock == sock) {
942  skip = true;
943  break;
944  }
945  }
946  if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) {
947  skip = true;
948  }
949  if (skip) {
950  continue;
951  }
952 
953  bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
954 
955  node_group_input_update(ngroup, input_node);
956 
957  /* create new internal link */
958  bNodeSocket *input_sock = node_group_input_find_socket(input_node, iosock->identifier);
959  nodeAddLink(ngroup, input_node, input_sock, node, sock);
960  }
961 
962  LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
963  bool skip = false;
964  LISTBASE_FOREACH (bNodeLink *, link, &ngroup->links) {
965  if (link->fromsock == sock) {
966  skip = true;
967  }
968  }
969  if (sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) {
970  skip = true;
971  }
972  if (skip) {
973  continue;
974  }
975 
976  bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock);
977 
978  node_group_output_update(ngroup, output_node);
979 
980  /* create new internal link */
981  bNodeSocket *output_sock = node_group_output_find_socket(output_node,
982  iosock->identifier);
983  nodeAddLink(ngroup, node, sock, output_node, output_sock);
984  }
985  }
986  }
987  }
988 }
989 
991  bNodeTree &ntree,
992  const char *ntype,
993  const char *ntreetype)
994 {
995  Main *bmain = CTX_data_main(&C);
996 
997  float2 min, max;
998  const int totselect = node_get_selected_minmax(ntree, nullptr, min, max, false);
999  /* don't make empty group */
1000  if (totselect == 0) {
1001  return nullptr;
1002  }
1003 
1004  /* New node-tree. */
1005  bNodeTree *ngroup = ntreeAddTree(bmain, "NodeGroup", ntreetype);
1006 
1007  /* make group node */
1008  bNode *gnode = nodeAddNode(&C, &ntree, ntype);
1009  gnode->id = (ID *)ngroup;
1010 
1011  gnode->locx = 0.5f * (min[0] + max[0]);
1012  gnode->locy = 0.5f * (min[1] + max[1]);
1013 
1015 
1016  return gnode;
1017 }
1018 
1020 {
1021  SpaceNode &snode = *CTX_wm_space_node(C);
1022  bNodeTree &ntree = *snode.edittree;
1023  const char *ntree_idname = group_ntree_idname(C);
1024  const char *node_idname = node_group_idname(C);
1025  Main *bmain = CTX_data_main(C);
1026 
1028 
1029  if (!node_group_make_test_selected(ntree, nullptr, ntree_idname, *op->reports)) {
1030  return OPERATOR_CANCELLED;
1031  }
1032 
1033  bNode *gnode = node_group_make_from_selected(*C, ntree, node_idname, ntree_idname);
1034 
1035  if (gnode) {
1036  bNodeTree *ngroup = (bNodeTree *)gnode->id;
1037 
1038  nodeSetActive(&ntree, gnode);
1039  if (ngroup) {
1040  ED_node_tree_push(&snode, ngroup, gnode);
1041  LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
1042  sort_multi_input_socket_links(snode, *node, nullptr, nullptr);
1043  }
1044  }
1045  }
1046 
1047  ED_node_tree_propagate_change(C, bmain, nullptr);
1048 
1050 
1051  /* We broke relations in node tree, need to rebuild them in the graphs. */
1052  DEG_relations_tag_update(bmain);
1053 
1054  return OPERATOR_FINISHED;
1055 }
1056 
1058 {
1059  /* identifiers */
1060  ot->name = "Make Group";
1061  ot->description = "Make group from selected nodes";
1062  ot->idname = "NODE_OT_group_make";
1063 
1064  /* api callbacks */
1067 
1068  /* flags */
1070 }
1071 
1074 /* -------------------------------------------------------------------- */
1079 {
1080  SpaceNode *snode = CTX_wm_space_node(C);
1081  bNodeTree *ntree = snode->edittree;
1082  const char *node_idname = node_group_idname(C);
1083  Main *bmain = CTX_data_main(C);
1084 
1086 
1087  bNode *gnode = node_group_get_active(C, node_idname);
1088 
1089  if (!gnode || !gnode->id) {
1090  return OPERATOR_CANCELLED;
1091  }
1092 
1093  bNodeTree *ngroup = (bNodeTree *)gnode->id;
1094  if (!node_group_make_test_selected(*ntree, gnode, ngroup->idname, *op->reports)) {
1095  return OPERATOR_CANCELLED;
1096  }
1097 
1099 
1100  nodeSetActive(ntree, gnode);
1101  ED_node_tree_push(snode, ngroup, gnode);
1102  ED_node_tree_propagate_change(C, bmain, nullptr);
1103 
1104  return OPERATOR_FINISHED;
1105 }
1106 
1108 {
1109  /* identifiers */
1110  ot->name = "Group Insert";
1111  ot->description = "Insert selected nodes into a node group";
1112  ot->idname = "NODE_OT_group_insert";
1113 
1114  /* api callbacks */
1117 
1118  /* flags */
1120 }
1121 
1124 } // namespace blender::ed::space_node
Blender kernel action and pose functionality.
void BKE_animdata_transfer_by_basepath(struct Main *bmain, struct ID *srcID, struct ID *dstID, struct ListBase *basepaths)
Definition: anim_data.c:601
struct SpaceNode * CTX_wm_space_node(const bContext *C)
Definition: context.c:878
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct ID * BKE_id_copy(struct Main *bmain, const struct ID *id)
@ LIB_ID_COPY_DEFAULT
Definition: BKE_lib_id.h:181
void BKE_id_free(struct Main *bmain, void *idv)
void nodeUniqueName(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:2127
bool nodeLinkIsHidden(const struct bNodeLink *link)
void ntreeFreeCache(struct bNodeTree *ntree)
Definition: node.cc:3130
void ntreeFreeTree(struct bNodeTree *ntree)
Definition: node.cc:3106
struct bNode * nodeGetActive(struct bNodeTree *ntree)
Definition: node.cc:3601
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link)
Definition: node.cc:2338
struct bNodeSocket * ntreeAddSocketInterfaceFromSocket(struct bNodeTree *ntree, struct bNode *from_node, struct bNodeSocket *from_sock)
Definition: node.cc:3382
void nodeRemoveNode(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node, bool do_id_user)
Definition: node.cc:3011
struct bNodeLink * nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock)
Definition: node.cc:2296
struct bNodeTree * ntreeCopyTree(struct Main *bmain, const struct bNodeTree *ntree)
struct bNode * nodeAddNode(const struct bContext *C, struct bNodeTree *ntree, const char *idname)
Definition: node.cc:2133
void nodeSetSelected(struct bNode *node, bool select)
Definition: node.cc:3615
struct bNodeTree * ntreeAddTree(struct Main *bmain, const char *name, const char *idname)
Definition: node.cc:2674
struct bNode * nodeAddStaticNode(const struct bContext *C, struct bNodeTree *ntree, int type)
Definition: node.cc:2151
#define NODE_GROUP_INPUT
Definition: BKE_node.h:987
void nodeToView(const struct bNode *node, float x, float y, float *rx, float *ry)
void nodeDetachNode(struct bNode *node)
Definition: node.cc:2607
void nodeSetActive(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:3644
void BKE_ntree_update_tag_all(struct bNodeTree *ntree)
void BKE_ntree_update_tag_node_new(struct bNodeTree *ntree, struct bNode *node)
void BKE_ntree_update_tag_node_removed(struct bNodeTree *ntree)
void BKE_ntree_update_tag_link_added(struct bNodeTree *ntree, struct bNodeLink *link)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
MINLINE void mul_v2_fl(float r[2], float f)
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
Definition: math_vector.c:890
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
#define STR_ELEM(...)
Definition: BLI_string.h:539
#define INIT_MINMAX2(min, max)
#define UNUSED(x)
#define ELEM(...)
#define AT
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
void DEG_relations_tag_update(struct Main *bmain)
#define NODE_DO_OUTPUT
@ SOCK_HIDDEN
@ SOCK_UNAVAIL
#define NODE_SELECT
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
bool ED_node_is_compositor(struct SpaceNode *snode)
Definition: node_edit.cc:466
bool ED_node_is_texture(struct SpaceNode *snode)
Definition: node_edit.cc:476
void ED_node_tree_push(struct SpaceNode *snode, struct bNodeTree *ntree, struct bNode *gnode)
Definition: space_node.cc:83
bool ED_node_is_geometry(struct SpaceNode *snode)
Definition: node_edit.cc:481
bool ED_node_is_shader(struct SpaceNode *snode)
Definition: node_edit.cc:471
struct bNodeTree * ED_node_tree_get(struct SpaceNode *snode, int level)
Definition: space_node.cc:145
void ED_node_tree_propagate_change(const struct bContext *C, struct Main *bmain, struct bNodeTree *ntree)
void ED_node_tree_pop(struct SpaceNode *snode)
Definition: space_node.cc:118
void ED_preview_kill_jobs(struct wmWindowManager *wm, struct Main *bmain)
bool ED_operator_node_active(struct bContext *C)
Definition: screen_ops.c:307
bool ED_operator_node_editable(struct bContext *C)
Definition: screen_ops.c:318
NSNotificationCenter * center
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
NODE_GROUP_OUTPUT
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
#define C
Definition: RandGen.cpp:25
struct uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int icon, const char *propname, int value)
void UI_popup_menu_end(struct bContext *C, struct uiPopupMenu *pup)
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
uiPopupMenu * UI_popup_menu_begin(struct bContext *C, const char *title, int icon) ATTR_NONNULL()
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define NC_NODE
Definition: WM_types.h:344
#define NC_SCENE
Definition: WM_types.h:328
#define NA_ADDED
Definition: WM_types.h:525
#define ND_NODES
Definition: WM_types.h:384
@ WM_OP_EXEC_DEFAULT
Definition: WM_types.h:208
const Value & lookup(const Key &key) const
Definition: BLI_map.hh:485
void add_new(const Key &key, const Value &value)
Definition: BLI_map.hh:220
void append(const T &value)
Definition: BLI_vector.hh:433
OperationNode * node
bNodeTree * ntree
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
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)
Definition: node.cc:2198
static int node_group_make_exec(bContext *C, wmOperator *op)
Definition: node_group.cc:1019
static bool node_group_operator_active_poll(bContext *C)
Definition: node_group.cc:57
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode)
Definition: node_group.cc:736
static int node_group_insert_exec(bContext *C, wmOperator *op)
Definition: node_group.cc:1078
static int node_group_ungroup_exec(bContext *C, wmOperator *op)
Definition: node_group.cc:385
static int node_group_separate_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
Definition: node_group.cc:602
void NODE_OT_group_make(wmOperatorType *ot)
Definition: node_group.cc:1057
static const EnumPropertyItem node_group_separate_types[]
Definition: node_group.cc:555
void NODE_OT_group_insert(wmOperatorType *ot)
Definition: node_group.cc:1107
static bool node_group_separate_selected(Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
Definition: node_group.cc:433
static int node_group_edit_exec(bContext *C, wmOperator *op)
Definition: node_group.cc:135
static bool node_group_make_test_selected(bNodeTree &ntree, bNode *gnode, const char *ntree_idname, struct ReportList &reports)
Definition: node_group.cc:649
static bNode * node_group_make_from_selected(const bContext &C, bNodeTree &ntree, const char *ntype, const char *ntreetype)
Definition: node_group.cc:990
static bool node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
Definition: node_group.cc:211
static int node_get_selected_minmax(bNodeTree &ntree, bNode *gnode, float2 &min, float2 &max, bool use_size)
Definition: node_group.cc:708
static bNode * node_group_get_active(bContext *C, const char *node_idname)
Definition: node_group.cc:118
const char * node_group_idname(bContext *C)
Definition: node_group.cc:98
static bool node_group_operator_editable(bContext *C)
Definition: node_group.cc:76
float2 space_node_group_offset(const SpaceNode &snode)
Definition: space_node.cc:207
void NODE_OT_group_edit(wmOperatorType *ot)
Definition: node_group.cc:161
static const char * group_ntree_idname(bContext *C)
Definition: node_group.cc:92
void NODE_OT_group_separate(wmOperatorType *ot)
Definition: node_group.cc:619
void sort_multi_input_socket_links(SpaceNode &snode, bNode &node, bNodeLink *drag_link, const float2 *cursor)
static int node_group_separate_exec(bContext *C, wmOperator *op)
Definition: node_group.cc:561
void NODE_OT_group_ungroup(wmOperatorType *ot)
Definition: node_group.cc:409
static bool node_group_make_use_node(bNode &node, bNode *gnode)
Definition: node_group.cc:643
static AnimationBasePathChange * animation_basepath_change_new(const char *src_basepath, const char *dst_basepath)
Definition: node_group.cc:189
static void animation_basepath_change_free(AnimationBasePathChange *basepath_change)
Definition: node_group.cc:199
bNodeSocket * node_group_output_find_socket(bNode *node, const char *identifier)
Definition: node_common.cc:536
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
Definition: node_common.cc:213
void node_group_input_update(bNodeTree *ntree, bNode *node)
Definition: node_common.cc:455
bNodeSocket * node_group_find_output_socket(bNode *groupnode, const char *identifier)
Definition: node_common.cc:61
bNodeSocket * node_group_input_find_socket(bNode *node, const char *identifier)
Definition: node_common.cc:444
bNodeSocket * node_group_find_input_socket(bNode *groupnode, const char *identifier)
Definition: node_common.cc:56
void node_group_output_update(bNodeTree *ntree, bNode *node)
Definition: node_common.cc:547
void node_socket_skip_reroutes(ListBase *links, bNode *node, bNodeSocket *socket, bNode **r_node, bNodeSocket **r_socket)
Definition: node_socket.cc:486
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
Definition: rna_access.c:136
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
char * RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition: rna_path.cc:981
#define min(a, b)
Definition: sort.c:35
bAction * action
const char * dst_basepath
Definition: BKE_animsys.h:198
const char * src_basepath
Definition: BKE_animsys.h:197
Definition: DNA_ID.h:368
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
char tree_idname[64]
struct bNodeTree * edittree
char identifier[64]
char idname[64]
ListBase nodes
ListBase links
struct AnimData * adt
float locy
struct ID * id
struct bNode * parent
float locx
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
struct ReportList * reports
struct PointerRNA * ptr
float max
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition: wm_files.c:3480
wmOperatorType * ot
Definition: wm_files.c:3479