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
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) {
153 snode_set_context(*C);
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
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
587 {
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())) {
688 result.append(NodeInterfaceItemData::separator());
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
1794static Vector<std::string> lines_of_text(std::string text)
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 =
1942 geo_log::GeoModifierLog::get_tree_log_by_zone_for_node_editor(*snode);
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;
2621 geo_log::NodeWarningType highest_priority_type = geo_log::NodeWarningType::Info;
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;
2939 if ((usage & geo_log::NamedAttributeUsage::Read) != geo_log::NamedAttributeUsage::None) {
2940 usages.append(TIP_("read"));
2941 }
2942 if ((usage & geo_log::NamedAttributeUsage::Write) != geo_log::NamedAttributeUsage::None) {
2943 usages.append(TIP_("write"));
2944 }
2945 if ((usage & geo_log::NamedAttributeUsage::Remove) != geo_log::NamedAttributeUsage::None) {
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,
2993 GEO_NODE_STORE_NAMED_ATTRIBUTE,
2994 GEO_NODE_REMOVE_ATTRIBUTE,
2995 GEO_NODE_INPUT_NAMED_ATTRIBUTE))
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) {
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,
3126 GEO_NODE_SIMULATION_OUTPUT,
3127 GEO_NODE_REPEAT_OUTPUT,
3128 GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT)))
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
3231 ColorTheme4f color;
3232 if (node.flag & NODE_MUTED) {
3234 }
3235 else {
3236 UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.75f, color);
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,
3444 POINTER_FROM_INT(node.identifier),
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,
3470 POINTER_FROM_INT(node.identifier),
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,
3544 POINTER_FROM_INT(node.identifier),
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,
3790 POINTER_FROM_INT(node.identifier),
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;
4001 data->flag &= ~NODE_FRAME_RESIZEABLE;
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
4035static void node_update_nodetree(const bContext &C,
4036 TreeDrawContext &tree_draw_ctx,
4037 bNodeTree &ntree,
4038 Span<bNode *> nodes,
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()) {
4071 frame_node_prepare_for_draw(*nodes[i], nodes);
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
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;
4552 return bke::zone_type_by_node_type(node->type)->theme_id;
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,
4660 Span<bNode *> nodes,
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()) {
4681 if (!bke::node_link_is_hidden(link) && !bke::node_link_is_selected(link)) {
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()) {
4688 if (!bke::node_link_is_hidden(link) && bke::node_link_is_selected(link)) {
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
4727 const Vector<ui::ContextPathItem> context_path = ed::space_node::context_path_for_space_node(C);
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
4760 if (scene->r.compositor_device == SCE_COMPOSITOR_DEVICE_GPU) {
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
4798 Array<uiBlock *> blocks = node_uiblocks_init(C, nodes);
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) {
4804 tree_draw_ctx.geo_log_by_zone = geo_log::GeoModifierLog::get_tree_log_by_zone_for_node_editor(
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);
4811 tree_draw_ctx.active_geometry_nodes_viewer = viewer_path::find_geometry_nodes_viewer(
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. */
4877 UI_view2d_region_to_view(&region.v2d,
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
4935 WM_gizmomap_draw(region.gizmo_map, &C, WM_GIZMOMAP_DRAWSTEP_2D);
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. */
4957 ED_annotation_draw_view2d(&C, true);
4958 }
4959 }
4960 else {
4961
4962 /* Backdrop. */
4963 draw_nodespace_back_pix(C, region, snode, bke::NODE_INSTANCE_KEY_NONE);
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). */
4974 ED_annotation_draw_view2d(&C, false);
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 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 NODE_CUSTOM
Definition BKE_node.hh:799
#define NODE_CLASS_PATTERN
Definition BKE_node.hh:413
#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 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 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
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
@ BLF_WORD_WRAP
Definition BLF_api.hh:367
@ BLF_ASPECT
Definition BLF_api.hh:366
@ BLF_CLIPPING
Definition BLF_api.hh:362
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
@ NTREE_TEXTURE
@ NTREE_SHADER
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ NODE_OPTIONS
@ NODE_DO_OUTPUT
@ NODE_HIDDEN
@ NODE_ACTIVE
@ NODE_CUSTOM_COLOR
@ NODE_BACKGROUND
@ NODE_MUTED
@ NODE_SELECT
@ NODE_PREVIEW
@ NODE_LINK_VALID
@ 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
@ SOCK_CUSTOM
@ SOCK_MENU
@ NODE_FRAME_RESIZEABLE
@ NODE_FRAME_SHRINK
@ 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
@ 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:4010
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.
#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
@ UI_LAYOUT_ALIGN_RIGHT
@ UI_LAYOUT_ALIGN_EXPAND
void UI_but_icon_indicator_number_set(uiBut *but, const int indicator_number)
@ UI_BLOCK_CLIP_EVENTS
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)
#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()
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)
void UI_block_emboss_set(uiBlock *block, eUIEmbossType emboss)
void UI_but_drawflag_disable(uiBut *but, int flag)
void UI_block_draw(const bContext *C, uiBlock *block)
@ UI_LAYOUT_VERTICAL
@ UI_LAYOUT_PANEL
void UI_block_layout_resolve(uiBlock *block, int *r_x, int *r_y)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_CNR_NONE
#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)
void UI_but_flag_enable(uiBut *but, int flag)
@ UI_BUT_TEXT_LEFT
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr)
@ UI_BUT_DISABLED
@ UI_BUT_INACTIVE
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)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
unsigned int U
Definition btGjkEpa3.h:78
#define output
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
static const CPPType & get()
bool is() const
void destruct(void *ptr) const
const ComputeContext * current() const
const ComputeContextHash & hash() 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
constexpr T & first() const
Definition BLI_span.hh:680
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
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
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)
void reserve(const int64_t min_capacity)
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()
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
Vector< ItemDeclarationPtr > items
Span< bke::GeometryComponent::Type > supported_types() const
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
OperationNode * node
const char * label
#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
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
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
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
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)
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
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)
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
VecBase< float, 4 > float4
VecBase< float, 2 > float2
#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
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
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
bNodeTreeTypeHandle * typeinfo
bNodeTypeHandle * typeinfo
struct bNode * parent
char label[64]
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 xmin
uiFontStyle widget
int xy[2]
Definition WM_types.hh:726
struct wmEvent * eventstate
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)