Blender V4.5
node_group.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_anim_types.h"
14#include "DNA_node_types.h"
15
16#include "BLI_listbase.h"
17#include "BLI_map.hh"
18#include "BLI_math_vector.h"
19#include "BLI_math_vector.hh"
21#include "BLI_rand.hh"
22#include "BLI_set.hh"
23#include "BLI_string.h"
24#include "BLI_vector.hh"
25
26#include "BLT_translation.hh"
27
28#include "BKE_action.hh"
29#include "BKE_animsys.h"
30#include "BKE_context.hh"
31#include "BKE_lib_id.hh"
32#include "BKE_library.hh"
33#include "BKE_main.hh"
35#include "BKE_node_runtime.hh"
37#include "BKE_report.hh"
38
39#include "ANIM_action.hh"
40
42
43#include "ED_node.hh"
44#include "ED_node_preview.hh"
45#include "ED_render.hh"
46#include "ED_screen.hh"
47
48#include "RNA_access.hh"
49#include "RNA_define.hh"
50#include "RNA_path.hh"
51#include "RNA_prototypes.hh"
52
53#include "WM_api.hh"
54#include "WM_types.hh"
55
56#include "UI_resources.hh"
57
58#include "NOD_common.hh"
59#include "NOD_composite.hh"
60#include "NOD_geometry.hh"
62#include "NOD_shader.h"
63#include "NOD_socket.hh"
64#include "NOD_texture.h"
65
66#include "node_intern.hh" /* own include */
67
69
70/* -------------------------------------------------------------------- */
73
75{
78
79 /* Group operators only defined for standard node tree types.
80 * Disabled otherwise to allow python-nodes define their own operators
81 * with same key-map. */
82 if (STR_ELEM(snode->tree_idname,
83 "ShaderNodeTree",
84 "CompositorNodeTree",
85 "TextureNodeTree",
86 "GeometryNodeTree"))
87 {
88 return true;
89 }
90 }
91 return false;
92}
93
95{
98
99 /* Group operators only defined for standard node tree types.
100 * Disabled otherwise to allow python-nodes define their own operators
101 * with same key-map. */
102 if (ED_node_is_shader(snode) || ED_node_is_compositor(snode) || ED_node_is_texture(snode) ||
103 ED_node_is_geometry(snode))
104 {
105 return true;
106 }
107 }
108 return false;
109}
110
112{
113 SpaceNode *snode = CTX_wm_space_node(C);
114 return snode->tree_idname;
115}
116
118{
119 SpaceNode *snode = CTX_wm_space_node(C);
120
121 if (ED_node_is_shader(snode)) {
122 return ntreeType_Shader->group_idname;
123 }
124 if (ED_node_is_compositor(snode)) {
125 return ntreeType_Composite->group_idname;
126 }
127 if (ED_node_is_texture(snode)) {
128 return ntreeType_Texture->group_idname;
129 }
130 if (ED_node_is_geometry(snode)) {
131 return ntreeType_Geometry->group_idname;
132 }
133
134 return "";
135}
136
137static bNode *node_group_get_active(bContext *C, const StringRef node_idname)
138{
139 SpaceNode *snode = CTX_wm_space_node(C);
140 bNode *node = bke::node_get_active(*snode->edittree);
141
142 if (node && node->idname == node_idname) {
143 return node;
144 }
145 return nullptr;
146}
147
148/* Maps old to new identifiers for simulation input node pairing. */
149static void remap_pairing(bNodeTree &dst_tree,
151 const Map<int32_t, int32_t> &identifier_map)
152{
153 for (bNode *dst_node : nodes) {
154 if (bke::all_zone_input_node_types().contains(dst_node->type_legacy)) {
155 const bke::bNodeZoneType &zone_type = *bke::zone_type_by_node_type(dst_node->type_legacy);
156 int &output_node_id = zone_type.get_corresponding_output_id(*dst_node);
157 if (output_node_id == 0) {
158 continue;
159 }
160 output_node_id = identifier_map.lookup_default(output_node_id, 0);
161 if (output_node_id == 0) {
163 }
164 }
165 }
166}
167
169
170/* -------------------------------------------------------------------- */
173
175{
176 SpaceNode *snode = CTX_wm_space_node(C);
177 ARegion *region = CTX_wm_region(C);
178 const StringRef node_idname = node_group_idname(C);
179 const bool exit = RNA_boolean_get(op->ptr, "exit");
180
182
183 bNode *gnode = node_group_get_active(C, node_idname);
184
185 if (gnode && !exit) {
186 bNodeTree *ngroup = (bNodeTree *)gnode->id;
187
188 if (ngroup) {
189 ED_node_tree_push(region, snode, ngroup, gnode);
190 }
191 }
192 else {
193 ED_node_tree_pop(region, snode);
194 }
195
198
199 return OPERATOR_FINISHED;
200}
201
203{
204 /* identifiers */
205 ot->name = "Edit Group";
206 ot->description = "Edit node group";
207 ot->idname = "NODE_OT_group_edit";
208
209 /* API callbacks. */
210 ot->exec = node_group_edit_exec;
212
213 /* flags */
215
216 PropertyRNA *prop = RNA_def_boolean(ot->srna, "exit", false, "Exit", "");
218}
219
221
222/* -------------------------------------------------------------------- */
225
231 const StringRef dst_basepath)
232{
234 sizeof(*basepath_change), AT);
235 basepath_change->src_basepath = BLI_strdupn(src_basepath.data(), src_basepath.size());
236 basepath_change->dst_basepath = BLI_strdupn(dst_basepath.data(), dst_basepath.size());
237 return basepath_change;
238}
239
241{
242 if (basepath_change->src_basepath != basepath_change->dst_basepath) {
243 MEM_freeN(basepath_change->src_basepath);
244 }
245 MEM_freeN(basepath_change->dst_basepath);
246 MEM_freeN(basepath_change);
247}
248
250 const bNodeTree &ngroup,
251 const bNode &gnode,
252 const Map<int32_t, int32_t> &node_identifier_map)
253{
254 for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
255 if (ref.path.node_id != gnode.identifier) {
256 continue;
257 }
258 const bNestedNodeRef *child_ref = ngroup.find_nested_node_ref(ref.path.id_in_node);
259 if (!child_ref) {
260 continue;
261 }
262 constexpr int32_t missing_id = -1;
263 const int32_t new_node_id = node_identifier_map.lookup_default(child_ref->path.node_id,
264 missing_id);
265 if (new_node_id == missing_id) {
266 continue;
267 }
268 ref.path.node_id = new_node_id;
269 ref.path.id_in_node = child_ref->path.id_in_node;
270 }
271}
272
276static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
277{
278 ListBase anim_basepaths = {nullptr, nullptr};
279 Vector<bNode *> nodes_delayed_free;
280 const bNodeTree *ngroup = reinterpret_cast<const bNodeTree *>(gnode->id);
281
282 /* `wgroup` is a temporary copy of the #NodeTree we're merging in
283 * - All of wgroup's nodes are copied across to their new home.
284 * - `ngroup` (i.e. the source NodeTree) is left unscathed.
285 * - Temp copy. do change ID user-count for the copies.
286 */
287 bNodeTree *wgroup = bke::node_tree_copy_tree(bmain, *ngroup);
288
289 /* Add the nodes into the `ntree`. */
290 Vector<bNode *> new_nodes;
291 Map<int32_t, int32_t> node_identifier_map;
292 LISTBASE_FOREACH_MUTABLE (bNode *, node, &wgroup->nodes) {
293 new_nodes.append(node);
294 /* Remove interface nodes.
295 * This also removes remaining links to and from interface nodes.
296 */
297 if (node->is_group_input() || node->is_group_output()) {
298 /* We must delay removal since sockets will reference this node. see: #52092 */
299 nodes_delayed_free.append(node);
300 }
301
302 /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
303 * if the old node-tree has animation data which potentially covers this node. */
304 std::optional<std::string> old_animation_basepath;
305 if (wgroup->adt) {
306 PointerRNA ptr = RNA_pointer_create_discrete(&wgroup->id, &RNA_Node, node);
307 old_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
308 }
309
310 /* migrate node */
311 BLI_remlink(&wgroup->nodes, node);
312 BLI_addtail(&ntree->nodes, node);
313 const int32_t old_identifier = node->identifier;
314 bke::node_unique_id(*ntree, *node);
315 bke::node_unique_name(*ntree, *node);
316 node_identifier_map.add(old_identifier, node->identifier);
317
319
320 if (wgroup->adt) {
321 PointerRNA ptr = RNA_pointer_create_discrete(&ntree->id, &RNA_Node, node);
322 const std::optional<std::string> new_animation_basepath = RNA_path_from_ID_to_struct(&ptr);
323 BLI_addtail(&anim_basepaths,
324 animation_basepath_change_new(*old_animation_basepath, *new_animation_basepath));
325 }
326
327 node->location[0] += gnode->location[0];
328 node->location[1] += gnode->location[1];
329
330 node->flag |= NODE_SELECT;
331 }
332 wgroup->runtime->nodes_by_id.clear();
333
334 bNodeLink *glinks_first = (bNodeLink *)ntree->links.last;
335
336 /* Add internal links to the ntree */
337 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
338 BLI_remlink(&wgroup->links, link);
339 BLI_addtail(&ntree->links, link);
341 }
342
343 bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
344
345 /* and copy across the animation,
346 * note that the animation data's action can be nullptr here */
347 if (wgroup->adt) {
348 /* firstly, wgroup needs to temporary dummy action
349 * that can be destroyed, as it shares copies */
350 bAction *waction = reinterpret_cast<bAction *>(BKE_id_copy(bmain, &wgroup->adt->action->id));
351 const bool assign_ok = animrig::assign_action(waction, {wgroup->id, *wgroup->adt});
352 BLI_assert_msg(assign_ok, "assigning a copy of an already-assigned Action should work");
353 UNUSED_VARS_NDEBUG(assign_ok);
354
355 /* now perform the moving */
356 BKE_animdata_transfer_by_basepath(bmain, &wgroup->id, &ntree->id, &anim_basepaths);
357
358 /* paths + their wrappers need to be freed */
359 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
360 animation_basepath_change_free(basepath_change);
361 }
362
363 /* free temp action too */
364 if (waction) {
365 const bool unassign_ok = animrig::unassign_action({wgroup->id, *wgroup->adt});
366 BLI_assert_msg(unassign_ok, "unassigning an Action that was just assigned should work");
367 UNUSED_VARS_NDEBUG(unassign_ok);
368 BKE_id_free(bmain, waction);
369 }
370 }
371
372 remap_pairing(*ntree, new_nodes, node_identifier_map);
373
374 /* free the group tree (takes care of user count) */
375 BKE_id_free(bmain, wgroup);
376
377 /* restore external links to and from the gnode */
378
379 /* input links */
380 if (glinks_first != nullptr) {
381 for (bNodeLink *link = glinks_first->next; link != glinks_last->next; link = link->next) {
382 if (link->fromnode->is_group_input()) {
383 const char *identifier = link->fromsock->identifier;
384 int num_external_links = 0;
385
386 /* find external links to this input */
387 for (bNodeLink *tlink = (bNodeLink *)ntree->links.first; tlink != glinks_first->next;
388 tlink = tlink->next)
389 {
390 if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) {
392 *ntree, *tlink->fromnode, *tlink->fromsock, *link->tonode, *link->tosock);
393 num_external_links++;
394 }
395 }
396
397 /* if group output is not externally linked,
398 * convert the constant input value to ensure somewhat consistent behavior */
399 if (num_external_links == 0) {
400 /* TODO */
401#if 0
402 bNodeSocket *sock = node_group_find_input_socket(gnode, identifier);
403 BLI_assert(sock);
404
405 nodeSocketCopy(
406 ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode);
407#endif
408 }
409 }
410 }
411
412 /* Also iterate over new links to cover passthrough links. */
413 glinks_last = (bNodeLink *)ntree->links.last;
414
415 /* output links */
416 for (bNodeLink *link = (bNodeLink *)ntree->links.first; link != glinks_first->next;
417 link = link->next)
418 {
419 if (link->fromnode == gnode) {
420 const char *identifier = link->fromsock->identifier;
421 int num_internal_links = 0;
422
423 /* find internal links to this output */
424 for (bNodeLink *tlink = glinks_first->next; tlink != glinks_last->next;
425 tlink = tlink->next)
426 {
427 /* only use active output node */
428 if (tlink->tonode->is_group_output() && (tlink->tonode->flag & NODE_DO_OUTPUT)) {
429 if (STREQ(tlink->tosock->identifier, identifier)) {
431 *ntree, *tlink->fromnode, *tlink->fromsock, *link->tonode, *link->tosock);
432 num_internal_links++;
433 }
434 }
435 }
436
437 /* if group output is not internally linked,
438 * convert the constant output value to ensure somewhat consistent behavior */
439 if (num_internal_links == 0) {
440 /* TODO */
441#if 0
442 bNodeSocket *sock = node_group_find_output_socket(gnode, identifier);
443 BLI_assert(sock);
444
445 nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode);
446#endif
447 }
448 }
449 }
450 }
451
452 for (bNode *node : nodes_delayed_free) {
453 bke::node_remove_node(bmain, *ntree, *node, false);
454 }
455
456 update_nested_node_refs_after_ungroup(*ntree, *ngroup, *gnode, node_identifier_map);
457
458 /* delete the group instance and dereference group tree */
459 bke::node_remove_node(bmain, *ntree, *gnode, true);
460}
461
463{
464 Main *bmain = CTX_data_main(C);
465 SpaceNode *snode = CTX_wm_space_node(C);
466 const StringRef node_idname = node_group_idname(C);
467
469
470 Vector<bNode *> nodes_to_ungroup;
471 for (bNode *node : snode->edittree->all_nodes()) {
472 if (node->flag & NODE_SELECT) {
473 if (node->idname == node_idname) {
474 if (node->id != nullptr) {
475 nodes_to_ungroup.append(node);
476 }
477 }
478 }
479 }
480 if (nodes_to_ungroup.is_empty()) {
481 return OPERATOR_CANCELLED;
482 }
483 for (bNode *node : nodes_to_ungroup) {
484 node_group_ungroup(bmain, snode->edittree, node);
485 }
487 return OPERATOR_FINISHED;
488}
489
491{
492 /* identifiers */
493 ot->name = "Ungroup";
494 ot->description = "Ungroup selected nodes";
495 ot->idname = "NODE_OT_group_ungroup";
496
497 /* API callbacks. */
500
501 /* flags */
503}
504
506
507/* -------------------------------------------------------------------- */
510
515 Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
516{
517 node_deselect_all(ntree);
518
519 ListBase anim_basepaths = {nullptr, nullptr};
520
521 Map<bNode *, bNode *> node_map;
523 Map<int32_t, int32_t> node_identifier_map;
524
525 /* Add selected nodes into the ntree, ignoring interface nodes. */
526 VectorSet<bNode *> nodes_to_move = get_selected_nodes(ngroup);
527 nodes_to_move.remove_if(
528 [](const bNode *node) { return node->is_group_input() || node->is_group_output(); });
529
530 for (bNode *node : nodes_to_move) {
531 bNode *newnode;
532 if (make_copy) {
533 newnode = bke::node_copy_with_mapping(&ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
534 node_identifier_map.add(node->identifier, newnode->identifier);
535 }
536 else {
537 newnode = node;
538 BLI_remlink(&ngroup.nodes, newnode);
539 BLI_addtail(&ntree.nodes, newnode);
540 const int32_t old_identifier = node->identifier;
541 bke::node_unique_id(ntree, *newnode);
542 bke::node_unique_name(ntree, *newnode);
543 node_identifier_map.add(old_identifier, newnode->identifier);
544 }
545 node_map.add_new(node, newnode);
546
547 /* Keep track of this node's RNA "base" path (the part of the path identifying the node)
548 * if the old node-tree has animation data which potentially covers this node. */
549 if (ngroup.adt) {
550 PointerRNA ptr = RNA_pointer_create_discrete(&ngroup.id, &RNA_Node, newnode);
551 if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(&ptr)) {
552 BLI_addtail(&anim_basepaths, animation_basepath_change_new(*path, *path));
553 }
554 }
555
556 /* ensure valid parent pointers, detach if parent stays inside the group */
557 if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) {
558 bke::node_detach_node(ngroup, *newnode);
559 }
560
561 newnode->location[0] += offset.x;
562 newnode->location[1] += offset.y;
563 }
564 if (!make_copy) {
566 }
567
568 /* add internal links to the ntree */
569 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ngroup.links) {
570 const bool fromselect = (link->fromnode && nodes_to_move.contains(link->fromnode));
571 const bool toselect = (link->tonode && nodes_to_move.contains(link->tonode));
572
573 if (make_copy) {
574 /* make a copy of internal links */
575 if (fromselect && toselect) {
576 bke::node_add_link(ntree,
577 *node_map.lookup(link->fromnode),
578 *socket_map.lookup(link->fromsock),
579 *node_map.lookup(link->tonode),
580 *socket_map.lookup(link->tosock));
581 }
582 }
583 else {
584 /* move valid links over, delete broken links */
585 if (fromselect && toselect) {
586 BLI_remlink(&ngroup.links, link);
587 BLI_addtail(&ntree.links, link);
588 }
589 else if (fromselect || toselect) {
590 bke::node_remove_link(&ngroup, *link);
591 }
592 }
593 }
594
595 remap_pairing(ntree, nodes_to_move, node_identifier_map);
596
597 for (bNode *node : node_map.values()) {
598 bke::node_declaration_ensure(ntree, *node);
599 }
600
601 /* and copy across the animation,
602 * note that the animation data's action can be nullptr here */
603 if (ngroup.adt) {
604 /* now perform the moving */
605 BKE_animdata_transfer_by_basepath(&bmain, &ngroup.id, &ntree.id, &anim_basepaths);
606
607 /* paths + their wrappers need to be freed */
608 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
609 animation_basepath_change_free(basepath_change);
610 }
611 }
612
614 if (!make_copy) {
616 }
617
618 return true;
619}
620
625
626/* Operator Property */
628 {NODE_GS_COPY, "COPY", 0, "Copy", "Copy to parent node tree, keep group intact"},
629 {NODE_GS_MOVE, "MOVE", 0, "Move", "Move to parent node tree, remove from group"},
630 {0, nullptr, 0, nullptr, nullptr},
631};
632
634{
635 Main *bmain = CTX_data_main(C);
636 ARegion *region = CTX_wm_region(C);
637 SpaceNode *snode = CTX_wm_space_node(C);
638 int type = RNA_enum_get(op->ptr, "type");
639
641
642 /* are we inside of a group? */
643 bNodeTree *ngroup = snode->edittree;
644 bNodeTree *nparent = ED_node_tree_get(snode, 1);
645 if (!nparent) {
646 BKE_report(op->reports, RPT_WARNING, "Not inside node group");
647 return OPERATOR_CANCELLED;
648 }
649 /* get node tree offset */
650 const float2 offset = space_node_group_offset(*snode);
651
652 switch (type) {
653 case NODE_GS_COPY:
654 if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, true)) {
655 BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
656 return OPERATOR_CANCELLED;
657 }
658 break;
659 case NODE_GS_MOVE:
660 if (!node_group_separate_selected(*bmain, *nparent, *ngroup, offset, false)) {
661 BKE_report(op->reports, RPT_WARNING, "Cannot separate nodes");
662 return OPERATOR_CANCELLED;
663 }
664 break;
665 }
666
667 /* switch to parent tree */
668 ED_node_tree_pop(region, snode);
669
671
672 return OPERATOR_FINISHED;
673}
674
676 wmOperator * /*op*/,
677 const wmEvent * /*event*/)
678{
680 C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Separate"), ICON_NONE);
681 uiLayout *layout = UI_popup_menu_layout(pup);
682
684 uiItemEnumO(layout, "NODE_OT_group_separate", std::nullopt, ICON_NONE, "type", NODE_GS_COPY);
685 uiItemEnumO(layout, "NODE_OT_group_separate", std::nullopt, ICON_NONE, "type", NODE_GS_MOVE);
686
687 UI_popup_menu_end(C, pup);
688
689 return OPERATOR_INTERFACE;
690}
691
693{
694 /* identifiers */
695 ot->name = "Separate";
696 ot->description = "Separate selected nodes from the node group";
697 ot->idname = "NODE_OT_group_separate";
698
699 /* API callbacks. */
703
704 /* flags */
706
707 RNA_def_enum(ot->srna, "type", node_group_separate_types, NODE_GS_COPY, "Type", "");
708}
709
711
712/* -------------------------------------------------------------------- */
715
717{
718 VectorSet<bNode *> nodes_to_group = get_selected_nodes(node_tree);
719 nodes_to_group.remove_if(
720 [](bNode *node) { return node->is_group_input() || node->is_group_output(); });
721 nodes_to_group.remove(group_node);
722 return nodes_to_group;
723}
724
726 const VectorSet<bNode *> &nodes_to_group,
727 const StringRef ntree_idname,
729{
730 if (nodes_to_group.is_empty()) {
731 return false;
732 }
733 /* make a local pseudo node tree to pass to the node poll functions */
734 bNodeTree *ngroup = bke::node_tree_add_tree(nullptr, "Pseudo Node Group", ntree_idname);
735 BLI_SCOPED_DEFER([&]() {
737 MEM_freeN(ngroup);
738 });
739
740 /* check poll functions for selected nodes */
741 for (bNode *node : nodes_to_group) {
742 const char *disabled_hint = nullptr;
743 if (node->typeinfo->poll_instance &&
744 !node->typeinfo->poll_instance(node, ngroup, &disabled_hint))
745 {
746 if (disabled_hint) {
749 "Cannot add node '%s' in a group:\n %s",
750 node->name,
751 disabled_hint);
752 }
753 else {
754 BKE_reportf(&reports, RPT_WARNING, "Cannot add node '%s' in a group", node->name);
755 }
756 return false;
757 }
758 }
759
760 /* check if all connections are OK, no unselected node has both
761 * inputs and outputs to a selection */
762 ntree.ensure_topology_cache();
763 for (bNode *node : ntree.all_nodes()) {
764 if (nodes_to_group.contains(node)) {
765 continue;
766 }
767 auto sockets_connected_to_group = [&](const Span<bNodeSocket *> sockets) {
768 for (const bNodeSocket *socket : sockets) {
769 for (const bNodeSocket *other_socket : socket->directly_linked_sockets()) {
770 if (nodes_to_group.contains(const_cast<bNode *>(&other_socket->owner_node()))) {
771 return true;
772 }
773 }
774 }
775 return false;
776 };
777 if (sockets_connected_to_group(node->input_sockets()) &&
778 sockets_connected_to_group(node->output_sockets()))
779 {
780 return false;
781 }
782 }
783 /* Check if zone pairs are fully selected.
784 * Zone input or output nodes can only be grouped together with the paired node. */
785 for (const bke::bNodeZoneType *zone_type : bke::all_zone_types()) {
786 for (bNode *input_node : ntree.nodes_by_type(zone_type->input_idname)) {
787 if (bNode *output_node = zone_type->get_corresponding_output(ntree, *input_node)) {
788 const bool input_selected = nodes_to_group.contains(input_node);
789 const bool output_selected = nodes_to_group.contains(output_node);
790 if (input_selected && !output_selected) {
793 "Cannot add zone input node '%s' to a group without its paired output '%s'",
794 input_node->name,
795 output_node->name);
796 return false;
797 }
798 if (output_selected && !input_selected) {
801 "Cannot add zone output node '%s' to a group without its paired input '%s'",
802 output_node->name,
803 input_node->name);
804 return false;
805 }
806 }
807 }
808 }
809
810 return true;
811}
812
814 const bool use_size,
815 float2 &min,
816 float2 &max)
817{
818 if (nodes.is_empty()) {
819 min = float2(0);
820 max = float2(0);
821 return;
822 }
823
825 for (const bNode *node : nodes) {
826 float2 loc(node->location);
827 math::min_max(loc, min, max);
828 if (use_size) {
829 loc.x += node->width;
830 loc.y -= node->height;
831 math::min_max(loc, min, max);
832 }
833 }
834}
835
843 const bNodeSocket &socket)
844{
845 if (node_tree.has_available_link_cycle()) {
846 return socket;
847 }
848 const bNode &node = socket.owner_node();
849 if (!node.is_reroute()) {
850 return socket;
851 }
852 const bNodeSocket &other_socket = socket.in_out == SOCK_IN ? node.output_socket(0) :
853 node.input_socket(0);
854 if (!other_socket.is_logically_linked()) {
855 return socket;
856 }
857 return *other_socket.logically_linked_sockets().first();
858}
859
864static bool prefer_node_for_interface_name(const bNode &node)
865{
866 return node.is_group() || node.is_group_input() || node.is_group_output();
867}
868
870 bNodeTree &tree_for_interface,
871 const bNodeSocket &socket)
872{
873 /* The "example socket" has to have the same `in_out` status as the new interface socket. */
874 const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket);
875 const bNode &node_for_io = socket_for_io.owner_node();
876 const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ?
877 socket :
878 socket_for_io;
880 tree_for_interface, node_for_io, socket_for_io, socket_for_io.idname, socket_for_name.name);
881}
882
884 bNodeTree &ntree,
885 bNodeTree &group,
886 bNode &gnode,
887 const Map<int32_t, int32_t> &node_identifier_map)
888{
889 /* Update nested node references in the parent and child node tree. */
891 Vector<bNestedNodeRef> new_nested_node_refs;
892 /* Keep all nested node references that were in the group before. */
893 for (const bNestedNodeRef &ref : group.nested_node_refs_span()) {
894 new_nested_node_refs.append(ref);
895 }
896 Set<int32_t> used_nested_node_ref_ids;
897 for (const bNestedNodeRef &ref : group.nested_node_refs_span()) {
898 used_nested_node_ref_ids.add(ref.id);
899 }
900 Map<bNestedNodePath, int32_t> new_id_by_old_path;
901 for (bNestedNodeRef &ref : ntree.nested_node_refs_span()) {
902 const int32_t new_node_id = node_identifier_map.lookup_default(ref.path.node_id, -1);
903 if (new_node_id == -1) {
904 /* The node was not moved between node groups. */
905 continue;
906 }
907 bNestedNodeRef new_ref = ref;
908 new_ref.path.node_id = new_node_id;
909 /* Find new unique identifier for the nested node ref. */
910 while (true) {
911 const int32_t new_id = rng.get_int32(INT32_MAX);
912 if (used_nested_node_ref_ids.add(new_id)) {
913 new_ref.id = new_id;
914 break;
915 }
916 }
917 new_id_by_old_path.add_new(ref.path, new_ref.id);
918 new_nested_node_refs.append(new_ref);
919 /* Updated the nested node ref in the parent so that it points to the same node that is now
920 * inside of a nested group. */
921 ref.path.node_id = gnode.identifier;
922 ref.path.id_in_node = new_ref.id;
923 }
925 group.nested_node_refs = MEM_malloc_arrayN<bNestedNodeRef>(new_nested_node_refs.size(),
926 __func__);
928 new_nested_node_refs.data(), new_nested_node_refs.size(), group.nested_node_refs);
929 group.nested_node_refs_num = new_nested_node_refs.size();
930}
931
933 bNodeTree &ntree,
934 bNode *gnode,
935 const VectorSet<bNode *> &nodes_to_move)
936{
937 Main *bmain = CTX_data_main(&C);
938 bNodeTree &group = *reinterpret_cast<bNodeTree *>(gnode->id);
939 BLI_assert(!nodes_to_move.contains(gnode));
940
941 node_deselect_all(group);
942
943 float2 min, max;
944 get_min_max_of_nodes(nodes_to_move, false, min, max);
945 const float2 center = math::midpoint(min, max);
946
947 float2 real_min, real_max;
948 get_min_max_of_nodes(nodes_to_move, true, real_min, real_max);
949
950 /* If only one node is selected expose all its sockets regardless of links. */
951 const bool expose_visible = nodes_to_move.size() == 1;
952
953 /* Reuse an existing output node or create a new one. */
954 group.ensure_topology_cache();
955 bNode *output_node = [&]() {
956 if (bNode *node = group.group_output_node()) {
957 return node;
958 }
959 bNode *output_node = bke::node_add_static_node(&C, group, NODE_GROUP_OUTPUT);
960 output_node->location[0] = real_max[0] - center[0] + 50.0f;
961 return output_node;
962 }();
963
964 /* Create new group input node for easier organization of the new nodes inside the group. */
965 bNode *input_node = bke::node_add_static_node(&C, group, NODE_GROUP_INPUT);
966 input_node->location[0] = real_min[0] - center[0] - 200.0f;
967
968 struct InputSocketInfo {
969 /* The unselected node the original link came from. */
970 bNode *from_node;
971 /* All the links that came from the socket on the unselected node. */
973 const bNodeTreeInterfaceSocket *interface_socket;
974 };
975
976 struct OutputLinkInfo {
977 bNodeLink *link;
978 const bNodeTreeInterfaceSocket *interface_socket;
979 };
980
981 struct NewInternalLinkInfo {
982 bNode *node;
983 bNodeSocket *socket;
984 const bNodeTreeInterfaceSocket *interface_socket;
985 };
986
987 /* Map from single non-selected output sockets to potentially many selected input sockets. */
989 Vector<OutputLinkInfo> output_links;
990 Set<bNodeLink *> internal_links_to_move;
991 Set<bNodeLink *> links_to_remove;
992 /* Map old to new node identifiers. */
993 Map<int32_t, int32_t> node_identifier_map;
994 Vector<NewInternalLinkInfo> new_internal_links;
995
996 ntree.ensure_topology_cache();
997 /* Add all outputs first. */
998 for (bNode *node : nodes_to_move) {
999 for (bNodeSocket *output_socket : node->output_sockets()) {
1000 if (!output_socket->is_visible()) {
1001 for (bNodeLink *link : output_socket->directly_linked_links()) {
1002 links_to_remove.add(link);
1003 }
1004 continue;
1005 }
1006
1007 for (bNodeLink *link : output_socket->directly_linked_links()) {
1008 if (bke::node_link_is_hidden(*link)) {
1009 links_to_remove.add(link);
1010 continue;
1011 }
1012 if (link->tonode == gnode) {
1013 links_to_remove.add(link);
1014 continue;
1015 }
1016 if (nodes_to_move.contains(link->tonode)) {
1017 internal_links_to_move.add(link);
1018 continue;
1019 }
1021 ntree, group, *link->fromsock);
1022 if (io_socket) {
1023 output_links.append({link, io_socket});
1024 }
1025 else {
1026 links_to_remove.add(link);
1027 }
1028 }
1029 if (expose_visible && !output_socket->is_directly_linked()) {
1031 group, *node, *output_socket);
1032 if (io_socket) {
1033 new_internal_links.append({node, output_socket, io_socket});
1034 }
1035 }
1036 }
1037 }
1038 /* Now add all inputs. */
1039 for (bNode *node : nodes_to_move) {
1040 for (bNodeSocket *input_socket : node->input_sockets()) {
1041 if (!input_socket->is_visible()) {
1042 for (bNodeLink *link : input_socket->directly_linked_links()) {
1043 links_to_remove.add(link);
1044 }
1045 continue;
1046 }
1047
1048 for (bNodeLink *link : input_socket->directly_linked_links()) {
1049 if (bke::node_link_is_hidden(*link)) {
1050 links_to_remove.add(link);
1051 continue;
1052 }
1053 if (link->fromnode == gnode) {
1054 links_to_remove.add(link);
1055 continue;
1056 }
1057 if (nodes_to_move.contains(link->fromnode)) {
1058 internal_links_to_move.add(link);
1059 continue;
1060 }
1061 InputSocketInfo &info = input_links.lookup_or_add_default(link->fromsock);
1062 info.from_node = link->fromnode;
1063 info.links.append(link);
1064 if (!info.interface_socket) {
1065 info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock);
1066 }
1067 }
1068 if (expose_visible && !input_socket->is_directly_linked()) {
1070 group, *node, *input_socket);
1071 if (io_socket) {
1072 new_internal_links.append({node, input_socket, io_socket});
1073 }
1074 }
1075 }
1076 }
1077
1078 /* Un-parent nodes when only the parent or child moves into the group. */
1079 for (bNode *node : ntree.all_nodes()) {
1080 if (node->parent && nodes_to_move.contains(node->parent) && !nodes_to_move.contains(node)) {
1081 bke::node_detach_node(ntree, *node);
1082 }
1083 }
1084 for (bNode *node : nodes_to_move) {
1085 if (node->parent && !nodes_to_move.contains(node->parent)) {
1086 bke::node_detach_node(ntree, *node);
1087 }
1088 }
1089
1090 /* Move animation data from the parent tree to the group. */
1091 if (ntree.adt) {
1092 ListBase anim_basepaths = {nullptr, nullptr};
1093 for (bNode *node : nodes_to_move) {
1094 PointerRNA ptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, node);
1095 if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(&ptr)) {
1096 BLI_addtail(&anim_basepaths, animation_basepath_change_new(*path, *path));
1097 }
1098 }
1099 BKE_animdata_transfer_by_basepath(bmain, &ntree.id, &group.id, &anim_basepaths);
1100
1101 LISTBASE_FOREACH_MUTABLE (AnimationBasePathChange *, basepath_change, &anim_basepaths) {
1102 animation_basepath_change_free(basepath_change);
1103 }
1104 }
1105
1106 /* Move nodes into the group. */
1107 for (bNode *node : nodes_to_move) {
1108 const int32_t old_identifier = node->identifier;
1109
1110 BLI_remlink(&ntree.nodes, node);
1111 BLI_addtail(&group.nodes, node);
1112 bke::node_unique_id(group, *node);
1113 bke::node_unique_name(group, *node);
1114
1115 node_identifier_map.add(old_identifier, node->identifier);
1116
1118 BKE_ntree_update_tag_node_new(&group, node);
1119 }
1121
1122 /* Update input and output node first, since the group node declaration can depend on them. */
1124 nodes::update_node_declaration_and_sockets(group, *output_node);
1125
1126 /* move nodes in the group to the center */
1127 for (bNode *node : nodes_to_move) {
1128 node->location[0] -= center[0];
1129 node->location[1] -= center[1];
1130 }
1131
1132 for (bNodeLink *link : internal_links_to_move) {
1133 BLI_remlink(&ntree.links, link);
1134 BLI_addtail(&group.links, link);
1136 BKE_ntree_update_tag_link_added(&group, link);
1137 }
1138
1139 for (bNodeLink *link : links_to_remove) {
1140 bke::node_remove_link(&ntree, *link);
1141 }
1142
1143 /* Handle links to the new group inputs. */
1144 for (const auto item : input_links.items()) {
1145 const StringRefNull interface_identifier = item.value.interface_socket->identifier;
1146 bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier);
1147
1148 for (bNodeLink *link : item.value.links) {
1149 /* Move the link into the new group, connected from the input node to the original socket. */
1150 BLI_remlink(&ntree.links, link);
1151 BLI_addtail(&group.links, link);
1153 BKE_ntree_update_tag_link_added(&group, link);
1154 link->fromnode = input_node;
1155 link->fromsock = input_socket;
1156 }
1157 }
1158
1159 /* Handle links to new group outputs. */
1160 for (const OutputLinkInfo &info : output_links) {
1161 /* Create a new link inside of the group. */
1162 const StringRefNull io_identifier = info.interface_socket->identifier;
1163 bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier);
1165 group, *info.link->fromnode, *info.link->fromsock, *output_node, *output_sock);
1166 }
1167
1168 /* Handle new links inside the group. */
1169 for (const NewInternalLinkInfo &info : new_internal_links) {
1170 const StringRefNull io_identifier = info.interface_socket->identifier;
1171 if (info.socket->in_out == SOCK_IN) {
1172 bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier);
1173 bke::node_add_link(group, *input_node, *input_socket, *info.node, *info.socket);
1174 }
1175 else {
1176 bNodeSocket *output_socket = node_group_output_find_socket(output_node, io_identifier);
1177 bke::node_add_link(group, *info.node, *info.socket, *output_node, *output_socket);
1178 }
1179 }
1180
1181 remap_pairing(group, nodes_to_move, node_identifier_map);
1182
1183 if (group.type == NTREE_GEOMETRY) {
1186 }
1188
1189 /* Add new links to inputs outside of the group. */
1190 for (const auto item : input_links.items()) {
1191 const StringRefNull interface_identifier = item.value.interface_socket->identifier;
1192 bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier);
1193 bke::node_add_link(ntree, *item.value.from_node, *item.key, *gnode, *group_node_socket);
1194 }
1195
1196 /* Add new links to outputs outside the group. */
1197 for (const OutputLinkInfo &info : output_links) {
1198 /* Reconnect the link to the group node instead of the node now inside the group. */
1199 info.link->fromnode = gnode;
1200 info.link->fromsock = node_group_find_output_socket(gnode, info.interface_socket->identifier);
1201 }
1202
1203 update_nested_node_refs_after_moving_nodes_into_group(ntree, group, *gnode, node_identifier_map);
1204
1206}
1207
1209 bNodeTree &ntree,
1210 const VectorSet<bNode *> &nodes_to_group,
1211 const StringRef ntype,
1212 const StringRef ntreetype)
1213{
1214 Main *bmain = CTX_data_main(&C);
1215
1216 float2 min, max;
1217 get_min_max_of_nodes(nodes_to_group, false, min, max);
1218
1219 /* New node-tree. */
1220 bNodeTree *ngroup = bke::node_tree_add_tree(bmain, "NodeGroup", ntreetype);
1221
1222 BKE_id_move_to_same_lib(*bmain, ngroup->id, ntree.id);
1223
1224 /* make group node */
1225 bNode *gnode = bke::node_add_node(&C, ntree, ntype);
1226 gnode->id = (ID *)ngroup;
1227
1228 gnode->location[0] = 0.5f * (min[0] + max[0]);
1229 gnode->location[1] = 0.5f * (min[1] + max[1]);
1230
1231 node_group_make_insert_selected(C, ntree, gnode, nodes_to_group);
1232
1233 return gnode;
1234}
1235
1237 int num_inputs = 0;
1243
1244 bNodeSocket *get_new_input(const bNodeSocket *old_socket, bNode &new_node) const
1245 {
1246 if (const std::optional<int> index = new_index_by_src_socket.lookup_try(old_socket)) {
1247 return &new_node.input_socket(*index);
1248 }
1249 return nullptr;
1250 }
1251
1252 bNodeSocket *get_new_output(const bNodeSocket *old_socket, bNode &new_node) const
1253 {
1254 if (const std::optional<int> index = new_index_by_src_socket.lookup_try(old_socket)) {
1255 return &new_node.output_socket(*index);
1256 }
1257 return nullptr;
1258 }
1259};
1260
1262 bNodeTree &group,
1263 const bNode &src_node,
1264 const nodes::ItemDeclaration &item_decl,
1266 WrapperNodeGroupMapping &r_mapping)
1267{
1268 if (const nodes::SocketDeclaration *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(
1269 &item_decl))
1270 {
1271 const bNodeSocket &socket = src_node.socket_by_decl(*socket_decl);
1272 if (!socket.is_available()) {
1273 return;
1274 }
1276 group, src_node, socket);
1277 if (!io_socket) {
1278 return;
1279 }
1280 group.tree_interface.move_item_to_parent(io_socket->item, parent, INT32_MAX);
1281 if (socket.is_input()) {
1282 r_mapping.new_index_by_src_socket.add_new(&socket, r_mapping.num_inputs++);
1283 r_mapping.exposed_input_indices.append(socket.index());
1284 }
1285 else {
1286 r_mapping.new_index_by_src_socket.add_new(&socket, r_mapping.num_outputs++);
1287 r_mapping.exposed_output_indices.append(socket.index());
1288 }
1289 }
1290 else if (const nodes::PanelDeclaration *panel_decl =
1291 dynamic_cast<const nodes::PanelDeclaration *>(&item_decl))
1292 {
1294 if (panel_decl->default_collapsed) {
1296 }
1297 bNodeTreeInterfacePanel *io_panel = group.tree_interface.add_panel(
1298 panel_decl->name, panel_decl->description, flag, parent);
1299 r_mapping.new_by_old_panel_identifier.add_new(panel_decl->identifier, io_panel->identifier);
1300 for (const nodes::ItemDeclaration *child_item_decl : panel_decl->items) {
1302 group, src_node, *child_item_decl, io_panel, r_mapping);
1303 }
1304 }
1305}
1306
1308 const bNodeTree &src_tree,
1309 const bNode &src_node,
1310 WrapperNodeGroupMapping &r_mapping)
1311{
1312 Main &bmain = *CTX_data_main(&C);
1313
1315 &bmain, bke::node_label(src_tree, src_node), src_tree.idname);
1316
1317 const nodes::NodeDeclaration &node_decl = *src_node.declaration();
1318 for (const nodes::ItemDeclaration *item_decl : node_decl.root_items) {
1320 *dst_group, src_node, *item_decl, nullptr, r_mapping);
1321 }
1322
1323 /* Add the node that make up the wrapper node group. */
1324 bNode &input_node = *bke::node_add_static_node(&C, *dst_group, NODE_GROUP_INPUT);
1325 bNode &output_node = *bke::node_add_static_node(&C, *dst_group, NODE_GROUP_OUTPUT);
1326 bNode &inner_node = *bke::node_copy(dst_group, src_node, 0, true);
1327
1328 /* Position nodes. */
1329 input_node.location[0] = -300 - input_node.width;
1330 output_node.location[0] = 300;
1331 inner_node.location[0] = -src_node.width / 2;
1332 inner_node.location[1] = 0;
1333 inner_node.width = src_node.width;
1334 inner_node.parent = nullptr;
1335
1336 /* This makes sure that all nodes have the correct sockets so that we can link. */
1337 BKE_main_ensure_invariants(bmain, dst_group->id);
1338
1339 /* Expand all panels in wrapper node group. */
1340 for (bNodePanelState &panel_state : inner_node.panel_states()) {
1341 panel_state.flag &= ~NODE_PANEL_COLLAPSED;
1342 }
1343 /* Make all sockets visible in wrapper node group. */
1344 for (bNodeSocket *socket : inner_node.input_sockets()) {
1345 socket->flag &= ~SOCK_HIDDEN;
1346 }
1347 for (bNodeSocket *socket : inner_node.output_sockets()) {
1348 socket->flag &= ~SOCK_HIDDEN;
1349 }
1350
1351 const Array<bNodeSocket *> group_inputs = input_node.output_sockets().drop_back(1);
1352 const Array<bNodeSocket *> group_outputs = output_node.input_sockets().drop_back(1);
1353 const Array<bNodeSocket *> inner_inputs = inner_node.input_sockets();
1354 const Array<bNodeSocket *> inner_outputs = inner_node.output_sockets();
1355 BLI_assert(group_inputs.size() == r_mapping.exposed_input_indices.size());
1356 BLI_assert(group_outputs.size() == r_mapping.exposed_output_indices.size());
1357
1358 /* Add links. */
1359 for (const int i : group_inputs.index_range()) {
1360 bke::node_add_link(*dst_group,
1361 input_node,
1362 *group_inputs[i],
1363 inner_node,
1364 *inner_inputs[r_mapping.exposed_input_indices[i]]);
1365 }
1366 for (const int i : group_outputs.index_range()) {
1367 bke::node_add_link(*dst_group,
1368 inner_node,
1369 *inner_outputs[r_mapping.exposed_output_indices[i]],
1370 output_node,
1371 *group_outputs[i]);
1372 }
1373
1374 BKE_main_ensure_invariants(bmain, dst_group->id);
1375 return dst_group;
1376}
1377
1379 bNodeTree &ntree,
1380 bNode &src_node,
1381 const StringRef node_idname)
1382{
1383 Main &bmain = *CTX_data_main(&C);
1384
1386 bNodeTree *wrapper_group = node_group_make_wrapper(C, ntree, src_node, mapping);
1387
1388 /* Create a group node. */
1389 bNode *gnode = bke::node_add_node(&C, ntree, node_idname);
1390 STRNCPY(gnode->name, BKE_id_name(wrapper_group->id));
1391 bke::node_unique_name(ntree, *gnode);
1392
1393 /* Assign the newly created wrapper group to the new group node. */
1394 gnode->id = &wrapper_group->id;
1395
1396 /* Position node exactly where the old node was. */
1397 gnode->parent = src_node.parent;
1398 gnode->width = std::max<float>(src_node.width, GROUP_NODE_MIN_WIDTH);
1399 copy_v2_v2(gnode->location, src_node.location);
1400
1402 ntree.ensure_topology_cache();
1403
1404 /* Keep old socket visibility. */
1405 for (const bNodeSocket *src_socket : src_node.input_sockets()) {
1406 if (bNodeSocket *new_socket = mapping.get_new_input(src_socket, *gnode)) {
1407 new_socket->flag |= src_socket->flag & (SOCK_HIDDEN | SOCK_COLLAPSED);
1408 }
1409 }
1410 for (const bNodeSocket *src_socket : src_node.output_sockets()) {
1411 if (bNodeSocket *new_socket = mapping.get_new_output(src_socket, *gnode)) {
1412 new_socket->flag |= src_socket->flag & (SOCK_HIDDEN | SOCK_COLLAPSED);
1413 }
1414 }
1415
1416 /* Keep old panel collapse status. */
1417 const Span<bNodePanelState> src_panel_states = src_node.panel_states();
1418 MutableSpan<bNodePanelState> new_panel_states = gnode->panel_states();
1419 for (const bNodePanelState &src_panel_state : src_panel_states) {
1420 if (const std::optional<int> new_identifier = mapping.new_by_old_panel_identifier.lookup_try(
1421 src_panel_state.identifier))
1422 {
1423 for (bNodePanelState &new_panel_state : new_panel_states) {
1424 if (new_panel_state.identifier == *new_identifier) {
1425 SET_FLAG_FROM_TEST(new_panel_state.flag,
1426 src_panel_state.flag & NODE_PANEL_COLLAPSED,
1428 }
1429 }
1430 }
1431 }
1432
1433 /* Relink links from old to new node. */
1434 LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
1435 if (link->tonode == &src_node) {
1436 if (bNodeSocket *new_to_socket = mapping.get_new_input(link->tosock, *gnode)) {
1437 link->tonode = gnode;
1438 link->tosock = new_to_socket;
1439 continue;
1440 }
1441 bke::node_remove_link(&ntree, *link);
1442 continue;
1443 }
1444 if (link->fromnode == &src_node) {
1445 if (bNodeSocket *new_from_socket = mapping.get_new_output(link->fromsock, *gnode)) {
1446 link->fromnode = gnode;
1447 link->fromsock = new_from_socket;
1448 continue;
1449 }
1450 bke::node_remove_link(&ntree, *link);
1451 continue;
1452 }
1453 }
1454
1455 /* Remove the old node because it has been replaced. Use the name of the removed node for the new
1456 * group node. This also keeps animation data working. */
1457 std::string old_node_name = src_node.name;
1458 bke::node_remove_node(&bmain, ntree, src_node, true, false);
1459 STRNCPY(gnode->name, old_node_name.c_str());
1460
1463 return gnode;
1464}
1465
1467{
1468 ARegion &region = *CTX_wm_region(C);
1469 SpaceNode &snode = *CTX_wm_space_node(C);
1470 bNodeTree &ntree = *snode.edittree;
1471 const StringRef ntree_idname = group_ntree_idname(C);
1472 const StringRef node_idname = node_group_idname(C);
1473 Main *bmain = CTX_data_main(C);
1474
1476
1477 VectorSet<bNode *> nodes_to_group = get_nodes_to_group(ntree, nullptr);
1478 if (!node_group_make_test_selected(ntree, nodes_to_group, ntree_idname, *op->reports)) {
1479 return OPERATOR_CANCELLED;
1480 }
1481
1482 bNode *gnode = nullptr;
1483 if (nodes_to_group.size() == 1 && nodes_to_group[0]->declaration()) {
1484 gnode = node_group_make_from_node_declaration(*C, ntree, *nodes_to_group[0], node_idname);
1485 }
1486 else {
1487 gnode = node_group_make_from_nodes(*C, ntree, nodes_to_group, node_idname, ntree_idname);
1488 }
1489
1490 if (gnode) {
1491 bNodeTree *ngroup = (bNodeTree *)gnode->id;
1492
1493 bke::node_set_active(ntree, *gnode);
1494 if (ngroup) {
1495 ED_node_tree_push(&region, &snode, ngroup, gnode);
1496 }
1497 }
1498
1500
1501 /* We broke relations in node tree, need to rebuild them in the graphs. */
1503
1504 return OPERATOR_FINISHED;
1505}
1506
1508{
1509 /* identifiers */
1510 ot->name = "Make Group";
1511 ot->description = "Make group from selected nodes";
1512 ot->idname = "NODE_OT_group_make";
1513
1514 /* API callbacks. */
1515 ot->exec = node_group_make_exec;
1517
1518 /* flags */
1519 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1520}
1521
1523
1524/* -------------------------------------------------------------------- */
1527
1529{
1530 SpaceNode *snode = CTX_wm_space_node(C);
1531 ARegion *region = CTX_wm_region(C);
1532 bNodeTree *ntree = snode->edittree;
1533 const StringRef node_idname = node_group_idname(C);
1534
1536
1537 bNode *gnode = node_group_get_active(C, node_idname);
1538 if (!gnode || !gnode->id) {
1539 return OPERATOR_CANCELLED;
1540 }
1541
1542 bNodeTree *ngroup = reinterpret_cast<bNodeTree *>(gnode->id);
1543 VectorSet<bNode *> nodes_to_group = get_nodes_to_group(*ntree, gnode);
1544
1545 /* Make sure that there won't be a node group containing itself afterwards. */
1546 for (const bNode *group : nodes_to_group) {
1547 if (!group->is_group() || group->id == nullptr) {
1548 continue;
1549 }
1550 if (bke::node_tree_contains_tree(*reinterpret_cast<bNodeTree *>(group->id), *ngroup)) {
1552 op->reports, RPT_WARNING, "Cannot insert group '%s' in '%s'", group->name, gnode->name);
1553 return OPERATOR_CANCELLED;
1554 }
1555 }
1556
1557 if (!node_group_make_test_selected(*ntree, nodes_to_group, ngroup->idname, *op->reports)) {
1558 return OPERATOR_CANCELLED;
1559 }
1560
1561 node_group_make_insert_selected(*C, *ntree, gnode, nodes_to_group);
1562
1563 bke::node_set_active(*ntree, *gnode);
1564 ED_node_tree_push(region, snode, ngroup, gnode);
1565
1566 return OPERATOR_FINISHED;
1567}
1568
1570{
1571 /* identifiers */
1572 ot->name = "Group Insert";
1573 ot->description = "Insert selected nodes into a node group";
1574 ot->idname = "NODE_OT_group_insert";
1575
1576 /* API callbacks. */
1577 ot->exec = node_group_insert_exec;
1579
1580 /* flags */
1581 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1582}
1583
1585
1586/* -------------------------------------------------------------------- */
1589
1591{
1592 SpaceNode *snode = CTX_wm_space_node(C);
1593 if (!snode) {
1594 return false;
1595 }
1596 bNodeTree *ntree = snode->edittree;
1597 if (!ntree) {
1598 return false;
1599 }
1600 if (!ID_IS_EDITABLE(ntree)) {
1601 return false;
1602 }
1603 if (snode->nodetree == snode->edittree) {
1604 /* Top-level node group does not have enough context to set the node width. */
1605 CTX_wm_operator_poll_msg_set(C, "There is no parent group node in this context");
1606 return false;
1607 }
1608 return true;
1609}
1610
1612{
1613 SpaceNode *snode = CTX_wm_space_node(C);
1614 bNodeTree *ntree = snode->edittree;
1615
1616 bNodeTreePath *last_path_item = static_cast<bNodeTreePath *>(snode->treepath.last);
1617 bNodeTreePath *parent_path_item = last_path_item->prev;
1618 if (!parent_path_item) {
1619 return OPERATOR_CANCELLED;
1620 }
1621 bNodeTree *parent_ntree = parent_path_item->nodetree;
1622 if (!parent_ntree) {
1623 return OPERATOR_CANCELLED;
1624 }
1625 parent_ntree->ensure_topology_cache();
1626 bNode *parent_node = bke::node_find_node_by_name(*parent_ntree, last_path_item->node_name);
1627 if (!parent_node) {
1628 return OPERATOR_CANCELLED;
1629 }
1630 ntree->default_group_node_width = parent_node->width;
1632 return OPERATOR_CANCELLED;
1633}
1634
1636{
1637 /* identifiers */
1638 ot->name = "Set Default Group Node Width";
1639 ot->description = "Set the width based on the parent group node in the current context";
1640 ot->idname = "NODE_OT_default_group_width_set";
1641
1642 /* API callbacks. */
1645
1646 /* flags */
1647 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1648}
1649
1651
1652} // namespace blender::ed::space_node
Functions and classes to work with Actions.
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.cc:610
SpaceNode * CTX_wm_space_node(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void BKE_id_free(Main *bmain, void *idv)
@ LIB_ID_COPY_DEFAULT
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id)
Definition lib_id.cc:869
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:772
const char * BKE_id_name(const ID &id)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
constexpr int GROUP_NODE_MIN_WIDTH
Definition BKE_node.hh:1228
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:800
#define NODE_GROUP_INPUT
Definition BKE_node.hh:799
void BKE_ntree_update_tag_node_new(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_all(bNodeTree *ntree)
void BKE_ntree_update_tag_link_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_removed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_link_added(bNodeTree *ntree, 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.cc:126
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
MINLINE void copy_v2_v2(float r[2], const float a[2])
#define BLI_SCOPED_DEFER(function_to_defer)
#define STR_ELEM(...)
Definition BLI_string.h:656
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define INIT_MINMAX2(min, max)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define AT
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
void DEG_relations_tag_update(Main *bmain)
@ NODE_INTERFACE_PANEL_DEFAULT_CLOSED
@ NODE_DO_OUTPUT
@ NODE_SELECT
@ NTREE_GEOMETRY
@ SOCK_IN
@ SOCK_COLLAPSED
@ SOCK_HIDDEN
@ NODE_PANEL_COLLAPSED
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
bNodeTree * ED_node_tree_get(SpaceNode *snode, int level)
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:548
void ED_node_tree_pop(ARegion *region, SpaceNode *snode)
void ED_node_tree_push(ARegion *region, SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
bool ED_node_is_shader(SpaceNode *snode)
Definition node_edit.cc:553
bool ED_node_is_geometry(SpaceNode *snode)
Definition node_edit.cc:563
bool ED_node_is_texture(SpaceNode *snode)
Definition node_edit.cc:558
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
bool ED_operator_node_editable(bContext *C)
bool ED_operator_node_active(bContext *C)
Read Guarded memory(de)allocation.
struct blender::bke::bNodeTreeType * ntreeType_Shader
struct blender::bke::bNodeTreeType * ntreeType_Texture
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#define C
Definition RandGen.cpp:29
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiItemEnumO(uiLayout *layout, blender::StringRefNull opname, std::optional< blender::StringRef > name, int icon, blender::StringRefNull propname, int value)
void uiLayoutSetOperatorContext(uiLayout *layout, wmOperatorCallContext opcontext)
#define NC_NODE
Definition WM_types.hh:391
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_SCENE
Definition WM_types.hh:375
#define NA_ADDED
Definition WM_types.hh:583
#define ND_NODES
Definition WM_types.hh:433
#define NA_EDITED
Definition WM_types.hh:581
ReportList * reports
Definition WM_types.hh:1025
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define ND_NODE_GIZMO
Definition WM_types.hh:513
std::optional< Value > lookup_try(const Key &key) const
Definition BLI_map.hh:531
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
int64_t size() const
Definition BLI_array.hh:245
IndexRange index_range() const
Definition BLI_array.hh:349
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:639
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
ItemIterator items() const &
Definition BLI_map.hh:902
static RandomNumberGenerator from_random_seed()
Definition rand.cc:288
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
constexpr const char * data() const
bool remove(const Key &key)
int64_t size() const
bool contains(const Key &key) const
int64_t remove_if(Predicate &&predicate)
int64_t size() const
void append(const T &value)
bool is_empty() const
virtual const int & get_corresponding_output_id(const bNode &input_bnode) const =0
Vector< ItemDeclaration * > root_items
#define INT32_MAX
#define MEM_SAFE_FREE(v)
#define ID_IS_EDITABLE(_id)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
bNodeTreeInterfaceSocket * add_interface_socket_from_node(bNodeTree &ntree, const bNode &from_node, const bNodeSocket &from_sock, StringRef socket_type, StringRef name)
bNode * node_find_node_by_name(bNodeTree &ntree, StringRefNull name)
Definition node.cc:3607
const bNodeZoneType * zone_type_by_node_type(const int node_type)
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:5242
void node_tree_free_tree(bNodeTree &ntree)
Definition node.cc:4718
bNodeTree * node_tree_copy_tree(Main *bmain, const bNodeTree &ntree)
Definition node.cc:4391
void node_remove_node(Main *bmain, bNodeTree &ntree, bNode &node, bool do_id_user, bool remove_animation=true)
Definition node.cc:4649
bNode * node_add_node(const bContext *C, bNodeTree &ntree, StringRef idname)
Definition node.cc:3788
bNode * node_get_active(bNodeTree &ntree)
Definition node.cc:4957
void node_unique_id(bNodeTree &ntree, bNode &node)
Definition node.cc:3770
bNode * node_copy_with_mapping(bNodeTree *dst_tree, const bNode &node_src, int flag, bool use_unique, Map< const bNodeSocket *, bNodeSocket * > &new_socket_map)
Definition node.cc:3857
bNode * node_copy(bNodeTree *dst_tree, const bNode &src_node, int flag, bool use_unique)
Definition node.cc:4070
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:4124
Span< const bNodeZoneType * > all_zone_types()
bool node_tree_contains_tree(const bNodeTree &tree_to_search_in, const bNodeTree &tree_to_search_for)
Definition node.cc:4935
void node_detach_node(bNodeTree &ntree, bNode &node)
Definition node.cc:4263
void node_rebuild_id_vector(bNodeTree &node_tree)
Definition node.cc:4568
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3804
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
Span< int > all_zone_input_node_types()
bNodeTree * node_tree_add_tree(Main *bmain, StringRef name, StringRef idname)
Definition node.cc:4362
bool node_declaration_ensure(bNodeTree &ntree, bNode &node)
Definition node.cc:5090
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4996
void node_unique_name(bNodeTree &ntree, bNode &node)
Definition node.cc:3764
bool node_link_is_hidden(const bNodeLink &link)
Definition node.cc:4159
static bool node_default_group_width_set_poll(bContext *C)
static bNode * node_group_make_from_node_declaration(bContext &C, bNodeTree &ntree, bNode &src_node, const StringRef node_idname)
static bool prefer_node_for_interface_name(const bNode &node)
static void add_node_group_interface_from_declaration_recursive(bNodeTree &group, const bNode &src_node, const nodes::ItemDeclaration &item_decl, bNodeTreeInterfacePanel *parent, WrapperNodeGroupMapping &r_mapping)
static bool node_group_operator_active_poll(bContext *C)
Definition node_group.cc:74
static bNodeTreeInterfaceSocket * add_interface_from_socket(const bNodeTree &original_tree, bNodeTree &tree_for_interface, const bNodeSocket &socket)
static AnimationBasePathChange * animation_basepath_change_new(const StringRef src_basepath, const StringRef dst_basepath)
static VectorSet< bNode * > get_nodes_to_group(bNodeTree &node_tree, bNode *group_node)
static wmOperatorStatus node_group_insert_exec(bContext *C, wmOperator *op)
static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree, bNode *gnode, const VectorSet< bNode * > &nodes_to_move)
static wmOperatorStatus node_default_group_width_set_exec(bContext *C, wmOperator *)
bool node_deselect_all(bNodeTree &node_tree)
static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
static wmOperatorStatus node_group_separate_invoke(bContext *C, wmOperator *, const wmEvent *)
static bool node_group_make_test_selected(bNodeTree &ntree, const VectorSet< bNode * > &nodes_to_group, const StringRef ntree_idname, ReportList &reports)
void NODE_OT_group_make(wmOperatorType *ot)
static void update_nested_node_refs_after_moving_nodes_into_group(bNodeTree &ntree, bNodeTree &group, bNode &gnode, const Map< int32_t, int32_t > &node_identifier_map)
static bNode * node_group_get_active(bContext *C, const StringRef node_idname)
static const EnumPropertyItem node_group_separate_types[]
void NODE_OT_group_insert(wmOperatorType *ot)
static bool node_group_separate_selected(Main &bmain, bNodeTree &ntree, bNodeTree &ngroup, const float2 &offset, const bool make_copy)
static wmOperatorStatus node_group_ungroup_exec(bContext *C, wmOperator *)
static const bNodeSocket & find_socket_to_use_for_interface(const bNodeTree &node_tree, const bNodeSocket &socket)
static wmOperatorStatus node_group_separate_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_group_edit_exec(bContext *C, wmOperator *op)
VectorSet< bNode * > get_selected_nodes(bNodeTree &node_tree)
static bool node_group_operator_editable(bContext *C)
Definition node_group.cc:94
float2 space_node_group_offset(const SpaceNode &snode)
static wmOperatorStatus node_group_make_exec(bContext *C, wmOperator *op)
static void get_min_max_of_nodes(const Span< bNode * > nodes, const bool use_size, float2 &min, float2 &max)
void NODE_OT_group_edit(wmOperatorType *ot)
static void update_nested_node_refs_after_ungroup(bNodeTree &ntree, const bNodeTree &ngroup, const bNode &gnode, const Map< int32_t, int32_t > &node_identifier_map)
void NODE_OT_group_separate(wmOperatorType *ot)
static bNode * node_group_make_from_nodes(const bContext &C, bNodeTree &ntree, const VectorSet< bNode * > &nodes_to_group, const StringRef ntype, const StringRef ntreetype)
static void remap_pairing(bNodeTree &dst_tree, Span< bNode * > nodes, const Map< int32_t, int32_t > &identifier_map)
void NODE_OT_default_group_width_set(wmOperatorType *ot)
void NODE_OT_group_ungroup(wmOperatorType *ot)
static bNodeTree * node_group_make_wrapper(const bContext &C, const bNodeTree &src_tree, const bNode &src_node, WrapperNodeGroupMapping &r_mapping)
StringRef node_group_idname(const bContext *C)
static StringRef group_ntree_idname(bContext *C)
static void animation_basepath_change_free(AnimationBasePathChange *basepath_change)
T midpoint(const T &a, const T &b)
void min_max(const T &value, T &min, T &max)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
VecBase< float, 2 > float2
void uninitialized_copy_n(const T *src, int64_t n, T *dst)
bNodeSocket * node_group_output_find_socket(bNode *node, const StringRef identifier)
bNodeSocket * node_group_input_find_socket(bNode *node, const StringRef identifier)
bNodeSocket * node_group_find_input_socket(bNode *groupnode, const blender::StringRef identifier)
bNodeSocket * node_group_find_output_socket(bNode *groupnode, const blender::StringRef identifier)
blender::bke::bNodeTreeType * ntreeType_Composite
blender::bke::bNodeTreeType * ntreeType_Geometry
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1014
#define min(a, b)
Definition sort.cc:36
bAction * action
const char * dst_basepath
const char * src_basepath
Definition DNA_ID.h:404
void * last
void * first
char tree_idname[64]
ListBase treepath
struct bNodeTree * edittree
struct bNodeTree * nodetree
bNestedNodePath path
char idname[64]
struct bNodeTree * nodetree
struct bNodeTreePath * prev
bNodeTreeRuntimeHandle * runtime
char idname[64]
int nested_node_refs_num
bNestedNodeRef * nested_node_refs
int default_group_node_width
bNodeTreeInterface tree_interface
ListBase nodes
ListBase links
struct AnimData * adt
float location[2]
float width
struct ID * id
struct bNode * parent
char name[64]
char idname[64]
int32_t identifier
bNodeSocket * get_new_input(const bNodeSocket *old_socket, bNode &new_node) const
Map< const bNodeSocket *, int > new_index_by_src_socket
bNodeSocket * get_new_output(const bNodeSocket *old_socket, bNode &new_node) const
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4226
wmOperatorType * ot
Definition wm_files.cc:4225
uint8_t flag
Definition wm_window.cc:139