diff options
Diffstat (limited to 'source/blender/editors/space_node/node_draw.cc')
-rw-r--r-- | source/blender/editors/space_node/node_draw.cc | 1526 |
1 files changed, 1000 insertions, 526 deletions
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index bf3a9ba0c52..d68f16f6197 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -22,21 +22,22 @@ * \brief higher level node drawing for the node editor. */ +#include <iomanip> + #include "MEM_guardedalloc.h" #include "DNA_light_types.h" #include "DNA_linestyle_types.h" #include "DNA_material_types.h" -#include "DNA_modifier_types.h" #include "DNA_node_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_text_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" -#include "BLI_blenlib.h" +#include "BLI_array.hh" #include "BLI_map.hh" -#include "BLI_math.h" #include "BLI_set.hh" #include "BLI_span.hh" #include "BLI_string_ref.hh" @@ -87,10 +88,8 @@ #include "node_intern.hh" /* own include */ -#ifdef WITH_COMPOSITOR -# include "COM_compositor.h" -#endif - +using blender::Array; +using blender::float2; using blender::Map; using blender::Set; using blender::Span; @@ -109,7 +108,7 @@ extern void ui_draw_dropshadow( const rctf *rct, float radius, float aspect, float alpha, int select); } -float ED_node_grid_size(void) +float ED_node_grid_size() { return U.widget_unit; } @@ -118,7 +117,7 @@ void ED_node_tree_update(const bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); if (snode) { - snode_set_context(C); + snode_set_context(*C); id_us_ensure_real(&snode->nodetree->id); } @@ -185,7 +184,7 @@ void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree, bNode *node) bool do_tag_update = true; if (node != nullptr) { - if (!node_connected_to_output(bmain, ntree, node)) { + if (!node_connected_to_output(*bmain, *ntree, *node)) { do_tag_update = false; } } @@ -262,10 +261,6 @@ static bool compare_nodes(const bNode *a, const bNode *b) return false; } -/** - * Sort nodes by selection: unselected nodes first, then selected, - * then the active node at the very end. Relative order is kept intact. - */ void ED_node_sort(bNodeTree *ntree) { /* Merge sort is the algorithm of choice here. */ @@ -321,67 +316,68 @@ void ED_node_sort(bNodeTree *ntree) } } -static void node_uiblocks_init(const bContext *C, bNodeTree *ntree) +static Array<uiBlock *> node_uiblocks_init(const bContext &C, Span<bNode *> nodes) { + Array<uiBlock *> blocks(nodes.size()); /* Add node uiBlocks in drawing order - prevents events going to overlapping nodes. */ - - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - /* ui block */ - char uiblockstr[32]; - BLI_snprintf(uiblockstr, sizeof(uiblockstr), "node buttons %p", (void *)node); - node->block = UI_block_begin(C, CTX_wm_region(C), uiblockstr, UI_EMBOSS); - + for (const int i : nodes.index_range()) { + const std::string block_name = "node_" + std::string(nodes[i]->name); + blocks[i] = UI_block_begin(&C, CTX_wm_region(&C), block_name.c_str(), UI_EMBOSS); /* this cancels events for background nodes */ - UI_block_flag_enable(node->block, UI_BLOCK_CLIP_EVENTS); + UI_block_flag_enable(blocks[i], UI_BLOCK_CLIP_EVENTS); } + + return blocks; } -void node_to_view(const bNode *node, float x, float y, float *rx, float *ry) +float2 node_to_view(const bNode &node, const float2 &co) { - nodeToView(node, x, y, rx, ry); - *rx *= UI_DPI_FAC; - *ry *= UI_DPI_FAC; + float2 result; + nodeToView(&node, co.x, co.y, &result.x, &result.y); + return result * UI_DPI_FAC; } -void node_to_updated_rect(const bNode *node, rctf *r_rect) +void node_to_updated_rect(const bNode &node, rctf &r_rect) { - node_to_view(node, node->offsetx, node->offsety, &r_rect->xmin, &r_rect->ymax); - node_to_view(node, - node->offsetx + node->width, - node->offsety - node->height, - &r_rect->xmax, - &r_rect->ymin); + const float2 xmin_ymax = node_to_view(node, {node.offsetx, node.offsety}); + r_rect.xmin = xmin_ymax.x; + r_rect.ymax = xmin_ymax.y; + const float2 xmax_ymin = node_to_view(node, + {node.offsetx + node.width, node.offsety - node.height}); + r_rect.xmax = xmax_ymin.x; + r_rect.ymin = xmax_ymin.y; } -void node_from_view(const bNode *node, float x, float y, float *rx, float *ry) +float2 node_from_view(const bNode &node, const float2 &co) { - x /= UI_DPI_FAC; - y /= UI_DPI_FAC; - nodeFromView(node, x, y, rx, ry); + const float x = co.x / UI_DPI_FAC; + const float y = co.y / UI_DPI_FAC; + float2 result; + nodeFromView(&node, x, y, &result.x, &result.y); + return result; } /** * Based on settings and sockets in node, set drawing rect info. */ -static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) +static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block) { PointerRNA nodeptr; - RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); /* Get "global" coordinates. */ - float locx, locy; - node_to_view(node, 0.0f, 0.0f, &locx, &locy); + float2 loc = node_to_view(node, float2(0)); /* Round the node origin because text contents are always pixel-aligned. */ - locx = round(locx); - locy = round(locy); + loc.x = round(loc.x); + loc.y = round(loc.y); - int dy = locy; + int dy = loc.y; /* Header. */ dy -= NODE_DY; - /* Little bit of space in top. */ - if (node->outputs.first) { + /* Add a little bit of padding above the top socket. */ + if (node.outputs.first || node.inputs.first) { dy -= NODE_DYS / 2; } @@ -389,25 +385,25 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) bool add_output_space = false; int buty; - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->outputs) { + LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) { if (nodeSocketIsHidden(nsock)) { continue; } PointerRNA sockptr; - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, nsock, &sockptr); - uiLayout *layout = UI_block_layout(node->block, + uiLayout *layout = UI_block_layout(&block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, - locx + NODE_DYS, + loc.x + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, 0, UI_style_get_dpi()); - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { uiLayoutSetActive(layout, false); } @@ -419,16 +415,16 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); const char *socket_label = nodeSocketLabel(nsock); - nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); - UI_block_align_end(node->block); - UI_block_layout_resolve(node->block, nullptr, &buty); + UI_block_align_end(&block); + UI_block_layout_resolve(&block, nullptr, &buty); /* Ensure minimum socket height in case layout is empty. */ buty = min_ii(buty, dy - NODE_DY); /* Round the socket location to stop it from jiggling. */ - nsock->locx = round(locx + NODE_WIDTH(node)); + nsock->locx = round(loc.x + NODE_WIDTH(node)); nsock->locy = round(0.5f * (dy + buty)); dy = buty; @@ -443,86 +439,80 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) dy -= NODE_DY / 4; } - node->prvr.xmin = locx + NODE_DYS; - node->prvr.xmax = locx + NODE_WIDTH(node) - NODE_DYS; + node.prvr.xmin = loc.x + NODE_DYS; + node.prvr.xmax = loc.x + NODE_WIDTH(node) - NODE_DYS; /* preview rect? */ - if (node->flag & NODE_PREVIEW) { + if (node.flag & NODE_PREVIEW) { float aspect = 1.0f; - if (node->preview_xsize && node->preview_ysize) { - aspect = (float)node->preview_ysize / (float)node->preview_xsize; + if (node.preview_xsize && node.preview_ysize) { + aspect = (float)node.preview_ysize / (float)node.preview_xsize; } dy -= NODE_DYS / 2; - node->prvr.ymax = dy; + node.prvr.ymax = dy; if (aspect <= 1.0f) { - node->prvr.ymin = dy - aspect * (NODE_WIDTH(node) - NODE_DY); + node.prvr.ymin = dy - aspect * (NODE_WIDTH(node) - NODE_DY); } else { /* Width correction of image. XXX huh? (ton) */ float dx = (NODE_WIDTH(node) - NODE_DYS) - (NODE_WIDTH(node) - NODE_DYS) / aspect; - node->prvr.ymin = dy - (NODE_WIDTH(node) - NODE_DY); + node.prvr.ymin = dy - (NODE_WIDTH(node) - NODE_DY); - node->prvr.xmin += 0.5f * dx; - node->prvr.xmax -= 0.5f * dx; + node.prvr.xmin += 0.5f * dx; + node.prvr.xmax -= 0.5f * dx; } - dy = node->prvr.ymin - NODE_DYS / 2; + dy = node.prvr.ymin - NODE_DYS / 2; /* Make sure that maximums are bigger or equal to minimums. */ - if (node->prvr.xmax < node->prvr.xmin) { - SWAP(float, node->prvr.xmax, node->prvr.xmin); + if (node.prvr.xmax < node.prvr.xmin) { + SWAP(float, node.prvr.xmax, node.prvr.xmin); } - if (node->prvr.ymax < node->prvr.ymin) { - SWAP(float, node->prvr.ymax, node->prvr.ymin); + if (node.prvr.ymax < node.prvr.ymin) { + SWAP(float, node.prvr.ymax, node.prvr.ymin); } } /* Buttons rect? */ - if (node->typeinfo->draw_buttons && (node->flag & NODE_OPTIONS)) { + if (node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS)) { dy -= NODE_DYS / 2; - /* Set this for `uifunc()` that don't use layout engine yet. */ - node->butr.xmin = 0; - node->butr.xmax = NODE_WIDTH(node) - 2 * NODE_DYS; - node->butr.ymin = 0; - node->butr.ymax = 0; - - uiLayout *layout = UI_block_layout(node->block, + uiLayout *layout = UI_block_layout(&block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, - locx + NODE_DYS, + loc.x + NODE_DYS, dy, - node->butr.xmax, + NODE_WIDTH(node) - NODE_DY, 0, 0, UI_style_get_dpi()); - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { uiLayoutSetActive(layout, false); } uiLayoutSetContextPointer(layout, "node", &nodeptr); - node->typeinfo->draw_buttons(layout, (bContext *)C, &nodeptr); + node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr); - UI_block_align_end(node->block); - UI_block_layout_resolve(node->block, nullptr, &buty); + UI_block_align_end(&block); + UI_block_layout_resolve(&block, nullptr, &buty); dy = buty - NODE_DYS / 2; } /* Input sockets. */ - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) { if (nodeSocketIsHidden(nsock)) { continue; } PointerRNA sockptr; - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, nsock, &sockptr); /* Add the half the height of a multi-input socket to cursor Y * to account for the increased height of the taller sockets. */ @@ -534,17 +524,17 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) } dy -= multi_input_socket_offset * 0.5f; - uiLayout *layout = UI_block_layout(node->block, + uiLayout *layout = UI_block_layout(&block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, - locx + NODE_DYS, + loc.x + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, 0, UI_style_get_dpi()); - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { uiLayoutSetActive(layout, false); } @@ -555,15 +545,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) uiLayout *row = uiLayoutRow(layout, true); const char *socket_label = nodeSocketLabel(nsock); - nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); - UI_block_align_end(node->block); - UI_block_layout_resolve(node->block, nullptr, &buty); + UI_block_align_end(&block); + UI_block_layout_resolve(&block, nullptr, &buty); /* Ensure minimum socket height in case layout is empty. */ buty = min_ii(buty, dy - NODE_DY); - nsock->locx = locx; + nsock->locx = loc.x; /* Round the socket vertical position to stop it from jiggling. */ nsock->locy = round(0.5f * (dy + buty)); @@ -574,45 +564,44 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) } /* Little bit of space in end. */ - if (node->inputs.first || (node->flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0) { + if (node.inputs.first || (node.flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0) { dy -= NODE_DYS / 2; } - node->totr.xmin = locx; - node->totr.xmax = locx + NODE_WIDTH(node); - node->totr.ymax = locy; - node->totr.ymin = min_ff(dy, locy - 2 * NODE_DY); + node.totr.xmin = loc.x; + node.totr.xmax = loc.x + NODE_WIDTH(node); + node.totr.ymax = loc.y; + node.totr.ymin = min_ff(dy, loc.y - 2 * NODE_DY); /* Set the block bounds to clip mouse events from underlying nodes. * Add a margin for sockets on each side. */ - UI_block_bounds_set_explicit(node->block, - node->totr.xmin - NODE_SOCKSIZE, - node->totr.ymin, - node->totr.xmax + NODE_SOCKSIZE, - node->totr.ymax); + UI_block_bounds_set_explicit(&block, + node.totr.xmin - NODE_SOCKSIZE, + node.totr.ymin, + node.totr.xmax + NODE_SOCKSIZE, + node.totr.ymax); } /** * Based on settings in node, sets drawing rect info. */ -static void node_update_hidden(bNode *node) +static void node_update_hidden(bNode &node, uiBlock &block) { int totin = 0, totout = 0; - /* Get "global" coords. */ - float locx, locy; - node_to_view(node, 0.0f, 0.0f, &locx, &locy); + /* Get "global" coordinates. */ + float2 loc = node_to_view(node, float2(0)); /* Round the node origin because text contents are always pixel-aligned. */ - locx = round(locx); - locy = round(locy); + loc.x = round(loc.x); + loc.y = round(loc.y); /* Calculate minimal radius. */ - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) { if (!nodeSocketIsHidden(nsock)) { totin++; } } - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->outputs) { + LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) { if (!nodeSocketIsHidden(nsock)) { totout++; } @@ -624,20 +613,20 @@ static void node_update_hidden(bNode *node) hiddenrad += 5.0f * (float)(tot - 4); } - node->totr.xmin = locx; - node->totr.xmax = locx + max_ff(NODE_WIDTH(node), 2 * hiddenrad); - node->totr.ymax = locy + (hiddenrad - 0.5f * NODE_DY); - node->totr.ymin = node->totr.ymax - 2 * hiddenrad; + node.totr.xmin = loc.x; + node.totr.xmax = loc.x + max_ff(NODE_WIDTH(node), 2 * hiddenrad); + node.totr.ymax = loc.y + (hiddenrad - 0.5f * NODE_DY); + node.totr.ymin = node.totr.ymax - 2 * hiddenrad; /* Output sockets. */ float rad = (float)M_PI / (1.0f + (float)totout); float drad = rad; - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->outputs) { + LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) { if (!nodeSocketIsHidden(nsock)) { /* Round the socket location to stop it from jiggling. */ - nsock->locx = round(node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad); - nsock->locy = round(node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad); + nsock->locx = round(node.totr.xmax - hiddenrad + sinf(rad) * hiddenrad); + nsock->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad); rad += drad; } } @@ -645,51 +634,31 @@ static void node_update_hidden(bNode *node) /* Input sockets. */ rad = drad = -(float)M_PI / (1.0f + (float)totin); - LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) { if (!nodeSocketIsHidden(nsock)) { /* Round the socket location to stop it from jiggling. */ - nsock->locx = round(node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad); - nsock->locy = round(node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad); + nsock->locx = round(node.totr.xmin + hiddenrad + sinf(rad) * hiddenrad); + nsock->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad); rad += drad; } } /* Set the block bounds to clip mouse events from underlying nodes. * Add a margin for sockets on each side. */ - UI_block_bounds_set_explicit(node->block, - node->totr.xmin - NODE_SOCKSIZE, - node->totr.ymin, - node->totr.xmax + NODE_SOCKSIZE, - node->totr.ymax); + UI_block_bounds_set_explicit(&block, + node.totr.xmin - NODE_SOCKSIZE, + node.totr.ymin, + node.totr.xmax + NODE_SOCKSIZE, + node.totr.ymax); } -void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node) +static int node_get_colorid(const bNode &node) { - if (node->flag & NODE_HIDDEN) { - node_update_hidden(node); - } - else { - node_update_basis(C, ntree, node); - } -} - -int node_select_area_default(bNode *node, int x, int y) -{ - return BLI_rctf_isect_pt(&node->totr, x, y); -} - -int node_tweak_area_default(bNode *node, int x, int y) -{ - return BLI_rctf_isect_pt(&node->totr, x, y); -} - -int node_get_colorid(bNode *node) -{ - switch (node->typeinfo->nclass) { + switch (node.typeinfo->nclass) { case NODE_CLASS_INPUT: return TH_NODE_INPUT; case NODE_CLASS_OUTPUT: - return (node->flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE; + return (node.flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE; case NODE_CLASS_CONVERTER: return TH_NODE_CONVERTER; case NODE_CLASS_OP_COLOR: @@ -725,21 +694,21 @@ int node_get_colorid(bNode *node) } } -static void node_draw_mute_line(const bContext *C, - const View2D *v2d, - const SpaceNode *snode, - const bNode *node) +static void node_draw_mute_line(const bContext &C, + const View2D &v2d, + const SpaceNode &snode, + const bNode &node) { GPU_blend(GPU_BLEND_ALPHA); - LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) { - node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); + LISTBASE_FOREACH (const bNodeLink *, link, &node.internal_links) { + node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); } GPU_blend(GPU_BLEND_NONE); } -static void node_socket_draw(const bNodeSocket *sock, +static void node_socket_draw(const bNodeSocket &sock, const float color[4], const float color_outline[4], float size, @@ -754,7 +723,7 @@ static void node_socket_draw(const bNodeSocket *sock, int flags; /* Set shape flags. */ - switch (sock->display_shape) { + switch (sock.display_shape) { case SOCK_DISPLAY_SHAPE_DIAMOND: case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: flags = GPU_KEYFRAME_SHAPE_DIAMOND; @@ -770,7 +739,7 @@ static void node_socket_draw(const bNodeSocket *sock, break; } - if (ELEM(sock->display_shape, + if (ELEM(sock.display_shape, SOCK_DISPLAY_SHAPE_DIAMOND_DOT, SOCK_DISPLAY_SHAPE_SQUARE_DOT, SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) { @@ -825,16 +794,17 @@ static void node_socket_outline_color_get(const bool selected, } } -/* Usual convention here would be node_socket_get_color(), but that's already used (for setting a - * color property socket). */ -void node_socket_color_get( - const bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) +void node_socket_color_get(const bContext &C, + const bNodeTree &ntree, + PointerRNA &node_ptr, + const bNodeSocket &sock, + float r_color[4]) { PointerRNA ptr; - BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node)); - RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); + BLI_assert(RNA_struct_is_a(node_ptr.type, &RNA_Node)); + RNA_pointer_create((ID *)&ntree, &RNA_NodeSocket, &const_cast<bNodeSocket &>(sock), &ptr); - sock->typeinfo->draw_color((bContext *)C, &ptr, node_ptr, r_color); + sock.typeinfo->draw_color((bContext *)&C, &ptr, &node_ptr, r_color); } struct SocketTooltipData { @@ -843,29 +813,43 @@ struct SocketTooltipData { bNodeSocket *socket; }; -static void create_inspection_string_for_generic_value(const geo_log::GenericValueLog &value_log, - std::stringstream &ss) +static void create_inspection_string_for_generic_value(const GPointer value, std::stringstream &ss) { auto id_to_inspection_string = [&](ID *id, short idcode) { ss << (id ? id->name + 2 : TIP_("None")) << " (" << BKE_idtype_idcode_to_name(idcode) << ")"; }; - const GPointer value = value_log.value(); const CPPType &type = *value.type(); + const void *buffer = value.get(); if (type.is<Object *>()) { - id_to_inspection_string((ID *)*value.get<Object *>(), ID_OB); + id_to_inspection_string((ID *)buffer, ID_OB); } else if (type.is<Material *>()) { - id_to_inspection_string((ID *)*value.get<Material *>(), ID_MA); + id_to_inspection_string((ID *)buffer, ID_MA); } else if (type.is<Tex *>()) { - id_to_inspection_string((ID *)*value.get<Tex *>(), ID_TE); + id_to_inspection_string((ID *)buffer, ID_TE); } else if (type.is<Image *>()) { - id_to_inspection_string((ID *)*value.get<Image *>(), ID_IM); + id_to_inspection_string((ID *)buffer, ID_IM); } else if (type.is<Collection *>()) { - id_to_inspection_string((ID *)*value.get<Collection *>(), ID_GR); + id_to_inspection_string((ID *)buffer, ID_GR); + } + else if (type.is<int>()) { + ss << *(int *)buffer << TIP_(" (Integer)"); + } + else if (type.is<float>()) { + ss << *(float *)buffer << TIP_(" (Float)"); + } + else if (type.is<blender::float3>()) { + ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); + } + else if (type.is<bool>()) { + ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (type.is<std::string>()) { + ss << *(std::string *)buffer << TIP_(" (String)"); } } @@ -880,21 +864,7 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v if (field) { BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); blender::fn::evaluate_constant_field(field, buffer); - if (type.is<int>()) { - ss << *(int *)buffer << TIP_(" (Integer)"); - } - else if (type.is<float>()) { - ss << *(float *)buffer << TIP_(" (Float)"); - } - else if (type.is<blender::float3>()) { - ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); - } - else if (type.is<bool>()) { - ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); - } - else if (type.is<std::string>()) { - ss << *(std::string *)buffer << TIP_(" (String)"); - } + create_inspection_string_for_generic_value({type, buffer}, ss); type.destruct(buffer); } else { @@ -1005,7 +975,6 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo } static std::optional<std::string> create_socket_inspection_string(bContext *C, - bNodeTree &UNUSED(ntree), bNode &node, bNodeSocket &socket) { @@ -1023,7 +992,7 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, std::stringstream ss; if (const geo_log::GenericValueLog *generic_value_log = dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { - create_inspection_string_for_generic_value(*generic_value_log, ss); + create_inspection_string_for_generic_value(generic_value_log->value(), ss); } if (const geo_log::GFieldValueLog *gfield_value_log = dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) { @@ -1037,54 +1006,52 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, return ss.str(); } -static void node_socket_draw_nested(const bContext *C, - bNodeTree *ntree, - PointerRNA *node_ptr, - bNodeSocket *sock, - uint pos_id, - uint col_id, - uint shape_id, - uint size_id, - uint outline_col_id, - float size, - bool selected) +static void node_socket_draw_nested(const bContext &C, + bNodeTree &ntree, + PointerRNA &node_ptr, + uiBlock &block, + 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) { float color[4]; float outline_color[4]; node_socket_color_get(C, ntree, node_ptr, sock, color); - node_socket_outline_color_get(selected, sock->type, outline_color); + node_socket_outline_color_get(selected, sock.type, outline_color); node_socket_draw(sock, color, outline_color, size, - sock->locx, - sock->locy, + sock.locx, + sock.locy, pos_id, col_id, shape_id, size_id, outline_col_id); - if (ntree->type != NTREE_GEOMETRY) { + if (ntree.type != NTREE_GEOMETRY) { /* Only geometry nodes has socket value tooltips currently. */ return; } - bNode *node = (bNode *)node_ptr->data; - uiBlock *block = node->block; - /* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible * button on top of them for the tooltip. */ - const eUIEmbossType old_emboss = UI_block_emboss_get(block); - UI_block_emboss_set(block, UI_EMBOSS_NONE); - uiBut *but = uiDefIconBut(block, + const eUIEmbossType old_emboss = UI_block_emboss_get(&block); + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(&block, UI_BTYPE_BUT, 0, ICON_NONE, - sock->locx - size / 2, - sock->locy - size / 2, + sock.locx - size / 2, + sock.locy - size / 2, size, size, nullptr, @@ -1095,16 +1062,16 @@ static void node_socket_draw_nested(const bContext *C, nullptr); SocketTooltipData *data = (SocketTooltipData *)MEM_mallocN(sizeof(SocketTooltipData), __func__); - data->ntree = ntree; - data->node = (bNode *)node_ptr->data; - data->socket = sock; + data->ntree = &ntree; + data->node = (bNode *)node_ptr.data; + data->socket = &sock; UI_but_func_tooltip_set( but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; std::optional<std::string> socket_inspection_str = create_socket_inspection_string( - C, *data->ntree, *data->node, *data->socket); + C, *data->node, *data->socket); std::stringstream output; if (data->socket->declaration != nullptr) { @@ -1126,14 +1093,9 @@ static void node_socket_draw_nested(const bContext *C, MEM_freeN); /* Disable the button so that clicks on it are ignored the the link operator still works. */ UI_but_flag_enable(but, UI_BUT_DISABLED); - UI_block_emboss_set(block, old_emboss); + UI_block_emboss_set(&block, old_emboss); } -/** - * Draw a single node socket at default size. - * \note this is only called from external code, internally #node_socket_draw_nested() is used for - * optimized drawing of multiple/all sockets of a node. - */ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale) { const float size = 2.25f * NODE_SOCKSIZE * scale; @@ -1162,7 +1124,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ /* Single point. */ immBegin(GPU_PRIM_POINTS, 1); - node_socket_draw(sock, + node_socket_draw(*sock, color, outline_color, BLI_rcti_size_y(&draw_rect), @@ -1260,34 +1222,38 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_ const char *opname = (const char *)op_argv; /* Select & activate only the button's node. */ - node_select_single(C, node); + node_select_single(*C, *node); WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, nullptr); } -void node_draw_shadow(const SpaceNode *snode, const bNode *node, float radius, float alpha) +static void node_draw_shadow(const SpaceNode &snode, + const bNode &node, + const float radius, + const float alpha) { - const rctf *rct = &node->totr; + const rctf &rct = node.totr; UI_draw_roundbox_corner_set(UI_CNR_ALL); - ui_draw_dropshadow(rct, radius, snode->runtime->aspect, alpha, node->flag & SELECT); + ui_draw_dropshadow(&rct, radius, snode.runtime->aspect, alpha, node.flag & SELECT); } -void node_draw_sockets(const View2D *v2d, - const bContext *C, - bNodeTree *ntree, - bNode *node, - bool draw_outputs, - bool select_all) +static void node_draw_sockets(const View2D &v2d, + const bContext &C, + bNodeTree &ntree, + bNode &node, + uiBlock &block, + const bool draw_outputs, + const bool select_all) { - const uint total_input_len = BLI_listbase_count(&node->inputs); - const uint total_output_len = BLI_listbase_count(&node->outputs); + const uint total_input_len = BLI_listbase_count(&node.inputs); + const uint total_output_len = BLI_listbase_count(&node.outputs); if (total_input_len + total_output_len == 0) { return; } PointerRNA node_ptr; - RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); + RNA_pointer_create((ID *)&ntree, &RNA_Node, &node, &node_ptr); bool selected = false; @@ -1307,7 +1273,7 @@ void node_draw_sockets(const View2D *v2d, /* Set handle size. */ float scale; - UI_view2d_scale_get(v2d, &scale, nullptr); + UI_view2d_scale_get(&v2d, &scale, nullptr); scale *= 2.25f * NODE_SOCKSIZE; if (!select_all) { @@ -1316,7 +1282,7 @@ void node_draw_sockets(const View2D *v2d, /* Socket inputs. */ short selected_input_len = 0; - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node.inputs) { if (nodeSocketIsHidden(sock)) { continue; } @@ -1331,8 +1297,9 @@ void node_draw_sockets(const View2D *v2d, node_socket_draw_nested(C, ntree, - &node_ptr, - sock, + node_ptr, + block, + *sock, pos_id, col_id, shape_id, @@ -1345,7 +1312,7 @@ void node_draw_sockets(const View2D *v2d, /* Socket outputs. */ short selected_output_len = 0; if (draw_outputs) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node.outputs) { if (nodeSocketIsHidden(sock)) { continue; } @@ -1356,8 +1323,9 @@ void node_draw_sockets(const View2D *v2d, node_socket_draw_nested(C, ntree, - &node_ptr, - sock, + node_ptr, + block, + *sock, pos_id, col_id, shape_id, @@ -1382,15 +1350,16 @@ void node_draw_sockets(const View2D *v2d, if (selected_input_len) { /* Socket inputs. */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node.inputs) { if (nodeSocketIsHidden(sock)) { continue; } if (select_all || (sock->flag & SELECT)) { node_socket_draw_nested(C, ntree, - &node_ptr, - sock, + node_ptr, + block, + *sock, pos_id, col_id, shape_id, @@ -1407,15 +1376,16 @@ void node_draw_sockets(const View2D *v2d, if (selected_output_len) { /* Socket outputs. */ - LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node.outputs) { if (nodeSocketIsHidden(sock)) { continue; } if (select_all || (sock->flag & SELECT)) { node_socket_draw_nested(C, ntree, - &node_ptr, - sock, + node_ptr, + block, + *sock, pos_id, col_id, shape_id, @@ -1440,7 +1410,7 @@ void node_draw_sockets(const View2D *v2d, /* Draw multi-input sockets after the others because they are drawn with `UI_draw_roundbox` * rather than with `GL_POINT`. */ - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { if (nodeSocketIsHidden(socket)) { continue; } @@ -1448,13 +1418,13 @@ void node_draw_sockets(const View2D *v2d, continue; } - const bool is_node_hidden = (node->flag & NODE_HIDDEN); + const bool is_node_hidden = (node.flag & NODE_HIDDEN); const float width = NODE_SOCKSIZE; - float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width; + float height = is_node_hidden ? width : node_socket_calculate_height(*socket) - width; float color[4]; float outline_color[4]; - node_socket_color_get(C, ntree, &node_ptr, socket, color); + node_socket_color_get(C, ntree, node_ptr, *socket, color); node_socket_outline_color_get(selected, socket->type, outline_color); node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); @@ -1536,9 +1506,9 @@ static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char #define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit) static void node_add_error_message_button( - const bContext *C, bNodeTree &UNUSED(ntree), bNode &node, const rctf &rect, float &icon_offset) + const bContext &C, bNode &node, uiBlock &block, const rctf &rect, float &icon_offset) { - SpaceNode *snode = CTX_wm_space_node(C); + SpaceNode *snode = CTX_wm_space_node(&C); const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(*snode, node); if (node_log == nullptr) { @@ -1558,8 +1528,8 @@ static void node_add_error_message_button( const geo_log::NodeWarningType display_type = node_error_highest_priority(warnings); icon_offset -= NODE_HEADER_ICON_SIZE; - UI_block_emboss_set(node.block, UI_EMBOSS_NONE); - uiBut *but = uiDefIconBut(node.block, + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(&block, UI_BTYPE_BUT, 0, node_error_type_to_icon(display_type), @@ -1574,47 +1544,288 @@ static void node_add_error_message_button( 0, nullptr); UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, MEM_freeN); - UI_block_emboss_set(node.block, UI_EMBOSS); + UI_block_emboss_set(&block, UI_EMBOSS); +} + +static void get_exec_time_other_nodes(const bNode &node, + const SpaceNode &snode, + std::chrono::microseconds &exec_time, + int &node_count) +{ + if (node.type == NODE_GROUP) { + const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( + snode); + if (root_tree_log == nullptr) { + return; + } + const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name); + if (tree_log == nullptr) { + return; + } + tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { + exec_time += node_log.execution_time(); + node_count++; + }); + } + else { + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context( + snode, node); + if (node_log) { + exec_time += node_log->execution_time(); + node_count++; + } + } +} + +static std::chrono::microseconds node_get_execution_time(const bNodeTree &ntree, + const bNode &node, + const SpaceNode &snode, + int &node_count) +{ + std::chrono::microseconds exec_time = std::chrono::microseconds::zero(); + if (node.type == NODE_GROUP_OUTPUT) { + const geo_log::TreeLog *tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context( + snode); + + if (tree_log == nullptr) { + return exec_time; + } + tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) { + exec_time += node_log.execution_time(); + node_count++; + }); + } + else if (node.type == NODE_FRAME) { + /* Could be cached in the future if this recursive code turns out to be slow. */ + LISTBASE_FOREACH (bNode *, tnode, &ntree.nodes) { + if (tnode->parent != &node) { + continue; + } + + if (tnode->type == NODE_FRAME) { + exec_time += node_get_execution_time(ntree, *tnode, snode, node_count); + } + else { + get_exec_time_other_nodes(*tnode, snode, exec_time, node_count); + } + } + } + else { + get_exec_time_other_nodes(node, snode, exec_time, node_count); + } + return exec_time; +} + +static std::string node_get_execution_time_label(const SpaceNode &snode, const bNode &node) +{ + int node_count = 0; + std::chrono::microseconds exec_time = node_get_execution_time( + *snode.nodetree, node, snode, node_count); + + if (node_count == 0) { + return std::string(""); + } + + uint64_t exec_time_us = exec_time.count(); + + /* Don't show time if execution time is 0 microseconds. */ + if (exec_time_us == 0) { + return std::string("-"); + } + if (exec_time_us < 100) { + return std::string("< 0.1 ms"); + } + + int precision = 0; + /* Show decimal if value is below 1ms */ + if (exec_time_us < 1000) { + precision = 2; + } + else if (exec_time_us < 10000) { + precision = 1; + } + + std::stringstream stream; + stream << std::fixed << std::setprecision(precision) << (exec_time_us / 1000.0f); + return stream.str() + " ms"; +} + +struct NodeExtraInfoRow { + std::string text; + const char *tooltip; + int icon; +}; + +static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, const bNode &node) +{ + Vector<NodeExtraInfoRow> rows; + if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS)) { + return rows; + } + + if (snode.overlay.flag & SN_OVERLAY_SHOW_TIMINGS && snode.edittree->type == NTREE_GEOMETRY && + (ELEM(node.typeinfo->nclass, NODE_CLASS_GEOMETRY, NODE_CLASS_GROUP, NODE_CLASS_ATTRIBUTE) || + ELEM(node.type, NODE_FRAME, NODE_GROUP_OUTPUT))) { + NodeExtraInfoRow row; + row.text = node_get_execution_time_label(snode, node); + if (!row.text.empty()) { + row.tooltip = TIP_( + "The execution time from the node tree's latest evaluation. For frame and group nodes, " + "the time for all sub-nodes"); + row.icon = ICON_PREVIEW_RANGE; + rows.append(std::move(row)); + } + } + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(snode, + node); + if (node_log != nullptr) { + for (const std::string &message : node_log->debug_messages()) { + NodeExtraInfoRow row; + row.text = message; + row.icon = ICON_INFO; + rows.append(std::move(row)); + } + } + return rows; +} + +static void node_draw_extra_info_row(const bNode &node, + uiBlock &block, + const rctf &rect, + const int row, + const NodeExtraInfoRow &extra_info_row) +{ + uiBut *but_timing = uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + extra_info_row.text.c_str(), + (int)(rect.xmin + 4.0f * U.dpi_fac + NODE_MARGIN_X + 0.4f), + (int)(rect.ymin + row * (20.0f * U.dpi_fac)), + (short)(rect.xmax - rect.xmin), + (short)NODE_DY, + nullptr, + 0, + 0, + 0, + 0, + ""); + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiBut *but_icon = uiDefIconBut(&block, + UI_BTYPE_BUT, + 0, + extra_info_row.icon, + (int)(rect.xmin + 6.0f * U.dpi_fac), + (int)(rect.ymin + row * (20.0f * U.dpi_fac)), + NODE_HEADER_ICON_SIZE * 0.8f, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + extra_info_row.tooltip); + UI_block_emboss_set(&block, UI_EMBOSS); + if (node.flag & NODE_MUTED) { + UI_but_flag_enable(but_timing, UI_BUT_INACTIVE); + UI_but_flag_enable(but_icon, UI_BUT_INACTIVE); + } +} + +static void node_draw_extra_info_panel(const SpaceNode &snode, const bNode &node, uiBlock &block) +{ + Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(snode, node); + + if (extra_info_rows.size() == 0) { + return; + } + + const rctf &rct = node.totr; + float color[4]; + rctf extra_info_rect; + + if (node.type == NODE_FRAME) { + extra_info_rect.xmin = rct.xmin; + extra_info_rect.xmax = rct.xmin + 95.0f * U.dpi_fac; + extra_info_rect.ymin = rct.ymin + 2.0f * U.dpi_fac; + extra_info_rect.ymax = rct.ymin + 2.0f * U.dpi_fac; + } + else { + extra_info_rect.xmin = rct.xmin + 3.0f * U.dpi_fac; + extra_info_rect.xmax = rct.xmin + 95.0f * U.dpi_fac; + extra_info_rect.ymin = rct.ymax; + extra_info_rect.ymax = rct.ymax + extra_info_rows.size() * (20.0f * U.dpi_fac); + + if (node.flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color); + } + else { + UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.75f, color); + } + color[3] -= 0.35f; + UI_draw_roundbox_corner_set( + UI_CNR_ALL & ~UI_CNR_BOTTOM_LEFT & + ((rct.xmax) > extra_info_rect.xmax ? ~UI_CNR_BOTTOM_RIGHT : UI_CNR_ALL)); + UI_draw_roundbox_4fv(&extra_info_rect, true, BASIS_RAD, color); + + /* Draw outline. */ + const float outline_width = 1.0f; + extra_info_rect.xmin = rct.xmin + 3.0f * U.dpi_fac - outline_width; + extra_info_rect.xmax = rct.xmin + 95.0f * U.dpi_fac + outline_width; + extra_info_rect.ymin = rct.ymax - outline_width; + extra_info_rect.ymax = rct.ymax + outline_width + extra_info_rows.size() * (20.0f * U.dpi_fac); + + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color); + UI_draw_roundbox_corner_set( + UI_CNR_ALL & ~UI_CNR_BOTTOM_LEFT & + ((rct.xmax) > extra_info_rect.xmax ? ~UI_CNR_BOTTOM_RIGHT : UI_CNR_ALL)); + UI_draw_roundbox_4fv(&extra_info_rect, false, BASIS_RAD, color); + } + + for (int row : extra_info_rows.index_range()) { + node_draw_extra_info_row(node, block, extra_info_rect, row, extra_info_rows[row]); + } } -static void node_draw_basis(const bContext *C, - const View2D *v2d, - const SpaceNode *snode, - bNodeTree *ntree, - bNode *node, +static void node_draw_basis(const bContext &C, + const View2D &v2d, + const SpaceNode &snode, + bNodeTree &ntree, + bNode &node, + uiBlock &block, bNodeInstanceKey key) { const float iconbutw = NODE_HEADER_ICON_SIZE; /* Skip if out of view. */ - if (BLI_rctf_isect(&node->totr, &v2d->cur, nullptr) == false) { - UI_block_end(C, node->block); - node->block = nullptr; + if (BLI_rctf_isect(&node.totr, &v2d.cur, nullptr) == false) { + UI_block_end(&C, &block); return; } /* Shadow. */ node_draw_shadow(snode, node, BASIS_RAD, 1.0f); - rctf *rct = &node->totr; + const rctf &rct = node.totr; float color[4]; int color_id = node_get_colorid(node); GPU_line_width(1.0f); + node_draw_extra_info_panel(snode, node, block); + /* Header. */ { const rctf rect = { - rct->xmin, - rct->xmax, - rct->ymax - NODE_DY, - rct->ymax, + rct.xmin, + rct.xmax, + rct.ymax - NODE_DY, + rct.ymax, }; float color_header[4]; /* Muted nodes get a mix of the background with the node color. */ - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_header); } else { @@ -1626,18 +1837,18 @@ static void node_draw_basis(const bContext *C, } /* Show/hide icons. */ - float iconofs = rct->xmax - 0.35f * U.widget_unit; + float iconofs = rct.xmax - 0.35f * U.widget_unit; /* Preview. */ - if (node->typeinfo->flag & NODE_PREVIEW) { + if (node.typeinfo->flag & NODE_PREVIEW) { iconofs -= iconbutw; - UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefIconBut(node->block, + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(&block, UI_BTYPE_BUT_TOGGLE, 0, ICON_MATERIAL, iconofs, - rct->ymax - NODE_DY, + rct.ymax - NODE_DY, iconbutw, UI_UNIT_Y, nullptr, @@ -1646,24 +1857,24 @@ static void node_draw_basis(const bContext *C, 0, 0, ""); - UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_preview_toggle"); + UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_preview_toggle"); /* XXX this does not work when node is activated and the operator called right afterwards, * since active ID is not updated yet (needs to process the notifier). * This can only work as visual indicator! */ - // if (!(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT))) + // if (!(node.flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT))) // UI_but_flag_enable(but, UI_BUT_DISABLED); - UI_block_emboss_set(node->block, UI_EMBOSS); + UI_block_emboss_set(&block, UI_EMBOSS); } /* Group edit. */ - if (node->type == NODE_GROUP) { + if (node.type == NODE_GROUP) { iconofs -= iconbutw; - UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefIconBut(node->block, + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(&block, UI_BTYPE_BUT_TOGGLE, 0, ICON_NODETREE, iconofs, - rct->ymax - NODE_DY, + rct.ymax - NODE_DY, iconbutw, UI_UNIT_Y, nullptr, @@ -1672,18 +1883,18 @@ static void node_draw_basis(const bContext *C, 0, 0, ""); - UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_group_edit"); - UI_block_emboss_set(node->block, UI_EMBOSS); + UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_group_edit"); + UI_block_emboss_set(&block, UI_EMBOSS); } - if (node->type == NODE_CUSTOM && node->typeinfo->ui_icon != ICON_NONE) { + if (node.type == NODE_CUSTOM && node.typeinfo->ui_icon != ICON_NONE) { iconofs -= iconbutw; - UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiDefIconBut(node->block, + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + uiDefIconBut(&block, UI_BTYPE_BUT, 0, - node->typeinfo->ui_icon, + node.typeinfo->ui_icon, iconofs, - rct->ymax - NODE_DY, + rct.ymax - NODE_DY, iconbutw, UI_UNIT_Y, nullptr, @@ -1692,13 +1903,13 @@ static void node_draw_basis(const bContext *C, 0, 0, ""); - UI_block_emboss_set(node->block, UI_EMBOSS); + UI_block_emboss_set(&block, UI_EMBOSS); } - node_add_error_message_button(C, *ntree, *node, *rct, iconofs); + node_add_error_message_button(C, node, block, rct, iconofs); /* Title. */ - if (node->flag & SELECT) { + if (node.flag & SELECT) { UI_GetThemeColor4fv(TH_SELECT, color); } else { @@ -1708,14 +1919,14 @@ static void node_draw_basis(const bContext *C, /* Collapse/expand icon. */ { const int but_size = U.widget_unit * 0.8f; - UI_block_emboss_set(node->block, UI_EMBOSS_NONE); + UI_block_emboss_set(&block, UI_EMBOSS_NONE); - uiBut *but = uiDefIconBut(node->block, + uiBut *but = uiDefIconBut(&block, UI_BTYPE_BUT_TOGGLE, 0, ICON_DOWNARROW_HLT, - rct->xmin + (NODE_MARGIN_X / 3), - rct->ymax - NODE_DY / 2.2f - but_size / 2, + rct.xmin + (NODE_MARGIN_X / 3), + rct.ymax - NODE_DY / 2.2f - but_size / 2, but_size, but_size, nullptr, @@ -1725,20 +1936,20 @@ static void node_draw_basis(const bContext *C, 0.0f, ""); - UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); - UI_block_emboss_set(node->block, UI_EMBOSS); + UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_hide_toggle"); + UI_block_emboss_set(&block, UI_EMBOSS); } char showname[128]; - nodeLabel(ntree, node, showname, sizeof(showname)); + nodeLabel(&ntree, &node, showname, sizeof(showname)); - uiBut *but = uiDefBut(node->block, + uiBut *but = uiDefBut(&block, UI_BTYPE_LABEL, 0, showname, - (int)(rct->xmin + NODE_MARGIN_X + 0.4f), - (int)(rct->ymax - NODE_DY), - (short)(iconofs - rct->xmin - (18.0f * U.dpi_fac)), + (int)(rct.xmin + NODE_MARGIN_X + 0.4f), + (int)(rct.ymax - NODE_DY), + (short)(iconofs - rct.xmin - (18.0f * U.dpi_fac)), (short)NODE_DY, nullptr, 0, @@ -1746,12 +1957,12 @@ static void node_draw_basis(const bContext *C, 0, 0, ""); - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } /* Wire across the node when muted/disabled. */ - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { node_draw_mute_line(C, v2d, snode, node); } @@ -1759,35 +1970,35 @@ static void node_draw_basis(const bContext *C, const float outline_width = 1.0f; { /* Use warning color to indicate undefined types. */ - if (nodeTypeUndefined(node)) { + if (nodeTypeUndefined(&node)) { UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); } /* Muted nodes get a mix of the background with the node color. */ - else if (node->flag & NODE_MUTED) { + else if (node.flag & NODE_MUTED) { UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color); } - else if (node->flag & NODE_CUSTOM_COLOR) { - rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + else if (node.flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], 1.0f); } else { UI_GetThemeColor4fv(TH_NODE, color); } /* Draw selected nodes fully opaque. */ - if (node->flag & SELECT) { + if (node.flag & SELECT) { color[3] = 1.0f; } /* Draw muted nodes slightly transparent so the wires inside are visible. */ - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { color[3] -= 0.2f; } const rctf rect = { - rct->xmin, - rct->xmax, - rct->ymin, - rct->ymax - (NODE_DY + outline_width), + rct.xmin, + rct.xmax, + rct.ymin, + rct.ymax - (NODE_DY + outline_width), }; UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); @@ -1798,7 +2009,7 @@ static void node_draw_basis(const bContext *C, { float color_underline[4]; - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { UI_GetThemeColor4fv(TH_WIRE, color_underline); } else { @@ -1806,10 +2017,10 @@ static void node_draw_basis(const bContext *C, } const rctf rect = { - rct->xmin, - rct->xmax, - rct->ymax - (NODE_DY + outline_width), - rct->ymax - NODE_DY, + rct.xmin, + rct.xmax, + rct.ymax - (NODE_DY + outline_width), + rct.ymax - NODE_DY, }; UI_draw_roundbox_corner_set(UI_CNR_NONE); @@ -1819,19 +2030,19 @@ static void node_draw_basis(const bContext *C, /* Outline. */ { const rctf rect = { - rct->xmin - outline_width, - rct->xmax + outline_width, - rct->ymin - outline_width, - rct->ymax + outline_width, + rct.xmin - outline_width, + rct.xmax + outline_width, + rct.ymin - outline_width, + rct.ymax + outline_width, }; /* Color the outline according to active, selected, or undefined status. */ float color_outline[4]; - if (node->flag & SELECT) { - UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + if (node.flag & SELECT) { + UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); } - else if (nodeTypeUndefined(node)) { + else if (nodeTypeUndefined(&node)) { UI_GetThemeColor4fv(TH_REDALERT, color_outline); } else { @@ -1843,42 +2054,42 @@ static void node_draw_basis(const bContext *C, } float scale; - UI_view2d_scale_get(v2d, &scale, nullptr); + UI_view2d_scale_get(&v2d, &scale, nullptr); /* Skip slow socket drawing if zoom is small. */ if (scale > 0.2f) { - node_draw_sockets(v2d, C, ntree, node, true, false); + node_draw_sockets(v2d, C, ntree, node, block, true, false); } /* Preview. */ - bNodeInstanceHash *previews = (bNodeInstanceHash *)CTX_data_pointer_get(C, "node_previews").data; - if (node->flag & NODE_PREVIEW && previews) { + bNodeInstanceHash *previews = + (bNodeInstanceHash *)CTX_data_pointer_get(&C, "node_previews").data; + if (node.flag & NODE_PREVIEW && previews) { bNodePreview *preview = (bNodePreview *)BKE_node_instance_hash_lookup(previews, key); if (preview && (preview->xsize && preview->ysize)) { - if (preview->rect && !BLI_rctf_is_empty(&node->prvr)) { - node_draw_preview(preview, &node->prvr); + if (preview->rect && !BLI_rctf_is_empty(&node.prvr)) { + node_draw_preview(preview, &node.prvr); } } } - UI_block_end(C, node->block); - UI_block_draw(C, node->block); - node->block = nullptr; + UI_block_end(&C, &block); + UI_block_draw(&C, &block); } -static void node_draw_hidden(const bContext *C, - const View2D *v2d, - const SpaceNode *snode, - bNodeTree *ntree, - bNode *node, - bNodeInstanceKey UNUSED(key)) +static void node_draw_hidden(const bContext &C, + const View2D &v2d, + const SpaceNode &snode, + bNodeTree &ntree, + bNode &node, + uiBlock &block) { - rctf *rct = &node->totr; - float centy = BLI_rctf_cent_y(rct); - float hiddenrad = BLI_rctf_size_y(rct) / 2.0f; + const rctf &rct = node.totr; + float centy = BLI_rctf_cent_y(&rct); + float hiddenrad = BLI_rctf_size_y(&rct) / 2.0f; float scale; - UI_view2d_scale_get(v2d, &scale, nullptr); + UI_view2d_scale_get(&v2d, &scale, nullptr); const int color_id = node_get_colorid(node); @@ -1886,43 +2097,43 @@ static void node_draw_hidden(const bContext *C, node_draw_shadow(snode, node, hiddenrad, 1.0f); /* Wire across the node when muted/disabled. */ - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { node_draw_mute_line(C, v2d, snode, node); } /* Body. */ float color[4]; { - if (nodeTypeUndefined(node)) { + if (nodeTypeUndefined(&node)) { /* Use warning color to indicate undefined types. */ UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); } - else if (node->flag & NODE_MUTED) { + else if (node.flag & NODE_MUTED) { /* Muted nodes get a mix of the background with the node color. */ UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color); } - else if (node->flag & NODE_CUSTOM_COLOR) { - rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + else if (node.flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], 1.0f); } else { UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color); } /* Draw selected nodes fully opaque. */ - if (node->flag & SELECT) { + if (node.flag & SELECT) { color[3] = 1.0f; } /* Draw muted nodes slightly transparent so the wires inside are visible. */ - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { color[3] -= 0.2f; } - UI_draw_roundbox_4fv(rct, true, hiddenrad, color); + UI_draw_roundbox_4fv(&rct, true, hiddenrad, color); } /* Title. */ - if (node->flag & SELECT) { + if (node.flag & SELECT) { UI_GetThemeColor4fv(TH_SELECT, color); } else { @@ -1932,13 +2143,13 @@ static void node_draw_hidden(const bContext *C, /* Collapse/expand icon. */ { const int but_size = U.widget_unit * 1.0f; - UI_block_emboss_set(node->block, UI_EMBOSS_NONE); + UI_block_emboss_set(&block, UI_EMBOSS_NONE); - uiBut *but = uiDefIconBut(node->block, + uiBut *but = uiDefIconBut(&block, UI_BTYPE_BUT_TOGGLE, 0, ICON_RIGHTARROW, - rct->xmin + (NODE_MARGIN_X / 3), + rct.xmin + (NODE_MARGIN_X / 3), centy - but_size / 2, but_size, but_size, @@ -1949,20 +2160,20 @@ static void node_draw_hidden(const bContext *C, 0.0f, ""); - UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); - UI_block_emboss_set(node->block, UI_EMBOSS); + UI_but_func_set(but, node_toggle_button_cb, &node, (void *)"NODE_OT_hide_toggle"); + UI_block_emboss_set(&block, UI_EMBOSS); } char showname[128]; - nodeLabel(ntree, node, showname, sizeof(showname)); + nodeLabel(&ntree, &node, showname, sizeof(showname)); - uiBut *but = uiDefBut(node->block, + uiBut *but = uiDefBut(&block, UI_BTYPE_LABEL, 0, showname, - round_fl_to_int(rct->xmin + NODE_MARGIN_X), + round_fl_to_int(rct.xmin + NODE_MARGIN_X), round_fl_to_int(centy - NODE_DY * 0.5f), - (short)(BLI_rctf_size_x(rct) - ((18.0f + 12.0f) * U.dpi_fac)), + (short)(BLI_rctf_size_x(&rct) - ((18.0f + 12.0f) * U.dpi_fac)), (short)NODE_DY, nullptr, 0, @@ -1975,19 +2186,19 @@ static void node_draw_hidden(const bContext *C, { const float outline_width = 1.0f; const rctf rect = { - rct->xmin - outline_width, - rct->xmax + outline_width, - rct->ymin - outline_width, - rct->ymax + outline_width, + rct.xmin - outline_width, + rct.xmax + outline_width, + rct.ymin - outline_width, + rct.ymax + outline_width, }; /* Color the outline according to active, selected, or undefined status. */ float color_outline[4]; - if (node->flag & SELECT) { - UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + if (node.flag & SELECT) { + UI_GetThemeColor4fv((node.flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); } - else if (nodeTypeUndefined(node)) { + else if (nodeTypeUndefined(&node)) { UI_GetThemeColor4fv(TH_REDALERT, color_outline); } else { @@ -1998,7 +2209,7 @@ static void node_draw_hidden(const bContext *C, UI_draw_roundbox_4fv(&rect, false, hiddenrad, color_outline); } - if (node->flag & NODE_MUTED) { + if (node.flag & NODE_MUTED) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } @@ -2009,39 +2220,38 @@ static void node_draw_hidden(const bContext *C, immUniformThemeColorShadeAlpha(TH_TEXT, -40, -180); float dx = 0.5f * U.widget_unit; - const float dx2 = 0.15f * U.widget_unit * snode->runtime->aspect; + const float dx2 = 0.15f * U.widget_unit * snode.runtime->aspect; const float dy = 0.2f * U.widget_unit; immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, rct->xmax - dx, centy - dy); - immVertex2f(pos, rct->xmax - dx, centy + dy); + immVertex2f(pos, rct.xmax - dx, centy - dy); + immVertex2f(pos, rct.xmax - dx, centy + dy); - immVertex2f(pos, rct->xmax - dx - dx2, centy - dy); - immVertex2f(pos, rct->xmax - dx - dx2, centy + dy); + immVertex2f(pos, rct.xmax - dx - dx2, centy - dy); + immVertex2f(pos, rct.xmax - dx - dx2, centy + dy); immEnd(); immUniformThemeColorShadeAlpha(TH_TEXT, 0, -180); - dx -= snode->runtime->aspect; + dx -= snode.runtime->aspect; immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, rct->xmax - dx, centy - dy); - immVertex2f(pos, rct->xmax - dx, centy + dy); + immVertex2f(pos, rct.xmax - dx, centy - dy); + immVertex2f(pos, rct.xmax - dx, centy + dy); - immVertex2f(pos, rct->xmax - dx - dx2, centy - dy); - immVertex2f(pos, rct->xmax - dx - dx2, centy + dy); + immVertex2f(pos, rct.xmax - dx - dx2, centy - dy); + immVertex2f(pos, rct.xmax - dx - dx2, centy + dy); immEnd(); immUnbindProgram(); GPU_blend(GPU_BLEND_NONE); - node_draw_sockets(v2d, C, ntree, node, true, false); + node_draw_sockets(v2d, C, ntree, node, block, true, false); - UI_block_end(C, node->block); - UI_block_draw(C, node->block); - node->block = nullptr; + UI_block_end(&C, &block); + UI_block_draw(&C, &block); } -int node_get_resize_cursor(int directions) +int node_get_resize_cursor(NodeResizeDirection directions) { if (directions == 0) { return WM_CURSOR_DEFAULT; @@ -2055,77 +2265,58 @@ int node_get_resize_cursor(int directions) return WM_CURSOR_EDIT; } -void node_set_cursor(wmWindow *win, SpaceNode *snode, float cursor[2]) +void node_set_cursor(wmWindow &win, SpaceNode &snode, const float2 &cursor) { - bNodeTree *ntree = snode->edittree; + const bNodeTree *ntree = snode.edittree; + if (ntree == nullptr) { + WM_cursor_set(&win, WM_CURSOR_DEFAULT); + return; + } + bNode *node; bNodeSocket *sock; int wmcursor = WM_CURSOR_DEFAULT; - if (ntree) { - if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN | SOCK_OUT)) { - /* Pass. */ - } - else { - /* Check nodes front to back. */ - for (node = (bNode *)ntree->nodes.last; node; node = node->prev) { - if (BLI_rctf_isect_pt(&node->totr, cursor[0], cursor[1])) { - break; /* First hit on node stops. */ - } - } - if (node) { - int dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]); - wmcursor = node_get_resize_cursor(dir); - } - } + if (node_find_indicated_socket( + snode, &node, &sock, cursor, (eNodeSocketInOut)(SOCK_IN | SOCK_OUT))) { + WM_cursor_set(&win, WM_CURSOR_DEFAULT); + return; } - WM_cursor_set(win, wmcursor); -} - -void node_draw_default(const bContext *C, - ARegion *region, - SpaceNode *snode, - bNodeTree *ntree, - bNode *node, - bNodeInstanceKey key) -{ - const View2D *v2d = ®ion->v2d; - if (node->flag & NODE_HIDDEN) { - node_draw_hidden(C, v2d, snode, ntree, node, key); + /* Check nodes front to back. */ + for (node = (bNode *)ntree->nodes.last; node; node = node->prev) { + if (BLI_rctf_isect_pt(&node->totr, cursor[0], cursor[1])) { + break; /* First hit on node stops. */ + } } - else { - node_draw_basis(C, v2d, snode, ntree, node, key); + if (node) { + NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]); + wmcursor = node_get_resize_cursor(dir); } -} -static void node_update(const bContext *C, bNodeTree *ntree, bNode *node) -{ - if (node->typeinfo->draw_nodetype_prepare) { - node->typeinfo->draw_nodetype_prepare(C, ntree, node); - } + WM_cursor_set(&win, wmcursor); } -static void count_multi_input_socket_links(bNodeTree *ntree, SpaceNode *snode) +static void count_multi_input_socket_links(bNodeTree &ntree, SpaceNode &snode) { Map<bNodeSocket *, int> counts; - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { if (link->tosock->flag & SOCK_MULTI_INPUT) { int &count = counts.lookup_or_add(link->tosock, 0); count++; } } /* Count temporary links going into this socket. */ - LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - bNodeLink *link = (bNodeLink *)linkdata->data; + if (snode.runtime->linkdrag) { + for (const bNodeLink *link : snode.runtime->linkdrag->links) { if (link->tosock && (link->tosock->flag & SOCK_MULTI_INPUT)) { int &count = counts.lookup_or_add(link->tosock, 0); count++; } } } - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + + LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { if (socket->flag & SOCK_MULTI_INPUT) { socket->total_inputs = counts.lookup_default(socket, 0); @@ -2134,86 +2325,367 @@ static void count_multi_input_socket_links(bNodeTree *ntree, SpaceNode *snode) } } -void node_update_nodetree(const bContext *C, bNodeTree *ntree) +/* XXX Does a bounding box update by iterating over all children. + * Not ideal to do this in every draw call, but doing as transform callback doesn't work, + * since the child node totr rects are not updated properly at that point. */ +static void frame_node_prepare_for_draw(bNode &node, Span<bNode *> nodes) +{ + const float margin = 1.5f * U.widget_unit; + NodeFrame *data = (NodeFrame *)node.storage; + + /* init rect from current frame size */ + rctf rect; + node_to_updated_rect(node, rect); + + /* frame can be resized manually only if shrinking is disabled or no children are attached */ + data->flag |= NODE_FRAME_RESIZEABLE; + /* for shrinking bbox, initialize the rect from first child node */ + bool bbinit = (data->flag & NODE_FRAME_SHRINK); + /* fit bounding box to all children */ + for (const bNode *tnode : nodes) { + if (tnode->parent != &node) { + continue; + } + + /* add margin to node rect */ + rctf noderect = tnode->totr; + noderect.xmin -= margin; + noderect.xmax += margin; + noderect.ymin -= margin; + noderect.ymax += margin; + + /* first child initializes frame */ + if (bbinit) { + bbinit = false; + rect = noderect; + data->flag &= ~NODE_FRAME_RESIZEABLE; + } + else { + BLI_rctf_union(&rect, &noderect); + } + } + + /* now adjust the frame size from view-space bounding box */ + const float2 offset = node_from_view(node, {rect.xmin, rect.ymax}); + node.offsetx = offset.x; + node.offsety = offset.y; + const float2 max = node_from_view(node, {rect.xmax, rect.ymin}); + node.width = max.x - node.offsetx; + node.height = -max.y + node.offsety; + + node.totr = rect; +} + +static void reroute_node_prepare_for_draw(bNode &node) +{ + /* get "global" coords */ + const float2 loc = node_to_view(node, float2(0)); + + /* reroute node has exactly one input and one output, both in the same place */ + bNodeSocket *nsock = (bNodeSocket *)node.outputs.first; + nsock->locx = loc.x; + nsock->locy = loc.y; + + nsock = (bNodeSocket *)node.inputs.first; + nsock->locx = loc.x; + nsock->locy = loc.y; + + const float size = 8.0f; + node.width = size * 2; + node.totr.xmin = loc.x - size; + node.totr.xmax = loc.x + size; + node.totr.ymax = loc.y + size; + node.totr.ymin = loc.y - size; +} + +static void node_update_nodetree(const bContext &C, + bNodeTree &ntree, + Span<bNode *> nodes, + Span<uiBlock *> blocks) { /* Make sure socket "used" tags are correct, for displaying value buttons. */ - SpaceNode *snode = CTX_wm_space_node(C); - ntreeTagUsedSockets(ntree); + SpaceNode *snode = CTX_wm_space_node(&C); + ntreeTagUsedSockets(&ntree); - count_multi_input_socket_links(ntree, snode); + count_multi_input_socket_links(ntree, *snode); /* Update nodes front to back, so children sizes get updated before parents. */ - LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) { - node_update(C, ntree, node); + for (const int i : nodes.index_range()) { + bNode &node = *nodes[i]; + uiBlock &block = *blocks[i]; + if (node.type == NODE_FRAME) { + frame_node_prepare_for_draw(node, nodes); + } + else if (node.type == NODE_REROUTE) { + reroute_node_prepare_for_draw(node); + } + else { + if (node.flag & NODE_HIDDEN) { + node_update_hidden(node, block); + } + else { + node_update_basis(C, ntree, node, block); + } + } } } -static void node_draw(const bContext *C, - ARegion *region, - SpaceNode *snode, - bNodeTree *ntree, - bNode *node, - bNodeInstanceKey key) +static void frame_node_draw_label(const bNodeTree &ntree, + const bNode &node, + const SpaceNode &snode) { - if (node->typeinfo->draw_nodetype) { - node->typeinfo->draw_nodetype(C, region, snode, ntree, node, key); + const float aspect = snode.runtime->aspect; + /* XXX font id is crap design */ + const int fontid = UI_style_get()->widgetlabel.uifont_id; + const NodeFrame *data = (const NodeFrame *)node.storage; + const float font_size = data->label_size / aspect; + + char label[MAX_NAME]; + nodeLabel(&ntree, &node, label, sizeof(label)); + + BLF_enable(fontid, BLF_ASPECT); + BLF_aspect(fontid, aspect, aspect, 1.0f); + /* clamp otherwise it can suck up a LOT of memory */ + BLF_size(fontid, MIN2(24.0f, font_size), U.dpi); + + /* title color */ + int color_id = node_get_colorid(node); + uchar color[3]; + UI_GetThemeColorBlendShade3ubv(TH_TEXT, color_id, 0.4f, 10, color); + BLF_color3ubv(fontid, color); + + const float margin = (float)(NODE_DY / 4); + const float width = BLF_width(fontid, label, sizeof(label)); + const float ascender = BLF_ascender(fontid); + const int label_height = ((margin / aspect) + (ascender * aspect)); + + /* 'x' doesn't need aspect correction */ + const rctf &rct = node.totr; + /* XXX a bit hacky, should use separate align values for x and y */ + float x = BLI_rctf_cent_x(&rct) - (0.5f * width); + float y = rct.ymax - label_height; + + /* label */ + const bool has_label = node.label[0] != '\0'; + if (has_label) { + BLF_position(fontid, x, y, 0); + BLF_draw(fontid, label, BLF_DRAW_STR_DUMMY_MAX); + } + + /* draw text body */ + if (node.id) { + const Text *text = (const Text *)node.id; + const int line_height_max = BLF_height_max(fontid); + const float line_spacing = (line_height_max * aspect); + const float line_width = (BLI_rctf_size_x(&rct) - margin) / aspect; + + /* 'x' doesn't need aspect correction */ + x = rct.xmin + margin; + y = rct.ymax - label_height - (has_label ? line_spacing : 0); + + /* early exit */ + int y_min = y + ((margin * 2) - (y - rct.ymin)); + + BLF_enable(fontid, BLF_CLIPPING | BLF_WORD_WRAP); + BLF_clipping(fontid, + rct.xmin, + /* round to avoid clipping half-way through a line */ + y - (floorf(((y - rct.ymin) - (margin * 2)) / line_spacing) * line_spacing), + rct.xmin + line_width, + rct.ymax); + + BLF_wordwrap(fontid, line_width); + + LISTBASE_FOREACH (const TextLine *, line, &text->lines) { + struct ResultBLF info; + if (line->line[0]) { + BLF_position(fontid, x, y, 0); + BLF_draw_ex(fontid, line->line, line->len, &info); + y -= line_spacing * info.lines; + } + else { + y -= line_spacing; + } + if (y < y_min) { + break; + } + } + + BLF_disable(fontid, BLF_CLIPPING | BLF_WORD_WRAP); } + + BLF_disable(fontid, BLF_ASPECT); } -#define USE_DRAW_TOT_UPDATE +static void frame_node_draw(const bContext &C, + const ARegion ®ion, + const SpaceNode &snode, + bNodeTree &ntree, + bNode &node, + uiBlock &block) +{ + /* skip if out of view */ + if (BLI_rctf_isect(&node.totr, ®ion.v2d.cur, nullptr) == false) { + UI_block_end(&C, &block); + return; + } + + float color[4]; + UI_GetThemeColor4fv(TH_NODE_FRAME, color); + const float alpha = color[3]; -void node_draw_nodetree(const bContext *C, - ARegion *region, - SpaceNode *snode, - bNodeTree *ntree, - bNodeInstanceKey parent_key) + /* shadow */ + node_draw_shadow(snode, node, BASIS_RAD, alpha); + + /* body */ + if (node.flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node.color[0], node.color[1], node.color[2], alpha); + } + else { + UI_GetThemeColor4fv(TH_NODE_FRAME, color); + } + + const rctf &rct = node.totr; + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&rct, true, BASIS_RAD, color); + + /* outline active and selected emphasis */ + if (node.flag & SELECT) { + if (node.flag & NODE_ACTIVE) { + UI_GetThemeColorShadeAlpha4fv(TH_ACTIVE, 0, -40, color); + } + else { + UI_GetThemeColorShadeAlpha4fv(TH_SELECT, 0, -40, color); + } + + UI_draw_roundbox_aa(&rct, false, BASIS_RAD, color); + } + + /* label and text */ + frame_node_draw_label(ntree, node, snode); + + node_draw_extra_info_panel(snode, node, block); + + UI_block_end(&C, &block); + UI_block_draw(&C, &block); +} + +static void reroute_node_draw( + const bContext &C, ARegion ®ion, bNodeTree &ntree, bNode &node, uiBlock &block) { - if (ntree == nullptr) { - return; /* Groups. */ + char showname[128]; /* 128 used below */ + const rctf &rct = node.totr; + + /* skip if out of view */ + if (rct.xmax < region.v2d.cur.xmin || rct.xmin > region.v2d.cur.xmax || + rct.ymax < region.v2d.cur.ymin || node.totr.ymin > region.v2d.cur.ymax) { + UI_block_end(&C, &block); + return; } -#ifdef USE_DRAW_TOT_UPDATE - if (ntree->nodes.first) { - BLI_rctf_init_minmax(®ion->v2d.tot); + if (node.label[0] != '\0') { + /* draw title (node label) */ + BLI_strncpy(showname, node.label, sizeof(showname)); + uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + showname, + (int)(rct.xmin - NODE_DYS), + (int)(rct.ymax), + (short)512, + (short)NODE_DY, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } + + /* only draw input socket. as they all are placed on the same position. + * highlight also if node itself is selected, since we don't display the node body separately! + */ + node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT); + + UI_block_end(&C, &block); + UI_block_draw(&C, &block); +} + +static void node_draw(const bContext &C, + ARegion ®ion, + const SpaceNode &snode, + bNodeTree &ntree, + bNode &node, + uiBlock &block, + bNodeInstanceKey key) +{ + if (node.type == NODE_FRAME) { + frame_node_draw(C, region, snode, ntree, node, block); + } + else if (node.type == NODE_REROUTE) { + reroute_node_draw(C, region, ntree, node, block); + } + else { + const View2D &v2d = region.v2d; + if (node.flag & NODE_HIDDEN) { + node_draw_hidden(C, v2d, snode, ntree, node, block); + } + else { + node_draw_basis(C, v2d, snode, ntree, node, block, key); + } } +} + +#define USE_DRAW_TOT_UPDATE + +static void node_draw_nodetree(const bContext &C, + ARegion ®ion, + SpaceNode &snode, + bNodeTree &ntree, + Span<bNode *> nodes, + Span<uiBlock *> blocks, + bNodeInstanceKey parent_key) +{ +#ifdef USE_DRAW_TOT_UPDATE + BLI_rctf_init_minmax(®ion.v2d.tot); #endif /* Draw background nodes, last nodes in front. */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + for (const int i : nodes.index_range()) { #ifdef USE_DRAW_TOT_UPDATE /* Unrelated to background nodes, update the v2d->tot, * can be anywhere before we draw the scroll bars. */ - BLI_rctf_union(®ion->v2d.tot, &node->totr); + BLI_rctf_union(®ion.v2d.tot, &nodes[i]->totr); #endif - if (!(node->flag & NODE_BACKGROUND)) { + if (!(nodes[i]->flag & NODE_BACKGROUND)) { continue; } - bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node); - node_draw(C, region, snode, ntree, node, key); + bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]); + node_draw(C, region, snode, ntree, *nodes[i], *blocks[i], key); } /* Node lines. */ GPU_blend(GPU_BLEND_ALPHA); nodelink_batch_start(snode); - LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { if (!nodeLinkIsHidden(link)) { - node_draw_link(C, ®ion->v2d, snode, link); + node_draw_link(C, region.v2d, snode, *link); } } nodelink_batch_end(snode); GPU_blend(GPU_BLEND_NONE); /* Draw foreground nodes, last nodes in front. */ - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->flag & NODE_BACKGROUND) { + for (const int i : nodes.index_range()) { + if (nodes[i]->flag & NODE_BACKGROUND) { continue; } - bNodeInstanceKey key = BKE_node_instance_key(parent_key, ntree, node); - node_draw(C, region, snode, ntree, node, key); + bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]); + node_draw(C, region, snode, ntree, *nodes[i], *blocks[i], key); } } @@ -2247,36 +2719,38 @@ static void draw_tree_path(const bContext &C, ARegion ®ion) GPU_matrix_pop_projection(); } -static void snode_setup_v2d(SpaceNode *snode, ARegion *region, const float center[2]) +static void snode_setup_v2d(SpaceNode &snode, ARegion ®ion, const float2 ¢er) { - View2D *v2d = ®ion->v2d; + View2D &v2d = region.v2d; /* Shift view to node tree center. */ - UI_view2d_center_set(v2d, center[0], center[1]); - UI_view2d_view_ortho(v2d); + UI_view2d_center_set(&v2d, center[0], center[1]); + UI_view2d_view_ortho(&v2d); /* Aspect + font, set each time. */ - snode->runtime->aspect = BLI_rctf_size_x(&v2d->cur) / (float)region->winx; + snode.runtime->aspect = BLI_rctf_size_x(&v2d.cur) / (float)region.winx; // XXX snode->curfont = uiSetCurFont_ext(snode->aspect); } -static void draw_nodetree(const bContext *C, - ARegion *region, - bNodeTree *ntree, +static void draw_nodetree(const bContext &C, + ARegion ®ion, + bNodeTree &ntree, bNodeInstanceKey parent_key) { - SpaceNode *snode = CTX_wm_space_node(C); + SpaceNode *snode = CTX_wm_space_node(&C); + + Vector<bNode *> nodes = ntree.nodes; - node_uiblocks_init(C, ntree); + Array<uiBlock *> blocks = node_uiblocks_init(C, nodes); - node_update_nodetree(C, ntree); - node_draw_nodetree(C, region, snode, ntree, parent_key); + node_update_nodetree(C, ntree, nodes, blocks); + node_draw_nodetree(C, region, *snode, ntree, nodes, blocks, parent_key); } /** * Make the background slightly brighter to indicate that users are inside a node-group. */ -static void draw_background_color(const SpaceNode *snode) +static void draw_background_color(const SpaceNode &snode) { const int max_tree_length = 3; const float bright_factor = 0.25f; @@ -2284,7 +2758,7 @@ static void draw_background_color(const SpaceNode *snode) /* We ignore the first element of the path since it is the top-most tree and it doesn't need to * be brighter. We also set a cap to how many levels we want to set apart, to avoid the * background from getting too bright. */ - const int clamped_tree_path_length = BLI_listbase_count_at_most(&snode->treepath, + const int clamped_tree_path_length = BLI_listbase_count_at_most(&snode.treepath, max_tree_length); const int depth = max_ii(0, clamped_tree_path_length - 1); @@ -2294,34 +2768,34 @@ static void draw_background_color(const SpaceNode *snode) GPU_clear_color(color[0], color[1], color[2], 1.0); } -void node_draw_space(const bContext *C, ARegion *region) +void node_draw_space(const bContext &C, ARegion ®ion) { - wmWindow *win = CTX_wm_window(C); - SpaceNode *snode = CTX_wm_space_node(C); - View2D *v2d = ®ion->v2d; + wmWindow *win = CTX_wm_window(&C); + SpaceNode &snode = *CTX_wm_space_node(&C); + View2D &v2d = region.v2d; /* Setup off-screen buffers. */ - GPUViewport *viewport = WM_draw_region_get_viewport(region); + GPUViewport *viewport = WM_draw_region_get_viewport(®ion); GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); GPU_framebuffer_bind_no_srgb(framebuffer_overlay); - UI_view2d_view_ortho(v2d); + UI_view2d_view_ortho(&v2d); draw_background_color(snode); GPU_depth_test(GPU_DEPTH_NONE); GPU_scissor_test(true); /* XXX `snode->runtime->cursor` set in coordinate-space for placing new nodes, * used for drawing noodles too. */ - UI_view2d_region_to_view(®ion->v2d, - win->eventstate->xy[0] - region->winrct.xmin, - win->eventstate->xy[1] - region->winrct.ymin, - &snode->runtime->cursor[0], - &snode->runtime->cursor[1]); - snode->runtime->cursor[0] /= UI_DPI_FAC; - snode->runtime->cursor[1] /= UI_DPI_FAC; + UI_view2d_region_to_view(®ion.v2d, + win->eventstate->xy[0] - region.winrct.xmin, + win->eventstate->xy[1] - region.winrct.ymin, + &snode.runtime->cursor[0], + &snode.runtime->cursor[1]); + snode.runtime->cursor[0] /= UI_DPI_FAC; + snode.runtime->cursor[1] /= UI_DPI_FAC; - ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); + ED_region_draw_cb_draw(&C, ®ion, REGION_DRAW_PRE_VIEW); /* Only set once. */ GPU_blend(GPU_BLEND_ALPHA); @@ -2330,15 +2804,15 @@ void node_draw_space(const bContext *C, ARegion *region) snode_set_context(C); const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); - UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); + UI_view2d_dot_grid_draw(&v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); /* Draw parent node trees. */ - if (snode->treepath.last) { - bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; + if (snode.treepath.last) { + bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last; /* Update tree path name (drawn in the bottom left). */ - ID *name_id = (path->nodetree && path->nodetree != snode->nodetree) ? &path->nodetree->id : - snode->id; + ID *name_id = (path->nodetree && path->nodetree != snode.nodetree) ? &path->nodetree->id : + snode.id; if (name_id && UNLIKELY(!STREQ(path->display_name, name_id->name + 2))) { BLI_strncpy(path->display_name, name_id->name + 2, sizeof(path->display_name)); @@ -2346,12 +2820,12 @@ void node_draw_space(const bContext *C, ARegion *region) /* Current View2D center, will be set temporarily for parent node trees. */ float center[2]; - UI_view2d_center_get(v2d, ¢er[0], ¢er[1]); + UI_view2d_center_get(&v2d, ¢er[0], ¢er[1]); /* Store new view center in path and current edit tree. */ copy_v2_v2(path->view_center, center); - if (snode->edittree) { - copy_v2_v2(snode->edittree->view_center, center); + if (snode.edittree) { + copy_v2_v2(snode.edittree->view_center, center); } /* Top-level edit tree. */ @@ -2369,31 +2843,31 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_matrix_push(); GPU_matrix_identity_set(); - wmOrtho2_pixelspace(region->winx, region->winy); + wmOrtho2_pixelspace(region.winx, region.winy); - WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + WM_gizmomap_draw(region.gizmo_map, &C, WM_GIZMOMAP_DRAWSTEP_2D); GPU_matrix_pop(); GPU_matrix_projection_set(original_proj); } - draw_nodetree(C, region, ntree, path->parent_key); + draw_nodetree(C, region, *ntree, path->parent_key); } /* Temporary links. */ GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); - LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { - LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - node_draw_link(C, v2d, snode, (bNodeLink *)linkdata->data); + if (snode.runtime->linkdrag) { + for (const bNodeLink *link : snode.runtime->linkdrag->links) { + node_draw_link(C, v2d, snode, *link); } } GPU_line_smooth(false); GPU_blend(GPU_BLEND_NONE); - if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode->flag & SNODE_SHOW_GPENCIL) { + if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode.flag & SNODE_SHOW_GPENCIL) { /* Draw grease-pencil annotations. */ - ED_annotation_draw_view2d(C, true); + ED_annotation_draw_view2d(&C, true); } } else { @@ -2402,23 +2876,23 @@ void node_draw_space(const bContext *C, ARegion *region) draw_nodespace_back_pix(C, region, snode, NODE_INSTANCE_KEY_NONE); } - ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW); + ED_region_draw_cb_draw(&C, ®ion, REGION_DRAW_POST_VIEW); /* Reset view matrix. */ - UI_view2d_view_restore(C); + UI_view2d_view_restore(&C); - if (snode->treepath.last) { - if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode->flag & SNODE_SHOW_GPENCIL) { + if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) { + if (snode.flag & SNODE_SHOW_GPENCIL && snode.treepath.last) { /* Draw grease-pencil (screen strokes, and also paint-buffer). */ - ED_annotation_draw_view2d(C, false); + ED_annotation_draw_view2d(&C, false); } - } - /* Draw context path. */ - if (snode->edittree) { - draw_tree_path(*C, *region); + /* Draw context path. */ + if (snode.overlay.flag & SN_OVERLAY_SHOW_PATH && snode.edittree) { + draw_tree_path(C, region); + } } /* Scrollers. */ - UI_view2d_scrollers_draw(v2d, nullptr); + UI_view2d_scrollers_draw(&v2d, nullptr); } |