Blender  V3.3
node_templates.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <cstdlib>
8 #include <cstring>
9 #include <optional>
10 
11 #include "MEM_guardedalloc.h"
12 
13 #include "DNA_node_types.h"
14 #include "DNA_screen_types.h"
15 
16 #include "BLI_array.h"
17 #include "BLI_listbase.h"
18 #include "BLI_string.h"
19 #include "BLI_vector.hh"
20 
21 #include "BLT_translation.h"
22 
23 #include "BKE_context.h"
24 #include "BKE_lib_id.h"
25 #include "BKE_main.h"
26 #include "BKE_node_tree_update.h"
27 
28 #include "RNA_access.h"
29 #include "RNA_prototypes.h"
30 
31 #include "NOD_node_declaration.hh"
32 #include "NOD_socket.h"
34 
35 #include "../interface/interface_intern.h" /* XXX bad level */
36 #include "UI_interface.h"
37 
38 #include "ED_node.h" /* own include */
39 #include "node_intern.hh"
40 
41 #include "ED_undo.h"
42 
44 
45 namespace blender::ed::space_node {
46 
47 /************************* Node Socket Manipulation **************************/
48 
49 /* describes an instance of a node type and a specific socket to link */
50 struct NodeLinkItem {
51  int socket_index = -1; /* index for linking */
52  int socket_type = SOCK_CUSTOM; /* socket type for compatibility check */
53  const char *socket_name = nullptr; /* ui label of the socket */
54  const char *node_name = nullptr; /* ui label of the node */
55 
56  /* extra settings */
57  bNodeTree *ngroup = nullptr; /* group node tree */
58 };
59 
60 /* Compare an existing node to a link item to see if it can be reused.
61  * item must be for the same node type!
62  * XXX should become a node type callback
63  */
65 {
66  if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
67  return (node->id == (ID *)item->ngroup);
68  }
69  return true;
70 }
71 
73 {
74  if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
75  node->id = (ID *)item->ngroup;
77  }
78  else {
79  /* nothing to do for now */
80  }
81 
82  if (node->id) {
83  id_us_plus(node->id);
84  }
85 }
86 
88 {
90 
91  if (!node || (node->flag & NODE_TEST)) {
92  return; /* in case of cycles */
93  }
94 
95  node->flag |= NODE_TEST;
96 
97  for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
98  if (input->link) {
99  node_tag_recursive(input->link->fromnode);
100  }
101  }
102 }
103 
105 {
107 
108  if (!node || !(node->flag & NODE_TEST)) {
109  return; /* in case of cycles */
110  }
111 
112  node->flag &= ~NODE_TEST;
113 
114  for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
115  if (input->link) {
116  node_clear_recursive(input->link->fromnode);
117  }
118  }
119 }
120 
121 static void node_remove_linked(Main *bmain, bNodeTree *ntree, bNode *rem_node)
122 {
123  bNode *node, *next;
124  bNodeSocket *sock;
125 
126  if (!rem_node) {
127  return;
128  }
129 
130  /* tag linked nodes to be removed */
131  for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
132  node->flag &= ~NODE_TEST;
133  }
134 
135  node_tag_recursive(rem_node);
136 
137  /* clear tags on nodes that are still used by other nodes */
138  for (node = (bNode *)ntree->nodes.first; node; node = node->next) {
139  if (!(node->flag & NODE_TEST)) {
140  for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
141  if (sock->link && sock->link->fromnode != rem_node) {
143  }
144  }
145  }
146  }
147 
148  /* remove nodes */
149  for (node = (bNode *)ntree->nodes.first; node; node = next) {
150  next = node->next;
151 
152  if (node->flag & NODE_TEST) {
153  nodeRemoveNode(bmain, ntree, node, true);
154  }
155  }
156 }
157 
158 /* disconnect socket from the node it is connected to */
159 static void node_socket_disconnect(Main *bmain,
160  bNodeTree *ntree,
161  bNode *node_to,
162  bNodeSocket *sock_to)
163 {
164  if (!sock_to->link) {
165  return;
166  }
167 
168  nodeRemLink(ntree, sock_to->link);
169  sock_to->flag |= SOCK_COLLAPSED;
170 
172  ED_node_tree_propagate_change(nullptr, bmain, ntree);
173 }
174 
175 /* remove all nodes connected to this socket, if they aren't connected to other nodes */
176 static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to)
177 {
178  if (!sock_to->link) {
179  return;
180  }
181 
182  node_remove_linked(bmain, ntree, sock_to->link->fromnode);
183  sock_to->flag |= SOCK_COLLAPSED;
184 
186  ED_node_tree_propagate_change(nullptr, bmain, ntree);
187 }
188 
189 /* add new node connected to this socket, or replace an existing one */
190 static void node_socket_add_replace(const bContext *C,
191  bNodeTree *ntree,
192  bNode *node_to,
193  bNodeSocket *sock_to,
194  int type,
195  NodeLinkItem *item)
196 {
197  Main *bmain = CTX_data_main(C);
198  bNode *node_from;
199  bNodeSocket *sock_from_tmp;
200  bNode *node_prev = nullptr;
201 
202  /* unlink existing node */
203  if (sock_to->link) {
204  node_prev = sock_to->link->fromnode;
205  nodeRemLink(ntree, sock_to->link);
206  }
207 
208  /* find existing node that we can use */
209  for (node_from = (bNode *)ntree->nodes.first; node_from; node_from = node_from->next) {
210  if (node_from->type == type) {
211  break;
212  }
213  }
214 
215  if (node_from) {
216  if (node_from->inputs.first || node_from->typeinfo->draw_buttons ||
217  node_from->typeinfo->draw_buttons_ex) {
218  node_from = nullptr;
219  }
220  }
221 
222  if (node_prev && node_prev->type == type && node_link_item_compare(node_prev, item)) {
223  /* keep the previous node if it's the same type */
224  node_from = node_prev;
225  }
226  else if (!node_from) {
227  node_from = nodeAddStaticNode(C, ntree, type);
228  if (node_prev != nullptr) {
229  /* If we're replacing existing node, use its location. */
230  node_from->locx = node_prev->locx;
231  node_from->locy = node_prev->locy;
232  node_from->offsetx = node_prev->offsetx;
233  node_from->offsety = node_prev->offsety;
234  }
235  else {
236  sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
237  nodePositionRelative(node_from, node_to, sock_from_tmp, sock_to);
238  }
239 
240  node_link_item_apply(ntree, node_from, item);
242  }
243 
244  nodeSetActive(ntree, node_from);
245 
246  /* add link */
247  sock_from_tmp = (bNodeSocket *)BLI_findlink(&node_from->outputs, item->socket_index);
248  nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to);
249  sock_to->flag &= ~SOCK_COLLAPSED;
250 
251  /* copy input sockets from previous node */
252  if (node_prev && node_from != node_prev) {
253  bNodeSocket *sock_prev, *sock_from;
254 
255  for (sock_prev = (bNodeSocket *)node_prev->inputs.first; sock_prev;
256  sock_prev = sock_prev->next) {
257  for (sock_from = (bNodeSocket *)node_from->inputs.first; sock_from;
258  sock_from = sock_from->next) {
259  if (nodeCountSocketLinks(ntree, sock_from) >= nodeSocketLinkLimit(sock_from)) {
260  continue;
261  }
262 
263  if (STREQ(sock_prev->name, sock_from->name) && sock_prev->type == sock_from->type) {
264  bNodeLink *link = sock_prev->link;
265 
266  if (link && link->fromnode) {
267  nodeAddLink(ntree, link->fromnode, link->fromsock, node_from, sock_from);
268  nodeRemLink(ntree, link);
269  }
270 
271  node_socket_copy_default_value(sock_from, sock_prev);
272  }
273  }
274  }
275 
276  /* also preserve mapping for texture nodes */
277  if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE &&
278  node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE &&
279  /* White noise texture node does not have NodeTexBase. */
280  node_from->storage != nullptr && node_prev->storage != nullptr) {
281  memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase));
282  }
283 
284  /* remove node */
285  node_remove_linked(bmain, ntree, node_prev);
286  }
287 
290  ED_node_tree_propagate_change(nullptr, bmain, ntree);
291 }
292 
293 /****************************** Node Link Menu *******************************/
294 
295 // #define UI_NODE_LINK_ADD 0
296 #define UI_NODE_LINK_DISCONNECT -1
297 #define UI_NODE_LINK_REMOVE -2
298 
299 struct NodeLinkArg {
305 
308 
310 };
311 
313  int in_out,
314  std::optional<NodeDeclaration> &r_node_decl)
315 {
316  Vector<NodeLinkItem> items;
317 
318  /* XXX this should become a callback for node types! */
319  if (arg->node_type->type == NODE_GROUP) {
320  bNodeTree *ngroup;
321  int i;
322 
323  for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
324  ngroup = (bNodeTree *)ngroup->id.next) {
325  const char *disabled_hint;
326  if ((ngroup->type != arg->ntree->type) ||
327  !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) {
328  continue;
329  }
330  }
331 
332  i = 0;
333  for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup;
334  ngroup = (bNodeTree *)ngroup->id.next) {
335  const char *disabled_hint;
336  if ((ngroup->type != arg->ntree->type) ||
337  !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) {
338  continue;
339  }
340 
341  ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs);
342  bNodeSocket *stemp;
343  int index;
344  for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) {
345  NodeLinkItem item;
346  item.socket_index = index;
347  /* NOTE: int stemp->type is not fully reliable, not used for node group
348  * interface sockets. use the typeinfo->type instead.
349  */
350  item.socket_type = stemp->typeinfo->type;
351  item.socket_name = stemp->name;
352  item.node_name = ngroup->id.name + 2;
353  item.ngroup = ngroup;
354 
355  items.append(item);
356  }
357  }
358  }
359  else if (arg->node_type->declare != nullptr) {
360  using namespace blender;
361  using namespace blender::nodes;
362 
363  r_node_decl.emplace(NodeDeclaration());
364  NodeDeclarationBuilder node_decl_builder{*r_node_decl};
365  arg->node_type->declare(node_decl_builder);
366  Span<SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs() :
367  r_node_decl->outputs();
368  int index = 0;
369  for (const SocketDeclarationPtr &socket_decl_ptr : socket_decls) {
370  const SocketDeclaration &socket_decl = *socket_decl_ptr;
371  NodeLinkItem item;
372  item.socket_index = index++;
373  if (dynamic_cast<const decl::Float *>(&socket_decl)) {
374  item.socket_type = SOCK_FLOAT;
375  }
376  else if (dynamic_cast<const decl::Int *>(&socket_decl)) {
377  item.socket_type = SOCK_INT;
378  }
379  else if (dynamic_cast<const decl::Bool *>(&socket_decl)) {
380  item.socket_type = SOCK_BOOLEAN;
381  }
382  else if (dynamic_cast<const decl::Vector *>(&socket_decl)) {
383  item.socket_type = SOCK_VECTOR;
384  }
385  else if (dynamic_cast<const decl::Color *>(&socket_decl)) {
386  item.socket_type = SOCK_RGBA;
387  }
388  else if (dynamic_cast<const decl::String *>(&socket_decl)) {
389  item.socket_type = SOCK_STRING;
390  }
391  else if (dynamic_cast<const decl::Image *>(&socket_decl)) {
392  item.socket_type = SOCK_IMAGE;
393  }
394  else if (dynamic_cast<const decl::Texture *>(&socket_decl)) {
395  item.socket_type = SOCK_TEXTURE;
396  }
397  else if (dynamic_cast<const decl::Material *>(&socket_decl)) {
398  item.socket_type = SOCK_MATERIAL;
399  }
400  else if (dynamic_cast<const decl::Shader *>(&socket_decl)) {
401  item.socket_type = SOCK_SHADER;
402  }
403  else if (dynamic_cast<const decl::Collection *>(&socket_decl)) {
405  }
406  else if (dynamic_cast<const decl::Object *>(&socket_decl)) {
407  item.socket_type = SOCK_OBJECT;
408  }
409  else {
410  item.socket_type = SOCK_CUSTOM;
411  }
412  item.socket_name = socket_decl.name().c_str();
413  item.node_name = arg->node_type->ui_name;
414  items.append(item);
415  }
416  }
417  else {
418  bNodeSocketTemplate *socket_templates = (in_out == SOCK_IN ? arg->node_type->inputs :
419  arg->node_type->outputs);
420  bNodeSocketTemplate *stemp;
421  int i;
422 
423  i = 0;
424  for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) {
425  NodeLinkItem item;
426  item.socket_index = i;
427  item.socket_type = stemp->type;
428  item.socket_name = stemp->name;
429  item.node_name = arg->node_type->ui_name;
430  items.append(item);
431  }
432  }
433 
434  return items;
435 }
436 
437 static void ui_node_link(bContext *C, void *arg_p, void *event_p)
438 {
439  NodeLinkArg *arg = (NodeLinkArg *)arg_p;
440  Main *bmain = arg->bmain;
441  bNode *node_to = arg->node;
442  bNodeSocket *sock_to = arg->sock;
443  bNodeTree *ntree = arg->ntree;
444  int event = POINTER_AS_INT(event_p);
445 
446  if (event == UI_NODE_LINK_DISCONNECT) {
447  node_socket_disconnect(bmain, ntree, node_to, sock_to);
448  }
449  else if (event == UI_NODE_LINK_REMOVE) {
450  node_socket_remove(bmain, ntree, node_to, sock_to);
451  }
452  else {
453  node_socket_add_replace(C, ntree, node_to, sock_to, arg->node_type->type, &arg->item);
454  }
455 
456  ED_undo_push(C, "Node input modify");
457 }
458 
459 static void ui_node_sock_name(const bNodeTree *ntree,
460  bNodeSocket *sock,
461  char name[UI_MAX_NAME_STR])
462 {
463  if (sock->link && sock->link->fromnode) {
464  bNode *node = sock->link->fromnode;
465  char node_name[UI_MAX_NAME_STR];
466 
467  nodeLabel(ntree, node, node_name, sizeof(node_name));
468 
469  if (BLI_listbase_is_empty(&node->inputs) && node->outputs.first != node->outputs.last) {
470  BLI_snprintf(
471  name, UI_MAX_NAME_STR, "%s | %s", IFACE_(node_name), IFACE_(sock->link->fromsock->name));
472  }
473  else {
474  BLI_strncpy(name, IFACE_(node_name), UI_MAX_NAME_STR);
475  }
476  }
477  else if (sock->type == SOCK_SHADER) {
478  BLI_strncpy(name, IFACE_("None"), UI_MAX_NAME_STR);
479  }
480  else {
481  BLI_strncpy(name, IFACE_("Default"), UI_MAX_NAME_STR);
482  }
483 }
484 
485 static int ui_compatible_sockets(int typeA, int typeB)
486 {
487  return (typeA == typeB);
488 }
489 
490 static int ui_node_item_name_compare(const void *a, const void *b)
491 {
492  const bNodeType *type_a = *(const bNodeType **)a;
493  const bNodeType *type_b = *(const bNodeType **)b;
494  return BLI_strcasecmp_natural(type_a->ui_name, type_b->ui_name);
495 }
496 
497 static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype)
498 {
499  if (STREQ(ntype->idname, "ShaderNodeUVAlongStroke")) {
500  /* TODO(sergey): Currently we don't have Freestyle nodes edited from
501  * the buttons context, so can ignore its nodes completely.
502  *
503  * However, we might want to do some extra checks here later.
504  */
505  return false;
506  }
507  return true;
508 }
509 
510 static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname)
511 {
512  bNodeTree *ntree = arg->ntree;
513  bNodeSocket *sock = arg->sock;
514  uiLayout *layout = arg->layout;
515  uiLayout *column = nullptr;
516  uiBlock *block = uiLayoutGetBlock(layout);
517  uiBut *but;
518  NodeLinkArg *argN;
519  int first = 1;
520 
521  /* generate array of node types sorted by UI name */
522  blender::Vector<bNodeType *> sorted_ntypes;
523 
524  NODE_TYPES_BEGIN (ntype) {
525  const char *disabled_hint;
526  if (!(ntype->poll && ntype->poll(ntype, ntree, &disabled_hint))) {
527  continue;
528  }
529 
530  if (ntype->nclass != nclass) {
531  continue;
532  }
533 
534  if (!ui_node_item_special_poll(ntree, ntype)) {
535  continue;
536  }
537 
538  sorted_ntypes.append(ntype);
539  }
541 
542  qsort(
543  sorted_ntypes.data(), sorted_ntypes.size(), sizeof(bNodeType *), ui_node_item_name_compare);
544 
545  /* generate UI */
546  for (int j = 0; j < sorted_ntypes.size(); j++) {
547  bNodeType *ntype = sorted_ntypes[j];
548  char name[UI_MAX_NAME_STR];
549  const char *cur_node_name = nullptr;
550  int num = 0;
551  int icon = ICON_NONE;
552 
553  arg->node_type = ntype;
554 
555  std::optional<blender::nodes::NodeDeclaration> node_decl;
556  Vector<NodeLinkItem> items = ui_node_link_items(arg, SOCK_OUT, node_decl);
557 
558  for (const NodeLinkItem &item : items) {
559  if (ui_compatible_sockets(item.socket_type, sock->type)) {
560  num++;
561  }
562  }
563 
564  for (const NodeLinkItem &item : items) {
565  if (!ui_compatible_sockets(item.socket_type, sock->type)) {
566  continue;
567  }
568 
569  if (first) {
570  column = uiLayoutColumn(layout, false);
571  UI_block_layout_set_current(block, column);
572 
573  uiItemL(column, IFACE_(cname), ICON_NODE);
574  but = (uiBut *)block->buttons.last;
575 
576  first = 0;
577  }
578 
579  if (num > 1) {
580  if (!cur_node_name || !STREQ(cur_node_name, item.node_name)) {
581  cur_node_name = item.node_name;
582  /* XXX Do not use uiItemL here,
583  * it would add an empty icon as we are in a menu! */
584  uiDefBut(block,
586  0,
587  IFACE_(cur_node_name),
588  0,
589  0,
590  UI_UNIT_X * 4,
591  UI_UNIT_Y,
592  nullptr,
593  0.0,
594  0.0,
595  0.0,
596  0.0,
597  "");
598  }
599 
600  BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(item.socket_name));
601  icon = ICON_BLANK1;
602  }
603  else {
604  BLI_strncpy(name, IFACE_(item.node_name), UI_MAX_NAME_STR);
605  icon = ICON_NONE;
606  }
607 
608  but = uiDefIconTextBut(block,
609  UI_BTYPE_BUT,
610  0,
611  icon,
612  name,
613  0,
614  0,
615  UI_UNIT_X * 4,
616  UI_UNIT_Y,
617  nullptr,
618  0.0,
619  0.0,
620  0.0,
621  0.0,
622  TIP_("Add node to input"));
623 
624  argN = (NodeLinkArg *)MEM_dupallocN(arg);
625  argN->item = item;
626  UI_but_funcN_set(but, ui_node_link, argN, nullptr);
627  }
628  }
629 }
630 
631 static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name)
632 {
633  NodeLinkArg *arg = (NodeLinkArg *)calldata;
634 
635  if (!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) {
636  ui_node_menu_column(arg, nclass, name);
637  }
638 }
639 
640 static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
641 {
642  Main *bmain = CTX_data_main(C);
644  uiBlock *block = uiLayoutGetBlock(layout);
645  uiBut *but = (uiBut *)but_p;
646  uiLayout *split, *column;
647  NodeLinkArg *arg = (NodeLinkArg *)but->func_argN;
648  bNodeSocket *sock = arg->sock;
649  bNodeTreeType *ntreetype = arg->ntree->typeinfo;
650 
652  UI_block_layout_set_current(block, layout);
653  split = uiLayoutSplit(layout, 0.0f, false);
654 
655  arg->bmain = bmain;
656  arg->scene = scene;
657  arg->layout = split;
658 
659  if (ntreetype && ntreetype->foreach_nodeclass) {
660  ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb);
661  }
662 
663  column = uiLayoutColumn(split, false);
664  UI_block_layout_set_current(block, column);
665 
666  if (sock->link) {
667  uiItemL(column, IFACE_("Link"), ICON_NONE);
668  but = (uiBut *)block->buttons.last;
669  but->drawflag = UI_BUT_TEXT_LEFT;
670 
671  but = uiDefBut(block,
672  UI_BTYPE_BUT,
673  0,
674  IFACE_("Remove"),
675  0,
676  0,
677  UI_UNIT_X * 4,
678  UI_UNIT_Y,
679  nullptr,
680  0.0,
681  0.0,
682  0.0,
683  0.0,
684  TIP_("Remove nodes connected to the input"));
686 
687  but = uiDefBut(block,
688  UI_BTYPE_BUT,
689  0,
690  IFACE_("Disconnect"),
691  0,
692  0,
693  UI_UNIT_X * 4,
694  UI_UNIT_Y,
695  nullptr,
696  0.0,
697  0.0,
698  0.0,
699  0.0,
700  TIP_("Disconnect nodes connected to the input"));
703  }
704 
705  ui_node_menu_column(arg, NODE_CLASS_GROUP, N_("Group"));
706 }
707 
708 } // namespace blender::ed::space_node
709 
712 {
713  using namespace blender::ed::space_node;
714 
715  uiBlock *block = uiLayoutGetBlock(layout);
716  NodeLinkArg *arg;
717  uiBut *but;
718  float socket_col[4];
719 
720  arg = MEM_new<NodeLinkArg>("NodeLinkArg");
721  arg->ntree = ntree;
722  arg->node = node;
723  arg->sock = input;
724 
725  PointerRNA node_ptr;
726  RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
727  node_socket_color_get(*C, *ntree, node_ptr, *input, socket_col);
728 
729  UI_block_layout_set_current(block, layout);
730 
731  if (input->link || input->type == SOCK_SHADER || (input->flag & SOCK_HIDE_VALUE)) {
732  char name[UI_MAX_NAME_STR];
733  ui_node_sock_name(ntree, input, name);
734  but = uiDefMenuBut(
735  block, ui_template_node_link_menu, nullptr, name, 0, 0, UI_UNIT_X * 4, UI_UNIT_Y, "");
736  }
737  else {
738  but = uiDefIconMenuBut(
739  block, ui_template_node_link_menu, nullptr, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, "");
740  }
741 
743  UI_but_node_link_set(but, input, socket_col);
745 
746  but->poin = (char *)but;
747  but->func_argN = arg;
748 
749  if (input->link && input->link->fromnode) {
750  if (input->link->fromnode->flag & NODE_ACTIVE_TEXTURE) {
751  but->flag |= UI_BUT_NODE_ACTIVE;
752  }
753  }
754 }
755 
756 namespace blender::ed::space_node {
757 
758 /**************************** Node Tree Layout *******************************/
759 
760 static void ui_node_draw_input(
761  uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth);
762 
763 static void ui_node_draw_node(
764  uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth)
765 {
767  PointerRNA nodeptr;
768 
769  RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
770 
771  if (node->typeinfo->draw_buttons) {
772  if (node->type != NODE_GROUP) {
773  uiLayoutSetPropSep(layout, true);
774  node->typeinfo->draw_buttons(layout, C, &nodeptr);
775  }
776  }
777 
778  for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
779  ui_node_draw_input(layout, C, ntree, node, input, depth + 1);
780  }
781 }
782 
783 static void ui_node_draw_input(
784  uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth)
785 {
786  PointerRNA inputptr, nodeptr;
787  uiBlock *block = uiLayoutGetBlock(layout);
788  uiLayout *row = nullptr;
789  bool dependency_loop;
790 
791  if (input->flag & SOCK_UNAVAIL) {
792  return;
793  }
794 
795  /* to avoid eternal loops on cyclic dependencies */
796  node->flag |= NODE_TEST;
797  bNode *lnode = (input->link) ? input->link->fromnode : nullptr;
798 
799  dependency_loop = (lnode && (lnode->flag & NODE_TEST));
800  if (dependency_loop) {
801  lnode = nullptr;
802  }
803 
804  /* socket RNA pointer */
805  RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr);
806  RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
807 
808  row = uiLayoutRow(layout, true);
809  /* Decorations are added manually here. */
810  uiLayoutSetPropDecorate(row, false);
811 
813  /* Empty decorator item for alignment. */
814  bool add_dummy_decorator = false;
815 
816  {
817  uiLayout *sub = uiLayoutRow(split_wrapper.label_column, true);
818 
819  if (depth > 0) {
821 
822  if (lnode &&
823  (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) {
824  int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT :
825  ICON_DISCLOSURE_TRI_DOWN;
826  uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon);
827  }
828 
830  }
831 
832  sub = uiLayoutRow(sub, true);
834  uiItemL(sub, IFACE_(input->name), ICON_NONE);
835  }
836 
837  if (dependency_loop) {
838  uiItemL(row, IFACE_("Dependency Loop"), ICON_ERROR);
839  add_dummy_decorator = true;
840  }
841  else if (lnode) {
842  /* input linked to a node */
844  add_dummy_decorator = true;
845 
846  if (depth == 0 || !(input->flag & SOCK_COLLAPSED)) {
847  if (depth == 0) {
848  uiItemS(layout);
849  }
850 
851  ui_node_draw_node(layout, C, ntree, lnode, depth);
852  }
853  }
854  else {
855  uiLayout *sub = uiLayoutRow(row, true);
856 
858 
859  if (input->flag & SOCK_HIDE_VALUE) {
860  add_dummy_decorator = true;
861  }
862  /* input not linked, show value */
863  else {
864  switch (input->type) {
865  case SOCK_VECTOR:
866  uiItemS(sub);
867  sub = uiLayoutColumn(sub, true);
869  case SOCK_FLOAT:
870  case SOCK_INT:
871  case SOCK_BOOLEAN:
872  case SOCK_RGBA:
873  uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
875  split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
876  break;
877  case SOCK_STRING: {
878  const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
879  SpaceNode *snode = CTX_wm_space_node(C);
880  if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
881  /* Only add the attribute search in the node editor, in other places there is not
882  * enough context. */
884  }
885  else {
886  uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
887  }
889  split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX);
890  break;
891  }
892  default:
893  add_dummy_decorator = true;
894  }
895  }
896  }
897 
898  if (add_dummy_decorator) {
899  uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0);
900  }
901 
903 
904  /* clear */
905  node->flag &= ~NODE_TEST;
906 }
907 
908 } // namespace blender::ed::space_node
909 
912 {
913  using namespace blender::ed::space_node;
914 
915  bNode *tnode;
916 
917  if (!ntree) {
918  return;
919  }
920 
921  /* clear for cycle check */
922  for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) {
923  tnode->flag &= ~NODE_TEST;
924  }
925 
926  if (input) {
927  ui_node_draw_input(layout, C, ntree, node, input, 0);
928  }
929  else {
930  ui_node_draw_node(layout, C, ntree, node, 0);
931  }
932 }
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct SpaceNode * CTX_wm_space_node(const bContext *C)
Definition: context.c:878
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
void id_us_plus(struct ID *id)
Definition: lib_id.c:305
#define NODE_TYPES_BEGIN(ntype)
Definition: BKE_node.h:567
bool nodeGroupPoll(struct bNodeTree *nodetree, struct bNodeTree *grouptree, const char **r_disabled_hint)
Definition: node_common.cc:85
#define NODE_CUSTOM_GROUP
Definition: BKE_node.h:989
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link)
Definition: node.cc:2338
void nodeRemoveNode(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node, bool do_id_user)
Definition: node.cc:3011
int nodeSocketLinkLimit(const struct bNodeSocket *sock)
void nodePositionRelative(struct bNode *from_node, struct bNode *to_node, struct bNodeSocket *from_sock, struct bNodeSocket *to_sock)
Definition: node.cc:2621
struct bNodeLink * nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock)
Definition: node.cc:2296
void nodeLabel(const struct bNodeTree *ntree, const struct bNode *node, char *label, int maxlen)
#define NODE_CLASS_LAYOUT
Definition: BKE_node.h:361
struct bNode * nodeAddStaticNode(const struct bContext *C, struct bNodeTree *ntree, int type)
Definition: node.cc:2151
#define NODE_CLASS_GROUP
Definition: BKE_node.h:350
int nodeCountSocketLinks(const struct bNodeTree *ntree, const struct bNodeSocket *sock)
#define NODE_TYPES_END
Definition: BKE_node.h:574
#define NODE_CLASS_TEXTURE
Definition: BKE_node.h:355
void nodeSetActive(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:3644
void BKE_ntree_update_tag_node_property(struct bNodeTree *ntree, struct bNode *node)
A (mainly) macro array library.
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:719
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define POINTER_FROM_INT(i)
#define UNUSED(x)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define STREQ(a, b)
#define TIP_(msgid)
#define IFACE_(msgid)
#define NODE_TEST
#define NTREE_GEOMETRY
#define NODE_ACTIVE_TEXTURE
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDE_VALUE
@ SOCK_COLLAPSED
@ SOCK_UNAVAIL
@ SOCK_INT
@ SOCK_TEXTURE
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_MATERIAL
@ SOCK_SHADER
@ SOCK_FLOAT
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_CUSTOM
@ SOCK_OBJECT
@ SOCK_STRING
@ SOCK_RGBA
void ED_node_tree_propagate_change(const struct bContext *C, struct Main *bmain, struct bNodeTree *ntree)
void ED_undo_push(struct bContext *C, const char *str)
Definition: ed_undo.c:100
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
NODE_GROUP
#define C
Definition: RandGen.cpp:25
@ UI_LAYOUT_ALIGN_RIGHT
@ UI_BUT_ICON_LEFT
Definition: UI_interface.h:260
@ UI_BUT_TEXT_LEFT
Definition: UI_interface.h:259
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, const char *str, int x, int y, short width, short height, void *poin, float min, float max, float a1, float a2, const char *tip)
Definition: interface.cc:5623
#define UI_UNIT_Y
uiBlock * uiLayoutGetBlock(uiLayout *layout)
@ UI_BUT_NODE_ACTIVE
Definition: UI_interface.h:193
@ UI_EMBOSS_NONE
Definition: UI_interface.h:109
@ UI_EMBOSS
Definition: UI_interface.h:108
void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4])
Definition: interface.cc:6448
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
uiBut * uiDefBut(uiBlock *block, int type, int retval, const char *str, int x, int y, short width, short height, void *poin, float min, float max, float a1, float a2, const char *tip)
Definition: interface.cc:4806
uiPropertySplitWrapper uiItemPropertySplitWrapperCreate(uiLayout *parent_layout)
void uiItemL(uiLayout *layout, const char *name, int icon)
void UI_but_drawflag_enable(uiBut *but, int flag)
Definition: interface.cc:5873
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
@ UI_ITEM_R_ICON_ONLY
void UI_but_type_set_menu_from_pulldown(uiBut *but)
Definition: interface.cc:5895
void UI_block_emboss_set(uiBlock *block, eUIEmbossType emboss)
Definition: interface.cc:3629
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
uiBut * uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int icon, int x, int y, short width, short height, const char *tip)
Definition: interface.cc:6143
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutSplit(uiLayout *layout, float percentage, bool align)
uiBut * uiDefMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, const char *str, int x, int y, short width, short height, const char *tip)
Definition: interface.cc:6101
void uiItemDecoratorR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int index)
void UI_block_layout_set_current(uiBlock *block, uiLayout *layout)
void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2)
Definition: interface.cc:6007
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
Definition: interface.cc:5848
@ UI_BTYPE_BUT
Definition: UI_interface.h:330
@ UI_BTYPE_LABEL
Definition: UI_interface.h:354
#define UI_MAX_NAME_STR
Definition: UI_interface.h:92
@ UI_BLOCK_IS_FLIP
Definition: UI_interface.h:136
@ UI_BLOCK_NO_FLIP
Definition: UI_interface.h:137
constexpr const char * c_str() const
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
OperationNode * node
Scene scene
bNodeTree * ntree
#define RNA_NO_INDEX
ccl_global KernelShaderEvalInput * input
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
static ulong * next
static unsigned a[3]
Definition: RandGen.cpp:78
static void node_clear_recursive(bNode *node)
void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout)
Definition: node_draw.cc:1090
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_node_draw_node(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth)
static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
static void node_tag_recursive(bNode *node)
void node_geometry_add_attribute_search_button(const bContext &UNUSED(C), const bNode &node, PointerRNA &socket_ptr, uiLayout &layout)
static Vector< NodeLinkItem > ui_node_link_items(NodeLinkArg *arg, int in_out, std::optional< NodeDeclaration > &r_node_decl)
static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *name)
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])
Definition: node_draw.cc:764
static bool ui_node_item_special_poll(const bNodeTree *UNUSED(ntree), const bNodeType *ntype)
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 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_link(bContext *C, void *arg_p, void *event_p)
static void ui_node_draw_input(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth)
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition: abc_util.cc:92
std::unique_ptr< SocketDeclaration > SocketDeclarationPtr
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
Definition: node_socket.cc:391
#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
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
Definition: rna_access.c:136
Definition: DNA_ID.h:368
void * next
Definition: DNA_ID.h:369
char name[66]
Definition: DNA_ID.h:378
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
ListBase nodetrees
Definition: BKE_main.h:192
struct ID * owner_id
Definition: RNA_types.h:36
Compact definition of a node socket.
Definition: BKE_node.h:84
char name[64]
struct bNodeLink * link
struct bNodeSocket * next
struct bNodeSocketType * typeinfo
struct bNodeTreeType * typeinfo
ListBase nodes
ListBase inputs
ListBase outputs
Defines a node type.
Definition: BKE_node.h:226
char ui_name[64]
Definition: BKE_node.h:230
int type
Definition: BKE_node.h:228
bNodeSocketTemplate * outputs
Definition: BKE_node.h:239
short nclass
Definition: BKE_node.h:236
void(* draw_buttons_ex)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:246
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
bNodeSocketTemplate * inputs
Definition: BKE_node.h:239
char idname[64]
Definition: BKE_node.h:227
float locy
ListBase inputs
struct bNodeType * typeinfo
float locx
short type
struct bNode * next
void * storage
float offsetx
ListBase outputs
float offsety
ListBase buttons
char * poin
void * func_argN
#define N_(msgid)