Blender V4.3
node_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include <iomanip>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_light_types.h"
15#include "DNA_linestyle_types.h"
16#include "DNA_material_types.h"
17#include "DNA_modifier_types.h"
18#include "DNA_node_types.h"
19#include "DNA_screen_types.h"
20#include "DNA_space_types.h"
21#include "DNA_text_types.h"
22#include "DNA_texture_types.h"
23#include "DNA_world_types.h"
24
25#include "BLI_array.hh"
26#include "BLI_bounds.hh"
27#include "BLI_convexhull_2d.h"
28#include "BLI_map.hh"
29#include "BLI_set.hh"
30#include "BLI_span.hh"
31#include "BLI_string.h"
32#include "BLI_string_ref.hh"
33#include "BLI_vector.hh"
34
35#include "BLT_translation.hh"
36
38#include "BKE_context.hh"
39#include "BKE_curves.hh"
40#include "BKE_global.hh"
41#include "BKE_idtype.hh"
42#include "BKE_lib_id.hh"
43#include "BKE_main.hh"
44#include "BKE_node.hh"
45#include "BKE_node_enum.hh"
46#include "BKE_node_runtime.hh"
49#include "BKE_object.hh"
50#include "BKE_scene.hh"
51#include "BKE_scene_runtime.hh"
53
54#include "IMB_imbuf.hh"
55
56#include "DEG_depsgraph.hh"
57
58#include "BLF_api.hh"
59
60#include "BIF_glutil.hh"
61
62#include "GPU_framebuffer.hh"
63#include "GPU_immediate.hh"
64#include "GPU_immediate_util.hh"
65#include "GPU_matrix.hh"
66#include "GPU_shader_shared.hh"
67#include "GPU_state.hh"
68#include "GPU_viewport.hh"
69
70#include "WM_api.hh"
71#include "WM_types.hh"
72
73#include "ED_gpencil_legacy.hh"
74#include "ED_node.hh"
75#include "ED_node_preview.hh"
76#include "ED_screen.hh"
77#include "ED_space_api.hh"
78#include "ED_viewer_path.hh"
79
80#include "UI_interface.hh"
81#include "UI_resources.hh"
82#include "UI_view2d.hh"
83
84#include "RNA_access.hh"
85#include "RNA_prototypes.hh"
86
87#include "NOD_geometry_exec.hh"
93
94#include "FN_field.hh"
95
96#include "GEO_fillet_curves.hh"
97
98#include "node_intern.hh" /* own include */
99
100#include <fmt/format.h>
101#include <sstream>
102
108
141
143{
144 return NODE_GRID_STEP_SIZE;
145}
146
148{
149 using namespace blender::ed::space_node;
150
151 SpaceNode *snode = CTX_wm_space_node(C);
152 if (snode) {
154
155 if (snode->nodetree) {
156 id_us_ensure_real(&snode->nodetree->id);
157 }
158 }
159}
160
161/* id is supposed to contain a node tree */
163{
164 if (id) {
165 if (GS(id->name) == ID_NT) {
166 return (bNodeTree *)id;
167 }
169 }
170
171 return nullptr;
172}
173
175{
176 bNodeTree *ntree = node_tree_from_ID(id);
177 if (id == nullptr || ntree == nullptr) {
178 return;
179 }
180
181 /* TODO(sergey): With the new dependency graph it should be just enough to only tag ntree itself.
182 * All the users of this tree will have update flushed from the tree. */
183 DEG_id_tag_update(&ntree->id, 0);
184
185 if (ntree->type == NTREE_SHADER) {
186 DEG_id_tag_update(id, 0);
187
188 if (GS(id->name) == ID_MA) {
190 }
191 else if (GS(id->name) == ID_LA) {
193 }
194 else if (GS(id->name) == ID_WO) {
196 }
197 }
198 else if (ntree->type == NTREE_COMPOSIT) {
200 }
201 else if (ntree->type == NTREE_TEXTURE) {
202 DEG_id_tag_update(id, 0);
204 }
205 else if (ntree->type == NTREE_GEOMETRY) {
207 }
208 else if (id == &ntree->id) {
209 /* Node groups. */
210 DEG_id_tag_update(id, 0);
211 }
212}
213
214namespace blender::ed::space_node {
215
216static std::string node_socket_get_tooltip(const SpaceNode *snode,
217 const bNodeTree &ntree,
218 const bNodeSocket &socket);
219
220static const char *node_socket_get_translation_context(const bNodeSocket &socket)
221{
222 /* The node is not explicitly defined. */
223 if (socket.runtime->declaration == nullptr) {
224 return nullptr;
225 }
226
227 blender::StringRefNull translation_context = socket.runtime->declaration->translation_context;
228
229 /* Default context. */
230 if (translation_context.is_empty()) {
231 return nullptr;
232 }
233
234 return translation_context.data();
235}
236
237static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout);
238
240static bool compare_node_depth(const bNode *a, const bNode *b)
241{
242 /* These tell if either the node or any of the parent nodes is selected.
243 * A selected parent means an unselected node is also in foreground! */
244 bool a_select = (a->flag & NODE_SELECT) != 0, b_select = (b->flag & NODE_SELECT) != 0;
245 bool a_active = (a->flag & NODE_ACTIVE) != 0, b_active = (b->flag & NODE_ACTIVE) != 0;
246
247 /* If one is an ancestor of the other. */
248 /* XXX there might be a better sorting algorithm for stable topological sort,
249 * this is O(n^2) worst case. */
250 for (bNode *parent = a->parent; parent; parent = parent->parent) {
251 /* If B is an ancestor, it is always behind A. */
252 if (parent == b) {
253 return false;
254 }
255 /* Any selected ancestor moves the node forward. */
256 if (parent->flag & NODE_ACTIVE) {
257 a_active = true;
258 }
259 if (parent->flag & NODE_SELECT) {
260 a_select = true;
261 }
262 }
263 for (bNode *parent = b->parent; parent; parent = parent->parent) {
264 /* If A is an ancestor, it is always behind B. */
265 if (parent == a) {
266 return true;
267 }
268 /* Any selected ancestor moves the node forward. */
269 if (parent->flag & NODE_ACTIVE) {
270 b_active = true;
271 }
272 if (parent->flag & NODE_SELECT) {
273 b_select = true;
274 }
275 }
276
277 /* One of the nodes is in the background and the other not. */
278 if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND)) {
279 return true;
280 }
281 if ((b->flag & NODE_BACKGROUND) && !(a->flag & NODE_BACKGROUND)) {
282 return false;
283 }
284
285 /* One has a higher selection state (active > selected > nothing). */
286 if (a_active && !b_active) {
287 return false;
288 }
289 if (b_active && !a_active) {
290 return true;
291 }
292 if (!b_select && (a_active || a_select)) {
293 return false;
294 }
295 if (!a_select && (b_active || b_select)) {
296 return true;
297 }
298
299 return false;
300}
301
303{
304 Array<bNode *> sort_nodes = ntree.all_nodes();
305 std::sort(sort_nodes.begin(), sort_nodes.end(), [](bNode *a, bNode *b) {
306 return a->ui_order < b->ui_order;
307 });
308 std::stable_sort(sort_nodes.begin(), sort_nodes.end(), compare_node_depth);
309 for (const int i : sort_nodes.index_range()) {
310 sort_nodes[i]->ui_order = i;
311 }
312}
313
315{
316 Array<bNode *> nodes = ntree.all_nodes();
317 if (nodes.is_empty()) {
318 return {};
319 }
320 std::sort(nodes.begin(), nodes.end(), [](const bNode *a, const bNode *b) {
321 return a->ui_order < b->ui_order;
322 });
323 return nodes;
324}
325
327{
328 Array<bNode *> nodes = ntree.all_nodes();
329 if (nodes.is_empty()) {
330 return {};
331 }
332 std::sort(nodes.begin(), nodes.end(), [](const bNode *a, const bNode *b) {
333 return a->ui_order > b->ui_order;
334 });
335 return nodes;
336}
337
339{
340 Array<uiBlock *> blocks(nodes.size());
341 /* Add node uiBlocks in drawing order - prevents events going to overlapping nodes. */
342 for (const int i : nodes.index_range()) {
343 const bNode &node = *nodes[i];
344 std::string block_name = "node_" + std::string(node.name);
345 uiBlock *block = UI_block_begin(&C, CTX_wm_region(&C), std::move(block_name), UI_EMBOSS);
346 blocks[node.index()] = block;
347 /* This cancels events for background nodes. */
349 }
350
351 return blocks;
352}
353
354float2 node_to_view(const bNode &node, const float2 &co)
355{
356 const float2 node_location = bke::node_to_view(&node, co);
357 return node_location * UI_SCALE_FAC;
358}
359
360void node_to_updated_rect(const bNode &node, rctf &r_rect)
361{
362 const float2 xmin_ymax = node_to_view(node, {node.offsetx, node.offsety});
363 r_rect.xmin = xmin_ymax.x;
364 r_rect.ymax = xmin_ymax.y;
365 const float2 xmax_ymin = node_to_view(node,
366 {node.offsetx + node.width, node.offsety - node.height});
367 r_rect.xmax = xmax_ymin.x;
368 r_rect.ymin = xmax_ymin.y;
369}
370
371float2 node_from_view(const bNode &node, const float2 &co)
372{
373 const float2 node_location = co / UI_SCALE_FAC;
374 return bke::node_from_view(&node, node_location);
375}
376
377static bool is_node_panels_supported(const bNode &node)
378{
379 return node.declaration() && node.declaration()->use_custom_socket_order;
380}
381
382/* Draw UI for options, buttons, and previews. */
384 bNodeTree &ntree,
385 bNode &node,
387 uiBlock &block,
388 int &dy)
389{
390 /* Buttons rect? */
391 const bool node_options = draw_buttons && (node.flag & NODE_OPTIONS);
392 if (!node_options) {
393 return false;
394 }
395
396 PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node);
397
398 /* Get "global" coordinates. */
399 float2 loc = node_to_view(node, float2(0));
400 /* Round the node origin because text contents are always pixel-aligned. */
401 loc.x = round(loc.x);
402 loc.y = round(loc.y);
403
404 dy -= NODE_DYS / 4;
405
406 uiLayout *layout = UI_block_layout(&block,
409 loc.x + NODE_DYS,
410 dy,
411 NODE_WIDTH(node) - NODE_DY,
412 0,
413 0,
415
416 if (node.flag & NODE_MUTED) {
417 uiLayoutSetActive(layout, false);
418 }
419
420 uiLayoutSetContextPointer(layout, "node", &nodeptr);
421
422 draw_buttons(layout, (bContext *)&C, &nodeptr);
423
424 UI_block_align_end(&block);
425 int buty;
426 UI_block_layout_resolve(&block, nullptr, &buty);
427
428 dy = buty - NODE_DYS / 4;
429 return true;
430}
431
432const char *node_socket_get_label(const bNodeSocket *socket, const char *panel_label)
433{
434 /* Get the short label if possible. This is used when grouping sockets under panels,
435 * to avoid redundancy in the label. */
436 const char *socket_short_label = bke::nodeSocketShortLabel(socket);
437 const char *socket_translation_context = node_socket_get_translation_context(*socket);
438
439 if (socket_short_label) {
440 return CTX_IFACE_(socket_translation_context, socket_short_label);
441 }
442
443 const char *socket_label = bke::nodeSocketLabel(socket);
444 const char *translated_socket_label = CTX_IFACE_(socket_translation_context, socket_label);
445
446 /* Shorten socket label if it begins with the panel label. */
447 if (panel_label) {
448 const int len_prefix = strlen(panel_label);
449 if (STREQLEN(translated_socket_label, panel_label, len_prefix) &&
450 translated_socket_label[len_prefix] == ' ')
451 {
452 return translated_socket_label + len_prefix + 1;
453 }
454 }
455
456 /* Full label. */
457 return translated_socket_label;
458}
459
461 bNodeTree &ntree,
462 bNode &node,
463 const char *panel_label,
464 bNodeSocket *input_socket,
465 bNodeSocket *output_socket,
466 uiBlock &block,
467 const int &locx,
468 int &locy)
469{
470 if ((!input_socket || !input_socket->is_visible()) &&
471 (!output_socket || !output_socket->is_visible()))
472 {
473 return false;
474 }
475
476 const int topy = locy;
477
478 /* Add the half the height of a multi-input socket to cursor Y
479 * to account for the increased height of the taller sockets. */
480 const bool is_multi_input = (input_socket ? input_socket->flag & SOCK_MULTI_INPUT : false);
481 const float multi_input_socket_offset = is_multi_input ?
482 std::max(input_socket->runtime->total_inputs - 2,
483 0) *
485 0.0f;
486 locy -= multi_input_socket_offset * 0.5f;
487
488 uiLayout *layout = UI_block_layout(&block,
491 locx + NODE_DYS,
492 locy,
493 NODE_WIDTH(node) - NODE_DY,
494 NODE_DY,
495 0,
497
498 if (node.flag & NODE_MUTED) {
499 uiLayoutSetActive(layout, false);
500 }
501
502 uiLayout *row = uiLayoutRow(layout, true);
503 PointerRNA nodeptr = RNA_pointer_create(&ntree.id, &RNA_Node, &node);
504 uiLayoutSetContextPointer(row, "node", &nodeptr);
505
506 if (input_socket) {
507 /* Context pointers for current node and socket. */
508 PointerRNA sockptr = RNA_pointer_create(&ntree.id, &RNA_NodeSocket, input_socket);
509 uiLayoutSetContextPointer(row, "socket", &sockptr);
510
512
513 input_socket->typeinfo->draw(
514 (bContext *)&C, row, &sockptr, &nodeptr, node_socket_get_label(input_socket, panel_label));
515 }
516 else {
517 /* Context pointers for current node and socket. */
518 PointerRNA sockptr = RNA_pointer_create(&ntree.id, &RNA_NodeSocket, output_socket);
519 uiLayoutSetContextPointer(row, "socket", &sockptr);
520
521 /* Align output buttons to the right. */
523
524 output_socket->typeinfo->draw((bContext *)&C,
525 row,
526 &sockptr,
527 &nodeptr,
528 node_socket_get_label(output_socket, panel_label));
529 }
530
531 if (input_socket) {
532 node_socket_add_tooltip_in_node_editor(*input_socket, *row);
533 /* Round the socket location to stop it from jiggling. */
534 input_socket->runtime->location = float2(round(locx), round(locy - NODE_DYS));
535 }
536 if (output_socket) {
537 node_socket_add_tooltip_in_node_editor(*output_socket, *row);
538 /* Round the socket location to stop it from jiggling. */
539 output_socket->runtime->location = float2(round(locx + NODE_WIDTH(node)),
540 round(locy - NODE_DYS));
541 }
542
543 UI_block_align_end(&block);
544
545 int buty;
546 UI_block_layout_resolve(&block, nullptr, &buty);
547 /* Ensure minimum socket height in case layout is empty. */
548 buty = min_ii(buty, topy - NODE_DY);
549 locy = buty - multi_input_socket_offset * 0.5;
550 return true;
551}
552
553struct NodeInterfaceItemData {
554 private:
555 NodeInterfaceItemData() = default;
556
557 public:
558 /* Declaration of a socket (only for socket items). */
560 bNodeSocket *input = nullptr;
561 bNodeSocket *output = nullptr;
562
563 /* Declaration of a panel (only for panel items). */
565 /* State of the panel instance on the node.
566 * Mutable so that panel visibility can be updated. */
568 /* Runtime panel state for draw locations. */
570
571 bool is_separator = false;
572
574 bNodeSocket *_input,
575 bNodeSocket *_output)
576 : socket_decl(_socket_decl), input(_input), output(_output)
577 {
578 }
580 bNodePanelState *_state,
581 bke::bNodePanelRuntime *_runtime)
582 : panel_decl(_panel_decl), state(_state), runtime(_runtime)
583 {
584 }
585
586 static NodeInterfaceItemData separator()
587 {
588 NodeInterfaceItemData item;
589 item.is_separator = true;
590 return item;
591 }
592
593 bool is_valid_socket() const
594 {
595 /* At least one socket pointer must be valid. */
596 return this->socket_decl && (input || output);
597 }
598
599 bool is_valid_panel() const
600 {
601 /* Panel can only be drawn when state data is available. */
602 return this->panel_decl && this->state && this->runtime;
603 }
604
606 {
607 return this->is_separator;
608 }
609};
610
611/* Compile relevant socket and panel pointer data into a vector.
612 * This helps ensure correct pointer access in complex situations like inlined sockets.
613 */
615{
616 namespace nodes = blender::nodes;
618 using SocketIterator = blender::Span<bNodeSocket *>::iterator;
619 using PanelStateIterator = blender::MutableSpan<bNodePanelState>::iterator;
621
623 BLI_assert(node.runtime->panels.size() == node.num_panel_states);
624
625 ItemDeclIterator item_decl = node.declaration()->items.begin();
626 SocketIterator input = node.input_sockets().begin();
627 SocketIterator output = node.output_sockets().begin();
628 PanelStateIterator panel_state = node.panel_states().begin();
629 PanelRuntimeIterator panel_runtime = node.runtime->panels.begin();
630 const ItemDeclIterator item_decl_end = node.declaration()->items.end();
631 const SocketIterator input_end = node.input_sockets().end();
632 const SocketIterator output_end = node.output_sockets().end();
633 const PanelStateIterator panel_state_end = node.panel_states().end();
634 const PanelRuntimeIterator panel_runtime_end = node.runtime->panels.end();
635 UNUSED_VARS_NDEBUG(input_end, output_end, panel_state_end, panel_runtime_end);
636
638 result.reserve(node.declaration()->items.size());
639
640 while (item_decl != item_decl_end) {
641 if (const nodes::SocketDeclaration *socket_decl =
642 dynamic_cast<const nodes::SocketDeclaration *>(item_decl->get()))
643 {
644 if (socket_decl->align_with_previous_socket) {
645 NodeInterfaceItemData &last_item = result.last();
646 switch (socket_decl->in_out) {
647 case SOCK_IN:
648 BLI_assert(input != input_end);
649 BLI_assert(last_item.input == nullptr);
650 last_item.input = *input;
651 ++input;
652 break;
653 case SOCK_OUT:
654 BLI_assert(output != output_end);
655 BLI_assert(last_item.output == nullptr);
656 last_item.output = *output;
657 ++output;
658 break;
659 }
660 }
661 else {
662 switch (socket_decl->in_out) {
663 case SOCK_IN:
664 BLI_assert(input != input_end);
665 result.append({socket_decl, *input, nullptr});
666 ++input;
667 break;
668 case SOCK_OUT:
669 BLI_assert(output != output_end);
670 result.append({socket_decl, nullptr, *output});
671 ++output;
672 break;
673 }
674 }
675 ++item_decl;
676 }
677 else if (const nodes::PanelDeclaration *panel_decl =
678 dynamic_cast<const nodes::PanelDeclaration *>(item_decl->get()))
679 {
680 BLI_assert(panel_state != panel_state_end);
681 BLI_assert(panel_runtime != panel_runtime_end);
682 result.append({panel_decl, panel_state, panel_runtime});
683 ++item_decl;
684 ++panel_state;
685 ++panel_runtime;
686 }
687 else if (dynamic_cast<const nodes::SeparatorDeclaration *>(item_decl->get())) {
689 ++item_decl;
690 }
691 }
692 return result;
693}
694
696
700
702 : item_iter(items.begin()), item_end(items.end())
703 {
704 }
705};
706
707/* Recursive function to determine visibility of items before drawing. */
709 const bool is_parent_collapsed,
710 bNodePanelState &parent_state,
712{
713 parent_state.flag &= ~NODE_PANEL_CONTENT_VISIBLE;
714 while (state.item_iter != state.item_end) {
715 /* Stop after adding the expected number of items.
716 * Root panel consumes all remaining items (num_items == -1). */
717 if (num_items == 0) {
718 break;
719 }
720 else if (num_items > 0) {
721 --num_items;
722 }
723 /* Consume item. */
724 const NodeInterfaceItemData &item = *state.item_iter++;
725
726 if (item.is_valid_panel()) {
727 SET_FLAG_FROM_TEST(item.state->flag, is_parent_collapsed, NODE_PANEL_PARENT_COLLAPSED);
728 /* New top panel is collapsed if self or parent is collapsed. */
729 const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
730
732 item.panel_decl->num_child_decls, is_collapsed, *item.state, state);
733 if (item.panel_decl->draw_buttons) {
735 }
737 /* If child panel is visible so is the parent panel. */
738 parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
739 }
740 }
741 else if (item.is_valid_socket()) {
742 if (item.input) {
743 SET_FLAG_FROM_TEST(item.input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
744 if (item.input->is_visible()) {
745 parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
746 }
747 }
748 if (item.output) {
749 SET_FLAG_FROM_TEST(item.output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
750 if (item.output->is_visible()) {
751 parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
752 }
753 }
754 }
755 else if (item.is_valid_separator()) {
756 /* Nothing to do. */
757 }
758 else {
759 /* Should not happen. */
761 }
762 }
763}
764
768
769 /* Checked at various places to avoid adding duplicate spacers without anything in between. */
771 /* Makes sure buttons are only drawn once. */
772 bool buttons_drawn = false;
773 /* Only true for the first item in the layout. */
774 bool is_first = true;
775
777 : item_iter(items.begin()), item_end(items.end())
778 {
779 }
780};
781
782/* Recursive function that adds the expected number of items in a panel and advances the
783 * iterator. */
785 bNodeTree &ntree,
786 bNode &node,
787 uiBlock &block,
788 const int locx,
789 int &locy,
790 int num_items,
791 const bool is_parent_collapsed,
792 const char *parent_label,
793 bke::bNodePanelRuntime *parent_runtime,
795{
796 while (state.item_iter != state.item_end) {
797 /* Stop after adding the expected number of items.
798 * Root panel consumes all remaining items (num_items == -1). */
799 if (num_items == 0) {
800 break;
801 }
802 else if (num_items > 0) {
803 --num_items;
804 }
805 /* Consume item. */
806 const NodeInterfaceItemData &item = *state.item_iter++;
807
808 if (item.is_valid_panel()) {
809 /* Draw buttons before the first panel. */
810 if (!state.buttons_drawn) {
811 state.buttons_drawn = true;
812 state.need_spacer_after_item = node_update_basis_buttons(
813 C, ntree, node, node.typeinfo->draw_buttons, block, locy);
814 }
815
816 /* Panel visible if any content is visible. */
817 if (item.state->has_visible_content()) {
818 if (!is_parent_collapsed) {
819 locy -= NODE_DY;
820 state.is_first = false;
821 }
822
823 /* New top panel is collapsed if self or parent is collapsed. */
824 const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
825
826 /* Round the socket location to stop it from jiggling. */
827 item.runtime->location_y = round(locy + NODE_DYS);
828 if (is_collapsed) {
829 item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
830 }
831 else {
832 locy -= NODE_ITEM_SPACING_Y / 2; /* Space at bottom of panel header. */
833 item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
834 locy -= NODE_ITEM_SPACING_Y; /* Space at top of panel contents. */
835
836 node_update_basis_buttons(C, ntree, node, item.panel_decl->draw_buttons, block, locy);
837 }
838
840 ntree,
841 node,
842 block,
843 locx,
844 locy,
846 is_collapsed,
847 item.panel_decl->name.c_str(),
848 item.runtime,
849 state);
850 }
851 }
852 else if (item.is_valid_socket()) {
853 bool need_socket_spacing = false;
854 if (item.input) {
855 /* Draw buttons before the first input. */
856 if (!state.buttons_drawn) {
857 state.buttons_drawn = true;
858 state.need_spacer_after_item = node_update_basis_buttons(
859 C, ntree, node, node.typeinfo->draw_buttons, block, locy);
860 }
861
862 if (is_parent_collapsed) {
863 item.input->runtime->location = float2(locx, round(locy + NODE_DYS));
864 }
865 else {
866 /* Space between items. */
867 if (!state.is_first && item.input->is_visible()) {
868 need_socket_spacing = true;
869 }
870 }
871 }
872 if (item.output) {
873 if (is_parent_collapsed) {
874 item.output->runtime->location = float2(round(locx + NODE_WIDTH(node)),
875 round(locy + NODE_DYS));
876 }
877 else {
878 /* Space between items. */
879 if (!state.is_first && item.output->is_visible()) {
880 need_socket_spacing = true;
881 }
882 }
883 }
884 if (need_socket_spacing) {
885 locy -= NODE_ITEM_SPACING_Y;
886 }
887
888 if (!is_parent_collapsed &&
890 C, ntree, node, parent_label, item.input, item.output, block, locx, locy))
891 {
892 state.is_first = false;
893 state.need_spacer_after_item = true;
894 }
895 }
896 else if (item.is_valid_separator()) {
897 if (!is_parent_collapsed) {
898 uiLayout *layout = UI_block_layout(&block,
901 locx + NODE_DYS,
902 locy,
903 NODE_WIDTH(node) - NODE_DY,
904 NODE_DY,
905 0,
908 UI_block_layout_resolve(&block, nullptr, nullptr);
909 locy -= NODE_ITEM_SPACING_Y;
910 }
911 }
912 else {
913 /* Should not happen. */
915 }
916 }
917
918 /* Finalize the vertical extent of the content. */
919 if (!is_parent_collapsed) {
920 if (parent_runtime) {
921 locy -= 2 * NODE_ITEM_SPACING_Y; /* Space at bottom of panel contents. */
922 parent_runtime->min_content_y = round(locy);
923 }
924 locy -= NODE_ITEM_SPACING_Y / 2; /* Space at top of next panel header. */
925 }
926}
927
928/* Advanced drawing with panels and arbitrary input/output ordering. */
930 const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
931{
932 namespace nodes = blender::nodes;
933
935 BLI_assert(node.runtime->panels.size() == node.num_panel_states);
936
938
939 /* Update item visibility flags first. */
940 VisibilityUpdateState visibility_state(item_data);
941 /* Dummy state item to write into, unused. */
942 bNodePanelState root_panel_state;
943 node_update_panel_items_visibility_recursive(-1, false, root_panel_state, visibility_state);
944
945 /* Space at the top. */
946 locy -= NODE_DYS / 2;
947
948 /* Start by adding root panel items. */
949 LocationUpdateState location_state(item_data);
950
951 /* Draw buttons at the top when the node has a custom socket order. This could be customized in
952 * the future to support showing the buttons in any place. */
953 if (node.declaration()->allow_any_socket_order) {
954 location_state.buttons_drawn = true;
956 C, ntree, node, node.typeinfo->draw_buttons, block, locy);
957 }
958
960 C, ntree, node, block, locx, locy, -1, false, "", nullptr, location_state);
961
962 /* Draw buttons at the bottom if no inputs exist. */
963 if (!location_state.buttons_drawn) {
965 C, ntree, node, node.typeinfo->draw_buttons, block, locy);
966 }
967
968 if (location_state.need_spacer_after_item) {
969 locy -= NODE_DYS / 2;
970 }
971}
972
973/* Conventional drawing in outputs/buttons/inputs order. */
975 const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
976{
977 /* Space at the top. */
978 locy -= NODE_DYS / 2;
979
980 /* Output sockets. */
981 bool add_output_space = false;
982
983 for (bNodeSocket *socket : node.output_sockets()) {
984 /* Clear flag, conventional drawing does not support panels. */
985 socket->flag &= ~SOCK_PANEL_COLLAPSED;
986
987 if (node_update_basis_socket(C, ntree, node, nullptr, nullptr, socket, block, locx, locy)) {
988 if (socket->next) {
989 locy -= NODE_ITEM_SPACING_Y;
990 }
991 add_output_space = true;
992 }
993 }
994
995 if (add_output_space) {
996 locy -= NODE_DY / 4;
997 }
998
999 const bool add_button_space = node_update_basis_buttons(
1000 C, ntree, node, node.typeinfo->draw_buttons, block, locy);
1001
1002 bool add_input_space = false;
1003
1004 /* Input sockets. */
1005 for (bNodeSocket *socket : node.input_sockets()) {
1006 /* Clear flag, conventional drawing does not support panels. */
1007 socket->flag &= ~SOCK_PANEL_COLLAPSED;
1008
1009 if (node_update_basis_socket(C, ntree, node, nullptr, socket, nullptr, block, locx, locy)) {
1010 if (socket->next) {
1011 locy -= NODE_ITEM_SPACING_Y;
1012 }
1013 add_input_space = true;
1014 }
1015 }
1016
1017 /* Little bit of padding at the bottom. */
1018 if (add_input_space || add_button_space) {
1019 locy -= NODE_DYS / 2;
1020 }
1021}
1022
1026static void node_update_basis(const bContext &C,
1027 const TreeDrawContext & /*tree_draw_ctx*/,
1028 bNodeTree &ntree,
1029 bNode &node,
1030 uiBlock &block)
1031{
1032 /* Get "global" coordinates. */
1033 float2 loc = node_to_view(node, float2(0));
1034 /* Round the node origin because text contents are always pixel-aligned. */
1035 loc.x = round(loc.x);
1036 loc.y = round(loc.y);
1037
1038 int dy = loc.y;
1039
1040 /* Header. */
1041 dy -= NODE_DY;
1042
1043 if (is_node_panels_supported(node)) {
1044 node_update_basis_from_declaration(C, ntree, node, block, loc.x, dy);
1045 }
1046 else {
1047 node_update_basis_from_socket_lists(C, ntree, node, block, loc.x, dy);
1048 }
1049
1050 node.runtime->totr.xmin = loc.x;
1051 node.runtime->totr.xmax = loc.x + NODE_WIDTH(node);
1052 node.runtime->totr.ymax = loc.y;
1053 node.runtime->totr.ymin = min_ff(dy, loc.y - 2 * NODE_DY);
1054
1055 /* Set the block bounds to clip mouse events from underlying nodes.
1056 * Add a margin for sockets on each side. */
1058 node.runtime->totr.xmin - NODE_SOCKSIZE,
1059 node.runtime->totr.ymin,
1060 node.runtime->totr.xmax + NODE_SOCKSIZE,
1061 node.runtime->totr.ymax);
1062}
1063
1067static void node_update_hidden(bNode &node, uiBlock &block)
1068{
1069 int totin = 0, totout = 0;
1070
1071 /* Get "global" coordinates. */
1072 float2 loc = node_to_view(node, float2(0));
1073 /* Round the node origin because text contents are always pixel-aligned. */
1074 loc.x = round(loc.x);
1075 loc.y = round(loc.y);
1076
1077 /* Calculate minimal radius. */
1078 for (const bNodeSocket *socket : node.input_sockets()) {
1079 if (socket->is_visible()) {
1080 totin++;
1081 }
1082 }
1083 for (const bNodeSocket *socket : node.output_sockets()) {
1084 if (socket->is_visible()) {
1085 totout++;
1086 }
1087 }
1088
1089 float hiddenrad = HIDDEN_RAD;
1090 float tot = std::max(totin, totout);
1091 if (tot > 4) {
1092 hiddenrad += 5.0f * float(tot - 4);
1093 }
1094
1095 node.runtime->totr.xmin = loc.x;
1096 node.runtime->totr.xmax = loc.x + max_ff(NODE_WIDTH(node), 2 * hiddenrad);
1097 node.runtime->totr.ymax = loc.y + (hiddenrad - 0.5f * NODE_DY);
1098 node.runtime->totr.ymin = node.runtime->totr.ymax - 2 * hiddenrad;
1099
1100 /* Output sockets. */
1101 float rad = float(M_PI) / (1.0f + float(totout));
1102 float drad = rad;
1103
1104 for (bNodeSocket *socket : node.output_sockets()) {
1105 if (socket->is_visible()) {
1106 /* Round the socket location to stop it from jiggling. */
1107 socket->runtime->location = {
1108 round(node.runtime->totr.xmax - hiddenrad + sinf(rad) * hiddenrad),
1109 round(node.runtime->totr.ymin + hiddenrad + cosf(rad) * hiddenrad)};
1110 rad += drad;
1111 }
1112 }
1113
1114 /* Input sockets. */
1115 rad = drad = -float(M_PI) / (1.0f + float(totin));
1116
1117 for (bNodeSocket *socket : node.input_sockets()) {
1118 if (socket->is_visible()) {
1119 /* Round the socket location to stop it from jiggling. */
1120 socket->runtime->location = {
1121 round(node.runtime->totr.xmin + hiddenrad + sinf(rad) * hiddenrad),
1122 round(node.runtime->totr.ymin + hiddenrad + cosf(rad) * hiddenrad)};
1123 rad += drad;
1124 }
1125 }
1126
1127 /* Set the block bounds to clip mouse events from underlying nodes.
1128 * Add a margin for sockets on each side. */
1130 node.runtime->totr.xmin - NODE_SOCKSIZE,
1131 node.runtime->totr.ymin,
1132 node.runtime->totr.xmax + NODE_SOCKSIZE,
1133 node.runtime->totr.ymax);
1134}
1135
1136static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
1137{
1138 const int nclass = (node.typeinfo->ui_class == nullptr) ? node.typeinfo->nclass :
1139 node.typeinfo->ui_class(&node);
1140 switch (nclass) {
1141 case NODE_CLASS_INPUT:
1142 return TH_NODE_INPUT;
1143 case NODE_CLASS_OUTPUT: {
1144 if (node.type == GEO_NODE_VIEWER) {
1145 return &node == tree_draw_ctx.active_geometry_nodes_viewer ? TH_NODE_OUTPUT : TH_NODE;
1146 }
1147 const bool is_output_node = (node.flag & NODE_DO_OUTPUT) ||
1148 (node.type == CMP_NODE_OUTPUT_FILE);
1149 return is_output_node ? TH_NODE_OUTPUT : TH_NODE;
1150 }
1152 return TH_NODE_CONVERTER;
1154 return TH_NODE_COLOR;
1156 return TH_NODE_VECTOR;
1158 return TH_NODE_FILTER;
1159 case NODE_CLASS_GROUP:
1160 return TH_NODE_GROUP;
1162 return TH_NODE_INTERFACE;
1163 case NODE_CLASS_MATTE:
1164 return TH_NODE_MATTE;
1165 case NODE_CLASS_DISTORT:
1166 return TH_NODE_DISTORT;
1167 case NODE_CLASS_TEXTURE:
1168 return TH_NODE_TEXTURE;
1169 case NODE_CLASS_SHADER:
1170 return TH_NODE_SHADER;
1171 case NODE_CLASS_SCRIPT:
1172 return TH_NODE_SCRIPT;
1173 case NODE_CLASS_PATTERN:
1174 return TH_NODE_PATTERN;
1175 case NODE_CLASS_LAYOUT:
1176 return TH_NODE_LAYOUT;
1178 return TH_NODE_GEOMETRY;
1180 return TH_NODE_ATTRIBUTE;
1181 default:
1182 return TH_NODE;
1183 }
1184}
1185
1186static void node_draw_mute_line(const bContext &C,
1187 const View2D &v2d,
1188 const SpaceNode &snode,
1189 const bNode &node)
1190{
1192
1193 for (const bNodeLink &link : node.internal_links()) {
1194 if (!bke::node_link_is_hidden(&link)) {
1195 node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
1196 }
1197 }
1198
1200}
1201
1202static void node_socket_draw(const bNodeSocket &sock,
1203 const float color[4],
1204 const float color_outline[4],
1205 const float size,
1206 const float locx,
1207 const float locy,
1208 uint pos_id,
1209 uint col_id,
1210 uint shape_id,
1211 uint size_id,
1212 uint outline_col_id)
1213{
1214 int flags;
1215
1216 /* Set shape flags. */
1217 switch (sock.display_shape) {
1221 break;
1225 break;
1226 default:
1230 break;
1231 }
1232
1233 if (ELEM(sock.display_shape,
1237 {
1239 }
1240
1241 immAttr4fv(col_id, color);
1242 immAttr1u(shape_id, flags);
1243 immAttr1f(size_id, size);
1244 immAttr4fv(outline_col_id, color_outline);
1245 immVertex2f(pos_id, locx, locy);
1246}
1247
1249 const int socket_index_in_tree,
1250 const float2 location,
1251 const float2 size)
1252{
1253 /* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible
1254 * button on top of them for the tooltip. */
1255 const eUIEmbossType old_emboss = UI_block_emboss_get(&block);
1257 uiBut *but = uiDefIconBut(&block,
1259 0,
1260 ICON_NONE,
1261 location.x - size.x / 2.0f,
1262 location.y - size.y / 2.0f,
1263 size.x,
1264 size.y,
1265 nullptr,
1266 0,
1267 0,
1268 nullptr);
1269
1271 but,
1272 [](bContext *C, void *argN, const char * /*tip*/) {
1273 const SpaceNode &snode = *CTX_wm_space_node(C);
1274 const bNodeTree &ntree = *snode.edittree;
1275 const int index_in_tree = POINTER_AS_INT(argN);
1276 ntree.ensure_topology_cache();
1277 return node_socket_get_tooltip(&snode, ntree, *ntree.all_sockets()[index_in_tree]);
1278 },
1279 POINTER_FROM_INT(socket_index_in_tree),
1280 nullptr);
1281 /* Disable the button so that clicks on it are ignored the link operator still works. */
1283 UI_block_emboss_set(&block, old_emboss);
1284}
1285
1287 const int index_in_tree,
1288 const float2 location,
1289 const float2 draw_size,
1290 const float color[4],
1291 const float color_outline[4],
1292 const float2 tooltip_size)
1293{
1294 /* The other sockets are drawn with the keyframe shader. There, the outline has a base
1295 * thickness that can be varied but always scales with the size the socket is drawn at. Using
1296 * `UI_SCALE_FAC` has the same effect here. It scales the outline correctly across different
1297 * screen DPI's and UI scales without being affected by the 'line-width'. */
1298 const float half_outline_width = NODE_SOCK_OUTLINE_SCALE * UI_SCALE_FAC * 0.5f;
1299
1300 /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width.
1301 */
1302 const rctf rect = {
1303 location.x - draw_size.x + half_outline_width,
1304 location.x + draw_size.x + half_outline_width,
1305 location.y - draw_size.y + half_outline_width,
1306 location.y + draw_size.y + half_outline_width,
1307 };
1308
1311 color,
1312 nullptr,
1313 1.0f,
1314 color_outline,
1315 half_outline_width * 2.0f,
1316 draw_size.x - half_outline_width);
1317
1318 node_socket_tooltip_set(block, index_in_tree, location, tooltip_size);
1319}
1320
1321static const float virtual_node_socket_outline_color[4] = {0.5, 0.5, 0.5, 1.0};
1322
1323static void node_socket_outline_color_get(const bool selected,
1324 const int socket_type,
1325 float r_outline_color[4])
1326{
1327 if (selected) {
1328 UI_GetThemeColor4fv(TH_ACTIVE, r_outline_color);
1329 }
1330 else if (socket_type == SOCK_CUSTOM) {
1331 /* Until there is a better place for per socket color,
1332 * the outline color for virtual sockets is set here. */
1334 }
1335 else {
1336 UI_GetThemeColor4fv(TH_WIRE, r_outline_color);
1337 r_outline_color[3] = 1.0f;
1338 }
1339}
1340
1342 const bNodeTree &ntree,
1343 PointerRNA &node_ptr,
1344 const bNodeSocket &sock,
1345 float r_color[4])
1346{
1347 if (!sock.typeinfo->draw_color) {
1348 /* Fallback to the simple variant. If not defined either, fallback to a magenta color. */
1349 if (sock.typeinfo->draw_color_simple) {
1350 sock.typeinfo->draw_color_simple(sock.typeinfo, r_color);
1351 }
1352 else {
1353 copy_v4_v4(r_color, float4(1.0f, 0.0f, 1.0f, 1.0f));
1354 }
1355 return;
1356 }
1357
1358 BLI_assert(RNA_struct_is_a(node_ptr.type, &RNA_Node));
1360 &const_cast<ID &>(ntree.id), &RNA_NodeSocket, &const_cast<bNodeSocket &>(sock));
1361 sock.typeinfo->draw_color((bContext *)&C, &ptr, &node_ptr, r_color);
1362}
1363
1365 const GPointer value,
1366 fmt::memory_buffer &buf)
1367{
1368 auto id_to_inspection_string = [&](const ID *id, const short idcode) {
1369 fmt::format_to(fmt::appender(buf), (id ? id->name + 2 : TIP_("None")));
1370 fmt::format_to(fmt::appender(buf), " (");
1371 fmt::format_to(fmt::appender(buf), TIP_(BKE_idtype_idcode_to_name(idcode)));
1372 fmt::format_to(fmt::appender(buf), ")");
1373 };
1374
1375 const CPPType &value_type = *value.type();
1376 const void *buffer = value.get();
1377 if (value_type.is<Object *>()) {
1378 id_to_inspection_string(*static_cast<const ID *const *>(buffer), ID_OB);
1379 return;
1380 }
1381 if (value_type.is<Material *>()) {
1382 id_to_inspection_string(*static_cast<const ID *const *>(buffer), ID_MA);
1383 return;
1384 }
1385 if (value_type.is<Tex *>()) {
1386 id_to_inspection_string(*static_cast<const ID *const *>(buffer), ID_TE);
1387 return;
1388 }
1389 if (value_type.is<Image *>()) {
1390 id_to_inspection_string(*static_cast<const ID *const *>(buffer), ID_IM);
1391 return;
1392 }
1393 if (value_type.is<Collection *>()) {
1394 id_to_inspection_string(*static_cast<const ID *const *>(buffer), ID_GR);
1395 return;
1396 }
1397 if (value_type.is<std::string>()) {
1398 fmt::format_to(
1399 fmt::appender(buf), TIP_("{} (String)"), *static_cast<const std::string *>(buffer));
1400 return;
1401 }
1402
1403 const CPPType &socket_type = *socket.typeinfo->base_cpp_type;
1404
1405 if (socket.type == SOCK_MENU) {
1406 if (!value_type.is<int>()) {
1407 return;
1408 }
1409 const int item_identifier = *static_cast<const int *>(buffer);
1410 const auto *socket_storage = socket.default_value_typed<bNodeSocketValueMenu>();
1411 if (!socket_storage->enum_items) {
1412 return;
1413 }
1414 if (socket_storage->has_conflict()) {
1415 return;
1416 }
1417 const bke::RuntimeNodeEnumItem *enum_item =
1418 socket_storage->enum_items->find_item_by_identifier(item_identifier);
1419 if (!enum_item) {
1420 return;
1421 }
1422 fmt::format_to(fmt::appender(buf), TIP_("{} (Menu)"), enum_item->name);
1423 return;
1424 }
1425
1427 if (value_type != socket_type) {
1428 if (!convert.is_convertible(value_type, socket_type)) {
1429 return;
1430 }
1431 }
1432 BUFFER_FOR_CPP_TYPE_VALUE(socket_type, socket_value);
1433 /* This will just copy the value if the types are equal. */
1434 convert.convert_to_uninitialized(value_type, socket_type, buffer, socket_value);
1435 BLI_SCOPED_DEFER([&]() { socket_type.destruct(socket_value); });
1436
1437 if (socket_type.is<int>()) {
1438 fmt::format_to(fmt::appender(buf), TIP_("{} (Integer)"), *static_cast<int *>(socket_value));
1439 }
1440 else if (socket_type.is<float>()) {
1441 const float float_value = *static_cast<float *>(socket_value);
1442 /* Above that threshold floats can't represent fractions anymore. */
1443 if (std::abs(float_value) > (1 << 24)) {
1444 /* Use higher precision to display correct integer value instead of one that is rounded to
1445 * fewer significant digits. */
1446 fmt::format_to(fmt::appender(buf), TIP_("{:.10} (Float)"), float_value);
1447 }
1448 else {
1449 fmt::format_to(fmt::appender(buf), TIP_("{} (Float)"), float_value);
1450 }
1451 }
1452 else if (socket_type.is<blender::float3>()) {
1453 const blender::float3 &vector = *static_cast<blender::float3 *>(socket_value);
1454 fmt::format_to(
1455 fmt::appender(buf), TIP_("({}, {}, {}) (Vector)"), vector.x, vector.y, vector.z);
1456 }
1457 else if (socket_type.is<blender::ColorGeometry4f>()) {
1458 const blender::ColorGeometry4f &color = *static_cast<blender::ColorGeometry4f *>(socket_value);
1459 fmt::format_to(
1460 fmt::appender(buf), TIP_("({}, {}, {}, {}) (Color)"), color.r, color.g, color.b, color.a);
1461 }
1462 else if (socket_type.is<math::Quaternion>()) {
1463 const math::Quaternion &rotation = *static_cast<math::Quaternion *>(socket_value);
1464 const math::EulerXYZ euler = math::to_euler(rotation);
1465 fmt::format_to(fmt::appender(buf),
1467 ", {}" BLI_STR_UTF8_DEGREE_SIGN ")"),
1468 euler.x().degree(),
1469 euler.y().degree(),
1470 euler.z().degree());
1471 fmt::format_to(fmt::appender(buf), TIP_("(Rotation)"));
1472 }
1473 else if (socket_type.is<bool>()) {
1474 fmt::format_to(fmt::appender(buf),
1475 TIP_("{} (Boolean)"),
1476 ((*static_cast<bool *>(socket_value)) ? TIP_("True") : TIP_("False")));
1477 }
1478 else if (socket_type.is<float4x4>()) {
1479 /* Transpose to be able to print row by row. */
1480 const float4x4 value = math::transpose(*static_cast<const float4x4 *>(socket_value));
1481 std::stringstream ss;
1482 ss << value[0] << ",\n";
1483 ss << value[1] << ",\n";
1484 ss << value[2] << ",\n";
1485 ss << value[3] << ",\n";
1486 buf.append(ss.str());
1487 fmt::format_to(fmt::appender(buf), TIP_("(Matrix)"));
1488 }
1489}
1490
1492 const geo_log::FieldInfoLog &value_log,
1493 fmt::memory_buffer &buf)
1494{
1495 const CPPType &socket_type = *socket.typeinfo->base_cpp_type;
1496 const Span<std::string> input_tooltips = value_log.input_tooltips;
1497
1498 if (input_tooltips.is_empty()) {
1499 /* Should have been logged as constant value. */
1501 fmt::format_to(fmt::appender(buf), TIP_("Value has not been logged"));
1502 }
1503 else {
1504 if (socket_type.is<int>()) {
1505 fmt::format_to(fmt::appender(buf), TIP_("Integer field based on:"));
1506 }
1507 else if (socket_type.is<float>()) {
1508 fmt::format_to(fmt::appender(buf), TIP_("Float field based on:"));
1509 }
1510 else if (socket_type.is<blender::float3>()) {
1511 fmt::format_to(fmt::appender(buf), TIP_("Vector field based on:"));
1512 }
1513 else if (socket_type.is<bool>()) {
1514 fmt::format_to(fmt::appender(buf), TIP_("Boolean field based on:"));
1515 }
1516 else if (socket_type.is<std::string>()) {
1517 fmt::format_to(fmt::appender(buf), TIP_("String field based on:"));
1518 }
1519 else if (socket_type.is<blender::ColorGeometry4f>()) {
1520 fmt::format_to(fmt::appender(buf), TIP_("Color field based on:"));
1521 }
1522 else if (socket_type.is<math::Quaternion>()) {
1523 fmt::format_to(fmt::appender(buf), TIP_("Rotation field based on:"));
1524 }
1525 fmt::format_to(fmt::appender(buf), "\n");
1526
1527 for (const int i : input_tooltips.index_range()) {
1528 const blender::StringRefNull tooltip = input_tooltips[i];
1529 fmt::format_to(fmt::appender(buf), TIP_("\u2022 {}"), TIP_(tooltip.c_str()));
1530 if (i < input_tooltips.size() - 1) {
1531 fmt::format_to(fmt::appender(buf), ".\n");
1532 }
1533 }
1534 }
1535}
1536
1538 fmt::memory_buffer &buf)
1539{
1540 auto to_string = [](int value) {
1543 return std::string(str);
1544 };
1545
1546 if (value_log.grid_info) {
1547 const geo_log::GeometryInfoLog::GridInfo &grid_info = *value_log.grid_info;
1548 fmt::format_to(fmt::appender(buf),
1549 grid_info.is_empty ? TIP_("Empty Grid") : TIP_("\u2022 Grid"));
1550 return;
1551 }
1552
1553 Span<bke::GeometryComponent::Type> component_types = value_log.component_types;
1554 if (component_types.is_empty()) {
1555 fmt::format_to(fmt::appender(buf), TIP_("Empty Geometry"));
1556 return;
1557 }
1558
1559 fmt::format_to(fmt::appender(buf), TIP_("Geometry:"));
1560 if (!value_log.name.empty()) {
1561 fmt::format_to(fmt::appender(buf), " \"{}\"", value_log.name);
1562 }
1563 fmt::format_to(fmt::appender(buf), "\n");
1564 for (bke::GeometryComponent::Type type : component_types) {
1565 switch (type) {
1567 const geo_log::GeometryInfoLog::MeshInfo &mesh_info = *value_log.mesh_info;
1568 fmt::format_to(fmt::appender(buf),
1569 TIP_("\u2022 Mesh: {} vertices, {} edges, {} faces"),
1570 to_string(mesh_info.verts_num),
1571 to_string(mesh_info.edges_num),
1572 to_string(mesh_info.faces_num));
1573 break;
1574 }
1576 const geo_log::GeometryInfoLog::PointCloudInfo &pointcloud_info =
1577 *value_log.pointcloud_info;
1578 fmt::format_to(fmt::appender(buf),
1579 TIP_("\u2022 Point Cloud: {} points"),
1580 to_string(pointcloud_info.points_num));
1581 break;
1582 }
1584 const geo_log::GeometryInfoLog::CurveInfo &curve_info = *value_log.curve_info;
1585 fmt::format_to(fmt::appender(buf),
1586 TIP_("\u2022 Curve: {} points, {} splines"),
1587 to_string(curve_info.points_num),
1588 to_string(curve_info.splines_num));
1589 break;
1590 }
1592 const geo_log::GeometryInfoLog::InstancesInfo &instances_info = *value_log.instances_info;
1593 fmt::format_to(fmt::appender(buf),
1594 TIP_("\u2022 Instances: {}"),
1595 to_string(instances_info.instances_num));
1596 break;
1597 }
1599 const geo_log::GeometryInfoLog::VolumeInfo &volume_info = *value_log.volume_info;
1600 fmt::format_to(fmt::appender(buf), TIP_("\u2022 Volume: {} grids"), volume_info.grids_num);
1601 break;
1602 }
1604 if (value_log.edit_data_info.has_value()) {
1605 const geo_log::GeometryInfoLog::EditDataInfo &edit_info = *value_log.edit_data_info;
1606 fmt::format_to(fmt::appender(buf),
1607 TIP_("\u2022 Edit: {}, {}, {}"),
1608 edit_info.has_deformed_positions ? TIP_("positions") :
1609 TIP_("no positions"),
1610 edit_info.has_deform_matrices ? TIP_("matrices") : TIP_("no matrices"),
1611 edit_info.gizmo_transforms_num > 0 ? TIP_("gizmos") : TIP_("no gizmos"));
1612 }
1613 break;
1614 }
1616 const geo_log::GeometryInfoLog::GreasePencilInfo &grease_pencil_info =
1617 *value_log.grease_pencil_info;
1618 fmt::format_to(fmt::appender(buf),
1619 TIP_("\u2022 Grease Pencil: {} layers"),
1620 to_string(grease_pencil_info.layers_num));
1621 break;
1622 }
1623 }
1624 if (type != component_types.last()) {
1625 fmt::format_to(fmt::appender(buf), ".\n");
1626 }
1627 }
1628}
1629
1630static void create_inspection_string_for_geometry_socket(fmt::memory_buffer &buf,
1631 const nodes::decl::Geometry *socket_decl)
1632{
1633 /* If the geometry declaration is null, as is the case for input to group output,
1634 * or it is an output socket don't show supported types. */
1635 if (socket_decl == nullptr || socket_decl->in_out == SOCK_OUT) {
1636 return;
1637 }
1638
1639 Span<bke::GeometryComponent::Type> supported_types = socket_decl->supported_types();
1640 if (supported_types.is_empty()) {
1641 fmt::format_to(fmt::appender(buf), TIP_("Supported: All Types"));
1642 return;
1643 }
1644
1645 fmt::format_to(fmt::appender(buf), TIP_("Supported: "));
1646 for (bke::GeometryComponent::Type type : supported_types) {
1647 switch (type) {
1649 fmt::format_to(fmt::appender(buf), TIP_("Mesh"));
1650 break;
1651 }
1653 fmt::format_to(fmt::appender(buf), TIP_("Point Cloud"));
1654 break;
1655 }
1657 fmt::format_to(fmt::appender(buf), TIP_("Curve"));
1658 break;
1659 }
1661 fmt::format_to(fmt::appender(buf), TIP_("Instances"));
1662 break;
1663 }
1665 fmt::format_to(fmt::appender(buf), CTX_TIP_(BLT_I18NCONTEXT_ID_ID, "Volume"));
1666 break;
1667 }
1669 break;
1670 }
1672 fmt::format_to(fmt::appender(buf), TIP_("Grease Pencil"));
1673 break;
1674 }
1675 }
1676 if (type != supported_types.last()) {
1677 fmt::format_to(fmt::appender(buf), ", ");
1678 }
1679 }
1680}
1681
1683 fmt::memory_buffer &buf)
1684{
1685 if (!socket.is_input()) {
1686 return;
1687 }
1688 if (socket.is_multi_input()) {
1689 return;
1690 }
1691 if (socket.owner_node().is_reroute()) {
1692 return;
1693 }
1694 const Span<const bNodeSocket *> connected_sockets = socket.directly_linked_sockets();
1695 if (!connected_sockets.is_empty() && !connected_sockets[0]->owner_node().is_dangling_reroute()) {
1696 return;
1697 }
1698 if (const nodes::SocketDeclaration *socket_decl = socket.runtime->declaration) {
1699 if (socket_decl->input_field_type == nodes::InputSocketFieldType::Implicit) {
1700 return;
1701 }
1702 }
1703 if (socket.typeinfo->base_cpp_type == nullptr) {
1704 return;
1705 }
1706
1707 const CPPType &value_type = *socket.typeinfo->base_cpp_type;
1708 BUFFER_FOR_CPP_TYPE_VALUE(value_type, socket_value);
1709 socket.typeinfo->get_base_cpp_value(socket.default_value, socket_value);
1710 create_inspection_string_for_generic_value(socket, GPointer(value_type, socket_value), buf);
1711 value_type.destruct(socket_value);
1712}
1713
1714static std::optional<std::string> create_description_inspection_string(const bNodeSocket &socket)
1715{
1716 if (socket.runtime->declaration == nullptr) {
1717 return std::nullopt;
1718 }
1719 const blender::nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration;
1720 blender::StringRefNull description = socket_decl.description;
1721 if (description.is_empty()) {
1722 return std::nullopt;
1723 }
1724
1725 return TIP_(description.c_str());
1726}
1727
1728static std::optional<std::string> create_log_inspection_string(geo_log::GeoTreeLog *geo_tree_log,
1729 const bNodeSocket &socket)
1730{
1731 using namespace blender::nodes::geo_eval_log;
1732
1733 if (geo_tree_log == nullptr) {
1734 return std::nullopt;
1735 }
1736 if (socket.typeinfo->base_cpp_type == nullptr) {
1737 return std::nullopt;
1738 }
1739
1740 geo_tree_log->ensure_socket_values();
1741 ValueLog *value_log = geo_tree_log->find_socket_value_log(socket);
1742 fmt::memory_buffer buf;
1743 if (const geo_log::GenericValueLog *generic_value_log =
1744 dynamic_cast<const geo_log::GenericValueLog *>(value_log))
1745 {
1746 create_inspection_string_for_generic_value(socket, generic_value_log->value, buf);
1747 }
1748 else if (const geo_log::FieldInfoLog *gfield_value_log =
1749 dynamic_cast<const geo_log::FieldInfoLog *>(value_log))
1750 {
1751 create_inspection_string_for_field_info(socket, *gfield_value_log, buf);
1752 }
1753 else if (const geo_log::GeometryInfoLog *geo_value_log =
1754 dynamic_cast<const geo_log::GeometryInfoLog *>(value_log))
1755 {
1756 create_inspection_string_for_geometry_info(*geo_value_log, buf);
1757 }
1758
1759 std::string str = fmt::to_string(buf);
1760 if (str.empty()) {
1761 return std::nullopt;
1762 }
1763 return str;
1764}
1765
1766static std::optional<std::string> create_declaration_inspection_string(const bNodeSocket &socket)
1767{
1768 fmt::memory_buffer buf;
1769 if (const nodes::decl::Geometry *socket_decl = dynamic_cast<const nodes::decl::Geometry *>(
1770 socket.runtime->declaration))
1771 {
1773 }
1774
1775 std::string str = fmt::to_string(buf);
1776 if (str.empty()) {
1777 return std::nullopt;
1778 }
1779 return str;
1780}
1781
1783 const bNodeSocket &socket,
1784 TreeDrawContext &tree_draw_ctx)
1785{
1786 const bNodeTreeZones *zones = ntree.zones();
1787 if (!zones) {
1788 return nullptr;
1789 }
1790 const bNodeTreeZone *zone = zones->get_zone_by_socket(socket);
1791 return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
1792}
1793
1795{
1797 std::istringstream text_stream(text);
1798 for (std::string line; std::getline(text_stream, line);) {
1799 result.append(line);
1800 }
1801 return result;
1802}
1803
1804static std::optional<std::string> create_multi_input_log_inspection_string(
1805 const bNodeTree &ntree, const bNodeSocket &socket, TreeDrawContext &tree_draw_ctx)
1806{
1807 if (!socket.is_multi_input()) {
1808 return std::nullopt;
1809 }
1810
1811 Vector<std::pair<int, std::string>, 8> numerated_info;
1812
1813 const Span<const bNodeLink *> connected_links = socket.directly_linked_links();
1814 for (const int index : connected_links.index_range()) {
1815 const bNodeLink *link = connected_links[index];
1816 const int connection_number = index + 1;
1817 if (!link->is_used()) {
1818 continue;
1819 }
1820 if (!(link->flag & NODE_LINK_VALID)) {
1821 continue;
1822 }
1823 if (link->fromnode->is_dangling_reroute()) {
1824 continue;
1825 }
1826 const bNodeSocket &connected_socket = *link->fromsock;
1828 ntree, connected_socket, tree_draw_ctx);
1829 const std::optional<std::string> input_log = create_log_inspection_string(geo_tree_log,
1830 connected_socket);
1831 if (!input_log.has_value()) {
1832 continue;
1833 }
1834 numerated_info.append({connection_number, std::move(*input_log)});
1835 }
1836
1837 if (numerated_info.is_empty()) {
1838 return std::nullopt;
1839 }
1840
1841 fmt::memory_buffer buf;
1842 for (const std::pair<int, std::string> &info : numerated_info) {
1843 const Vector<std::string> lines = lines_of_text(info.second);
1844 fmt::format_to(fmt::appender(buf), "{}", info.first);
1845 fmt::format_to(fmt::appender(buf), ". ");
1846 fmt::format_to(fmt::appender(buf), lines.first());
1847 for (const std::string &line : lines.as_span().drop_front(1)) {
1848 fmt::format_to(fmt::appender(buf), "\n {}", line);
1849 }
1850 if (&info != &numerated_info.last()) {
1851 buf.append(StringRef(".\n"));
1852 }
1853 }
1854
1855 const std::string str = fmt::to_string(buf);
1856 if (str.empty()) {
1857 return std::nullopt;
1858 }
1859
1860 return str;
1861}
1862
1863static std::optional<std::string> create_default_value_inspection_string(const bNodeSocket &socket)
1864{
1865 fmt::memory_buffer buf;
1867
1868 std::string str = fmt::to_string(buf);
1869 if (str.empty()) {
1870 return std::nullopt;
1871 }
1872 return str;
1873}
1874
1875static const bNodeSocket *target_for_reroute(const bNodeSocket &reroute_output)
1876{
1877 const bNodeSocket *output = &reroute_output;
1878 Set<const bNode *> visited_nodes;
1879 visited_nodes.add(&reroute_output.owner_node());
1880 while (true) {
1881 const Span<const bNodeSocket *> linked_sockets = output->directly_linked_sockets();
1882 if (linked_sockets.size() != 1) {
1883 return nullptr;
1884 }
1885 const bNode &target_node = linked_sockets[0]->owner_node();
1886 if (!visited_nodes.add(&target_node)) {
1887 return nullptr;
1888 }
1889 if (!target_node.is_dangling_reroute()) {
1890 return linked_sockets[0];
1891 }
1892 output = target_node.output_sockets()[0];
1893 }
1894}
1895
1896static std::optional<std::string> create_dangling_reroute_inspection_string(
1897 const bNodeTree &ntree, const bNodeSocket &socket)
1898{
1899 if (ntree.type != NTREE_GEOMETRY) {
1900 return std::nullopt;
1901 }
1902
1903 const bNode &node = socket.owner_node();
1904 if (!node.is_dangling_reroute()) {
1905 return std::nullopt;
1906 }
1907
1908 const bNodeSocket &output_socket = *node.output_sockets()[0];
1909 const bNodeSocket *target_socket = target_for_reroute(output_socket);
1910
1911 if (target_socket == nullptr) {
1912 if (!output_socket.directly_linked_sockets().is_empty()) {
1913 return TIP_("Dangling reroute is ignored by all targets");
1914 }
1915 return std::nullopt;
1916 }
1917
1918 if (target_socket->is_multi_input()) {
1919 return TIP_("Dangling reroute branch is ignored by multi input socket");
1920 }
1921
1922 fmt::memory_buffer buf;
1924 std::string str = fmt::to_string(buf);
1925 if (str.empty()) {
1926 return TIP_("Dangling reroute is ignored");
1927 }
1928 fmt::format_to(fmt::appender(buf), ".\n\n");
1929 fmt::format_to(fmt::appender(buf),
1930 TIP_("Dangling reroute is ignored, default value of target socket is used"));
1931 return str;
1932}
1933
1934static std::string node_socket_get_tooltip(const SpaceNode *snode,
1935 const bNodeTree &ntree,
1936 const bNodeSocket &socket)
1937{
1938 TreeDrawContext tree_draw_ctx;
1939 if (snode != nullptr) {
1940 if (ntree.type == NTREE_GEOMETRY) {
1941 tree_draw_ctx.geo_log_by_zone =
1943 }
1944 }
1945
1946 geo_log::GeoTreeLog *geo_tree_log = geo_tree_log_for_socket(ntree, socket, tree_draw_ctx);
1947
1948 Vector<std::string> inspection_strings;
1949
1950 if (std::optional<std::string> info = create_description_inspection_string(socket)) {
1951 inspection_strings.append(std::move(*info));
1952 }
1953 if (std::optional<std::string> info = create_log_inspection_string(geo_tree_log, socket)) {
1954 inspection_strings.append(std::move(*info));
1955 }
1956 else if (std::optional<std::string> info = create_dangling_reroute_inspection_string(ntree,
1957 socket))
1958 {
1959 inspection_strings.append(std::move(*info));
1960 }
1961 else if (std::optional<std::string> info = create_default_value_inspection_string(socket)) {
1962 inspection_strings.append(std::move(*info));
1963 }
1964 else if (std::optional<std::string> info = create_multi_input_log_inspection_string(
1965 ntree, socket, tree_draw_ctx))
1966 {
1967 inspection_strings.append(std::move(*info));
1968 }
1969 if (std::optional<std::string> info = create_declaration_inspection_string(socket)) {
1970 inspection_strings.append(std::move(*info));
1971 }
1972
1973 std::stringstream output;
1974 for (const std::string &info : inspection_strings) {
1975 output << info;
1976 if (&info != &inspection_strings.last()) {
1977 output << ".\n\n";
1978 }
1979 }
1980
1981 if (inspection_strings.is_empty()) {
1982 const bool is_extend = StringRef(socket.idname) == "NodeSocketVirtual";
1983 const bNode &node = socket.owner_node();
1984 if (node.is_reroute()) {
1985 char reroute_name[MAX_NAME];
1986 bke::nodeLabel(&ntree, &node, reroute_name, sizeof(reroute_name));
1987 output << reroute_name;
1988 }
1989 else if (is_extend) {
1990 output << TIP_("Connect a link to create a new socket");
1991 }
1992 else {
1993 output << bke::nodeSocketLabel(&socket);
1994 }
1995
1996 if (ntree.type == NTREE_GEOMETRY && !is_extend) {
1997 output << ".\n\n";
1998 output << TIP_(
1999 "Unknown socket value. Either the socket was not used or its value was not logged "
2000 "during the last evaluation");
2001 }
2002 }
2003
2004 return output.str();
2005}
2006
2008{
2010 &layout,
2011 [](bContext *C, void *argN, const char * /*tip*/) {
2012 const SpaceNode &snode = *CTX_wm_space_node(C);
2013 const bNodeTree &ntree = *snode.edittree;
2014 const int index_in_tree = POINTER_AS_INT(argN);
2015 ntree.ensure_topology_cache();
2016 return node_socket_get_tooltip(&snode, ntree, *ntree.all_sockets()[index_in_tree]);
2017 },
2018 POINTER_FROM_INT(sock.index_in_tree()),
2019 nullptr,
2020 nullptr);
2021}
2022
2023void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
2024{
2025 struct SocketTooltipData {
2026 const bNodeTree *ntree;
2027 const bNodeSocket *socket;
2028 };
2029
2030 SocketTooltipData *data = MEM_cnew<SocketTooltipData>(__func__);
2031 data->ntree = &ntree;
2032 data->socket = &sock;
2033
2035 &layout,
2036 [](bContext *C, void *argN, const char * /*tip*/) {
2037 SocketTooltipData *data = static_cast<SocketTooltipData *>(argN);
2038 const SpaceNode *snode = CTX_wm_space_node(C);
2039 return node_socket_get_tooltip(snode, *data->ntree, *data->socket);
2040 },
2041 data,
2043 MEM_freeN);
2044}
2045
2047 const bNodeTree &ntree,
2048 PointerRNA &node_ptr,
2049 uiBlock &block,
2050 const bNodeSocket &sock,
2051 const uint pos_id,
2052 const uint col_id,
2053 const uint shape_id,
2054 const uint size_id,
2055 const uint outline_col_id,
2056 const float size,
2057 const bool selected)
2058{
2059 const float2 location = sock.runtime->location;
2060
2061 float color[4];
2062 float outline_color[4];
2063 node_socket_color_get(C, ntree, node_ptr, sock, color);
2064 node_socket_outline_color_get(selected, sock.type, outline_color);
2065
2066 node_socket_draw(sock,
2067 color,
2068 outline_color,
2069 size,
2070 location.x,
2071 location.y,
2072 pos_id,
2073 col_id,
2074 shape_id,
2075 size_id,
2076 outline_col_id);
2077
2078 node_socket_tooltip_set(block, sock.index_in_tree(), location, float2(size, size));
2079}
2080
2081void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
2082{
2083 const float size = NODE_SOCKSIZE_DRAW_MULIPLIER * NODE_SOCKSIZE * scale;
2084 rcti draw_rect = *rect;
2085 float outline_color[4] = {0};
2086
2087 node_socket_outline_color_get(sock->flag & SELECT, sock->type, outline_color);
2088
2089 BLI_rcti_resize(&draw_rect, size, size);
2090
2096 uint outline_col_id = GPU_vertformat_attr_add(
2097 format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
2098
2102
2104 immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE);
2105 immUniform2f("ViewportSize", -1.0f, -1.0f);
2106
2107 /* Single point. */
2109 node_socket_draw(*sock,
2110 color,
2111 outline_color,
2112 BLI_rcti_size_y(&draw_rect),
2113 BLI_rcti_cent_x(&draw_rect),
2114 BLI_rcti_cent_y(&draw_rect),
2115 pos_id,
2116 col_id,
2117 shape_id,
2118 size_id,
2119 outline_col_id);
2120 immEnd();
2121
2124
2125 /* Restore. */
2127}
2128
2130#define NODE_TREE_SCALE_SMALL 0.2f
2131
2133static float node_tree_view_scale(const SpaceNode &snode)
2134{
2135 return (1.0f / snode.runtime->aspect) * UI_SCALE_FAC;
2136}
2137
2139{
2142
2144
2145 /* Drawing the checkerboard. */
2146 const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f;
2147 const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f;
2148 immUniform4f("color1", checker_dark, checker_dark, checker_dark, 1.0f);
2149 immUniform4f("color2", checker_light, checker_light, checker_light, 1.0f);
2150 immUniform1i("size", 8);
2151 immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
2153}
2154
2155/* Not a callback. */
2156static void node_draw_preview(const Scene *scene, ImBuf *preview, const rctf *prv)
2157{
2158 float xrect = BLI_rctf_size_x(prv);
2159 float yrect = BLI_rctf_size_y(prv);
2160 float xscale = xrect / float(preview->x);
2161 float yscale = yrect / float(preview->y);
2162 float scale;
2163
2164 /* Uniform scale and offset. */
2165 rctf draw_rect = *prv;
2166 if (xscale < yscale) {
2167 float offset = 0.5f * (yrect - float(preview->y) * xscale);
2168 draw_rect.ymin += offset;
2169 draw_rect.ymax -= offset;
2170 scale = xscale;
2171 }
2172 else {
2173 float offset = 0.5f * (xrect - float(preview->x) * yscale);
2174 draw_rect.xmin += offset;
2175 draw_rect.xmax -= offset;
2176 scale = yscale;
2177 }
2178
2179 node_draw_preview_background(&draw_rect);
2180
2182 /* Pre-multiply graphics. */
2184
2185 ED_draw_imbuf(preview,
2186 draw_rect.xmin,
2187 draw_rect.ymin,
2188 false,
2189 &scene->view_settings,
2190 &scene->display_settings,
2191 scale,
2192 scale);
2193
2195
2196 float black[4] = {0.0f, 0.0f, 0.0f, 1.0f};
2198 const float outline_width = 1.0f;
2199 draw_rect.xmin -= outline_width;
2200 draw_rect.xmax += outline_width;
2201 draw_rect.ymin -= outline_width;
2202 draw_rect.ymax += outline_width;
2203 UI_draw_roundbox_4fv(&draw_rect, false, BASIS_RAD / 2, black);
2204}
2205
2206/* Common handle function for operator buttons that need to select the node first. */
2207static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv)
2208{
2209 SpaceNode &snode = *CTX_wm_space_node(C);
2210 bNodeTree &node_tree = *snode.edittree;
2211 bNode &node = *node_tree.node_by_id(POINTER_AS_INT(node_argv));
2212 const char *opname = (const char *)op_argv;
2213
2214 /* Select & activate only the button's node. */
2215 node_select_single(*C, node);
2216
2217 WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
2218}
2219
2220static void node_draw_shadow(const SpaceNode &snode,
2221 const bNode &node,
2222 const float radius,
2223 const float alpha)
2224{
2225 const rctf &rct = node.runtime->totr;
2227
2228 const float shadow_width = 0.6f * U.widget_unit;
2229 const float shadow_alpha = 0.5f * alpha;
2230
2231 ui_draw_dropshadow(&rct, radius, shadow_width, snode.runtime->aspect, shadow_alpha);
2232
2233 /* Outline emphasis. Slight darkening _inside_ the outline. */
2234 const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
2235 rctf rect{};
2236 rect.xmin = rct.xmin - 0.5f;
2237 rect.xmax = rct.xmax + 0.5f;
2238 rect.ymin = rct.ymin - 0.5f;
2239 rect.ymax = rct.ymax + 0.5f;
2240 UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
2241}
2242
2243static void node_draw_sockets(const View2D &v2d,
2244 const bContext &C,
2245 const bNodeTree &ntree,
2246 const bNode &node,
2247 uiBlock &block,
2248 const bool draw_outputs,
2249 const bool select_all)
2250{
2251 if (node.input_sockets().is_empty() && node.output_sockets().is_empty()) {
2252 return;
2253 }
2254
2255 bool selected = false;
2256
2262 uint outline_col_id = GPU_vertformat_attr_add(
2263 format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
2264
2268 immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE);
2269 immUniform2f("ViewportSize", -1.0f, -1.0f);
2270
2271 /* Set handle size. */
2272 const float socket_draw_size = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER;
2273 float scale;
2274 UI_view2d_scale_get(&v2d, &scale, nullptr);
2275 scale *= socket_draw_size;
2276
2277 if (!select_all) {
2278 immBeginAtMost(GPU_PRIM_POINTS, node.input_sockets().size() + node.output_sockets().size());
2279 }
2280
2281 PointerRNA node_ptr = RNA_pointer_create(
2282 &const_cast<ID &>(ntree.id), &RNA_Node, &const_cast<bNode &>(node));
2283
2284 /* Socket inputs. */
2285 int selected_input_len = 0;
2286 for (const bNodeSocket *sock : node.input_sockets()) {
2287 /* In "hidden" nodes: draw sockets even when panels are collapsed. */
2288 if (!node.is_socket_icon_drawn(*sock)) {
2289 continue;
2290 }
2291 if (select_all || (sock->flag & SELECT)) {
2292 if (!(sock->flag & SOCK_MULTI_INPUT)) {
2293 /* Don't add multi-input sockets here since they are drawn in a different batch. */
2294 selected_input_len++;
2295 }
2296 continue;
2297 }
2298 /* Don't draw multi-input sockets here since they are drawn in a different batch. */
2299 if (sock->flag & SOCK_MULTI_INPUT) {
2300 continue;
2301 }
2302
2304 ntree,
2305 node_ptr,
2306 block,
2307 *sock,
2308 pos_id,
2309 col_id,
2310 shape_id,
2311 size_id,
2312 outline_col_id,
2313 scale,
2314 selected);
2315 }
2316
2317 /* Socket outputs. */
2318 int selected_output_len = 0;
2319 if (draw_outputs) {
2320 for (const bNodeSocket *sock : node.output_sockets()) {
2321 /* In "hidden" nodes: draw sockets even when panels are collapsed. */
2322 if (!node.is_socket_icon_drawn(*sock)) {
2323 continue;
2324 }
2325 if (select_all || (sock->flag & SELECT)) {
2326 selected_output_len++;
2327 continue;
2328 }
2329
2331 ntree,
2332 node_ptr,
2333 block,
2334 *sock,
2335 pos_id,
2336 col_id,
2337 shape_id,
2338 size_id,
2339 outline_col_id,
2340 scale,
2341 selected);
2342 }
2343 }
2344
2345 if (!select_all) {
2346 immEnd();
2347 }
2348
2349 /* Go back and draw selected sockets. */
2350 if (selected_input_len + selected_output_len > 0) {
2351 /* Outline for selected sockets. */
2352
2353 selected = true;
2354
2355 immBegin(GPU_PRIM_POINTS, selected_input_len + selected_output_len);
2356
2357 if (selected_input_len) {
2358 /* Socket inputs. */
2359 for (const bNodeSocket *sock : node.input_sockets()) {
2360 if (!node.is_socket_icon_drawn(*sock)) {
2361 continue;
2362 }
2363 /* Don't draw multi-input sockets here since they are drawn in a different batch. */
2364 if (sock->flag & SOCK_MULTI_INPUT) {
2365 continue;
2366 }
2367 if (select_all || (sock->flag & SELECT)) {
2369 ntree,
2370 node_ptr,
2371 block,
2372 *sock,
2373 pos_id,
2374 col_id,
2375 shape_id,
2376 size_id,
2377 outline_col_id,
2378 scale,
2379 selected);
2380 if (--selected_input_len == 0) {
2381 /* Stop as soon as last one is drawn. */
2382 break;
2383 }
2384 }
2385 }
2386 }
2387
2388 if (selected_output_len) {
2389 /* Socket outputs. */
2390 for (const bNodeSocket *sock : node.output_sockets()) {
2391 if (!node.is_socket_icon_drawn(*sock)) {
2392 continue;
2393 }
2394 if (select_all || (sock->flag & SELECT)) {
2396 ntree,
2397 node_ptr,
2398 block,
2399 *sock,
2400 pos_id,
2401 col_id,
2402 shape_id,
2403 size_id,
2404 outline_col_id,
2405 scale,
2406 selected);
2407 if (--selected_output_len == 0) {
2408 /* Stop as soon as last one is drawn. */
2409 break;
2410 }
2411 }
2412 }
2413 }
2414
2415 immEnd();
2416 }
2417
2419
2422
2423 /* Draw multi-input sockets after the others because they are drawn with `UI_draw_roundbox`
2424 * rather than with `GL_POINT`. */
2425 for (const bNodeSocket *socket : node.input_sockets()) {
2426 if (!node.is_socket_icon_drawn(*socket)) {
2427 continue;
2428 }
2429 if (!(socket->flag & SOCK_MULTI_INPUT)) {
2430 continue;
2431 }
2432
2433 const bool is_node_hidden = (node.flag & NODE_HIDDEN);
2434 const float width = 0.5f * socket_draw_size;
2435 float height = is_node_hidden ? width : node_socket_calculate_height(*socket) - width;
2436
2437 float color[4];
2438 float outline_color[4];
2439 node_socket_color_get(C, ntree, node_ptr, *socket, color);
2440 node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
2441
2442 const int index_in_tree = socket->index_in_tree();
2443 const float2 location = socket->runtime->location;
2444 const float2 draw_size(width, height);
2445 const float2 tooltip_size(scale, height * 2.0f - socket_draw_size + scale);
2447 block, index_in_tree, location, draw_size, color, outline_color, tooltip_size);
2448 }
2449}
2450
2451static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
2452{
2453 Main *bmain = CTX_data_main(C);
2454 bNodePanelState *panel_state = static_cast<bNodePanelState *>(panel_state_argv);
2455 bNodeTree *ntree = static_cast<bNodeTree *>(ntree_argv);
2456
2457 panel_state->flag ^= NODE_PANEL_COLLAPSED;
2458
2459 ED_node_tree_propagate_change(C, bmain, ntree);
2460}
2461
2462/* Draw panel backgrounds first, so other node elements can be rendered on top. */
2463static void node_draw_panels_background(const bNode &node, uiBlock &block)
2464{
2465 namespace nodes = blender::nodes;
2466
2468 BLI_assert(node.runtime->panels.size() == node.panel_states().size());
2469
2470 const nodes::NodeDeclaration &decl = *node.declaration();
2471 const rctf &rct = node.runtime->totr;
2472 float color_panel[4];
2473 UI_GetThemeColorShade4fv(TH_NODE, -15, color_panel);
2474
2475 /* True if the last panel is open, draw bottom gap as background. */
2476 bool is_last_panel_visible = false;
2477 float last_panel_content_y = 0.0f;
2478
2479 int panel_i = 0;
2480 for (const nodes::ItemDeclarationPtr &item_decl : decl.items) {
2481 const nodes::PanelDeclaration *panel_decl = dynamic_cast<nodes::PanelDeclaration *>(
2482 item_decl.get());
2483 if (panel_decl == nullptr) {
2484 /* Not a panel. */
2485 continue;
2486 }
2487
2488 const bNodePanelState &state = node.panel_states()[panel_i];
2489 const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i];
2490
2491 /* Don't draw hidden or collapsed panels. */
2492 const bool is_background_visible = state.has_visible_content() &&
2493 !(state.is_collapsed() || state.is_parent_collapsed());
2494 is_last_panel_visible = is_background_visible;
2495 last_panel_content_y = runtime.max_content_y;
2496 if (!is_background_visible) {
2497 ++panel_i;
2498 continue;
2499 }
2500
2502
2503 /* Panel background. */
2504 const rctf content_rect = {rct.xmin, rct.xmax, runtime.min_content_y, runtime.max_content_y};
2506 UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, color_panel);
2507
2509
2510 ++panel_i;
2511 }
2512
2513 /* If last item is an open panel, extend the panel background to cover the bottom border. */
2514 if (is_last_panel_visible) {
2516
2517 const rctf content_rect = {rct.xmin, rct.xmax, rct.ymin, last_panel_content_y};
2519 UI_draw_roundbox_4fv(&content_rect, true, BASIS_RAD, color_panel);
2520
2522 }
2523}
2524
2525static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block)
2526{
2527 namespace nodes = blender::nodes;
2528
2530 BLI_assert(node.runtime->panels.size() == node.panel_states().size());
2531
2532 const nodes::NodeDeclaration &decl = *node.declaration();
2533 const rctf &rct = node.runtime->totr;
2534
2535 int panel_i = 0;
2536 for (const nodes::ItemDeclarationPtr &item_decl : decl.items) {
2537 const nodes::PanelDeclaration *panel_decl = dynamic_cast<nodes::PanelDeclaration *>(
2538 item_decl.get());
2539 if (panel_decl == nullptr) {
2540 /* Not a panel. */
2541 continue;
2542 }
2543
2544 const bNodePanelState &state = node.panel_states()[panel_i];
2545 /* Don't draw hidden panels. */
2546 const bool is_header_visible = state.has_visible_content() && !state.is_parent_collapsed();
2547 if (!is_header_visible) {
2548 ++panel_i;
2549 continue;
2550 }
2551 const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i];
2552
2553 const rctf rect = {
2554 rct.xmin,
2555 rct.xmax,
2556 runtime.location_y - NODE_DYS,
2557 runtime.location_y + NODE_DYS,
2558 };
2559
2561
2562 /* Collapse/expand icon. */
2563 const int but_size = U.widget_unit * 0.8f;
2564 uiDefIconBut(&block,
2566 0,
2567 state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT,
2568 rct.xmin + (NODE_MARGIN_X / 3),
2569 runtime.location_y - but_size / 2,
2570 but_size,
2571 but_size,
2572 nullptr,
2573 0.0f,
2574 0.0f,
2575 "");
2576
2577 /* Panel label. */
2578 uiBut *but = uiDefBut(&block,
2580 0,
2581 IFACE_(panel_decl->name.c_str()),
2582 int(rct.xmin + NODE_MARGIN_X + 0.4f),
2583 int(runtime.location_y - NODE_DYS),
2584 short(rct.xmax - rct.xmin - (30.0f * UI_SCALE_FAC)),
2585 short(NODE_DY),
2586 nullptr,
2587 0,
2588 0,
2589 "");
2590 if (node.flag & NODE_MUTED) {
2592 }
2593
2594 /* Invisible button covering the entire header for collapsing/expanding. */
2595 const int header_but_margin = NODE_MARGIN_X / 3;
2596 but = uiDefIconBut(&block,
2598 0,
2599 ICON_NONE,
2600 rect.xmin + header_but_margin,
2601 rect.ymin,
2602 std::max(int(rect.xmax - rect.xmin - 2 * header_but_margin), 0),
2603 rect.ymax - rect.ymin,
2604 nullptr,
2605 0.0f,
2606 0.0f,
2607 panel_decl->description.c_str());
2608 UI_but_func_pushed_state_set(but, [&state](const uiBut &) { return state.is_collapsed(); });
2610 but, node_panel_toggle_button_cb, const_cast<bNodePanelState *>(&state), &ntree);
2611
2613
2614 ++panel_i;
2615 }
2616}
2617
2619{
2620 int highest_priority = 0;
2622 for (const geo_log::NodeWarning &warning : warnings) {
2623 const int priority = node_warning_type_severity(warning.type);
2624 if (priority > highest_priority) {
2625 highest_priority = priority;
2626 highest_priority_type = warning.type;
2627 }
2628 }
2629 return highest_priority_type;
2630}
2631
2635
2636static std::string node_errors_tooltip_fn(bContext * /*C*/, void *argN, const char * /*tip*/)
2637{
2639
2640 std::string complete_string;
2641
2642 for (const geo_log::NodeWarning &warning : data.warnings.drop_back(1)) {
2643 complete_string += warning.message;
2644 /* Adding the period is not ideal for multi-line messages, but it is consistent
2645 * with other tooltip implementations in Blender, so it is added here. */
2646 complete_string += '.';
2647 complete_string += '\n';
2648 }
2649
2650 /* Let the tooltip system automatically add the last period. */
2651 complete_string += data.warnings.last().message;
2652
2653 return complete_string;
2654}
2655
2656#define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit)
2657
2659 uiBlock &block,
2660 const rctf &rect,
2661 float &icon_offset)
2662{
2663 icon_offset -= NODE_HEADER_ICON_SIZE;
2665 uiDefIconBut(&block,
2667 0,
2668 ICON_ERROR,
2669 icon_offset,
2670 rect.ymax - NODE_DY,
2672 UI_UNIT_Y,
2673 nullptr,
2674 0,
2675 0,
2676 TIP_(node.typeinfo->realtime_compositor_unsupported_message));
2678}
2679
2680static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx,
2681 const bNode &node,
2682 uiBlock &block,
2683 const rctf &rect,
2684 float &icon_offset)
2685{
2686 if (tree_draw_ctx.used_by_realtime_compositor &&
2687 node.typeinfo->realtime_compositor_unsupported_message)
2688 {
2690 return;
2691 }
2692
2693 geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * {
2694 const bNodeTreeZones *zones = node.owner_tree().zones();
2695 if (!zones) {
2696 return nullptr;
2697 }
2698 const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier);
2699 if (zone && ELEM(&node, zone->input_node, zone->output_node)) {
2700 zone = zone->parent_zone;
2701 }
2702 return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
2703 }();
2704
2706 if (geo_tree_log) {
2707 geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier);
2708 if (node_log != nullptr) {
2709 warnings = node_log->warnings;
2710 }
2711 }
2712 if (warnings.is_empty()) {
2713 return;
2714 }
2715
2716 const geo_log::NodeWarningType display_type = node_error_highest_priority(warnings);
2717 NodeErrorsTooltipData *tooltip_data = MEM_new<NodeErrorsTooltipData>(__func__);
2718 tooltip_data->warnings = warnings;
2719
2720 icon_offset -= NODE_HEADER_ICON_SIZE;
2722 uiBut *but = uiDefIconBut(&block,
2724 0,
2725 geo_log::node_warning_type_icon(display_type),
2726 icon_offset,
2727 rect.ymax - NODE_DY,
2729 UI_UNIT_Y,
2730 nullptr,
2731 0,
2732 0,
2733 nullptr);
2734 UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, [](void *arg) {
2735 MEM_delete(static_cast<NodeErrorsTooltipData *>(arg));
2736 });
2738}
2739
2740static std::optional<std::chrono::nanoseconds> geo_node_get_execution_time(
2741 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2742{
2743 const bNodeTree &ntree = *snode.edittree;
2744
2745 geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * {
2746 const bNodeTreeZones *zones = ntree.zones();
2747 if (!zones) {
2748 return nullptr;
2749 }
2750 const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier);
2751 if (zone && ELEM(&node, zone->input_node, zone->output_node)) {
2752 zone = zone->parent_zone;
2753 }
2754 return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
2755 }();
2756
2757 if (tree_log == nullptr) {
2758 return std::nullopt;
2759 }
2760 if (node.type == NODE_GROUP_OUTPUT) {
2761 return tree_log->execution_time;
2762 }
2763 if (node.is_frame()) {
2764 /* Could be cached in the future if this recursive code turns out to be slow. */
2765 std::chrono::nanoseconds run_time{0};
2766 bool found_node = false;
2767
2768 for (const bNode *tnode : node.direct_children_in_frame()) {
2769 if (tnode->is_frame()) {
2770 std::optional<std::chrono::nanoseconds> sub_frame_run_time = geo_node_get_execution_time(
2771 tree_draw_ctx, snode, *tnode);
2772 if (sub_frame_run_time.has_value()) {
2773 run_time += *sub_frame_run_time;
2774 found_node = true;
2775 }
2776 }
2777 else {
2778 if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr_as(tnode->identifier))
2779 {
2780 found_node = true;
2781 run_time += node_log->execution_time;
2782 }
2783 }
2784 }
2785 if (found_node) {
2786 return run_time;
2787 }
2788 return std::nullopt;
2789 }
2790 if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.identifier)) {
2791 return node_log->execution_time;
2792 }
2793 return std::nullopt;
2794}
2795
2796/* Create node key instance, assuming the node comes from the currently edited node tree. */
2798{
2799 const bNodeTreePath *path = static_cast<const bNodeTreePath *>(snode.treepath.last);
2800
2801 /* Some code in this file checks for the non-null elements of the tree path. However, if we did
2802 * iterate into a node it is expected that there is a tree, and it should be in the path.
2803 * Otherwise something else went wrong. */
2804 BLI_assert(path);
2805
2806 /* Assume that the currently editing tree is the last in the path. */
2807 BLI_assert(snode.edittree == path->nodetree);
2808
2809 return bke::node_instance_key(path->parent_key, snode.edittree, &node);
2810}
2811
2812static std::optional<std::chrono::nanoseconds> compositor_accumulate_frame_node_execution_time(
2813 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2814{
2816
2817 timeit::Nanoseconds frame_execution_time(0);
2818 bool has_any_execution_time = false;
2819
2820 for (const bNode *current_node : node.direct_children_in_frame()) {
2821 const bNodeInstanceKey key = current_node_instance_key(snode, *current_node);
2822 if (const timeit::Nanoseconds *node_execution_time =
2824 {
2825 frame_execution_time += *node_execution_time;
2826 has_any_execution_time = true;
2827 }
2828 }
2829
2830 if (!has_any_execution_time) {
2831 return std::nullopt;
2832 }
2833
2834 return frame_execution_time;
2835}
2836
2837static std::optional<std::chrono::nanoseconds> compositor_node_get_execution_time(
2838 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2839{
2841
2842 /* For the frame nodes accumulate execution time of its children. */
2843 if (node.is_frame()) {
2844 return compositor_accumulate_frame_node_execution_time(tree_draw_ctx, snode, node);
2845 }
2846
2847 /* For other nodes simply lookup execution time.
2848 * The group node instances have their own entries in the execution times map. */
2849 const bNodeInstanceKey key = current_node_instance_key(snode, node);
2850 if (const timeit::Nanoseconds *execution_time =
2852 {
2853 return *execution_time;
2854 }
2855
2856 return std::nullopt;
2857}
2858
2859static std::optional<std::chrono::nanoseconds> node_get_execution_time(
2860 const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
2861{
2862 switch (snode.edittree->type) {
2863 case NTREE_GEOMETRY:
2864 return geo_node_get_execution_time(tree_draw_ctx, snode, node);
2865 case NTREE_COMPOSIT:
2866 return compositor_node_get_execution_time(tree_draw_ctx, snode, node);
2867 }
2868 return std::nullopt;
2869}
2870
2871static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx,
2872 const SpaceNode &snode,
2873 const bNode &node)
2874{
2875 const std::optional<std::chrono::nanoseconds> exec_time = node_get_execution_time(
2876 tree_draw_ctx, snode, node);
2877
2878 if (!exec_time.has_value()) {
2879 return std::string("");
2880 }
2881
2882 const uint64_t exec_time_us =
2883 std::chrono::duration_cast<std::chrono::microseconds>(*exec_time).count();
2884
2885 /* Don't show time if execution time is 0 microseconds. */
2886 if (exec_time_us == 0) {
2887 return std::string("-");
2888 }
2889 if (exec_time_us < 100) {
2890 return std::string("< 0.1 ms");
2891 }
2892
2893 int precision = 0;
2894 /* Show decimal if value is below 1ms */
2895 if (exec_time_us < 1000) {
2896 precision = 2;
2897 }
2898 else if (exec_time_us < 10000) {
2899 precision = 1;
2900 }
2901
2902 std::stringstream stream;
2903 stream << std::fixed << std::setprecision(precision) << (exec_time_us / 1000.0f);
2904 return stream.str() + " ms";
2905}
2906
2910
2911static std::string named_attribute_tooltip(bContext * /*C*/, void *argN, const char * /*tip*/)
2912{
2913 NamedAttributeTooltipArg &arg = *static_cast<NamedAttributeTooltipArg *>(argN);
2914
2915 fmt::memory_buffer buf;
2916 fmt::format_to(fmt::appender(buf), TIP_("Accessed named attributes:"));
2917 fmt::format_to(fmt::appender(buf), "\n");
2918
2919 struct NameWithUsage {
2920 StringRefNull name;
2922 };
2923
2924 Vector<NameWithUsage> sorted_used_attribute;
2925 for (auto &&item : arg.usage_by_attribute.items()) {
2926 sorted_used_attribute.append({item.key, item.value});
2927 }
2928 std::sort(sorted_used_attribute.begin(),
2929 sorted_used_attribute.end(),
2930 [](const NameWithUsage &a, const NameWithUsage &b) {
2931 return BLI_strcasecmp_natural(a.name.c_str(), b.name.c_str()) < 0;
2932 });
2933
2934 for (const NameWithUsage &attribute : sorted_used_attribute) {
2935 const StringRefNull name = attribute.name;
2936 const geo_log::NamedAttributeUsage usage = attribute.usage;
2937 fmt::format_to(fmt::appender(buf), TIP_(" \u2022 \"{}\": "), name);
2938 Vector<std::string> usages;
2940 usages.append(TIP_("read"));
2941 }
2943 usages.append(TIP_("write"));
2944 }
2946 usages.append(TIP_("remove"));
2947 }
2948 for (const int i : usages.index_range()) {
2949 fmt::format_to(fmt::appender(buf), usages[i]);
2950 if (i < usages.size() - 1) {
2951 fmt::format_to(fmt::appender(buf), ", ");
2952 }
2953 }
2954 fmt::format_to(fmt::appender(buf), "\n");
2955 }
2956 fmt::format_to(fmt::appender(buf), "\n");
2957 fmt::format_to(fmt::appender(buf),
2958 TIP_("Attributes with these names used within the group may conflict with "
2959 "existing attributes"));
2960 return fmt::to_string(buf);
2961}
2962
2964 const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute_name)
2965{
2966 const int attributes_num = usage_by_attribute_name.size();
2967
2968 NodeExtraInfoRow row;
2969 row.text = std::to_string(attributes_num) +
2970 (attributes_num == 1 ? RPT_(" Named Attribute") : RPT_(" Named Attributes"));
2971 row.icon = ICON_SPREADSHEET;
2973 row.tooltip_fn_arg = new NamedAttributeTooltipArg{usage_by_attribute_name};
2974 row.tooltip_fn_free_arg = [](void *arg) { delete static_cast<NamedAttributeTooltipArg *>(arg); };
2975 return row;
2976}
2977
2978static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(
2979 TreeDrawContext &tree_draw_ctx, const bNode &node)
2980{
2981 geo_log::GeoTreeLog *geo_tree_log = [&]() -> geo_log::GeoTreeLog * {
2982 const bNodeTreeZones *zones = node.owner_tree().zones();
2983 if (!zones) {
2984 return nullptr;
2985 }
2986 const bNodeTreeZone *zone = zones->get_zone_by_node(node.identifier);
2987 return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
2988 }();
2989 if (geo_tree_log == nullptr) {
2990 return std::nullopt;
2991 }
2992 if (ELEM(node.type,
2996 {
2997 /* Only show the overlay when the name is passed in from somewhere else. */
2998 for (const bNodeSocket *socket : node.input_sockets()) {
2999 if (STREQ(socket->name, "Name")) {
3000 if (!socket->is_directly_linked()) {
3001 return std::nullopt;
3002 }
3003 }
3004 }
3005 }
3006 geo_tree_log->ensure_used_named_attributes();
3007 geo_log::GeoNodeLog *node_log = geo_tree_log->nodes.lookup_ptr(node.identifier);
3008 if (node_log == nullptr) {
3009 return std::nullopt;
3010 }
3011 if (node_log->used_named_attributes.is_empty()) {
3012 return std::nullopt;
3013 }
3015}
3016
3017static std::optional<NodeExtraInfoRow> node_get_execution_time_label_row(
3018 TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
3019{
3020 NodeExtraInfoRow row;
3021 row.text = node_get_execution_time_label(tree_draw_ctx, snode, node);
3022 if (row.text.empty()) {
3023 return std::nullopt;
3024 }
3025 row.tooltip = TIP_(
3026 "The execution time from the node tree's latest evaluation. For frame and group "
3027 "nodes, the time for all sub-nodes");
3028 row.icon = ICON_PREVIEW_RANGE;
3029 return row;
3030}
3031
3033 const SpaceNode &snode,
3034 const bNode &node,
3036{
3037 if (snode.overlay.flag & SN_OVERLAY_SHOW_TIMINGS) {
3038 std::optional<NodeExtraInfoRow> row = node_get_execution_time_label_row(
3039 tree_draw_ctx, snode, node);
3040 if (row.has_value()) {
3041 rows.append(std::move(*row));
3042 }
3043 }
3044}
3045
3047 const bNode &node,
3049{
3050 const bNodeTree &tree = *snode.edittree;
3051 const Span<bke::NodeLinkError> link_errors = tree.runtime->link_errors_by_target_node.lookup(
3052 node.identifier);
3053 if (link_errors.is_empty()) {
3054 return;
3055 }
3056 NodeExtraInfoRow row;
3057 row.text = IFACE_("Invalid Link");
3058
3059 row.tooltip_fn = [](bContext *C, void *arg, const char * /*tip*/) {
3061 const bNode &node = *static_cast<const bNode *>(arg);
3062 const Span<bke::NodeLinkError> link_errors = tree.runtime->link_errors_by_target_node.lookup(
3063 node.identifier);
3064 std::stringstream ss;
3065 Set<StringRef> already_added_errors;
3066 for (const int i : link_errors.index_range()) {
3067 const StringRefNull tooltip = link_errors[i].tooltip;
3068 if (already_added_errors.add_as(tooltip)) {
3069 ss << "\u2022 " << tooltip << "\n";
3070 }
3071 }
3072 ss << "\n";
3073 ss << "Any invalid links are highlighted";
3074 return ss.str();
3075 };
3076 row.tooltip_fn_arg = const_cast<bNode *>(&node);
3077 row.icon = ICON_ERROR;
3078 rows.append(std::move(row));
3079}
3080
3082 TreeDrawContext &tree_draw_ctx,
3083 const SpaceNode &snode,
3084 const bNode &node)
3085{
3087
3088 if (node.typeinfo->get_extra_info) {
3089 nodes::NodeExtraInfoParams params{rows, node, C};
3090 node.typeinfo->get_extra_info(params);
3091 }
3092
3093 if (node.typeinfo->deprecation_notice) {
3094 NodeExtraInfoRow row;
3095 row.text = IFACE_("Deprecated");
3096 row.icon = ICON_INFO;
3097 row.tooltip = TIP_(node.typeinfo->deprecation_notice);
3098 rows.append(std::move(row));
3099 }
3100
3101 node_get_invalid_links_extra_info(snode, node, rows);
3102
3103 if (snode.edittree->type == NTREE_COMPOSIT) {
3104 node_get_compositor_extra_info(tree_draw_ctx, snode, node, rows);
3105 return rows;
3106 }
3107
3108 if (!(snode.edittree->type == NTREE_GEOMETRY)) {
3109 /* Currently geometry and compositor nodes are the only nodes to have extra info per nodes. */
3110 return rows;
3111 }
3112
3114 if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(tree_draw_ctx,
3115 node))
3116 {
3117 rows.append(std::move(*row));
3118 }
3119 }
3120
3121 if (snode.overlay.flag & SN_OVERLAY_SHOW_TIMINGS &&
3123 ELEM(node.type,
3124 NODE_FRAME,
3129 {
3130 std::optional<NodeExtraInfoRow> row = node_get_execution_time_label_row(
3131 tree_draw_ctx, snode, node);
3132 if (row.has_value()) {
3133 rows.append(std::move(*row));
3134 }
3135 }
3136
3137 geo_log::GeoTreeLog *tree_log = [&]() -> geo_log::GeoTreeLog * {
3138 const bNodeTreeZones *tree_zones = node.owner_tree().zones();
3139 if (!tree_zones) {
3140 return nullptr;
3141 }
3142 const bNodeTreeZone *zone = tree_zones->get_zone_by_node(node.identifier);
3143 return tree_draw_ctx.geo_log_by_zone.lookup_default(zone, nullptr);
3144 }();
3145
3146 if (tree_log) {
3147 tree_log->ensure_debug_messages();
3148 const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.identifier);
3149 if (node_log != nullptr) {
3150 for (const StringRef message : node_log->debug_messages) {
3151 NodeExtraInfoRow row;
3152 row.text = message;
3153 row.icon = ICON_INFO;
3154 rows.append(std::move(row));
3155 }
3156 }
3157 }
3158
3159 return rows;
3160}
3161
3162static void node_draw_extra_info_row(const bNode &node,
3163 uiBlock &block,
3164 const rctf &rect,
3165 const int row,
3166 const NodeExtraInfoRow &extra_info_row)
3167{
3168 const float but_icon_left = rect.xmin + 6.0f * UI_SCALE_FAC;
3169 const float but_icon_width = NODE_HEADER_ICON_SIZE * 0.8f;
3170 const float but_icon_right = but_icon_left + but_icon_width;
3171
3173 uiBut *but_icon = uiDefIconBut(&block,
3175 0,
3176 extra_info_row.icon,
3177 int(but_icon_left),
3178 int(rect.ymin + row * (20.0f * UI_SCALE_FAC)),
3179 but_icon_width,
3180 UI_UNIT_Y,
3181 nullptr,
3182 0,
3183 0,
3184 extra_info_row.tooltip);
3185 if (extra_info_row.tooltip_fn != nullptr) {
3186 UI_but_func_tooltip_set(but_icon,
3187 extra_info_row.tooltip_fn,
3188 extra_info_row.tooltip_fn_arg,
3189 extra_info_row.tooltip_fn_free_arg);
3190 }
3192
3193 const float but_text_left = but_icon_right + 6.0f * UI_SCALE_FAC;
3194 const float but_text_right = rect.xmax;
3195 const float but_text_width = but_text_right - but_text_left;
3196
3197 uiBut *but_text = uiDefBut(&block,
3199 0,
3200 extra_info_row.text.c_str(),
3201 int(but_text_left),
3202 int(rect.ymin + row * (20.0f * UI_SCALE_FAC)),
3203 short(but_text_width),
3204 short(NODE_DY),
3205 nullptr,
3206 0,
3207 0,
3208 extra_info_row.tooltip);
3209
3210 if (extra_info_row.tooltip_fn != nullptr) {
3211 /* Don't pass tooltip free function because it's already used on the uiBut above. */
3213 but_text, extra_info_row.tooltip_fn, extra_info_row.tooltip_fn_arg, nullptr);
3214 }
3215
3216 if (node.flag & NODE_MUTED) {
3219 }
3220}
3221
3222static void node_draw_extra_info_panel_back(const bNode &node, const rctf &extra_info_rect)
3223{
3224 const rctf &node_rect = node.runtime->totr;
3225 rctf panel_back_rect = extra_info_rect;
3226 /* Extend the panel behind hidden nodes to accommodate the large rounded corners. */
3227 if (node.flag & NODE_HIDDEN) {
3228 panel_back_rect.ymin = BLI_rctf_cent_y(&node_rect);
3229 }
3230
3232 if (node.flag & NODE_MUTED) {
3234 }
3235 else {
3237 }
3238 color.a -= 0.35f;
3239
3240 ColorTheme4f color_outline;
3241 UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline);
3242
3243 const float outline_width = U.pixelsize;
3244 BLI_rctf_pad(&panel_back_rect, outline_width, outline_width);
3245
3248 &panel_back_rect, color, nullptr, 0.0f, color_outline, outline_width, BASIS_RAD);
3249}
3250
3252 TreeDrawContext &tree_draw_ctx,
3253 const SpaceNode &snode,
3254 const bNode &node,
3255 ImBuf *preview,
3256 uiBlock &block)
3257{
3258 const Scene *scene = CTX_data_scene(&C);
3259 if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS)) {
3260 return;
3261 }
3262 if (preview && !(preview->x > 0 && preview->y > 0)) {
3263 /* If the preview has an non-drawable size, just don't draw it. */
3264 preview = nullptr;
3265 }
3266 Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(C, tree_draw_ctx, snode, node);
3267 if (extra_info_rows.is_empty() && !preview) {
3268 return;
3269 }
3270
3271 const rctf &rct = node.runtime->totr;
3272 rctf extra_info_rect;
3273
3274 if (node.is_frame()) {
3275 extra_info_rect.xmin = rct.xmin;
3276 extra_info_rect.xmax = rct.xmin + 95.0f * UI_SCALE_FAC;
3277 extra_info_rect.ymin = rct.ymin + 2.0f * UI_SCALE_FAC;
3278 extra_info_rect.ymax = rct.ymin + 2.0f * UI_SCALE_FAC;
3279 }
3280 else {
3281 const float padding = 3.0f * UI_SCALE_FAC;
3282
3283 extra_info_rect.xmin = rct.xmin + padding;
3284 extra_info_rect.xmax = rct.xmax - padding;
3285 extra_info_rect.ymin = rct.ymax;
3286 extra_info_rect.ymax = rct.ymax + extra_info_rows.size() * (20.0f * UI_SCALE_FAC);
3287
3288 float preview_height = 0.0f;
3289 rctf preview_rect;
3290 if (preview) {
3291 const float width = BLI_rctf_size_x(&extra_info_rect);
3292 if (preview->x > preview->y) {
3293 preview_height = (width - 2.0f * padding) * float(preview->y) / float(preview->x) +
3294 2.0f * padding;
3295 preview_rect.ymin = extra_info_rect.ymin + padding;
3296 preview_rect.ymax = extra_info_rect.ymin + preview_height - padding;
3297 preview_rect.xmin = extra_info_rect.xmin + padding;
3298 preview_rect.xmax = extra_info_rect.xmax - padding;
3299 extra_info_rect.ymax += preview_height;
3300 }
3301 else {
3302 preview_height = width;
3303 const float preview_width = (width - 2.0f * padding) * float(preview->x) /
3304 float(preview->y) +
3305 2.0f * padding;
3306 preview_rect.ymin = extra_info_rect.ymin + padding;
3307 preview_rect.ymax = extra_info_rect.ymin + preview_height - padding;
3308 preview_rect.xmin = extra_info_rect.xmin + padding + (width - preview_width) / 2;
3309 preview_rect.xmax = extra_info_rect.xmax - padding - (width - preview_width) / 2;
3310 extra_info_rect.ymax += preview_height;
3311 }
3312 }
3313
3314 node_draw_extra_info_panel_back(node, extra_info_rect);
3315
3316 if (preview) {
3317 node_draw_preview(scene, preview, &preview_rect);
3318 }
3319
3320 /* Resize the rect to draw the textual infos on top of the preview. */
3321 extra_info_rect.ymin += preview_height;
3322 }
3323
3324 for (int row : extra_info_rows.index_range()) {
3325 node_draw_extra_info_row(node, block, extra_info_rect, row, extra_info_rows[row]);
3326 }
3327}
3328
3329static void node_draw_basis(const bContext &C,
3330 TreeDrawContext &tree_draw_ctx,
3331 const View2D &v2d,
3332 const SpaceNode &snode,
3333 bNodeTree &ntree,
3334 const bNode &node,
3335 uiBlock &block,
3336 bNodeInstanceKey key)
3337{
3338 const float iconbutw = NODE_HEADER_ICON_SIZE;
3339 const bool show_preview = (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) &&
3341 (node.flag & NODE_PREVIEW) &&
3342 (U.experimental.use_shader_node_previews ||
3343 ntree.type != NTREE_SHADER);
3344
3345 /* Skip if out of view. */
3346 rctf rect_with_preview = node.runtime->totr;
3347 if (show_preview) {
3348 rect_with_preview.ymax += NODE_WIDTH(node);
3349 }
3350 if (BLI_rctf_isect(&rect_with_preview, &v2d.cur, nullptr) == false) {
3351 UI_block_end(&C, &block);
3352 return;
3353 }
3354
3355 /* Shadow. */
3356 if (!bke::all_zone_node_types().contains(node.type)) {
3357 node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
3358 }
3359
3360 const rctf &rct = node.runtime->totr;
3361 float color[4];
3362 int color_id = node_get_colorid(tree_draw_ctx, node);
3363
3364 GPU_line_width(1.0f);
3365
3366 /* Overlay atop the node. */
3367 {
3368 bool drawn_with_previews = false;
3369
3370 if (show_preview) {
3371 bke::bNodeInstanceHash *previews_compo = static_cast<bke::bNodeInstanceHash *>(
3372 CTX_data_pointer_get(&C, "node_previews").data);
3373 NestedTreePreviews *previews_shader = tree_draw_ctx.nested_group_infos;
3374
3375 if (previews_shader) {
3376 ImBuf *preview = node_preview_acquire_ibuf(ntree, *previews_shader, node);
3377 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, preview, block);
3378 node_release_preview_ibuf(*previews_shader);
3379 drawn_with_previews = true;
3380 }
3381 else if (previews_compo) {
3382 bNodePreview *preview_compositor = static_cast<bNodePreview *>(
3383 bke::node_instance_hash_lookup(previews_compo, key));
3384 if (preview_compositor) {
3386 C, tree_draw_ctx, snode, node, preview_compositor->ibuf, block);
3387 drawn_with_previews = true;
3388 }
3389 }
3390 }
3391
3392 if (drawn_with_previews == false) {
3393 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, nullptr, block);
3394 }
3395 }
3396
3397 const float padding = 0.5f;
3398 const float corner_radius = BASIS_RAD + padding;
3399 /* Header. */
3400 {
3401 /* Add some padding to prevent transparent gaps with the outline. */
3402 const rctf rect = {
3403 rct.xmin - padding,
3404 rct.xmax + padding,
3405 rct.ymax - NODE_DY - padding,
3406 rct.ymax + padding,
3407 };
3408
3409 float color_header[4];
3410
3411 /* Muted nodes get a mix of the background with the node color. */
3412 if (node.flag & NODE_MUTED) {
3413 UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_header);
3414 }
3415 else {
3416 UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color_header);
3417 }
3418
3420 UI_draw_roundbox_4fv(&rect, true, corner_radius, color_header);
3421 }
3422
3423 /* Show/hide icons. */
3424 float iconofs = rct.xmax - 0.35f * U.widget_unit;
3425
3426 /* Group edit. This icon should be the first for the node groups. */
3427 if (node.type == NODE_GROUP) {
3428 iconofs -= iconbutw;
3430 uiBut *but = uiDefIconBut(&block,
3432 0,
3433 ICON_NODETREE,
3434 iconofs,
3435 rct.ymax - NODE_DY,
3436 iconbutw,
3437 UI_UNIT_Y,
3438 nullptr,
3439 0,
3440 0,
3441 "");
3442 UI_but_func_set(but,
3445 (void *)"NODE_OT_group_edit");
3446 if (node.id) {
3448 }
3450 }
3451 /* Preview. */
3452 if (node_is_previewable(snode, ntree, node)) {
3453 const bool is_active = node.flag & NODE_PREVIEW;
3454 iconofs -= iconbutw;
3456 uiBut *but = uiDefIconBut(&block,
3458 0,
3459 is_active ? ICON_HIDE_OFF : ICON_HIDE_ON,
3460 iconofs,
3461 rct.ymax - NODE_DY,
3462 iconbutw,
3463 UI_UNIT_Y,
3464 nullptr,
3465 0,
3466 0,
3467 "");
3468 UI_but_func_set(but,
3471 (void *)"NODE_OT_preview_toggle");
3473 }
3474 if (ELEM(node.type, NODE_CUSTOM, NODE_CUSTOM_GROUP) && node.typeinfo->ui_icon != ICON_NONE) {
3475 iconofs -= iconbutw;
3477 uiDefIconBut(&block,
3479 0,
3480 node.typeinfo->ui_icon,
3481 iconofs,
3482 rct.ymax - NODE_DY,
3483 iconbutw,
3484 UI_UNIT_Y,
3485 nullptr,
3486 0,
3487 0,
3488 "");
3490 }
3491 if (node.type == GEO_NODE_VIEWER) {
3492 const bool is_active = &node == tree_draw_ctx.active_geometry_nodes_viewer;
3493 iconofs -= iconbutw;
3495 uiBut *but = uiDefIconBut(&block,
3497 0,
3498 is_active ? ICON_HIDE_OFF : ICON_HIDE_ON,
3499 iconofs,
3500 rct.ymax - NODE_DY,
3501 iconbutw,
3502 UI_UNIT_Y,
3503 nullptr,
3504 0,
3505 0,
3506 "");
3507 /* Selection implicitly activates the node. */
3508 const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" : "NODE_OT_select";
3510 but, node_toggle_button_cb, POINTER_FROM_INT(node.identifier), (void *)operator_idname);
3512 }
3513
3514 node_add_error_message_button(tree_draw_ctx, node, block, rct, iconofs);
3515
3516 /* Title. */
3517 if (node.flag & SELECT) {
3519 }
3520 else {
3521 UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color);
3522 }
3523
3524 /* Collapse/expand icon. */
3525 {
3526 const int but_size = U.widget_unit * 0.8f;
3528
3529 uiBut *but = uiDefIconBut(&block,
3531 0,
3532 ICON_DOWNARROW_HLT,
3533 rct.xmin + (NODE_MARGIN_X / 3),
3534 rct.ymax - NODE_DY / 2.2f - but_size / 2,
3535 but_size,
3536 but_size,
3537 nullptr,
3538 0.0f,
3539 0.0f,
3540 "");
3541
3542 UI_but_func_set(but,
3545 (void *)"NODE_OT_hide_toggle");
3547 }
3548
3549 char showname[128];
3550 bke::nodeLabel(&ntree, &node, showname, sizeof(showname));
3551
3552 uiBut *but = uiDefBut(&block,
3554 0,
3555 showname,
3556 int(rct.xmin + NODE_MARGIN_X + 0.4f),
3557 int(rct.ymax - NODE_DY),
3558 short(iconofs - rct.xmin - (18.0f * UI_SCALE_FAC)),
3559 short(NODE_DY),
3560 nullptr,
3561 0,
3562 0,
3563 TIP_(node.typeinfo->ui_description));
3565 but,
3566 [](bContext * /*C*/, void *arg, const char *tip) -> std::string {
3567 const bNode &node = *static_cast<const bNode *>(arg);
3568 if (node.typeinfo->ui_description_fn) {
3569 return node.typeinfo->ui_description_fn(node);
3570 }
3571 return StringRef(tip);
3572 },
3573 const_cast<bNode *>(&node),
3574 nullptr);
3575
3576 if (node.flag & NODE_MUTED) {
3578 }
3579
3580 /* Wire across the node when muted/disabled. */
3581 if (node.flag & NODE_MUTED) {
3582 node_draw_mute_line(C, v2d, snode, node);
3583 }
3584
3585 /* Body. */
3586 const float outline_width = U.pixelsize;
3587 {
3588 /* Use warning color to indicate undefined types. */
3589 if (bke::node_type_is_undefined(&node)) {
3591 }
3592 /* Muted nodes get a mix of the background with the node color. */
3593 else if (node.flag & NODE_MUTED) {
3595 }
3596 else if (node.flag & NODE_CUSTOM_COLOR) {
3597 rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], 1.0f);
3598 }
3599 else {
3601 }
3602
3603 /* Draw selected nodes fully opaque. */
3604 if (node.flag & SELECT) {
3605 color[3] = 1.0f;
3606 }
3607
3608 /* Draw muted nodes slightly transparent so the wires inside are visible. */
3609 if (node.flag & NODE_MUTED) {
3610 color[3] -= 0.2f;
3611 }
3612
3613 /* Add some padding to prevent transparent gaps with the outline. */
3614 const rctf rect = {
3615 rct.xmin - padding,
3616 rct.xmax + padding,
3617 rct.ymin - padding,
3618 rct.ymax - (NODE_DY + outline_width) + padding,
3619 };
3620
3622 UI_draw_roundbox_4fv(&rect, true, corner_radius, color);
3623
3624 if (is_node_panels_supported(node)) {
3625 node_draw_panels_background(node, block);
3626 }
3627 }
3628
3629 /* Header underline. */
3630 {
3631 float color_underline[4];
3632
3633 if (node.flag & NODE_MUTED) {
3634 UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.05f, color_underline);
3635 color_underline[3] = 1.0f;
3636 }
3637 else {
3638 UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.2f, color_underline);
3639 }
3640
3641 const rctf rect = {
3642 rct.xmin,
3643 rct.xmax,
3644 rct.ymax - (NODE_DY + outline_width),
3645 rct.ymax - NODE_DY,
3646 };
3647
3649 UI_draw_roundbox_4fv(&rect, true, 0.0f, color_underline);
3650 }
3651
3652 /* Outline. */
3653 {
3654 const rctf rect = {
3655 rct.xmin - outline_width,
3656 rct.xmax + outline_width,
3657 rct.ymin - outline_width,
3658 rct.ymax + outline_width,
3659 };
3660
3661 /* Color the outline according to active, selected, or undefined status. */
3662 float color_outline[4];
3663
3664 if (node.flag & SELECT) {
3665 UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline);
3666 }
3667 else if (bke::node_type_is_undefined(&node)) {
3668 UI_GetThemeColor4fv(TH_REDALERT, color_outline);
3669 }
3670 else if (const bke::bNodeZoneType *zone_type = bke::zone_type_by_node_type(node.type)) {
3671 UI_GetThemeColor4fv(zone_type->theme_id, color_outline);
3672 color_outline[3] = 1.0f;
3673 }
3674 else {
3675 UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline);
3676 }
3677
3679 UI_draw_roundbox_4fv(&rect, false, BASIS_RAD + outline_width, color_outline);
3680 }
3681
3682 /* Skip slow socket drawing if zoom is small. */
3684 node_draw_sockets(v2d, C, ntree, node, block, true, false);
3685 }
3686
3687 if (is_node_panels_supported(node)) {
3688 node_draw_panels(ntree, node, block);
3689 }
3690
3691 UI_block_end(&C, &block);
3692 UI_block_draw(&C, &block);
3693}
3694
3695static void node_draw_hidden(const bContext &C,
3696 TreeDrawContext &tree_draw_ctx,
3697 const View2D &v2d,
3698 const SpaceNode &snode,
3699 bNodeTree &ntree,
3700 bNode &node,
3701 uiBlock &block)
3702{
3703 const rctf &rct = node.runtime->totr;
3704 float centy = BLI_rctf_cent_y(&rct);
3705 float hiddenrad = BLI_rctf_size_y(&rct) / 2.0f;
3706
3707 float scale;
3708 UI_view2d_scale_get(&v2d, &scale, nullptr);
3709
3710 const int color_id = node_get_colorid(tree_draw_ctx, node);
3711
3712 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, nullptr, block);
3713
3714 /* Shadow. */
3715 node_draw_shadow(snode, node, hiddenrad, 1.0f);
3716
3717 /* Wire across the node when muted/disabled. */
3718 if (node.flag & NODE_MUTED) {
3719 node_draw_mute_line(C, v2d, snode, node);
3720 }
3721
3722 /* Body. */
3723 float color[4];
3724 {
3725 if (bke::node_type_is_undefined(&node)) {
3726 /* Use warning color to indicate undefined types. */
3728 }
3729 else if (node.flag & NODE_MUTED) {
3730 /* Muted nodes get a mix of the background with the node color. */
3731 UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color);
3732 }
3733 else if (node.flag & NODE_CUSTOM_COLOR) {
3734 rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], 1.0f);
3735 }
3736 else {
3737 UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color);
3738 }
3739
3740 /* Draw selected nodes fully opaque. */
3741 if (node.flag & SELECT) {
3742 color[3] = 1.0f;
3743 }
3744
3745 /* Draw muted nodes slightly transparent so the wires inside are visible. */
3746 if (node.flag & NODE_MUTED) {
3747 color[3] -= 0.2f;
3748 }
3749
3750 /* Add some padding to prevent transparent gaps with the outline. */
3751 const float padding = 0.5f;
3752 const rctf rect = {
3753 rct.xmin - padding,
3754 rct.xmax + padding,
3755 rct.ymin - padding,
3756 rct.ymax + padding,
3757 };
3758
3759 UI_draw_roundbox_4fv(&rect, true, hiddenrad + padding, color);
3760 }
3761
3762 /* Title. */
3763 if (node.flag & SELECT) {
3765 }
3766 else {
3767 UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color);
3768 }
3769
3770 /* Collapse/expand icon. */
3771 {
3772 const int but_size = U.widget_unit * 1.0f;
3774
3775 uiBut *but = uiDefIconBut(&block,
3777 0,
3778 ICON_RIGHTARROW,
3779 rct.xmin + (NODE_MARGIN_X / 3),
3780 centy - but_size / 2,
3781 but_size,
3782 but_size,
3783 nullptr,
3784 0.0f,
3785 0.0f,
3786 "");
3787
3788 UI_but_func_set(but,
3791 (void *)"NODE_OT_hide_toggle");
3793 }
3794
3795 char showname[128];
3796 bke::nodeLabel(&ntree, &node, showname, sizeof(showname));
3797
3798 uiBut *but = uiDefBut(&block,
3800 0,
3801 showname,
3803 round_fl_to_int(centy - NODE_DY * 0.5f),
3804 short(BLI_rctf_size_x(&rct) - ((18.0f + 12.0f) * UI_SCALE_FAC)),
3805 short(NODE_DY),
3806 nullptr,
3807 0,
3808 0,
3809 TIP_(node.typeinfo->ui_description));
3810
3811 /* Outline. */
3812 {
3813 const float outline_width = U.pixelsize;
3814 const rctf rect = {
3815 rct.xmin - outline_width,
3816 rct.xmax + outline_width,
3817 rct.ymin - outline_width,
3818 rct.ymax + outline_width,
3819 };
3820
3821 /* Color the outline according to active, selected, or undefined status. */
3822 float color_outline[4];
3823
3824 if (node.flag & SELECT) {
3825 UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline);
3826 }
3827 else if (bke::node_type_is_undefined(&node)) {
3828 UI_GetThemeColor4fv(TH_REDALERT, color_outline);
3829 }
3830 else {
3831 UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline);
3832 }
3833
3835 UI_draw_roundbox_4fv(&rect, false, hiddenrad + outline_width, color_outline);
3836 }
3837
3838 if (node.flag & NODE_MUTED) {
3840 }
3841
3842 /* Scale widget thing. */
3846
3848 float dx = 0.5f * U.widget_unit;
3849 const float dx2 = 0.15f * U.widget_unit * snode.runtime->aspect;
3850 const float dy = 0.2f * U.widget_unit;
3851
3853 immVertex2f(pos, rct.xmax - dx, centy - dy);
3854 immVertex2f(pos, rct.xmax - dx, centy + dy);
3855
3856 immVertex2f(pos, rct.xmax - dx - dx2, centy - dy);
3857 immVertex2f(pos, rct.xmax - dx - dx2, centy + dy);
3858 immEnd();
3859
3861 dx -= snode.runtime->aspect;
3862
3864 immVertex2f(pos, rct.xmax - dx, centy - dy);
3865 immVertex2f(pos, rct.xmax - dx, centy + dy);
3866
3867 immVertex2f(pos, rct.xmax - dx - dx2, centy - dy);
3868 immVertex2f(pos, rct.xmax - dx - dx2, centy + dy);
3869 immEnd();
3870
3873
3874 node_draw_sockets(v2d, C, ntree, node, block, true, false);
3875
3876 UI_block_end(&C, &block);
3877 UI_block_draw(&C, &block);
3878}
3879
3881{
3882 if (directions == 0) {
3883 return WM_CURSOR_DEFAULT;
3884 }
3885 if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0) {
3886 return WM_CURSOR_Y_MOVE;
3887 }
3888 if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0) {
3889 return WM_CURSOR_X_MOVE;
3890 }
3891 return WM_CURSOR_EDIT;
3892}
3893
3894static const bNode *find_node_under_cursor(SpaceNode &snode, const float2 &cursor)
3895{
3896 for (const bNode *node : tree_draw_order_calc_nodes_reversed(*snode.edittree)) {
3897 if (BLI_rctf_isect_pt(&node->runtime->totr, cursor[0], cursor[1])) {
3898 return node;
3899 }
3900 }
3901 return nullptr;
3902}
3903
3904void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor)
3905{
3906 const bNodeTree *ntree = snode.edittree;
3907 if (ntree == nullptr) {
3909 return;
3910 }
3911 if (node_find_indicated_socket(snode, region, cursor, SOCK_IN | SOCK_OUT)) {
3913 return;
3914 }
3915 const bNode *node = find_node_under_cursor(snode, cursor);
3916 if (!node) {
3918 return;
3919 }
3920 const NodeResizeDirection dir = node_get_resize_direction(snode, node, cursor[0], cursor[1]);
3921 if (node->is_frame() && dir == NODE_RESIZE_NONE) {
3922 /* Indicate that frame nodes can be moved/selected on their borders. */
3923 const rctf frame_inside = node_frame_rect_inside(snode, *node);
3924 if (!BLI_rctf_isect_pt(&frame_inside, cursor[0], cursor[1])) {
3926 return;
3927 }
3929 return;
3930 }
3931
3933}
3934
3936{
3937 for (bNode *node : ntree.all_nodes()) {
3938 for (bNodeSocket *socket : node->input_sockets()) {
3939 if (socket->is_multi_input()) {
3940 socket->runtime->total_inputs = socket->directly_linked_links().size();
3941 }
3942 }
3943 }
3944 /* Count temporary links going into this socket. */
3945 if (snode.runtime->linkdrag) {
3946 for (const bNodeLink &link : snode.runtime->linkdrag->links) {
3947 if (link.tosock && (link.tosock->flag & SOCK_MULTI_INPUT)) {
3948 link.tosock->runtime->total_inputs++;
3949 }
3950 }
3951 }
3952}
3953
3954static float frame_node_label_height(const NodeFrame &frame_data)
3955{
3956 return frame_data.label_size * UI_SCALE_FAC;
3957}
3958
3959#define NODE_FRAME_MARGIN (1.5f * U.widget_unit)
3960
3961/* XXX Does a bounding box update by iterating over all children.
3962 * Not ideal to do this in every draw call, but doing as transform callback doesn't work,
3963 * since the child node totr rects are not updated properly at that point. */
3965{
3966 NodeFrame *data = (NodeFrame *)node.storage;
3967
3968 const float margin = NODE_FRAME_MARGIN;
3969 const float has_label = node.label[0] != '\0';
3970
3971 const float label_height = frame_node_label_height(*data);
3972 /* Add an additional 25% to account for the glyphs descender.
3973 * This works well in most cases. */
3974 const float margin_top = 0.5f * margin + (has_label ? 1.25f * label_height : 0.5f * margin);
3975
3976 /* Initialize rect from current frame size. */
3977 rctf rect;
3978 node_to_updated_rect(node, rect);
3979
3980 /* Frame can be resized manually only if shrinking is disabled or no children are attached. */
3981 data->flag |= NODE_FRAME_RESIZEABLE;
3982 /* For shrinking bounding box, initialize the rect from first child node. */
3983 bool bbinit = (data->flag & NODE_FRAME_SHRINK);
3984 /* Fit bounding box to all children. */
3985 for (const bNode *tnode : nodes) {
3986 if (tnode->parent != &node) {
3987 continue;
3988 }
3989
3990 /* Add margin to node rect. */
3991 rctf noderect = tnode->runtime->totr;
3992 noderect.xmin -= margin;
3993 noderect.xmax += margin;
3994 noderect.ymin -= margin;
3995 noderect.ymax += margin_top;
3996
3997 /* First child initializes frame. */
3998 if (bbinit) {
3999 bbinit = false;
4000 rect = noderect;
4002 }
4003 else {
4004 BLI_rctf_union(&rect, &noderect);
4005 }
4006 }
4007
4008 /* Now adjust the frame size from view-space bounding box. */
4009 const float2 offset = node_from_view(node, {rect.xmin, rect.ymax});
4010 node.offsetx = offset.x;
4011 node.offsety = offset.y;
4012 const float2 max = node_from_view(node, {rect.xmax, rect.ymin});
4013 node.width = max.x - node.offsetx;
4014 node.height = -max.y + node.offsety;
4015
4016 node.runtime->totr = rect;
4017}
4018
4020{
4021 const float2 loc = node_to_view(node, float2(0));
4022
4023 /* Reroute node has exactly one input and one output, both in the same place. */
4024 node.input_socket(0).runtime->location = loc;
4025 node.output_socket(0).runtime->location = loc;
4026
4027 const float size = 8.0f;
4028 node.width = size * 2;
4029 node.runtime->totr.xmin = loc.x - size;
4030 node.runtime->totr.xmax = loc.x + size;
4031 node.runtime->totr.ymax = loc.y + size;
4032 node.runtime->totr.ymin = loc.y - size;
4033}
4034
4036 TreeDrawContext &tree_draw_ctx,
4037 bNodeTree &ntree,
4039 Span<uiBlock *> blocks)
4040{
4041 /* Make sure socket "used" tags are correct, for displaying value buttons. */
4042 SpaceNode *snode = CTX_wm_space_node(&C);
4043
4044 count_multi_input_socket_links(ntree, *snode);
4045
4046 for (const int i : nodes.index_range()) {
4047 bNode &node = *nodes[i];
4048 uiBlock &block = *blocks[node.index()];
4049 if (node.is_frame()) {
4050 /* Frame sizes are calculated after all other nodes have calculating their #totr. */
4051 continue;
4052 }
4053
4054 if (node.is_reroute()) {
4056 }
4057 else {
4058 if (node.flag & NODE_HIDDEN) {
4059 node_update_hidden(node, block);
4060 }
4061 else {
4062 node_update_basis(C, tree_draw_ctx, ntree, node, block);
4063 }
4064 }
4065 }
4066
4067 /* Now calculate the size of frame nodes, which can depend on the size of other nodes.
4068 * Update nodes in reverse, so children sizes get updated before parents. */
4069 for (int i = nodes.size() - 1; i >= 0; i--) {
4070 if (nodes[i]->is_frame()) {
4072 }
4073 }
4074}
4075
4076static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx,
4077 const bNodeTree &ntree,
4078 const bNode &node,
4079 const SpaceNode &snode)
4080{
4081 const float aspect = snode.runtime->aspect;
4082 /* XXX font id is crap design */
4083 const int fontid = UI_style_get()->widget.uifont_id;
4084 const NodeFrame *data = (const NodeFrame *)node.storage;
4085 const float font_size = data->label_size / aspect;
4086
4087 char label[MAX_NAME];
4088 bke::nodeLabel(&ntree, &node, label, sizeof(label));
4089
4090 BLF_enable(fontid, BLF_ASPECT);
4091 BLF_aspect(fontid, aspect, aspect, 1.0f);
4092 BLF_size(fontid, font_size * UI_SCALE_FAC);
4093
4094 /* Title color. */
4095 int color_id = node_get_colorid(tree_draw_ctx, node);
4096 uchar color[3];
4097 UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color);
4098 BLF_color3ubv(fontid, color);
4099
4100 const float margin = NODE_FRAME_MARGIN;
4101 const float width = BLF_width(fontid, label, sizeof(label));
4102 const int label_height = frame_node_label_height(*data);
4103
4104 const rctf &rct = node.runtime->totr;
4105 const float label_x = BLI_rctf_cent_x(&rct) - (0.5f * width);
4106 const float label_y = rct.ymax - label_height - (0.5f * margin);
4107
4108 /* Label. */
4109 const bool has_label = node.label[0] != '\0';
4110 if (has_label) {
4111 BLF_position(fontid, label_x, label_y, 0);
4112 BLF_draw(fontid, label, sizeof(label));
4113 }
4114
4115 /* Draw text body. */
4116 if (node.id) {
4117 const Text *text = (const Text *)node.id;
4118 const int line_height_max = BLF_height_max(fontid);
4119 const float line_spacing = (line_height_max * aspect);
4120 const float line_width = (BLI_rctf_size_x(&rct) - 2 * margin) / aspect;
4121
4122 const float x = rct.xmin + margin;
4123 float y = rct.ymax - label_height - (has_label ? line_spacing + margin : 0);
4124
4125 const int y_min = rct.ymin + margin;
4126
4128 BLF_clipping(fontid, rct.xmin, rct.ymin + margin, rct.xmax, rct.ymax);
4129
4130 BLF_wordwrap(fontid, line_width);
4131
4132 LISTBASE_FOREACH (const TextLine *, line, &text->lines) {
4133 if (line->line[0]) {
4134 BLF_position(fontid, x, y, 0);
4135 ResultBLF info;
4136 BLF_draw(fontid, line->line, line->len, &info);
4137 y -= line_spacing * info.lines;
4138 }
4139 else {
4140 y -= line_spacing;
4141 }
4142 if (y < y_min) {
4143 break;
4144 }
4145 }
4146
4148 }
4149
4150 BLF_disable(fontid, BLF_ASPECT);
4151}
4152
4153static void frame_node_draw_background(const ARegion &region,
4154 const SpaceNode &snode,
4155 const bNode &node)
4156{
4157 /* Skip if out of view. */
4158 if (BLI_rctf_isect(&node.runtime->totr, &region.v2d.cur, nullptr) == false) {
4159 return;
4160 }
4161
4162 float color[4];
4164 const float alpha = color[3];
4165
4166 node_draw_shadow(snode, node, BASIS_RAD, alpha);
4167
4168 if (node.flag & NODE_CUSTOM_COLOR) {
4169 rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], alpha);
4170 }
4171 else {
4173 }
4174
4175 const rctf &rct = node.runtime->totr;
4177 UI_draw_roundbox_4fv(&rct, true, BASIS_RAD, color);
4178
4179 /* Outline active and selected emphasis. */
4180 if (node.flag & SELECT) {
4181 if (node.flag & NODE_ACTIVE) {
4183 }
4184 else {
4186 }
4187
4188 UI_draw_roundbox_aa(&rct, false, BASIS_RAD, color);
4189 }
4190}
4191
4193 TreeDrawContext &tree_draw_ctx,
4194 const ARegion &region,
4195 const SpaceNode &snode,
4196 const bNodeTree &ntree,
4197 const bNode &node,
4198 uiBlock &block)
4199{
4200 /* Skip if out of view. */
4201 if (BLI_rctf_isect(&node.runtime->totr, &region.v2d.cur, nullptr) == false) {
4202 UI_block_end(&C, &block);
4203 return;
4204 }
4205
4206 /* Label and text. */
4207 frame_node_draw_label(tree_draw_ctx, ntree, node, snode);
4208
4209 node_draw_extra_info_panel(C, tree_draw_ctx, snode, node, nullptr, block);
4210
4211 UI_block_end(&C, &block);
4212 UI_block_draw(&C, &block);
4213}
4214
4216 const SpaceNode &snode)
4217{
4218 const std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
4220 if (!object_and_modifier) {
4221 return {};
4222 }
4223 snode.edittree->ensure_topology_cache();
4224
4225 /* Compute the compute context hash for the current node tree path. */
4226 std::optional<ComputeContextHash> current_compute_context_hash =
4227 [&]() -> std::optional<ComputeContextHash> {
4228 ComputeContextBuilder compute_context_builder;
4229 compute_context_builder.push<bke::ModifierComputeContext>(
4230 object_and_modifier->nmd->modifier.name);
4231 if (!ed::space_node::push_compute_context_for_tree_path(snode, compute_context_builder)) {
4232 return std::nullopt;
4233 }
4234 return compute_context_builder.current()->hash();
4235 }();
4236 if (!current_compute_context_hash) {
4237 return {};
4238 }
4239
4240 Set<const bNodeSocket *> sockets_on_gizmo_paths;
4241
4242 ComputeContextBuilder compute_context_builder;
4244 C,
4245 compute_context_builder,
4246 [&](const Object &gizmo_object,
4247 const NodesModifierData &gizmo_nmd,
4248 const ComputeContext &gizmo_context,
4249 const bNode &gizmo_node,
4250 const bNodeSocket &gizmo_socket) {
4251 if (&gizmo_object != object_and_modifier->object) {
4252 return;
4253 }
4254 if (&gizmo_nmd != object_and_modifier->nmd) {
4255 return;
4256 }
4258 gizmo_context,
4259 gizmo_node,
4260 gizmo_socket,
4261 [&](const ComputeContext &compute_context,
4262 const bNodeSocket &socket,
4263 const nodes::inverse_eval::ElemVariant & /*elem*/) {
4264 if (compute_context.hash() == *current_compute_context_hash) {
4265 sockets_on_gizmo_paths.add(&socket);
4266 }
4267 });
4268 });
4269
4270 return sockets_on_gizmo_paths;
4271}
4272
4276static const bNode *reroute_node_get_linked_reroute(const bNode &reroute)
4277{
4278 BLI_assert(reroute.is_reroute());
4279
4280 const bNodeSocket *input_socket = reroute.input_sockets().first();
4281 if (input_socket->directly_linked_links().is_empty()) {
4282 return nullptr;
4283 }
4284 const bNodeLink *input_link = input_socket->directly_linked_links().first();
4285 const bNode *from_node = input_link->fromnode;
4286 return from_node->is_reroute() ? from_node : nullptr;
4287}
4288
4294 const bNode &src_reroute)
4295{
4296 BLI_assert(src_reroute.is_reroute());
4297
4298 if (src_reroute.label[0] != '\0') {
4299 return StringRefNull(src_reroute.label);
4300 }
4301
4302 Map<const bNode *, StringRefNull> &reroute_auto_labels = tree_draw_ctx.reroute_auto_labels;
4303
4304 StringRefNull label;
4305 Vector<const bNode *> reroute_path;
4306
4307 /* Traverse reroute path backwards until label, non-reroute node or link-cycle is found. */
4308 for (const bNode *reroute = &src_reroute; reroute;
4309 reroute = reroute_node_get_linked_reroute(*reroute))
4310 {
4311 reroute_path.append(reroute);
4312 if (const StringRefNull *label_ptr = reroute_auto_labels.lookup_ptr(reroute)) {
4313 label = *label_ptr;
4314 break;
4315 }
4316 if (reroute->label[0] != '\0') {
4317 label = reroute->label;
4318 break;
4319 }
4320 /* This makes sure that the loop eventually ends even if there are link-cycles. */
4321 reroute_auto_labels.add(reroute, "");
4322 }
4323
4324 /* Remember the label for each node on the path to avoid recomputing it. */
4325 for (const bNode *reroute : reroute_path) {
4326 reroute_auto_labels.add_overwrite(reroute, label);
4327 }
4328
4329 return label;
4330}
4331
4332static void reroute_node_draw_label(TreeDrawContext &tree_draw_ctx,
4333 const SpaceNode &snode,
4334 const bNode &node,
4335 uiBlock &block)
4336{
4337 const bool has_label = node.label[0] != '\0';
4338 const bool use_auto_label = !has_label && (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) &&
4340
4341 if (!has_label && !use_auto_label) {
4342 return;
4343 }
4344
4345 /* Don't show the automatic label, when being zoomed out. */
4346 if (!has_label && node_tree_view_scale(snode) < NODE_TREE_SCALE_SMALL) {
4347 return;
4348 }
4349
4350 char showname[128];
4351 STRNCPY(showname,
4352 has_label ? node.label : reroute_node_get_auto_label(tree_draw_ctx, node).c_str());
4353
4354 const short width = 512;
4355 const int x = BLI_rctf_cent_x(&node.runtime->totr) - (width / 2);
4356 const int y = node.runtime->totr.ymax;
4357
4358 uiBut *label_but = uiDefBut(
4359 &block, UI_BTYPE_LABEL, 0, showname, x, y, width, short(NODE_DY), nullptr, 0, 0, nullptr);
4360
4362
4363 if (use_auto_label && !(node.flag & NODE_SELECT)) {
4365 }
4366}
4367
4368static void reroute_node_draw(const bContext &C,
4369 TreeDrawContext &tree_draw_ctx,
4370 ARegion &region,
4371 const SpaceNode &snode,
4372 bNodeTree &ntree,
4373 const bNode &node,
4374 uiBlock &block)
4375{
4376 /* Skip if out of view. */
4377 const rctf &rct = node.runtime->totr;
4378 if (rct.xmax < region.v2d.cur.xmin || rct.xmin > region.v2d.cur.xmax ||
4379 rct.ymax < region.v2d.cur.ymin || node.runtime->totr.ymin > region.v2d.cur.ymax)
4380 {
4381 UI_block_end(&C, &block);
4382 return;
4383 }
4384
4385 reroute_node_draw_label(tree_draw_ctx, snode, node, block);
4386
4387 /* Only draw input socket as they all are placed on the same position highlight
4388 * if node itself is selected, since we don't display the node body separately. */
4389 node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT);
4390
4391 UI_block_end(&C, &block);
4392 UI_block_draw(&C, &block);
4393}
4394
4395static void node_draw(const bContext &C,
4396 TreeDrawContext &tree_draw_ctx,
4397 ARegion &region,
4398 const SpaceNode &snode,
4399 bNodeTree &ntree,
4400 bNode &node,
4401 uiBlock &block,
4402 bNodeInstanceKey key)
4403{
4404 if (node.is_frame()) {
4405 /* Should have been drawn before already. */
4407 }
4408 else if (node.is_reroute()) {
4409 reroute_node_draw(C, tree_draw_ctx, region, snode, ntree, node, block);
4410 }
4411 else {
4412 const View2D &v2d = region.v2d;
4413 if (node.flag & NODE_HIDDEN) {
4414 node_draw_hidden(C, tree_draw_ctx, v2d, snode, ntree, node, block);
4415 }
4416 else {
4417 node_draw_basis(C, tree_draw_ctx, v2d, snode, ntree, node, block, key);
4418 }
4419 }
4420}
4421
4422static void add_rect_corner_positions(Vector<float2> &positions, const rctf &rect)
4423{
4424 positions.append({rect.xmin, rect.ymin});
4425 positions.append({rect.xmin, rect.ymax});
4426 positions.append({rect.xmax, rect.ymin});
4427 positions.append({rect.xmax, rect.ymax});
4428}
4429
4431 const bNodeTreeZone &zone,
4432 const Span<std::unique_ptr<bNodeTreeZone>> all_zones,
4433 MutableSpan<Vector<float2>> r_bounds_by_zone)
4434{
4435 const float node_padding = UI_UNIT_X;
4436 const float zone_padding = 0.3f * UI_UNIT_X;
4437
4438 Vector<float2> &bounds = r_bounds_by_zone[zone.index];
4439 if (!bounds.is_empty()) {
4440 return;
4441 }
4442
4443 Vector<float2> possible_bounds;
4444 for (const bNodeTreeZone *child_zone : zone.child_zones) {
4445 find_bounds_by_zone_recursive(snode, *child_zone, all_zones, r_bounds_by_zone);
4446 const Span<float2> child_bounds = r_bounds_by_zone[child_zone->index];
4447 for (const float2 &pos : child_bounds) {
4448 rctf rect;
4449 BLI_rctf_init_pt_radius(&rect, pos, zone_padding);
4450 add_rect_corner_positions(possible_bounds, rect);
4451 }
4452 }
4453 for (const bNode *child_node : zone.child_nodes) {
4454 rctf rect = child_node->runtime->totr;
4455 BLI_rctf_pad(&rect, node_padding, node_padding);
4456 add_rect_corner_positions(possible_bounds, rect);
4457 }
4458 if (zone.input_node) {
4459 const rctf &totr = zone.input_node->runtime->totr;
4460 rctf rect = totr;
4461 BLI_rctf_pad(&rect, node_padding, node_padding);
4462 rect.xmin = math::interpolate(totr.xmin, totr.xmax, 0.25f);
4463 add_rect_corner_positions(possible_bounds, rect);
4464 }
4465 if (zone.output_node) {
4466 const rctf &totr = zone.output_node->runtime->totr;
4467 rctf rect = totr;
4468 BLI_rctf_pad(&rect, node_padding, node_padding);
4469 rect.xmax = math::interpolate(totr.xmin, totr.xmax, 0.75f);
4470 add_rect_corner_positions(possible_bounds, rect);
4471 }
4472
4473 if (snode.runtime->linkdrag) {
4474 for (const bNodeLink &link : snode.runtime->linkdrag->links) {
4475 if (link.fromnode == nullptr) {
4476 continue;
4477 }
4478 if (zone.contains_node_recursively(*link.fromnode) && zone.output_node != link.fromnode) {
4479 const float2 pos = node_link_bezier_points_dragged(snode, link)[3];
4480 rctf rect;
4481 BLI_rctf_init_pt_radius(&rect, pos, node_padding);
4482 add_rect_corner_positions(possible_bounds, rect);
4483 }
4484 }
4485 }
4486
4487 Vector<int> convex_indices(possible_bounds.size());
4488 const int convex_positions_num = BLI_convexhull_2d(
4489 reinterpret_cast<float(*)[2]>(possible_bounds.data()),
4490 possible_bounds.size(),
4491 convex_indices.data());
4492 convex_indices.resize(convex_positions_num);
4493
4494 for (const int i : convex_indices) {
4495 bounds.append(possible_bounds[i]);
4496 }
4497}
4498
4500 TreeDrawContext &tree_draw_ctx,
4501 const ARegion &region,
4502 const SpaceNode &snode,
4503 const bNodeTree &ntree,
4504 Span<uiBlock *> blocks)
4505{
4506 const bNodeTreeZones *zones = ntree.zones();
4507 const int zones_num = zones ? zones->zones.size() : 0;
4508
4509 Array<Vector<float2>> bounds_by_zone(zones_num);
4510 Array<bke::CurvesGeometry> fillet_curve_by_zone(zones_num);
4511 /* Bounding box area of zones is used to determine draw order. */
4512 Array<float> bounding_box_width_by_zone(zones_num);
4513
4514 for (const int zone_i : IndexRange(zones_num)) {
4515 const bNodeTreeZone &zone = *zones->zones[zone_i];
4516
4517 find_bounds_by_zone_recursive(snode, zone, zones->zones, bounds_by_zone);
4518 const Span<float2> boundary_positions = bounds_by_zone[zone_i];
4519 const int boundary_positions_num = boundary_positions.size();
4520
4521 const Bounds<float2> bounding_box = *bounds::min_max(boundary_positions);
4522 const float bounding_box_width = bounding_box.max.x - bounding_box.min.x;
4523 bounding_box_width_by_zone[zone_i] = bounding_box_width;
4524
4525 bke::CurvesGeometry boundary_curve(boundary_positions_num, 1);
4526 boundary_curve.cyclic_for_write().first() = true;
4527 boundary_curve.fill_curve_types(CURVE_TYPE_POLY);
4528 MutableSpan<float3> boundary_curve_positions = boundary_curve.positions_for_write();
4529 boundary_curve.offsets_for_write().copy_from({0, boundary_positions_num});
4530 for (const int i : boundary_positions.index_range()) {
4531 boundary_curve_positions[i] = float3(boundary_positions[i], 0.0f);
4532 }
4533
4534 fillet_curve_by_zone[zone_i] = geometry::fillet_curves_poly(
4535 boundary_curve,
4536 IndexRange(1),
4537 VArray<float>::ForSingle(BASIS_RAD, boundary_positions_num),
4538 VArray<int>::ForSingle(5, boundary_positions_num),
4539 true,
4540 {});
4541 }
4542
4543 const View2D &v2d = region.v2d;
4544 float scale;
4545 UI_view2d_scale_get(&v2d, &scale, nullptr);
4546 float line_width = 1.0f * scale;
4547 float viewport[4] = {};
4548 GPU_viewport_size_get_f(viewport);
4549
4550 const auto get_theme_id = [&](const int zone_i) {
4551 const bNode *node = zones->zones[zone_i]->output_node;
4553 };
4554
4557
4558 using ZoneOrNode = std::variant<const bNodeTreeZone *, const bNode *>;
4559 Vector<ZoneOrNode> draw_order;
4560 for (const int zone_i : IndexRange(zones_num)) {
4561 draw_order.append(zones->zones[zone_i].get());
4562 }
4563 for (const bNode *node : ntree.all_nodes()) {
4564 if (node->flag & NODE_BACKGROUND) {
4565 draw_order.append(node);
4566 }
4567 }
4568 auto get_zone_or_node_width = [&](const ZoneOrNode &zone_or_node) {
4569 if (const bNodeTreeZone *const *zone_p = std::get_if<const bNodeTreeZone *>(&zone_or_node)) {
4570 const bNodeTreeZone &zone = **zone_p;
4571 return bounding_box_width_by_zone[zone.index];
4572 }
4573 if (const bNode *const *node_p = std::get_if<const bNode *>(&zone_or_node)) {
4574 const bNode &node = **node_p;
4575 return BLI_rctf_size_x(&node.runtime->totr);
4576 }
4578 return 0.0f;
4579 };
4580 std::sort(draw_order.begin(), draw_order.end(), [&](const ZoneOrNode &a, const ZoneOrNode &b) {
4581 /* Draw zones with smaller bounding box on top to make them visible. */
4582 return get_zone_or_node_width(a) > get_zone_or_node_width(b);
4583 });
4584
4585 for (const ZoneOrNode &zone_or_node : draw_order) {
4586 if (const bNodeTreeZone *const *zone_p = std::get_if<const bNodeTreeZone *>(&zone_or_node)) {
4587 const bNodeTreeZone &zone = **zone_p;
4588 const int zone_i = zone.index;
4589 float zone_color[4];
4590 UI_GetThemeColor4fv(get_theme_id(zone_i), zone_color);
4591 if (zone_color[3] == 0.0f) {
4592 continue;
4593 }
4594 const Span<float3> fillet_boundary_positions = fillet_curve_by_zone[zone_i].positions();
4595 /* Draw the background. */
4597 immUniformThemeColorBlend(TH_BACK, get_theme_id(zone_i), zone_color[3]);
4598
4599 immBegin(GPU_PRIM_TRI_FAN, fillet_boundary_positions.size() + 1);
4600 for (const float3 &p : fillet_boundary_positions) {
4601 immVertex3fv(pos, p);
4602 }
4603 immVertex3fv(pos, fillet_boundary_positions[0]);
4604 immEnd();
4605
4607 }
4608 if (const bNode *const *node_p = std::get_if<const bNode *>(&zone_or_node)) {
4609 const bNode &node = **node_p;
4610 frame_node_draw_background(region, snode, node);
4611 }
4612 }
4613
4615
4616 /* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */
4617 for (const ZoneOrNode &zone_or_node : draw_order) {
4618 const bNodeTreeZone *const *zone_p = std::get_if<const bNodeTreeZone *>(&zone_or_node);
4619 if (!zone_p) {
4620 continue;
4621 }
4622 const bNodeTreeZone &zone = **zone_p;
4623 const int zone_i = zone.index;
4624 const Span<float3> fillet_boundary_positions = fillet_curve_by_zone[zone_i].positions();
4625 /* Draw the contour lines. */
4627
4628 immUniform2fv("viewportSize", &viewport[2]);
4629 immUniform1f("lineWidth", line_width * U.pixelsize);
4630
4631 immUniformThemeColorAlpha(get_theme_id(zone_i), 1.0f);
4632 immBegin(GPU_PRIM_LINE_STRIP, fillet_boundary_positions.size() + 1);
4633 for (const float3 &p : fillet_boundary_positions) {
4634 immVertex3fv(pos, p);
4635 }
4636 immVertex3fv(pos, fillet_boundary_positions[0]);
4637 immEnd();
4638
4640 }
4641
4643
4644 /* Draw text on frame nodes. */
4645 for (const ZoneOrNode &zone_or_node : draw_order) {
4646 if (const bNode *const *node_p = std::get_if<const bNode *>(&zone_or_node)) {
4647 const bNode &node = **node_p;
4648 frame_node_draw_overlay(C, tree_draw_ctx, region, snode, ntree, node, *blocks[node.index()]);
4649 }
4650 }
4651}
4652
4653#define USE_DRAW_TOT_UPDATE
4654
4655static void node_draw_nodetree(const bContext &C,
4656 TreeDrawContext &tree_draw_ctx,
4657 ARegion &region,
4658 SpaceNode &snode,
4659 bNodeTree &ntree,
4661 Span<uiBlock *> blocks,
4662 bNodeInstanceKey parent_key)
4663{
4664#ifdef USE_DRAW_TOT_UPDATE
4665 BLI_rctf_init_minmax(&region.v2d.tot);
4666#endif
4667
4668 for (const int i : nodes.index_range()) {
4669#ifdef USE_DRAW_TOT_UPDATE
4670 /* Unrelated to background nodes, update the v2d->tot,
4671 * can be anywhere before we draw the scroll bars. */
4672 BLI_rctf_union(&region.v2d.tot, &nodes[i]->runtime->totr);
4673#endif
4674 }
4675
4676 /* Node lines. */
4678 nodelink_batch_start(snode);
4679
4680 for (const bNodeLink *link : ntree.all_links()) {
4682 node_draw_link(C, region.v2d, snode, *link, false);
4683 }
4684 }
4685
4686 /* Draw selected node links after the unselected ones, so they are shown on top. */
4687 for (const bNodeLink *link : ntree.all_links()) {
4689 node_draw_link(C, region.v2d, snode, *link, true);
4690 }
4691 }
4692
4693 nodelink_batch_end(snode);
4695
4696 /* Draw foreground nodes, last nodes in front. */
4697 for (const int i : nodes.index_range()) {
4698 bNode &node = *nodes[i];
4699 if (node.flag & NODE_BACKGROUND) {
4700 /* Background nodes are drawn before mixed with zones already. */
4701 continue;
4702 }
4703
4704 const bNodeInstanceKey key = bke::node_instance_key(parent_key, &ntree, &node);
4705 node_draw(C, tree_draw_ctx, region, snode, ntree, node, *blocks[node.index()], key);
4706 }
4707}
4708
4709/* Draw the breadcrumb on the top of the editor. */
4710static void draw_tree_path(const bContext &C, ARegion &region)
4711{
4714
4715 const rcti *rect = ED_region_visible_rect(&region);
4716
4717 const uiStyle *style = UI_style_get_dpi();
4718 const float padding_x = 16 * UI_SCALE_FAC;
4719 const int x = rect->xmin + padding_x;
4720 const int y = region.winy - UI_UNIT_Y * 0.6f;
4721 const int width = BLI_rcti_size_x(rect) - 2 * padding_x;
4722
4723 uiBlock *block = UI_block_begin(&C, &region, __func__, UI_EMBOSS_NONE);
4724 uiLayout *layout = UI_block_layout(
4725 block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y, width, 1, 0, style);
4726
4728 ui::template_breadcrumbs(*layout, context_path);
4729
4730 UI_block_layout_resolve(block, nullptr, nullptr);
4731 UI_block_end(&C, block);
4732 UI_block_draw(&C, block);
4733
4735}
4736
4737static void snode_setup_v2d(SpaceNode &snode, ARegion &region, const float2 &center)
4738{
4739 View2D &v2d = region.v2d;
4740
4741 /* Shift view to node tree center. */
4742 UI_view2d_center_set(&v2d, center[0], center[1]);
4744
4745 snode.runtime->aspect = BLI_rctf_size_x(&v2d.cur) / float(region.winx);
4746}
4747
4748/* Similar to DRW_is_viewport_compositor_enabled() in `draw_manager.cc` but checks all 3D views. */
4749static bool realtime_compositor_is_in_use(const bContext &context)
4750{
4751 const Scene *scene = CTX_data_scene(&context);
4752 if (!scene->use_nodes) {
4753 return false;
4754 }
4755
4756 if (!scene->nodetree) {
4757 return false;
4758 }
4759
4761 return true;
4762 }
4763
4764 wmWindowManager *wm = CTX_wm_manager(&context);
4765 LISTBASE_FOREACH (const wmWindow *, win, &wm->windows) {
4766 const bScreen *screen = WM_window_get_active_screen(win);
4767 LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
4768 const SpaceLink &space = *static_cast<const SpaceLink *>(area->spacedata.first);
4769 if (space.spacetype == SPACE_VIEW3D) {
4770 const View3D &view_3d = reinterpret_cast<const View3D &>(space);
4771
4773 continue;
4774 }
4775
4776 if (!(view_3d.shading.type >= OB_MATERIAL)) {
4777 continue;
4778 }
4779
4780 return true;
4781 }
4782 }
4783 }
4784
4785 return false;
4786}
4787
4788static void draw_nodetree(const bContext &C,
4789 ARegion &region,
4790 bNodeTree &ntree,
4791 bNodeInstanceKey parent_key)
4792{
4793 SpaceNode *snode = CTX_wm_space_node(&C);
4794 ntree.ensure_topology_cache();
4795
4797
4799
4800 TreeDrawContext tree_draw_ctx;
4801
4802 BLI_SCOPED_DEFER([&]() { ntree.runtime->sockets_on_active_gizmo_paths.clear(); });
4803 if (ntree.type == NTREE_GEOMETRY) {
4805 *snode);
4806 for (geo_log::GeoTreeLog *log : tree_draw_ctx.geo_log_by_zone.values()) {
4807 log->ensure_node_warnings(&ntree);
4808 log->ensure_execution_times();
4809 }
4810 const WorkSpace *workspace = CTX_wm_workspace(&C);
4812 workspace->viewer_path, *snode);
4813
4814 /* This set of socket is used when drawing links to determine which links should use the
4815 * special gizmo drawing. */
4816 ntree.runtime->sockets_on_active_gizmo_paths = find_sockets_on_active_gizmo_paths(C, *snode);
4817 }
4818 else if (ntree.type == NTREE_COMPOSIT) {
4819 const Scene *scene = CTX_data_scene(&C);
4822 &scene->runtime->compositor.per_node_execution_time;
4823 }
4824 else if (ntree.type == NTREE_SHADER && U.experimental.use_shader_node_previews &&
4828 {
4829 tree_draw_ctx.nested_group_infos = get_nested_previews(C, *snode);
4830 }
4831
4832 node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
4833 node_draw_zones_and_frames(C, tree_draw_ctx, region, *snode, ntree, blocks);
4834 node_draw_nodetree(C, tree_draw_ctx, region, *snode, ntree, nodes, blocks, parent_key);
4835}
4836
4840static void draw_background_color(const SpaceNode &snode)
4841{
4842 const int max_tree_length = 3;
4843 const float bright_factor = 0.25f;
4844
4845 /* We ignore the first element of the path since it is the top-most tree and it doesn't need to
4846 * be brighter. We also set a cap to how many levels we want to set apart, to avoid the
4847 * background from getting too bright. */
4848 const int clamped_tree_path_length = BLI_listbase_count_at_most(&snode.treepath,
4849 max_tree_length);
4850 const int depth = max_ii(0, clamped_tree_path_length - 1);
4851
4852 float color[3];
4854 mul_v3_fl(color, 1.0f + bright_factor * depth);
4855 GPU_clear_color(color[0], color[1], color[2], 1.0);
4856}
4857
4858void node_draw_space(const bContext &C, ARegion &region)
4859{
4860 wmWindow *win = CTX_wm_window(&C);
4861 SpaceNode &snode = *CTX_wm_space_node(&C);
4862 View2D &v2d = region.v2d;
4863
4864 /* Setup off-screen buffers. */
4865 GPUViewport *viewport = WM_draw_region_get_viewport(&region);
4866
4867 GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport);
4868 GPU_framebuffer_bind_no_srgb(framebuffer_overlay);
4869
4871 draw_background_color(snode);
4873 GPU_scissor_test(true);
4874
4875 /* XXX `snode->runtime->cursor` set in coordinate-space for placing new nodes,
4876 * used for drawing noodles too. */
4878 win->eventstate->xy[0] - region.winrct.xmin,
4879 win->eventstate->xy[1] - region.winrct.ymin,
4880 &snode.runtime->cursor[0],
4881 &snode.runtime->cursor[1]);
4882 snode.runtime->cursor[0] /= UI_SCALE_FAC;
4883 snode.runtime->cursor[1] /= UI_SCALE_FAC;
4884
4886
4887 /* Only set once. */
4889
4890 /* Nodes. */
4892
4893 const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE);
4895
4896 /* Draw parent node trees. */
4897 if (snode.treepath.last) {
4898 bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last;
4899
4900 /* Update tree path name (drawn in the bottom left). */
4901 ID *name_id = (path->nodetree && path->nodetree != snode.nodetree) ? &path->nodetree->id :
4902 snode.id;
4903
4904 if (name_id && UNLIKELY(!STREQ(path->display_name, name_id->name + 2))) {
4905 STRNCPY(path->display_name, name_id->name + 2);
4906 }
4907
4908 /* Current View2D center, will be set temporarily for parent node trees. */
4909 float2 center;
4910 UI_view2d_center_get(&v2d, &center.x, &center.y);
4911
4912 /* Store new view center in path and current edit tree. */
4913 copy_v2_v2(path->view_center, center);
4914 if (snode.edittree) {
4915 copy_v2_v2(snode.edittree->view_center, center);
4916 }
4917
4918 /* Top-level edit tree. */
4919 bNodeTree *ntree = path->nodetree;
4920 if (ntree) {
4921 snode_setup_v2d(snode, region, center);
4922
4923 /* Backdrop. */
4924 draw_nodespace_back_pix(C, region, snode, path->parent_key);
4925
4926 {
4927 float original_proj[4][4];
4928 GPU_matrix_projection_get(original_proj);
4929
4932
4933 wmOrtho2_pixelspace(region.winx, region.winy);
4934
4936
4938 GPU_matrix_projection_set(original_proj);
4939 }
4940
4941 draw_nodetree(C, region, *ntree, path->parent_key);
4942 }
4943
4944 /* Temporary links. */
4946 GPU_line_smooth(true);
4947 if (snode.runtime->linkdrag) {
4948 for (const bNodeLink &link : snode.runtime->linkdrag->links) {
4949 node_draw_link_dragged(C, v2d, snode, link);
4950 }
4951 }
4952 GPU_line_smooth(false);
4954
4956 /* Draw grease-pencil annotations. */
4958 }
4959 }
4960 else {
4961
4962 /* Backdrop. */
4964 }
4965
4967
4968 /* Reset view matrix. */
4970
4972 if (snode.flag & SNODE_SHOW_GPENCIL && snode.treepath.last) {
4973 /* Draw grease-pencil (screen strokes, and also paint-buffer). */
4975 }
4976
4977 /* Draw context path. */
4978 if (snode.overlay.flag & SN_OVERLAY_SHOW_PATH && snode.edittree) {
4979 draw_tree_path(C, region);
4980 }
4981 }
4982
4983 /* Scrollers. */
4984 UI_view2d_scrollers_draw(&v2d, nullptr);
4985}
4986
4987} // namespace blender::ed::space_node
void ED_draw_imbuf(ImBuf *ibuf, float x, float y, bool use_filter, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, float zoom_x, float zoom_y)
Definition glutil.cc:549
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member)
WorkSpace * CTX_wm_workspace(const bContext *C)
SpaceNode * CTX_wm_space_node(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
Low-level operations for curves.
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:168
void id_us_ensure_real(ID *id)
Definition lib_id.cc:306
#define NODE_CLASS_OUTPUT
Definition BKE_node.hh:405
#define GEO_NODE_STORE_NAMED_ATTRIBUTE
Definition BKE_node.hh:1285
#define NODE_CLASS_INTERFACE
Definition BKE_node.hh:416
#define NODE_CUSTOM_GROUP
Definition BKE_node.hh:807
#define NODE_CLASS_MATTE
Definition BKE_node.hh:411
#define NODE_CLASS_CONVERTER
Definition BKE_node.hh:410
#define GEO_NODE_VIEWER
Definition BKE_node.hh:1201
#define GEO_NODE_REMOVE_ATTRIBUTE
Definition BKE_node.hh:1287
#define NODE_CUSTOM
Definition BKE_node.hh:799
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
Definition BKE_node.hh:1379
#define NODE_CLASS_PATTERN
Definition BKE_node.hh:413
#define GEO_NODE_INPUT_NAMED_ATTRIBUTE
Definition BKE_node.hh:1286
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:806
#define NODE_CLASS_DISTORT
Definition BKE_node.hh:412
#define GEO_NODE_SIMULATION_OUTPUT
Definition BKE_node.hh:1331
#define CMP_NODE_OUTPUT_FILE
Definition BKE_node.hh:1034
#define NODE_CLASS_OP_VECTOR
Definition BKE_node.hh:407
#define NODE_GROUP
Definition BKE_node.hh:800
#define NODE_CLASS_LAYOUT
Definition BKE_node.hh:420
#define NODE_CLASS_OP_COLOR
Definition BKE_node.hh:406
#define NODE_CLASS_INPUT
Definition BKE_node.hh:404
#define GEO_NODE_REPEAT_OUTPUT
Definition BKE_node.hh:1338
#define NODE_CLASS_OP_FILTER
Definition BKE_node.hh:408
#define NODE_FRAME
Definition BKE_node.hh:803
#define NODE_CLASS_GROUP
Definition BKE_node.hh:409
#define NODE_CLASS_ATTRIBUTE
Definition BKE_node.hh:419
#define NODE_CLASS_TEXTURE
Definition BKE_node.hh:414
#define NODE_CLASS_SHADER
Definition BKE_node.hh:417
#define NODE_CLASS_SCRIPT
Definition BKE_node.hh:415
General operations, lookup, etc. for blender objects.
bool BKE_scene_uses_shader_previews(const Scene *scene)
Definition scene.cc:2787
@ BLF_WORD_WRAP
Definition BLF_api.hh:367
@ BLF_ASPECT
Definition BLF_api.hh:366
@ BLF_CLIPPING
Definition BLF_api.hh:362
void BLF_size(int fontid, float size)
Definition blf.cc:426
void BLF_aspect(int fontid, float x, float y, float z)
Definition blf.cc:360
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:459
void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax)
Definition blf.cc:881
void BLF_disable(int fontid, int option)
Definition blf.cc:321
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:568
void BLF_enable(int fontid, int option)
Definition blf.cc:312
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:791
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:828
void BLF_wordwrap(int fontid, int wrap_width)
Definition blf.cc:893
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
int BLI_convexhull_2d(const float(*points)[2], int points_num, int r_points[])
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count_at_most(const struct ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int round_fl_to_int(float a)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
#define M_PI
MINLINE void rgba_float_args_set(float col[4], float r, float g, float b, float a)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
#define BLI_SCOPED_DEFER(function_to_defer)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:184
bool BLI_rctf_isect(const struct rctf *src1, const struct rctf *src2, struct rctf *dest)
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:180
void BLI_rcti_resize(struct rcti *rect, int x, int y)
Definition rct.c:615
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.c:631
bool BLI_rctf_isect_pt(const struct rctf *rect, float x, float y)
BLI_INLINE int BLI_rcti_cent_y(const struct rcti *rct)
Definition BLI_rect.h:176
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
void BLI_rctf_init_pt_radius(struct rctf *rect, const float xy[2], float size)
Definition rct.c:462
BLI_INLINE int BLI_rcti_cent_x(const struct rcti *rct)
Definition BLI_rect.h:172
void BLI_rctf_init_minmax(struct rctf *rect)
Definition rct.c:484
size_t BLI_str_format_int_grouped(char dst[BLI_STR_FORMAT_INT32_GROUPED_SIZE], int num) ATTR_NONNULL(1)
Definition string.c:1168
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define BLI_STR_FORMAT_INT32_GROUPED_SIZE
Definition BLI_string.h:25
#define BLI_STR_UTF8_DEGREE_SIGN
unsigned char uchar
unsigned int uint
#define STREQLEN(a, b, n)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define POINTER_AS_INT(i)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define BLT_I18NCONTEXT_ID_ID
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define CTX_TIP_(context, msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
#define ID_REAL_USERS(id)
Definition DNA_ID.h:637
@ ID_TE
@ ID_IM
@ ID_NT
@ ID_LA
@ ID_WO
@ ID_MA
@ ID_GR
@ ID_OB
@ CURVE_TYPE_POLY
#define MAX_NAME
Definition DNA_defs.h:50
@ NODE_OPTIONS
@ NODE_DO_OUTPUT
@ NODE_HIDDEN
@ NODE_ACTIVE
@ NODE_CUSTOM_COLOR
@ NODE_BACKGROUND
@ NODE_MUTED
@ NODE_SELECT
@ NODE_PREVIEW
@ NODE_FRAME_RESIZEABLE
@ NODE_FRAME_SHRINK
@ SOCK_OUT
@ SOCK_IN
@ SOCK_MULTI_INPUT
@ SOCK_PANEL_COLLAPSED
@ SOCK_DISPLAY_SHAPE_CIRCLE_DOT
@ SOCK_DISPLAY_SHAPE_CIRCLE
@ SOCK_DISPLAY_SHAPE_SQUARE_DOT
@ SOCK_DISPLAY_SHAPE_SQUARE
@ SOCK_DISPLAY_SHAPE_DIAMOND
@ SOCK_DISPLAY_SHAPE_DIAMOND_DOT
@ NTREE_TEXTURE
@ NTREE_SHADER
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ SOCK_CUSTOM
@ SOCK_MENU
struct bNodeTree bNodeTree
@ NODE_LINK_VALID
struct bNodeSocket bNodeSocket
@ NODE_PANEL_COLLAPSED
@ NODE_PANEL_PARENT_COLLAPSED
@ NODE_PANEL_CONTENT_VISIBLE
@ OB_MATERIAL
@ SCE_COMPOSITOR_DEVICE_GPU
@ SN_OVERLAY_SHOW_PATH
@ SN_OVERLAY_SHOW_PREVIEWS
@ SN_OVERLAY_SHOW_REROUTE_AUTO_LABELS
@ SN_OVERLAY_SHOW_TIMINGS
@ SN_OVERLAY_SHOW_OVERLAYS
@ SN_OVERLAY_SHOW_NAMED_ATTRIBUTES
@ SNODE_SHOW_GPENCIL
struct SpaceNode SpaceNode
@ SPACE_NODE
@ SPACE_VIEW3D
#define UI_SCALE_FAC
@ V3D_SHADING_USE_COMPOSITOR_DISABLED
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ntree)
Definition node_edit.cc:492
#define NODE_GRID_STEP_SIZE
Definition ED_node_c.hh:35
const rcti * ED_region_visible_rect(ARegion *region)
Definition area.cc:4014
void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type)
#define REGION_DRAW_POST_VIEW
#define REGION_DRAW_PRE_VIEW
void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *framebuffer)
void GPU_clear_color(float red, float green, float blue, float alpha)
void immUniform4f(const char *name, float x, float y, float z, float w)
void immUniformThemeColorAlpha(int color_id, float a)
void immEnd()
void immUniform2fv(const char *name, const float data[2])
void immUnbindProgram()
void immAttr4fv(uint attr_id, const float data[4])
void immUniform2f(const char *name, float x, float y)
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
void immVertex2f(uint attr_id, float x, float y)
void immAttr1f(uint attr_id, float x)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1i(const char *name, int x)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
void immAttr1u(uint attr_id, uint x)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immUniformThemeColorBlend(int color_id1, int color_id2, float fac)
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void immRectf(uint pos, float x1, float y1, float x2, float y2)
void GPU_matrix_identity_set()
void GPU_matrix_push()
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
#define GPU_matrix_projection_get(x)
#define GPU_matrix_projection_set(x)
void GPU_matrix_pop()
@ GPU_PRIM_TRI_FAN
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_KEYFRAME_SHAPE
@ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR
@ GPU_SHADER_2D_CHECKER
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_KEYFRAME_SHAPE_INNER_DOT
@ GPU_KEYFRAME_SHAPE_CIRCLE
@ GPU_KEYFRAME_SHAPE_SQUARE
@ GPU_KEYFRAME_SHAPE_DIAMOND
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:175
eGPUBlend
Definition GPU_state.hh:84
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor_test(bool enable)
Definition gpu_state.cc:183
void GPU_line_width(float width)
Definition gpu_state.cc:161
eGPUBlend GPU_blend_get()
Definition gpu_state.cc:221
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_DEPTH_NONE
Definition GPU_state.hh:108
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:262
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ GPU_COMP_U32
GPUFrameBuffer * GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport)
Read Guarded memory(de)allocation.
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
void UI_but_func_pushed_state_set(uiBut *but, std::function< bool(const uiBut &)> func)
#define UI_ALPHA_CHECKER_LIGHT
#define UI_UNIT_Y
void uiLayoutSetActive(uiLayout *layout, bool active)
eUIEmbossType UI_block_emboss_get(uiBlock *block)
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
void ui_draw_dropshadow(const rctf *rct, float radius, float width, float aspect, float alpha)
eUIEmbossType
@ UI_EMBOSS_NONE
@ UI_EMBOSS
void UI_but_icon_indicator_number_set(uiBut *but, const int indicator_number)
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
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, const char *tip)
void UI_draw_roundbox_4fv_ex(const rctf *rect, const float inner1[4], const float inner2[4], float shade_dir, const float outline[4], float outline_width, float rad)
@ UI_BUT_TEXT_LEFT
#define UI_ALPHA_CHECKER_DARK
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
uiLayout * UI_block_layout(uiBlock *block, int dir, int type, int x, int y, int size, int em, int padding, const uiStyle *style)
const uiStyle * UI_style_get_dpi()
@ UI_LAYOUT_PANEL
uiBut * uiDefIconBut(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, const char *tip)
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, eUIEmbossType emboss)
void UI_draw_roundbox_corner_set(int type)
void uiItemS_ex(uiLayout *layout, float factor, LayoutSeparatorType type=LayoutSeparatorType::Auto)
const uiStyle * UI_style_get()
void uiLayoutSetTooltipFunc(uiLayout *layout, uiButToolTipFunc func, void *arg, uiCopyArgFunc copy_arg, uiFreeArgFunc free_arg)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_CNR_NONE
void UI_block_emboss_set(uiBlock *block, eUIEmbossType emboss)
@ UI_BUT_DISABLED
@ UI_BUT_INACTIVE
void UI_but_drawflag_disable(uiBut *but, int flag)
void UI_block_draw(const bContext *C, uiBlock *block)
@ UI_BLOCK_CLIP_EVENTS
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
#define UI_UNIT_X
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_BUT
@ UI_BTYPE_BUT_TOGGLE
@ UI_BTYPE_LABEL
void UI_draw_roundbox_aa(const rctf *rect, bool filled, float rad, const float color[4])
void UI_block_end(const bContext *C, uiBlock *block)
@ UI_LAYOUT_VERTICAL
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_LAYOUT_ALIGN_RIGHT
@ UI_LAYOUT_ALIGN_EXPAND
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr)
void UI_block_bounds_set_explicit(uiBlock *block, int minx, int miny, int maxx, int maxy)
Definition interface.cc:622
void UI_block_align_end(uiBlock *block)
void UI_GetThemeColor3fv(int colorid, float col[3])
int UI_GetThemeValueType(int colorid, int spacetype)
@ TH_NODE_FRAME
@ TH_NODE_INPUT
@ TH_NODE
@ TH_NODE_FILTER
@ TH_NODE_GROUP
@ TH_GRID
@ TH_NODE_SCRIPT
@ TH_BACK
@ TH_NODE_GEOMETRY
@ TH_NODE_OUTPUT
@ TH_NODE_COLOR
@ TH_WIRE
@ TH_NODE_PATTERN
@ TH_NODE_ATTRIBUTE
@ TH_NODE_DISTORT
@ TH_REDALERT
@ TH_NODE_MATTE
@ TH_NODE_GRID_LEVELS
@ TH_NODE_TEXTURE
@ TH_NODE_VECTOR
@ TH_NODE_CONVERTER
@ TH_SELECT
@ TH_NODE_LAYOUT
@ TH_TEXT
@ TH_NODE_SHADER
@ TH_WIRE_INNER
@ TH_NODE_INTERFACE
@ TH_ACTIVE
void UI_GetThemeColorBlend4f(int colorid1, int colorid2, float fac, float r_col[4])
void UI_GetThemeColorBlendShade4fv(int colorid1, int colorid2, float fac, int offset, float col[4])
void UI_GetThemeColorShadeAlpha4fv(int colorid, int coloffset, int alphaoffset, float col[4])
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
void UI_GetThemeColorBlendShade3ubv(int colorid1, int colorid2, float fac, int offset, unsigned char col[3])
void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
Definition view2d.cc:1605
void UI_view2d_view_restore(const bContext *C)
Definition view2d.cc:1158
void UI_view2d_center_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1934
void UI_view2d_view_ortho(const View2D *v2d)
Definition view2d.cc:1091
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1907
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
void UI_view2d_center_set(View2D *v2d, float x, float y)
Definition view2d.cc:1944
void UI_view2d_dot_grid_draw(const View2D *v2d, int grid_color_id, float min_step, int grid_subdivisions)
Definition view2d.cc:1283
@ WM_GIZMOMAP_DRAWSTEP_2D
#define NC_WORLD
Definition WM_types.hh:354
#define ND_SHADING
Definition WM_types.hh:444
#define ND_WORLD
Definition WM_types.hh:419
#define NC_SCENE
Definition WM_types.hh:345
#define ND_NODES
Definition WM_types.hh:403
#define ND_MODIFIER
Definition WM_types.hh:429
#define NC_MATERIAL
Definition WM_types.hh:347
#define NC_LAMP
Definition WM_types.hh:349
#define NC_TEXTURE
Definition WM_types.hh:348
#define ND_LIGHTING
Definition WM_types.hh:450
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
#define NC_OBJECT
Definition WM_types.hh:346
void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d)
#define U
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define output
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
constexpr T & first() const
Definition BLI_span.hh:680
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
const T * end() const
Definition BLI_array.hh:314
IndexRange index_range() const
Definition BLI_array.hh:349
const T * begin() const
Definition BLI_array.hh:310
bool is() const
void destruct(void *ptr) const
const ComputeContext * current() const
const ComputeContextHash & hash() const
const CPPType * type() const
const void * get() const
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
bool add_overwrite(const Key &key, const Value &value)
Definition BLI_map.hh:301
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
int64_t size() const
Definition BLI_map.hh:927
bool add_as(ForwardKey &&key)
Definition BLI_set.hh:256
bool add(const Key &key)
Definition BLI_set.hh:248
const T * iterator
Definition BLI_span.hh:82
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
constexpr bool is_empty() const
constexpr const char * data() const
constexpr const char * c_str() const
static VArray ForSingle(T value, const int64_t size)
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
void resize(const int64_t new_size)
Span< T > as_span() const
const T & first() const
const T * const_iterator
Definition BLI_vector.hh:73
MutableSpan< float3 > positions_for_write()
void fill_curve_types(CurveType type)
MutableSpan< int > offsets_for_write()
MutableSpan< bool > cyclic_for_write()
bool contains_node_recursively(const bNode &node) const
Vector< bNodeTreeZone * > child_zones
Vector< const bNode * > child_nodes
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
Vector< std::unique_ptr< bNodeTreeZone > > zones
const bNodeTreeZone * get_zone_by_socket(const bNodeSocket &socket) const
Span< bke::GeometryComponent::Type > supported_types() const
static Map< const bke::bNodeTreeZone *, GeoTreeLog * > get_tree_log_by_zone_for_node_editor(const SpaceNode &snode)
Map< StringRefNull, NamedAttributeUsage > used_named_attributes
ValueLog * find_socket_value_log(const bNodeSocket &query_socket)
std::optional< GreasePencilInfo > grease_pencil_info
Vector< bke::GeometryComponent::Type > component_types
local_group_size(16, 16) .push_constant(Type b
#define SELECT
#define sinf(x)
#define cosf(x)
KDTree_3d * tree
draw_view in_light_buf[] float
#define str(s)
static const char * to_string(const Interpolation &interp)
Definition gl_shader.cc:82
uint padding(uint offset, uint alignment)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define GS(x)
Definition iris.cc:202
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
ccl_device_inline float3 log(float3 v)
static ulong state[N]
const bNodeZoneType * zone_type_by_node_type(const int node_type)
float2 node_to_view(const bNode *node, float2 loc)
Definition node.cc:3089
bool node_type_is_undefined(const bNode *node)
Definition node.cc:1736
bool node_link_is_hidden(const bNodeLink *link)
Definition node.cc:2993
void * node_instance_hash_lookup(bNodeInstanceHash *hash, bNodeInstanceKey key)
Definition node.cc:4090
const DataTypeConversions & get_implicit_type_conversions()
void nodeLabel(const bNodeTree *ntree, const bNode *node, char *label, int maxlen)
Definition node.cc:4267
const char * nodeSocketShortLabel(const bNodeSocket *sock)
Definition node.cc:4285
bool node_link_is_selected(const bNodeLink *link)
Definition node.cc:2998
bNodeInstanceKey node_instance_key(bNodeInstanceKey parent_key, const bNodeTree *ntree, const bNode *node)
Definition node.cc:4041
Span< int > all_zone_node_types()
const char * nodeSocketLabel(const bNodeSocket *sock)
Definition node.cc:4296
const bNodeInstanceKey NODE_INSTANCE_KEY_NONE
Definition node.cc:4021
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:3732
float2 node_from_view(const bNode *node, float2 view_loc)
Definition node.cc:3098
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:46
static bool compare_node_depth(const bNode *a, const bNode *b)
Definition node_draw.cc:240
static void node_draw_zones_and_frames(const bContext &C, TreeDrawContext &tree_draw_ctx, const ARegion &region, const SpaceNode &snode, const bNodeTree &ntree, Span< uiBlock * > blocks)
static bool node_update_basis_socket(const bContext &C, bNodeTree &ntree, bNode &node, const char *panel_label, bNodeSocket *input_socket, bNodeSocket *output_socket, uiBlock &block, const int &locx, int &locy)
Definition node_draw.cc:460
float node_socket_calculate_height(const bNodeSocket &socket)
Definition node_edit.cc:108
rctf node_frame_rect_inside(const SpaceNode &snode, const bNode &node)
static bNodeInstanceKey current_node_instance_key(const SpaceNode &snode, const bNode &node)
int node_get_resize_cursor(NodeResizeDirection directions)
static void reroute_node_draw(const bContext &C, TreeDrawContext &tree_draw_ctx, ARegion &region, const SpaceNode &snode, bNodeTree &ntree, const bNode &node, uiBlock &block)
static void node_socket_outline_color_get(const bool selected, const int socket_type, float r_outline_color[4])
void nodelink_batch_start(SpaceNode &)
Definition drawnode.cc:2058
static geo_log::GeoTreeLog * geo_tree_log_for_socket(const bNodeTree &ntree, const bNodeSocket &socket, TreeDrawContext &tree_draw_ctx)
static const bNode * reroute_node_get_linked_reroute(const bNode &reroute)
static std::string node_errors_tooltip_fn(bContext *, void *argN, const char *)
void tree_draw_order_update(bNodeTree &ntree)
Definition node_draw.cc:302
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
Vector< NodeInterfaceItemData >::const_iterator ItemIterator
Definition node_draw.cc:695
static std::optional< std::string > create_description_inspection_string(const bNodeSocket &socket)
static void draw_nodetree(const bContext &C, ARegion &region, bNodeTree &ntree, bNodeInstanceKey parent_key)
static void node_update_nodetree(const bContext &C, TreeDrawContext &tree_draw_ctx, bNodeTree &ntree, Span< bNode * > nodes, Span< uiBlock * > blocks)
static float node_tree_view_scale(const SpaceNode &snode)
static std::optional< std::string > create_log_inspection_string(geo_log::GeoTreeLog *geo_tree_log, const bNodeSocket &socket)
static void frame_node_draw_background(const ARegion &region, const SpaceNode &snode, const bNode &node)
static void node_socket_draw_multi_input(uiBlock &block, const int index_in_tree, const float2 location, const float2 draw_size, const float color[4], const float color_outline[4], const float2 tooltip_size)
void node_draw_link_bezier(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const int th_col1, const int th_col2, const int th_col3, const bool selected)
Definition drawnode.cc:2318
static void draw_background_color(const SpaceNode &snode)
static std::optional< std::chrono::nanoseconds > node_get_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void create_inspection_string_for_geometry_info(const geo_log::GeometryInfoLog &value_log, fmt::memory_buffer &buf)
bool push_compute_context_for_tree_path(const SpaceNode &snode, ComputeContextBuilder &compute_context_builder)
void nodelink_batch_end(SpaceNode &snode)
Definition drawnode.cc:2063
static std::optional< std::chrono::nanoseconds > compositor_accumulate_frame_node_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void add_panel_items_recursive(const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy, int num_items, const bool is_parent_collapsed, const char *parent_label, bke::bNodePanelRuntime *parent_runtime, LocationUpdateState &state)
Definition node_draw.cc:784
static void reroute_node_draw_label(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node, uiBlock &block)
ImBuf * node_preview_acquire_ibuf(bNodeTree &ntree, NestedTreePreviews &tree_previews, const bNode &node)
std::array< float2, 4 > node_link_bezier_points_dragged(const SpaceNode &snode, const bNodeLink &link)
Definition drawnode.cc:2375
bNodeSocket * node_find_indicated_socket(SpaceNode &snode, ARegion &region, const float2 &cursor, const eNodeSocketInOut in_out)
static bool realtime_compositor_is_in_use(const bContext &context)
static void node_draw_hidden(const bContext &C, TreeDrawContext &tree_draw_ctx, const View2D &v2d, const SpaceNode &snode, bNodeTree &ntree, bNode &node, uiBlock &block)
static std::optional< NodeExtraInfoRow > node_get_accessed_attributes_row(TreeDrawContext &tree_draw_ctx, const bNode &node)
void node_socket_color_get(const bContext &C, const bNodeTree &ntree, PointerRNA &node_ptr, const bNodeSocket &sock, float r_color[4])
Array< bNode * > tree_draw_order_calc_nodes_reversed(bNodeTree &ntree)
Definition node_draw.cc:326
static void node_get_compositor_extra_info(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node, Vector< NodeExtraInfoRow > &rows)
void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor)
void node_draw_link(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNodeLink &link, const bool selected)
Definition drawnode.cc:2337
static const bNodeSocket * target_for_reroute(const bNodeSocket &reroute_output)
static void node_draw_mute_line(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNode &node)
static void reroute_node_prepare_for_draw(bNode &node)
static Vector< NodeInterfaceItemData > node_build_item_data(bNode &node)
Definition node_draw.cc:614
static const bNode * find_node_under_cursor(SpaceNode &snode, const float2 &cursor)
static bool is_node_panels_supported(const bNode &node)
Definition node_draw.cc:377
static NodeExtraInfoRow row_from_used_named_attribute(const Map< StringRefNull, geo_log::NamedAttributeUsage > &usage_by_attribute_name)
void node_draw_link_dragged(const bContext &C, const View2D &v2d, const SpaceNode &snode, const bNodeLink &link)
Definition drawnode.cc:2389
static bool node_update_basis_buttons(const bContext &C, bNodeTree &ntree, bNode &node, nodes::PanelDrawButtonsFunction draw_buttons, uiBlock &block, int &dy)
Definition node_draw.cc:383
static void node_add_unsupported_compositor_operation_error_message_button(const bNode &node, uiBlock &block, const rctf &rect, float &icon_offset)
static std::optional< std::chrono::nanoseconds > compositor_node_get_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void find_bounds_by_zone_recursive(const SpaceNode &snode, const bNodeTreeZone &zone, const Span< std::unique_ptr< bNodeTreeZone > > all_zones, MutableSpan< Vector< float2 > > r_bounds_by_zone)
static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv)
static Array< uiBlock * > node_uiblocks_init(const bContext &C, const Span< bNode * > nodes)
Definition node_draw.cc:338
static void add_rect_corner_positions(Vector< float2 > &positions, const rctf &rect)
bool node_is_previewable(const SpaceNode &snode, const bNodeTree &ntree, const bNode &node)
static void node_socket_tooltip_set(uiBlock &block, const int socket_index_in_tree, const float2 location, const float2 size)
void draw_nodespace_back_pix(const bContext &C, ARegion &region, SpaceNode &snode, bNodeInstanceKey parent_key)
Definition drawnode.cc:1641
static std::optional< std::string > create_default_value_inspection_string(const bNodeSocket &socket)
static StringRefNull reroute_node_get_auto_label(TreeDrawContext &tree_draw_ctx, const bNode &src_reroute)
static float frame_node_label_height(const NodeFrame &frame_data)
static std::optional< std::chrono::nanoseconds > geo_node_get_execution_time(const TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void node_draw_basis(const bContext &C, TreeDrawContext &tree_draw_ctx, const View2D &v2d, const SpaceNode &snode, bNodeTree &ntree, const bNode &node, uiBlock &block, bNodeInstanceKey key)
static void node_draw_extra_info_panel_back(const bNode &node, const rctf &extra_info_rect)
static void node_draw_extra_info_panel(const bContext &C, TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node, ImBuf *preview, uiBlock &block)
static void count_multi_input_socket_links(bNodeTree &ntree, SpaceNode &snode)
static void node_update_basis_from_declaration(const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
Definition node_draw.cc:929
static void node_update_basis(const bContext &C, const TreeDrawContext &, bNodeTree &ntree, bNode &node, uiBlock &block)
const char * node_socket_get_label(const bNodeSocket *socket, const char *panel_label)
Definition node_draw.cc:432
static void create_inspection_string_for_geometry_socket(fmt::memory_buffer &buf, const nodes::decl::Geometry *socket_decl)
static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static void create_inspection_string_for_default_socket_value(const bNodeSocket &socket, fmt::memory_buffer &buf)
static void node_draw_extra_info_row(const bNode &node, uiBlock &block, const rctf &rect, const int row, const NodeExtraInfoRow &extra_info_row)
static void node_draw_preview(const Scene *scene, ImBuf *preview, const rctf *prv)
static void node_draw_nodetree(const bContext &C, TreeDrawContext &tree_draw_ctx, ARegion &region, SpaceNode &snode, bNodeTree &ntree, Span< bNode * > nodes, Span< uiBlock * > blocks, bNodeInstanceKey parent_key)
static void node_update_basis_from_socket_lists(const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy)
Definition node_draw.cc:974
Array< bNode * > tree_draw_order_calc_nodes(bNodeTree &ntree)
Definition node_draw.cc:314
Vector< ui::ContextPathItem > context_path_for_space_node(const bContext &C)
static std::string named_attribute_tooltip(bContext *, void *argN, const char *)
static void node_update_hidden(bNode &node, uiBlock &block)
void node_select_single(bContext &C, bNode &node)
static Vector< NodeExtraInfoRow > node_get_extra_info(const bContext &C, TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
NestedTreePreviews * get_nested_previews(const bContext &C, SpaceNode &snode)
std::optional< ObjectAndModifier > get_modifier_for_node_editor(const SpaceNode &snode)
static std::optional< std::string > create_dangling_reroute_inspection_string(const bNodeTree &ntree, const bNodeSocket &socket)
static std::optional< NodeExtraInfoRow > node_get_execution_time_label_row(TreeDrawContext &tree_draw_ctx, const SpaceNode &snode, const bNode &node)
static std::optional< std::string > create_declaration_inspection_string(const bNodeSocket &socket)
static void node_update_panel_items_visibility_recursive(int num_items, const bool is_parent_collapsed, bNodePanelState &parent_state, VisibilityUpdateState &state)
Definition node_draw.cc:708
static const float virtual_node_socket_outline_color[4]
static void create_inspection_string_for_field_info(const bNodeSocket &socket, const geo_log::FieldInfoLog &value_log, fmt::memory_buffer &buf)
static void node_draw_sockets(const View2D &v2d, const bContext &C, const bNodeTree &ntree, const bNode &node, uiBlock &block, const bool draw_outputs, const bool select_all)
float2 node_to_view(const bNode &node, const float2 &co)
Definition node_draw.cc:354
void snode_set_context(const bContext &C)
Definition node_edit.cc:678
static void node_socket_add_tooltip_in_node_editor(const bNodeSocket &sock, uiLayout &layout)
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
NodeResizeDirection node_get_resize_direction(const SpaceNode &snode, const bNode *node, const int x, const int y)
Definition drawnode.cc:227
static const char * node_socket_get_translation_context(const bNodeSocket &socket)
Definition node_draw.cc:220
static void frame_node_draw_label(TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node, const SpaceNode &snode)
static void node_draw(const bContext &C, TreeDrawContext &tree_draw_ctx, ARegion &region, const SpaceNode &snode, bNodeTree &ntree, bNode &node, uiBlock &block, bNodeInstanceKey key)
void node_release_preview_ibuf(NestedTreePreviews &tree_previews)
static std::optional< std::string > create_multi_input_log_inspection_string(const bNodeTree &ntree, const bNodeSocket &socket, TreeDrawContext &tree_draw_ctx)
static void create_inspection_string_for_generic_value(const bNodeSocket &socket, const GPointer value, fmt::memory_buffer &buf)
void node_to_updated_rect(const bNode &node, rctf &r_rect)
Definition node_draw.cc:360
static std::string node_socket_get_tooltip(const SpaceNode *snode, const bNodeTree &ntree, const bNodeSocket &socket)
static void node_add_error_message_button(const TreeDrawContext &tree_draw_ctx, const bNode &node, uiBlock &block, const rctf &rect, float &icon_offset)
void node_draw_space(const bContext &C, ARegion &region)
static void node_draw_preview_background(rctf *rect)
static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block)
static void node_draw_shadow(const SpaceNode &snode, const bNode &node, const float radius, const float alpha)
static geo_log::NodeWarningType node_error_highest_priority(Span< geo_log::NodeWarning > warnings)
static void draw_tree_path(const bContext &C, ARegion &region)
static Vector< std::string > lines_of_text(std::string text)
static void node_draw_panels_background(const bNode &node, uiBlock &block)
static Set< const bNodeSocket * > find_sockets_on_active_gizmo_paths(const bContext &C, const SpaceNode &snode)
void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, uiLayout &layout)
static void frame_node_draw_overlay(const bContext &C, TreeDrawContext &tree_draw_ctx, const ARegion &region, const SpaceNode &snode, const bNodeTree &ntree, const bNode &node, uiBlock &block)
static void node_socket_draw_nested(const bContext &C, const bNodeTree &ntree, PointerRNA &node_ptr, uiBlock &block, const bNodeSocket &sock, const uint pos_id, const uint col_id, const uint shape_id, const uint size_id, const uint outline_col_id, const float size, const bool selected)
float2 node_from_view(const bNode &node, const float2 &co)
Definition node_draw.cc:371
static int node_get_colorid(TreeDrawContext &tree_draw_ctx, const bNode &node)
static void node_get_invalid_links_extra_info(const SpaceNode &snode, const bNode &node, Vector< NodeExtraInfoRow > &rows)
static void frame_node_prepare_for_draw(bNode &node, Span< bNode * > nodes)
static void snode_setup_v2d(SpaceNode &snode, ARegion &region, const float2 &center)
bNode * find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode)
bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, const IndexMask &curve_selection, const VArray< float > &radius, const VArray< int > &counts, bool limit_radius, const bke::AttributeFilter &attribute_filter)
QuaternionBase< float > Quaternion
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
EulerXYZBase< float > EulerXYZ
T interpolate(const T &a, const T &b, const FactorT &t)
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
int node_warning_type_icon(const NodeWarningType type)
int node_warning_type_severity(const NodeWarningType type)
void foreach_active_gizmo(const bContext &C, ComputeContextBuilder &compute_context_builder, const ForeachGizmoFn fn)
void foreach_socket_on_gizmo_path(const ComputeContext &gizmo_context, const bNode &gizmo_node, const bNodeSocket &gizmo_socket, FunctionRef< void(const ComputeContext &context, const bNodeSocket &socket, const ie::ElemVariant &elem)> fn)
void(*)(uiLayout *, bContext *, PointerRNA *) PanelDrawButtonsFunction
std::unique_ptr< ItemDeclaration > ItemDeclarationPtr
std::chrono::nanoseconds Nanoseconds
Definition BLI_timeit.hh:16
void template_breadcrumbs(uiLayout &layout, Span< ContextPathItem > context_path)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
ColorTheme4< float > ColorTheme4f
Definition BLI_color.hh:287
VecBase< float, 3 > float3
#define NODE_HEADER_ICON_SIZE
static bNodeTree * node_tree_from_ID(ID *id)
Definition node_draw.cc:162
float ED_node_grid_size()
Definition node_draw.cc:142
void ED_node_tag_update_id(ID *id)
Definition node_draw.cc:174
#define NODE_TREE_SCALE_SMALL
#define NODE_FRAME_MARGIN
void ED_node_tree_update(const bContext *C)
Definition node_draw.cc:147
#define NODE_SOCK_OUTLINE_SCALE
#define NODE_MULTI_INPUT_LINK_GAP
#define BASIS_RAD
#define NODE_SOCKSIZE
#define NODE_WIDTH(node)
#define NODE_DYS
#define NODE_SOCKSIZE_DRAW_MULIPLIER
#define NODE_ITEM_SPACING_Y
#define NODE_MARGIN_X
#define HIDDEN_RAD
#define NODE_DY
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
unsigned __int64 uint64_t
Definition stdint.h:90
struct wmGizmoMap * gizmo_map
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * last
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
int lines
Definition BLF_api.hh:406
struct bNodeTree * nodetree
ColorManagedViewSettings view_settings
SceneRuntimeHandle * runtime
struct RenderData r
ColorManagedDisplaySettings display_settings
SpaceNode_Runtime * runtime
ListBase treepath
struct bNodeTree * edittree
struct ID * id
SpaceNodeOverlay overlay
struct bNodeTree * nodetree
const bNode * active_geometry_nodes_viewer
Definition node_draw.cc:120
bool used_by_realtime_compositor
Definition node_draw.cc:131
blender::Map< const bNode *, blender::StringRefNull > reroute_auto_labels
Definition node_draw.cc:139
blender::Map< const bNodeTreeZone *, geo_log::GeoTreeLog * > geo_log_by_zone
Definition node_draw.cc:125
blender::Map< bNodeInstanceKey, blender::timeit::Nanoseconds > * compositor_per_node_execution_time
Definition node_draw.cc:134
NestedTreePreviews * nested_group_infos
Definition node_draw.cc:127
View3DShading shading
ViewerPath viewer_path
struct ImBuf * ibuf
bNodeSocketRuntimeHandle * runtime
bNodeSocketTypeHandle * typeinfo
void * default_value
char idname[64]
struct bNodeTree * nodetree
bNodeInstanceKey parent_key
float view_center[2]
bNodeTreeRuntimeHandle * runtime
bNodeTypeHandle * typeinfo
float width
int num_panel_states
float height
struct ID * id
float color[3]
struct bNode * parent
char name[64]
bNodeRuntimeHandle * runtime
void * storage
char label[64]
float offsetx
int32_t identifier
float offsety
int16_t type
ListBase areabase
LocationUpdateState(const Span< NodeInterfaceItemData > items)
Definition node_draw.cc:776
Map< StringRefNull, geo_log::NamedAttributeUsage > usage_by_attribute
const nodes::SocketDeclaration * socket_decl
Definition node_draw.cc:559
const nodes::PanelDeclaration * panel_decl
Definition node_draw.cc:564
static NodeInterfaceItemData separator()
Definition node_draw.cc:586
NodeInterfaceItemData(const nodes::PanelDeclaration *_panel_decl, bNodePanelState *_state, bke::bNodePanelRuntime *_runtime)
Definition node_draw.cc:579
NodeInterfaceItemData(const nodes::SocketDeclaration *_socket_decl, bNodeSocket *_input, bNodeSocket *_output)
Definition node_draw.cc:573
std::unique_ptr< bNodeLinkDrag > linkdrag
VisibilityUpdateState(const Span< NodeInterfaceItemData > items)
Definition node_draw.cc:701
float xmax
float xmin
float ymax
float ymin
int ymin
int xmin
uiFontStyle widget
int xy[2]
Definition WM_types.hh:726
struct wmEvent * eventstate
float max
void WM_cursor_set(wmWindow *win, int curs)
@ WM_CURSOR_NSEW_SCROLL
Definition wm_cursors.hh:51
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
@ WM_CURSOR_Y_MOVE
Definition wm_cursors.hh:39
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
@ WM_CURSOR_X_MOVE
Definition wm_cursors.hh:38
GPUViewport * WM_draw_region_get_viewport(ARegion *region)
Definition wm_draw.cc:905
void WM_main_add_notifier(uint type, void *reference)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
PointerRNA * ptr
Definition wm_files.cc:4126
void WM_gizmomap_draw(wmGizmoMap *gzmap, const bContext *C, const eWM_GizmoFlagMapDrawStep drawstep)
void wmOrtho2_pixelspace(const float x, const float y)
void wmOrtho2_region_pixelspace(const ARegion *region)
bScreen * WM_window_get_active_screen(const wmWindow *win)