Blender V4.5
node_templates.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10#include <cstring>
11#include <optional>
12
13#include <fmt/format.h>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_node_types.h"
18#include "DNA_screen_types.h"
19
20#include "BLI_listbase.h"
21#include "BLI_string.h"
22#include "BLI_string_utf8.h"
23#include "BLI_vector.hh"
24
25#include "BLT_translation.hh"
26
27#include "BKE_context.hh"
28#include "BKE_lib_id.hh"
29#include "BKE_main.hh"
31#include "BKE_node_runtime.hh"
33
34#include "RNA_access.hh"
35#include "RNA_prototypes.hh"
36
38#include "NOD_socket.hh"
39
40#include "../interface/interface_intern.hh" /* XXX bad level */
41#include "UI_interface.hh"
42
43#include "ED_node.hh" /* own include */
44#include "node_intern.hh"
45
46#include "ED_undo.hh"
47
48#include "WM_api.hh"
49
51
53
54/************************* Node Socket Manipulation **************************/
55
56/* describes an instance of a node type and a specific socket to link */
58 int socket_index; /* index for linking */
59 int socket_type; /* socket type for compatibility check */
60 const char *socket_name; /* ui label of the socket */
61 const char *node_name; /* ui label of the node */
62
63 /* extra settings */
64 bNodeTree *ngroup; /* group node tree */
65};
66
68{
69 item.socket_index = -1;
71 item.socket_name = nullptr;
72 item.node_name = nullptr;
73 item.ngroup = nullptr;
74}
75
76/* Compare an existing node to a link item to see if it can be reused.
77 * item must be for the same node type!
78 * XXX should become a node type callback
79 */
80static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
81{
82 if (node->is_group()) {
83 return (node->id == (ID *)item->ngroup);
84 }
85 return true;
86}
87
88static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item)
89{
90 if (node->is_group()) {
91 node->id = (ID *)item->ngroup;
93 }
94 else {
95 /* nothing to do for now */
96 }
97
98 if (node->id) {
99 id_us_plus(node->id);
100 }
101}
102
103static void node_tag_recursive(bNode *node)
104{
105 if (!node || (node->flag & NODE_TEST)) {
106 return; /* in case of cycles */
107 }
108
109 node->flag |= NODE_TEST;
110
112 if (input->link) {
113 node_tag_recursive(input->link->fromnode);
114 }
115 }
116}
117
118static void node_clear_recursive(bNode *node)
119{
120 if (!node || !(node->flag & NODE_TEST)) {
121 return; /* in case of cycles */
122 }
123
124 node->flag &= ~NODE_TEST;
125
127 if (input->link) {
128 node_clear_recursive(input->link->fromnode);
129 }
130 }
131}
132
133static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
134{
135 bNode *node, *next;
136
137 if (!rem_node) {
138 return;
139 }
140
141 /* tag linked nodes to be removed */
142 for (bNode *node : ntree->all_nodes()) {
143 node->flag &= ~NODE_TEST;
144 }
145
146 node_tag_recursive(rem_node);
147
148 /* clear tags on nodes that are still used by other nodes */
149 for (bNode *node : ntree->all_nodes()) {
150 if (!(node->flag & NODE_TEST)) {
151 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
152 if (sock->link && sock->link->fromnode != rem_node) {
153 node_clear_recursive(sock->link->fromnode);
154 }
155 }
156 }
157 }
158
159 /* remove nodes */
160 for (node = (bNode *)ntree->nodes.first; node; node = next) {
161 next = node->next;
162
163 if (node->flag & NODE_TEST) {
164 bke::node_remove_node(bmain, *ntree, *node, true);
165 }
166 }
167}
168
169/* disconnect socket from the node it is connected to */
170static void node_socket_disconnect(Main *bmain,
171 bNodeTree *ntree,
172 bNode *node_to,
173 bNodeSocket *sock_to)
174{
175 if (!sock_to->link) {
176 return;
177 }
178
179 bke::node_remove_link(ntree, *sock_to->link);
180 sock_to->flag |= SOCK_COLLAPSED;
181
183 BKE_main_ensure_invariants(*bmain, ntree->id);
184}
185
186/* remove all nodes connected to this socket, if they aren't connected to other nodes */
187static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
188{
189 if (!sock_to->link) {
190 return;
191 }
192
193 node_remove_linked(bmain, ntree, sock_to->link->fromnode);
194 sock_to->flag |= SOCK_COLLAPSED;
195
197 BKE_main_ensure_invariants(*bmain, ntree->id);
198}
199
200/* add new node connected to this socket, or replace an existing one */
202 bNodeTree *ntree,
203 bNode *node_to,
204 bNodeSocket *sock_to,
205 int type,
206 NodeLinkItem *item)
207{
208 Main *bmain = CTX_data_main(C);
209 bNode *node_from;
210 bNodeSocket *sock_from_tmp;
211 bNode *node_prev = nullptr;
212
213 /* unlink existing node */
214 if (sock_to->link) {
215 node_prev = sock_to->link->fromnode;
216 bke::node_remove_link(ntree, *sock_to->link);
217 }
218
219 /* find existing node that we can use */
220 for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) {
221 if (node_from->type_legacy == type) {
222 break;
223 }
224 }
225
226 if (node_from) {
227 if (node_from->inputs.first || node_from->typeinfo->draw_buttons ||
228 node_from->typeinfo->draw_buttons_ex)
229 {
230 node_from = nullptr;
231 }
232 }
233
234 if (node_prev && node_prev->type_legacy == type && node_link_item_compare(node_prev, item)) {
235 /* keep the previous node if it's the same type */
236 node_from = node_prev;
237 }
238 else if (!node_from) {
239 node_from = bke::node_add_static_node(C, *ntree, type);
240 if (node_prev != nullptr) {
241 /* If we're replacing existing node, use its location. */
242 node_from->location[0] = node_prev->location[0];
243 node_from->location[1] = node_prev->location[1];
244 }
245 else {
246 sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
247 bke::node_position_relative(*node_from, *node_to, sock_from_tmp, *sock_to);
248 }
249
250 node_link_item_apply(ntree, node_from, item);
251 BKE_main_ensure_invariants(*bmain, ntree->id);
252 }
253
254 bke::node_set_active(*ntree, *node_from);
255
256 /* add link */
257 sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
258 bke::node_add_link(*ntree, *node_from, *sock_from_tmp, *node_to, *sock_to);
259 sock_to->flag &= ~SOCK_COLLAPSED;
260
261 /* copy input sockets from previous node */
262 if (node_prev && node_from != node_prev) {
263 LISTBASE_FOREACH (bNodeSocket *, sock_prev, &node_prev->inputs) {
264 LISTBASE_FOREACH (bNodeSocket *, sock_from, &node_from->inputs) {
265 if (bke::node_count_socket_links(*ntree, *sock_from) >=
266 bke::node_socket_link_limit(*sock_from))
267 {
268 continue;
269 }
270
271 if (STREQ(sock_prev->identifier, sock_from->identifier) &&
272 sock_prev->type == sock_from->type)
273 {
274 bNodeLink *link = sock_prev->link;
275
276 if (link && link->fromnode) {
277 bke::node_add_link(*ntree, *link->fromnode, *link->fromsock, *node_from, *sock_from);
278 bke::node_remove_link(ntree, *link);
279 }
280
281 node_socket_copy_default_value(sock_from, sock_prev);
282 }
283 }
284 }
285
286 /* also preserve mapping for texture nodes */
287 if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE &&
288 node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE &&
289 /* White noise texture node does not have NodeTexBase. */
290 node_from->storage != nullptr && node_prev->storage != nullptr)
291 {
292 memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase));
293 }
294
295 /* remove node */
296 node_remove_linked(bmain, ntree, node_prev);
297 }
298
299 BKE_ntree_update_tag_node_property(ntree, node_from);
301 BKE_main_ensure_invariants(*bmain, ntree->id);
302}
303
304/****************************** Node Link Menu *******************************/
305
306// #define UI_NODE_LINK_ADD 0
307#define UI_NODE_LINK_DISCONNECT -1
308#define UI_NODE_LINK_REMOVE -2
309
322
324 int in_out,
325 std::optional<NodeDeclaration> &r_node_decl)
326{
328
329 if (arg->node_type->type_legacy == NODE_GROUP) {
330 LISTBASE_FOREACH (bNodeTree *, ngroup, &arg->bmain->nodetrees) {
331 if (BKE_id_name(ngroup->id)[0] == '.') {
332 /* Don't display hidden node groups, just like the add menu. */
333 continue;
334 }
335
336 const char *disabled_hint;
337 if ((ngroup->type != arg->ntree->type) ||
338 !bke::node_group_poll(arg->ntree, ngroup, &disabled_hint))
339 {
340 continue;
341 }
342
343 ngroup->ensure_interface_cache();
344 Span<bNodeTreeInterfaceSocket *> iosockets = (in_out == SOCK_IN ?
345 ngroup->interface_inputs() :
346 ngroup->interface_outputs());
347 for (const int index : iosockets.index_range()) {
348 bNodeTreeInterfaceSocket *iosock = iosockets[index];
349 NodeLinkItem item;
351 item.socket_index = index;
352 /* NOTE: int stemp->type is not fully reliable, not used for node group
353 * interface sockets. use the typeinfo->type instead.
354 */
355 const bke::bNodeSocketType *typeinfo = iosock->socket_typeinfo();
356 item.socket_type = typeinfo->type;
357 item.socket_name = iosock->name;
358 item.node_name = ngroup->id.name + 2;
359 item.ngroup = ngroup;
360
361 items.append(item);
362 }
363 }
364 }
365 else if (arg->node_type->declare != nullptr) {
366 using namespace blender;
367 using namespace blender::nodes;
368
369 r_node_decl.emplace(NodeDeclaration());
370 blender::nodes::build_node_declaration(*arg->node_type, *r_node_decl, nullptr, nullptr);
371 Span<SocketDeclaration *> socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs :
372 r_node_decl->outputs;
373 int index = 0;
374 for (const SocketDeclaration *socket_decl_ptr : socket_decls) {
375 const SocketDeclaration &socket_decl = *socket_decl_ptr;
376 NodeLinkItem item;
378 item.socket_index = index++;
379 item.socket_type = socket_decl.socket_type;
380 item.socket_name = socket_decl.name.c_str();
381 item.node_name = arg->node_type->ui_name.c_str();
382 items.append(item);
383 }
384 }
385 else {
386 bke::bNodeSocketTemplate *socket_templates = (in_out == SOCK_IN ? arg->node_type->inputs :
387 arg->node_type->outputs);
389 int i;
390
391 i = 0;
392 for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) {
393 NodeLinkItem item;
395 item.socket_index = i;
396 item.socket_type = stemp->type;
397 item.socket_name = stemp->name;
398 item.node_name = arg->node_type->ui_name.c_str();
399 items.append(item);
400 }
401 }
402
403 return items;
404}
405
406static void ui_node_link(bContext *C, void *arg_p, void *event_p)
407{
408 NodeLinkArg *arg = (NodeLinkArg *)arg_p;
409 Main *bmain = arg->bmain;
410 bNode *node_to = arg->node;
411 bNodeSocket *sock_to = arg->sock;
412 bNodeTree *ntree = arg->ntree;
413 int event = POINTER_AS_INT(event_p);
414
415 if (event == UI_NODE_LINK_DISCONNECT) {
416 node_socket_disconnect(bmain, ntree, node_to, sock_to);
417 }
418 else if (event == UI_NODE_LINK_REMOVE) {
419 node_socket_remove(bmain, ntree, node_to, sock_to);
420 }
421 else {
422 node_socket_add_replace(C, ntree, node_to, sock_to, arg->node_type->type_legacy, &arg->item);
423 }
424
425 ED_undo_push(C, "Node input modify");
426}
427
428static void ui_node_sock_name(const bNodeTree *ntree,
429 bNodeSocket *sock,
430 char name[UI_MAX_NAME_STR])
431{
432 if (sock->link && sock->link->fromnode) {
433 bNode *node = sock->link->fromnode;
434 const std::string node_name = bke::node_label(*ntree, *node);
435
436 if (BLI_listbase_is_empty(&node->inputs) && node->outputs.first != node->outputs.last) {
437 BLI_snprintf(name,
439 "%s | %s",
440 IFACE_(node_name.c_str()),
441 IFACE_(sock->link->fromsock->name));
442 }
443 else {
444 BLI_strncpy_utf8(name, IFACE_(node_name.c_str()), UI_MAX_NAME_STR);
445 }
446 }
447 else if (sock->type == SOCK_SHADER) {
449 }
450 else {
451 BLI_strncpy_utf8(name, IFACE_("Default"), UI_MAX_NAME_STR);
452 }
453}
454
455static int ui_compatible_sockets(int typeA, int typeB)
456{
457 return (typeA == typeB);
458}
459
460static int ui_node_item_name_compare(const void *a, const void *b)
461{
462 const bke::bNodeType *type_a = *(const bke::bNodeType **)a;
463 const bke::bNodeType *type_b = *(const bke::bNodeType **)b;
464 return BLI_strcasecmp_natural(type_a->ui_name.c_str(), type_b->ui_name.c_str());
465}
466
467static bool ui_node_item_special_poll(const bNodeTree * /*ntree*/, const bke::bNodeType *ntype)
468{
469 if (ntype->idname == "ShaderNodeUVAlongStroke") {
470 /* TODO(sergey): Currently we don't have Freestyle nodes edited from
471 * the buttons context, so can ignore its nodes completely.
472 *
473 * However, we might want to do some extra checks here later.
474 */
475 return false;
476 }
477 return true;
478}
479
480static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
481{
482 bNodeTree *ntree = arg->ntree;
483 bNodeSocket *sock = arg->sock;
484 uiLayout *layout = arg->layout;
485 uiLayout *column = nullptr;
486 uiBlock *block = uiLayoutGetBlock(layout);
487 uiBut *but;
488 NodeLinkArg *argN;
489 int first = 1;
490
491 /* generate array of node types sorted by UI name */
493
495 const char *disabled_hint;
496 if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) {
497 continue;
498 }
499
500 if (ntype->nclass != nclass) {
501 continue;
502 }
503
504 if (!ui_node_item_special_poll(ntree, ntype)) {
505 continue;
506 }
507
508 sorted_ntypes.append(ntype);
509 }
510
511 qsort(sorted_ntypes.data(),
512 sorted_ntypes.size(),
513 sizeof(bke::bNodeType *),
515
516 /* generate UI */
517 for (int j = 0; j < sorted_ntypes.size(); j++) {
518 bke::bNodeType *ntype = sorted_ntypes[j];
519 char name[UI_MAX_NAME_STR];
520 const char *cur_node_name = nullptr;
521 int num = 0;
522 int icon = ICON_NONE;
523
524 arg->node_type = ntype;
525
526 std::optional<blender::nodes::NodeDeclaration> node_decl;
527 Vector<NodeLinkItem> items = ui_node_link_items(arg, SOCK_OUT, node_decl);
528
529 for (const NodeLinkItem &item : items) {
530 if (ui_compatible_sockets(item.socket_type, sock->type)) {
531 num++;
532 }
533 }
534
535 for (const NodeLinkItem &item : items) {
536 if (!ui_compatible_sockets(item.socket_type, sock->type)) {
537 continue;
538 }
539
540 if (first) {
541 column = &layout->column(false);
542 UI_block_layout_set_current(block, column);
543
544 column->label(IFACE_(cname), ICON_NODE);
545 but = block->buttons.last().get();
546
547 first = 0;
548 }
549
550 if (num > 1) {
551 if (!cur_node_name || !STREQ(cur_node_name, item.node_name)) {
552 cur_node_name = item.node_name;
553 /* XXX Do not use uiLayout::label here,
554 * it would add an empty icon as we are in a menu! */
555 uiDefBut(block,
557 0,
558 IFACE_(cur_node_name),
559 0,
560 0,
561 UI_UNIT_X * 4,
562 UI_UNIT_Y,
563 nullptr,
564 0.0,
565 0.0,
566 "");
567 }
568
569 SNPRINTF(name, "%s", IFACE_(item.socket_name));
570 icon = ICON_BLANK1;
571 }
572 else {
573 STRNCPY_UTF8(name, IFACE_(item.node_name));
574 icon = ICON_NONE;
575 }
576
577 but = uiDefIconTextBut(block,
579 0,
580 icon,
581 name,
582 0,
583 0,
584 UI_UNIT_X * 4,
585 UI_UNIT_Y,
586 nullptr,
587 0.0,
588 0.0,
589 TIP_("Add node to input"));
590
591 argN = (NodeLinkArg *)MEM_dupallocN(arg);
592 argN->item = item;
593 UI_but_funcN_set(but, ui_node_link, argN, nullptr);
594 }
595 }
596}
597
598static void node_menu_column_foreach_cb(void *calldata, int nclass, const StringRefNull name)
599{
600 NodeLinkArg *arg = (NodeLinkArg *)calldata;
601
602 if (!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) {
603 ui_node_menu_column(arg, nclass, name.c_str());
604 }
605}
606
607static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
608{
609 Main *bmain = CTX_data_main(C);
610 Scene *scene = CTX_data_scene(C);
611 uiBlock *block = uiLayoutGetBlock(layout);
612 uiBut *but = (uiBut *)but_p;
613 uiLayout *split, *column;
614 NodeLinkArg *arg = (NodeLinkArg *)but->func_argN;
615 bNodeSocket *sock = arg->sock;
616 bke::bNodeTreeType *ntreetype = arg->ntree->typeinfo;
617
618 UI_block_layout_set_current(block, layout);
619 split = &layout->split(0.0f, false);
620
621 arg->bmain = bmain;
622 arg->scene = scene;
623 arg->layout = split;
624
625 if (ntreetype && ntreetype->foreach_nodeclass) {
627 }
628
629 column = &split->column(false);
630 UI_block_layout_set_current(block, column);
631
632 if (sock->link) {
633 column->label(IFACE_("Link"), ICON_NONE);
634 but = block->buttons.last().get();
636
637 but = uiDefBut(block,
639 0,
641 0,
642 0,
643 UI_UNIT_X * 4,
644 UI_UNIT_Y,
645 nullptr,
646 0.0,
647 0.0,
648 TIP_("Remove nodes connected to the input"));
650
651 but = uiDefBut(block,
653 0,
654 IFACE_("Disconnect"),
655 0,
656 0,
657 UI_UNIT_X * 4,
658 UI_UNIT_Y,
659 nullptr,
660 0.0,
661 0.0,
662 TIP_("Disconnect nodes connected to the input"));
665 }
666
668}
669
670} // namespace blender::ed::space_node
671
673 uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
674{
675 using namespace blender::ed::space_node;
676
677 uiBlock *block = uiLayoutGetBlock(layout);
678 NodeLinkArg *arg;
679 uiBut *but;
680 float socket_col[4];
681
682 arg = MEM_callocN<NodeLinkArg>("NodeLinkArg");
683 arg->ntree = ntree;
684 arg->node = node;
685 arg->sock = input;
687
688 PointerRNA node_ptr = RNA_pointer_create_discrete(&ntree->id, &RNA_Node, node);
689 node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col);
690
691 UI_block_layout_set_current(block, layout);
692
693 if (input->link || input->type == SOCK_SHADER || (input->flag & SOCK_HIDE_VALUE)) {
694 char name[UI_MAX_NAME_STR];
695 ui_node_sock_name(ntree, input, name);
696 but = uiDefMenuBut(
697 block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
698 }
699 else {
700 but = uiDefIconMenuBut(
701 block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
702 }
703
705 UI_but_node_link_set(but, input, socket_col);
707
708 but->poin = (char *)but;
709 but->func_argN = arg;
712
713 if (input->link && input->link->fromnode) {
714 if (input->link->fromnode->flag & NODE_ACTIVE_TEXTURE) {
715 but->flag |= UI_BUT_NODE_ACTIVE;
716 }
717 }
718}
719
720namespace blender::ed::space_node {
721
722/**************************** Node Tree Layout *******************************/
723
724static void ui_node_draw_input(uiLayout &layout,
725 bContext &C,
726 bNodeTree &ntree,
727 bNode &node,
729 int depth,
730 const char *panel_label);
731
733 bContext &C,
734 bNodeTree &ntree,
735 bNode &node,
736 const nodes::PanelDeclaration &panel_decl,
737 const int depth)
738{
739 const nodes::SocketDeclaration *panel_toggle_decl = panel_decl.panel_input_decl();
740 const std::string panel_id = fmt::format(
741 "{}_{}_{}", ntree.id.name, node.identifier, panel_decl.identifier);
742 const StringRef panel_translation_context = panel_decl.translation_context.has_value() ?
743 *panel_decl.translation_context :
744 "";
745 PanelLayout panel_layout = layout.panel(&C, panel_id.c_str(), panel_decl.default_collapsed);
746 if (panel_toggle_decl) {
747 uiLayoutSetPropSep(panel_layout.header, false);
748 uiLayoutSetPropDecorate(panel_layout.header, false);
750 &ntree.id, &RNA_NodeSocket, &node.socket_by_decl(*panel_toggle_decl));
751 panel_layout.header->prop(&toggle_ptr,
752 "default_value",
754 CTX_IFACE_(panel_translation_context, panel_decl.name),
755 ICON_NONE);
756 }
757 else {
758 panel_layout.header->label(CTX_IFACE_(panel_translation_context, panel_decl.name), ICON_NONE);
759 }
760
761 if (!panel_layout.body) {
762 return;
763 }
764 for (const nodes::ItemDeclaration *item_decl : panel_decl.items) {
765 if (item_decl == panel_toggle_decl) {
766 continue;
767 }
768 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl)) {
769 bNodeSocket &socket = node.socket_by_decl(*socket_decl);
770 if (socket_decl->custom_draw_fn) {
772 C,
773 *panel_layout.body,
774 ntree,
775 node,
776 socket,
777 RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node),
778 RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, &socket)};
779 (*socket_decl->custom_draw_fn)(params);
780 }
781 else if (socket_decl->in_out == SOCK_IN) {
783 *panel_layout.body, C, ntree, node, socket, depth, panel_decl.name.c_str());
784 }
785 }
786 else if (const auto *sub_panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl))
787 {
788 ui_node_draw_recursive(*panel_layout.body, C, ntree, node, *sub_panel_decl, depth + 1);
789 }
790 else if (const auto *layout_decl = dynamic_cast<const nodes::LayoutDeclaration *>(item_decl)) {
791 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
792 layout_decl->draw(panel_layout.body, &C, &nodeptr);
793 }
794 }
795}
796
798 uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth)
799{
800 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
801
802 /* Draw top-level node buttons. */
803 if (node.typeinfo->draw_buttons) {
804 if (node.type_legacy != NODE_GROUP) {
805 uiLayoutSetPropSep(&layout, true);
806 node.typeinfo->draw_buttons(&layout, &C, &nodeptr);
807 }
808 }
809
810 if (node.declaration()) {
811 const nodes::NodeDeclaration &node_decl = *node.declaration();
812 for (const nodes::ItemDeclaration *item_decl : node_decl.root_items) {
813 if (const auto *panel_decl = dynamic_cast<const nodes::PanelDeclaration *>(item_decl)) {
814 ui_node_draw_recursive(layout, C, ntree, node, *panel_decl, depth + 1);
815 }
816 else if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(item_decl))
817 {
818 bNodeSocket &socket = node.socket_by_decl(*socket_decl);
819 if (socket_decl->custom_draw_fn) {
821 C,
822 layout,
823 ntree,
824 node,
825 socket,
826 RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node),
827 RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, &socket)};
828 (*socket_decl->custom_draw_fn)(params);
829 }
830 else if (socket_decl->in_out == SOCK_IN) {
831 ui_node_draw_input(layout, C, ntree, node, socket, depth + 1, nullptr);
832 }
833 }
834 }
835 }
836 else {
837 /* Draw socket values using the flat inputs list. */
839 ui_node_draw_input(layout, C, ntree, node, *input, depth + 1, nullptr);
840 }
841 }
842}
843
844static void ui_node_draw_input(uiLayout &layout,
845 bContext &C,
846 bNodeTree &ntree,
847 bNode &node,
849 int depth,
850 const char *panel_label)
851{
852 uiBlock *block = uiLayoutGetBlock(&layout);
853 uiLayout *row = nullptr;
854 bool dependency_loop;
855
856 if (input.flag & SOCK_UNAVAIL) {
857 return;
858 }
859
860 /* to avoid eternal loops on cyclic dependencies */
861 node.flag |= NODE_TEST;
862 bNode *lnode = (input.link) ? input.link->fromnode : nullptr;
863
864 dependency_loop = (lnode && (lnode->flag & NODE_TEST));
865 if (dependency_loop) {
866 lnode = nullptr;
867 }
868
869 /* socket RNA pointer */
870 PointerRNA inputptr = RNA_pointer_create_discrete(&ntree.id, &RNA_NodeSocket, &input);
871 PointerRNA nodeptr = RNA_pointer_create_discrete(&ntree.id, &RNA_Node, &node);
872
873 row = &layout.row(true);
874
876 /* Decorations are added manually here. */
877 uiLayoutSetPropDecorate(row, false);
878 /* Empty decorator item for alignment. */
879 bool add_dummy_decorator = false;
880
881 {
882 uiLayout *sub = &split_wrapper.label_column->row(true);
883
884 if (depth > 0) {
886
887 if (lnode) {
888 /* Input linked to a node, we can expand/collapse if
889 * - linked node has inputs
890 * - linked node has dedicated button drawing
891 * - linked node has dedicated socket drawing */
892 bool can_expand = lnode->inputs.first;
893 if (lnode->type_legacy != NODE_GROUP) {
894 if (lnode->typeinfo->draw_buttons) {
895 can_expand = true;
896 }
897 else if (lnode->declaration()) {
898 const nodes::NodeDeclaration &lnode_decl = *lnode->declaration();
899 for (const nodes::ItemDeclaration *item_decl : lnode_decl.root_items) {
900 if (const auto *socket_decl = dynamic_cast<const nodes::SocketDeclaration *>(
901 item_decl))
902 {
903 if (socket_decl->custom_draw_fn) {
904 can_expand = true;
905 }
906 }
907 }
908 }
909 }
910 if (can_expand) {
911 int icon = (input.flag & SOCK_COLLAPSED) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT;
912 sub->prop(&inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon);
913 }
914 }
915
917 }
918
919 sub = &sub->row(true);
921 sub->label(node_socket_get_label(&input, panel_label), ICON_NONE);
922 }
923
924 if (dependency_loop) {
925 row->label(RPT_("Dependency Loop"), ICON_ERROR);
926 add_dummy_decorator = true;
927 }
928 else if (lnode) {
929 /* input linked to a node */
930 uiTemplateNodeLink(row, &C, &ntree, &node, &input);
931 add_dummy_decorator = true;
932
933 if (depth == 0 || !(input.flag & SOCK_COLLAPSED)) {
934 if (depth == 0) {
935 layout.separator();
936 }
937
938 ui_node_draw_node(layout, C, ntree, *lnode, depth);
939 }
940 }
941 else {
942 uiLayout *sub = &row->row(true);
943
944 uiTemplateNodeLink(sub, &C, &ntree, &node, &input);
945
946 if (input.flag & SOCK_HIDE_VALUE) {
947 add_dummy_decorator = true;
948 }
949 /* input not linked, show value */
950 else {
951 switch (input.type) {
952 case SOCK_VECTOR:
953 sub->separator();
954 sub = &sub->column(true);
956 case SOCK_FLOAT:
957 case SOCK_INT:
958 case SOCK_ROTATION:
959 case SOCK_BOOLEAN:
960 case SOCK_RGBA:
961 sub->prop(&inputptr, "default_value", UI_ITEM_NONE, "", ICON_NONE);
962 if (split_wrapper.decorate_column) {
964 split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
965 }
966 break;
967 case SOCK_STRING: {
968 const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
969 SpaceNode *snode = CTX_wm_space_node(&C);
970 if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
971 /* Only add the attribute search in the node editor, in other places there is not
972 * enough context. */
973 node_geometry_add_attribute_search_button(C, node, inputptr, *sub);
974 }
975 else {
976 sub->prop(&inputptr, "default_value", UI_ITEM_NONE, "", ICON_NONE);
977 }
978 if (split_wrapper.decorate_column) {
980 split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
981 }
982 break;
983 }
984 case SOCK_MENU:
985 sub->label(RPT_("Unsupported Menu Socket"), ICON_NONE);
986 break;
987 case SOCK_CUSTOM:
988 input.typeinfo->draw(&C, sub, &inputptr, &nodeptr, input.name);
989 break;
990 default:
991 add_dummy_decorator = true;
992 }
993 }
994 }
995
996 if (add_dummy_decorator && split_wrapper.decorate_column) {
997 uiItemDecoratorR(split_wrapper.decorate_column, nullptr, std::nullopt, 0);
998 }
999
1000 node_socket_add_tooltip(ntree, input, *row);
1001
1002 /* clear */
1003 node.flag &= ~NODE_TEST;
1004}
1005
1006} // namespace blender::ed::space_node
1007
1009 uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
1010{
1011 using namespace blender::ed::space_node;
1012
1013 if (!ntree) {
1014 return;
1015 }
1016 ntree->ensure_topology_cache();
1017
1018 /* clear for cycle check */
1019 for (bNode *tnode : ntree->all_nodes()) {
1020 tnode->flag &= ~NODE_TEST;
1021 }
1022
1023 if (input) {
1024 ui_node_draw_input(*layout, *C, *ntree, *node, *input, 0, nullptr);
1025 }
1026 else {
1027 ui_node_draw_node(*layout, *C, *ntree, *node, 0);
1028 }
1029}
SpaceNode * CTX_wm_space_node(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
void id_us_plus(ID *id)
Definition lib_id.cc:353
const char * BKE_id_name(const ID &id)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define NODE_GROUP
Definition BKE_node.hh:796
#define NODE_CLASS_LAYOUT
Definition BKE_node.hh:449
#define NODE_CLASS_GROUP
Definition BKE_node.hh:438
#define NODE_CLASS_TEXTURE
Definition BKE_node.hh:443
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
#define ATTR_FALLTHROUGH
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
ATTR_WARN_UNUSED_RESULT const size_t num
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
int char char int int int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
@ NTREE_GEOMETRY
@ SOCK_OUT
@ SOCK_IN
@ NODE_TEST
@ NODE_ACTIVE_TEXTURE
@ SOCK_HIDE_VALUE
@ SOCK_COLLAPSED
@ SOCK_UNAVAIL
struct bNode bNode
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_SHADER
@ SOCK_FLOAT
@ SOCK_CUSTOM
@ SOCK_ROTATION
@ SOCK_STRING
@ SOCK_RGBA
@ SOCK_MENU
struct bNodeTree bNodeTree
struct bNodeSocket bNodeSocket
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define UI_UNIT_Y
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, blender::StringRef str, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
void uiTemplateNodeLink(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_drawflag_enable(uiBut *but, int flag)
void UI_but_type_set_menu_from_pulldown(uiBut *but)
@ UI_BUT_ICON_LEFT
@ UI_BUT_TEXT_LEFT
uiBut * uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
#define UI_UNIT_X
@ UI_BTYPE_BUT
@ UI_BTYPE_LABEL
@ UI_BUT_NODE_ACTIVE
void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
void UI_but_node_link_set(uiBut *but, bNodeSocket *socket, const float draw_color[4])
@ UI_ITEM_R_ICON_ONLY
void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, std::optional< blender::StringRefNull > propname, int index)
uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
@ UI_LAYOUT_ALIGN_RIGHT
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void UI_block_layout_set_current(uiBlock *block, uiLayout *layout)
#define UI_MAX_NAME_STR
eNodeSocketDatatype socket_type
const T & last(const int64_t n=0) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const char * c_str() const
int64_t size() const
void append(const T &value)
Vector< ItemDeclaration * > root_items
std::optional< std::string > translation_context
Vector< ItemDeclaration * > items
const SocketDeclaration * panel_input_decl() const
#define input
#define RNA_NO_INDEX
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:5242
void node_remove_node(Main *bmain, bNodeTree &ntree, bNode &node, bool do_id_user, bool remove_animation=true)
Definition node.cc:4649
bool node_group_poll(const bNodeTree *nodetree, const bNodeTree *grouptree, const char **r_disabled_hint)
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:4124
Span< bNodeType * > node_types_get()
Definition node.cc:2779
int node_count_socket_links(const bNodeTree &ntree, const bNodeSocket &sock)
Definition node.cc:4946
void node_position_relative(bNode &from_node, const bNode &to_node, const bNodeSocket *from_sock, const bNodeSocket &to_sock)
Definition node.cc:4272
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
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4996
int node_socket_link_limit(const bNodeSocket &sock)
Definition node.cc:5025
static void node_clear_recursive(bNode *node)
static void node_socket_disconnect(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
static void node_link_item_apply(bNodeTree *ntree, bNode *node, NodeLinkItem *item)
static int ui_node_item_name_compare(const void *a, const void *b)
static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
static void node_tag_recursive(bNode *node)
static Vector< NodeLinkItem > ui_node_link_items(NodeLinkArg *arg, int in_out, std::optional< NodeDeclaration > &r_node_decl)
static void ui_node_draw_input(uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, bNodeSocket &input, int depth, const char *panel_label)
static void ui_node_sock_name(const bNodeTree *ntree, bNodeSocket *sock, char name[UI_MAX_NAME_STR])
void node_socket_color_get(const bContext &C, const bNodeTree &ntree, PointerRNA &node_ptr, const bNodeSocket &sock, float r_color[4])
static void node_socket_add_replace(const bContext *C, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to, int type, NodeLinkItem *item)
static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
static void ui_node_draw_node(uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth)
void node_geometry_add_attribute_search_button(const bContext &, const bNode &node, PointerRNA &socket_ptr, uiLayout &layout, const StringRef placeholder)
const char * node_socket_get_label(const bNodeSocket *socket, const char *panel_label)
Definition node_draw.cc:456
static int ui_compatible_sockets(int typeA, int typeB)
static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
static bool node_link_item_compare(bNode *node, NodeLinkItem *item)
static void ui_node_draw_recursive(uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, const nodes::PanelDeclaration &panel_decl, const int depth)
static void node_menu_column_foreach_cb(void *calldata, int nclass, const StringRefNull name)
static bool ui_node_item_special_poll(const bNodeTree *, const bke::bNodeType *ntype)
static void ui_node_link(bContext *C, void *arg_p, void *event_p)
void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
static void node_link_item_init(NodeLinkItem &item)
void build_node_declaration(const bke::bNodeType &typeinfo, NodeDeclaration &r_declaration, const bNodeTree *ntree, const bNode *node)
void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
#define UI_NODE_LINK_DISCONNECT
void uiTemplateNodeView(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
void uiTemplateNodeLink(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input)
#define UI_NODE_LINK_REMOVE
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * last
void * first
ListBase nodetrees
Definition BKE_main.hh:270
ID * owner_id
Definition RNA_types.hh:51
struct bNodeLink * link
bNodeTreeTypeHandle * typeinfo
ListBase nodes
float location[2]
bNodeTypeHandle * typeinfo
ListBase inputs
struct ID * id
int16_t type_legacy
struct bNode * next
void * storage
ListBase outputs
int32_t identifier
Compact definition of a node socket.
Definition BKE_node.hh:98
Defines a socket type.
Definition BKE_node.hh:152
eNodeSocketDatatype type
Definition BKE_node.hh:187
void(* foreach_nodeclass)(void *calldata, bNodeClassCallback func)
Definition BKE_node.hh:493
Defines a node type.
Definition BKE_node.hh:226
bNodeSocketTemplate * inputs
Definition BKE_node.hh:242
bNodeSocketTemplate * outputs
Definition BKE_node.hh:242
NodeDeclareFunction declare
Definition BKE_node.hh:355
blender::Vector< std::unique_ptr< uiBut > > buttons
uiButArgNFree func_argN_free_fn
uiButArgNCopy func_argN_copy_fn
void * func_argN
PanelLayout panel(const bContext *C, blender::StringRef idname, bool default_closed)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)