2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
21 * The Original Code is: all of this file.
22 * Contributor(s): Nathan Letwory
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/space_node/node_draw.c
29 * \brief higher level node drawing for the node editor.
32 #include "DNA_lamp_types.h"
33 #include "DNA_node_types.h"
34 #include "DNA_material_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_texture_types.h"
39 #include "DNA_world_types.h"
42 #include "BLI_blenlib.h"
44 #include "BLF_translation.h"
46 #include "BKE_context.h"
47 #include "BKE_depsgraph.h"
54 #include "BIF_glutil.h"
60 #include "ED_gpencil.h"
61 #include "ED_screen.h"
62 #include "ED_space_api.h"
64 #include "UI_resources.h"
65 #include "UI_view2d.h"
67 #include "RNA_access.h"
69 #include "node_intern.h" /* own include */
70 #include "COM_compositor.h"
73 extern void ui_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int select);
75 void ED_node_tree_update(const bContext *C)
77 SpaceNode *snode = CTX_wm_space_node(C);
81 if (snode->nodetree && snode->nodetree->id.us == 0) {
82 snode->nodetree->id.us = 1;
87 /* id is supposed to contain a node tree */
88 static bNodeTree *node_tree_from_ID(ID *id)
91 short idtype = GS(id->name);
95 return (bNodeTree *)id;
97 return ((Material *)id)->nodetree;
99 return ((Lamp *)id)->nodetree;
101 return ((World *)id)->nodetree;
103 return ((Scene *)id)->nodetree;
105 return ((Tex *)id)->nodetree;
112 void ED_node_tag_update_id(ID *id)
114 bNodeTree *ntree = node_tree_from_ID(id);
115 if (id == NULL || ntree == NULL)
118 if (ntree->type == NTREE_SHADER) {
119 DAG_id_tag_update(id, 0);
121 if (GS(id->name) == ID_MA)
122 WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
123 else if (GS(id->name) == ID_LA)
124 WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id);
125 else if (GS(id->name) == ID_WO)
126 WM_main_add_notifier(NC_WORLD | ND_WORLD, id);
128 else if (ntree->type == NTREE_COMPOSIT) {
129 WM_main_add_notifier(NC_SCENE | ND_NODES, id);
131 else if (ntree->type == NTREE_TEXTURE) {
132 DAG_id_tag_update(id, 0);
133 WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
137 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
144 for (node = ntree->nodes.first; node; node = node->next)
145 if (node->type == NODE_GROUP && node->id)
146 if (has_nodetree((bNodeTree *)node->id, lookup))
152 void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree)
157 /* look through all datablocks, to support groups */
158 FOREACH_NODETREE(bmain, tntree, id) {
159 /* check if nodetree uses the group */
160 if (has_nodetree(tntree, ntree))
161 ED_node_tag_update_id(id);
162 } FOREACH_NODETREE_END
164 if (ntree->type == NTREE_TEXTURE)
165 ntreeTexCheckCyclics(ntree);
168 static int compare_nodes(bNode *a, bNode *b)
171 /* These tell if either the node or any of the parent nodes is selected.
172 * A selected parent means an unselected node is also in foreground!
174 int a_select = (a->flag & NODE_SELECT), b_select = (b->flag & NODE_SELECT);
175 int a_active = (a->flag & NODE_ACTIVE), b_active = (b->flag & NODE_ACTIVE);
177 /* if one is an ancestor of the other */
178 /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
179 for (parent = a->parent; parent; parent = parent->parent) {
180 /* if b is an ancestor, it is always behind a */
183 /* any selected ancestor moves the node forward */
184 if (parent->flag & NODE_ACTIVE)
186 if (parent->flag & NODE_SELECT)
189 for (parent = b->parent; parent; parent = parent->parent) {
190 /* if a is an ancestor, it is always behind b */
193 /* any selected ancestor moves the node forward */
194 if (parent->flag & NODE_ACTIVE)
196 if (parent->flag & NODE_SELECT)
200 /* if one of the nodes is in the background and the other not */
201 if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
203 else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
206 /* if one has a higher selection state (active > selected > nothing) */
207 if (!b_active && a_active)
209 else if (!b_select && (a_active || a_select))
215 /* Sorts nodes by selection: unselected nodes first, then selected,
216 * then the active node at the very end. Relative order is kept intact!
218 void ED_node_sort(bNodeTree *ntree)
220 /* merge sort is the algorithm of choice here */
221 bNode *first_a, *first_b, *node_a, *node_b, *tmp;
222 int totnodes = BLI_countlist(&ntree->nodes);
226 while (k < totnodes) {
227 first_a = first_b = ntree->nodes.first;
230 /* setup first_b pointer */
231 for (b = 0; b < k && first_b; ++b) {
232 first_b = first_b->next;
234 /* all batches merged? */
242 while (a < k && b < k && node_b) {
243 if (compare_nodes(node_a, node_b) == 0) {
244 node_a = node_a->next;
249 node_b = node_b->next;
251 BLI_remlink(&ntree->nodes, tmp);
252 BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
256 /* setup first pointers for next batch */
259 /* all nodes sorted? */
262 first_b = first_b->next;
272 static void do_node_internal_buttons(bContext *C, void *UNUSED(node_v), int event)
274 if (event == B_NODE_EXEC) {
275 SpaceNode *snode = CTX_wm_space_node(C);
276 if (snode && snode->id)
277 ED_node_tag_update_id(snode->id);
281 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
286 /* add node uiBlocks in drawing order - prevents events going to overlapping nodes */
288 for (node = ntree->nodes.first; node; node = node->next) {
290 BLI_snprintf(uiblockstr, sizeof(uiblockstr), "node buttons %p", (void *)node);
291 node->block = uiBeginBlock(C, CTX_wm_region(C), uiblockstr, UI_EMBOSS);
292 uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
294 /* this cancels events for background nodes */
295 uiBlockSetFlag(node->block, UI_BLOCK_CLIP_EVENTS);
299 void node_to_view(struct bNode *node, float x, float y, float *rx, float *ry)
301 nodeToView(node, x, y, rx, ry);
306 void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry)
310 nodeFromView(node, x, y, rx, ry);
314 /* based on settings in node, sets drawing rect info. each redraw! */
315 static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
317 uiLayout *layout, *row;
318 PointerRNA nodeptr, sockptr;
324 RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
326 /* get "global" coords */
327 node_to_view(node, 0.0f, 0.0f, &locx, &locy);
333 /* little bit space in top */
334 if (node->outputs.first)
338 for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
339 if (nodeSocketIsHidden(nsock))
342 RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
344 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
345 locx + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, UI_GetStyle());
346 /* context pointers for current node and socket */
347 uiLayoutSetContextPointer(layout, "node", &nodeptr);
348 uiLayoutSetContextPointer(layout, "socket", &sockptr);
350 /* align output buttons to the right */
351 row = uiLayoutRow(layout, 1);
352 uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
354 node->typeinfo->draw_output((bContext *)C, row, &sockptr, &nodeptr);
356 uiBlockEndAlign(node->block);
357 uiBlockLayoutResolve(node->block, NULL, &buty);
359 /* ensure minimum socket height in case layout is empty */
360 buty = min_ii(buty, dy - NODE_DY);
362 nsock->locx = locx + NODE_WIDTH(node);
363 /* place the socket circle in the middle of the layout */
364 nsock->locy = 0.5f * (dy + buty);
371 node->prvr.xmin = locx + NODE_DYS;
372 node->prvr.xmax = locx + NODE_WIDTH(node) - NODE_DYS;
375 if (node->flag & NODE_PREVIEW) {
378 if (node->preview_xsize && node->preview_ysize)
379 aspect = (float)node->preview_ysize / (float)node->preview_xsize;
382 node->prvr.ymax = dy;
385 node->prvr.ymin = dy - aspect * (NODE_WIDTH(node) - NODE_DY);
387 /* width correction of image */
389 float dx = (NODE_WIDTH(node) - NODE_DYS) - (NODE_WIDTH(node) - NODE_DYS) / aspect;
391 node->prvr.ymin = dy - (NODE_WIDTH(node) - NODE_DY);
393 node->prvr.xmin += 0.5f * dx;
394 node->prvr.xmax -= 0.5f * dx;
397 dy = node->prvr.ymin - NODE_DYS / 2;
399 /* make sure that maximums are bigger or equal to minimums */
400 if (node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
401 if (node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
405 if (node->typeinfo->draw_buttons && (node->flag & NODE_OPTIONS)) {
408 /* set this for uifunc() that don't use layout engine yet */
410 node->butr.xmax = NODE_WIDTH(node) - 2 * NODE_DYS;
415 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
416 locx + NODE_DYS, dy, node->butr.xmax, 0, UI_GetStyle());
417 uiLayoutSetContextPointer(layout, "node", &nodeptr);
419 node->typeinfo->draw_buttons(layout, (bContext *)C, &nodeptr);
421 uiBlockEndAlign(node->block);
422 uiBlockLayoutResolve(node->block, NULL, &buty);
424 dy = buty - NODE_DYS / 2;
428 for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
429 if (nodeSocketIsHidden(nsock))
432 RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
434 layout = uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
435 locx + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, UI_GetStyle());
436 /* context pointers for current node and socket */
437 uiLayoutSetContextPointer(layout, "node", &nodeptr);
438 uiLayoutSetContextPointer(layout, "socket", &sockptr);
440 node->typeinfo->draw_input((bContext *)C, layout, &sockptr, &nodeptr);
442 uiBlockEndAlign(node->block);
443 uiBlockLayoutResolve(node->block, NULL, &buty);
445 /* ensure minimum socket height in case layout is empty */
446 buty = min_ii(buty, dy - NODE_DY);
449 /* place the socket circle in the middle of the layout */
450 nsock->locy = 0.5f * (dy + buty);
457 /* little bit space in end */
458 if (node->inputs.first || (node->flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0)
461 node->totr.xmin = locx;
462 node->totr.xmax = locx + NODE_WIDTH(node);
463 node->totr.ymax = locy;
464 node->totr.ymin = min_ff(dy, locy - 2 * NODE_DY);
466 /* Set the block bounds to clip mouse events from underlying nodes.
467 * Add a margin for sockets on each side.
469 uiExplicitBoundsBlock(node->block,
470 node->totr.xmin - NODE_SOCKSIZE,
472 node->totr.xmax + NODE_SOCKSIZE,
476 /* based on settings in node, sets drawing rect info. each redraw! */
477 static void node_update_hidden(bNode *node)
481 float rad, drad, hiddenrad = HIDDEN_RAD;
482 int totin = 0, totout = 0, tot;
484 /* get "global" coords */
485 node_to_view(node, 0.0f, 0.0f, &locx, &locy);
487 /* calculate minimal radius */
488 for (nsock = node->inputs.first; nsock; nsock = nsock->next)
489 if (!nodeSocketIsHidden(nsock))
491 for (nsock = node->outputs.first; nsock; nsock = nsock->next)
492 if (!nodeSocketIsHidden(nsock))
495 tot = MAX2(totin, totout);
497 hiddenrad += 5.0f * (float)(tot - 4);
500 node->totr.xmin = locx;
501 node->totr.xmax = locx + 3 * hiddenrad + node->miniwidth;
502 node->totr.ymax = locy + (hiddenrad - 0.5f * NODE_DY);
503 node->totr.ymin = node->totr.ymax - 2 * hiddenrad;
506 rad = drad = (float)M_PI / (1.0f + (float)totout);
508 for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
509 if (!nodeSocketIsHidden(nsock)) {
510 nsock->locx = node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad;
511 nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
517 rad = drad = -(float)M_PI / (1.0f + (float)totin);
519 for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
520 if (!nodeSocketIsHidden(nsock)) {
521 nsock->locx = node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad;
522 nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
527 /* Set the block bounds to clip mouse events from underlying nodes.
528 * Add a margin for sockets on each side.
530 uiExplicitBoundsBlock(node->block,
531 node->totr.xmin - NODE_SOCKSIZE,
533 node->totr.xmax + NODE_SOCKSIZE,
537 void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
539 if (node->flag & NODE_HIDDEN)
540 node_update_hidden(node);
542 node_update_basis(C, ntree, node);
545 int node_select_area_default(bNode *node, int x, int y)
547 return BLI_rctf_isect_pt(&node->totr, x, y);
550 int node_tweak_area_default(bNode *node, int x, int y)
552 return BLI_rctf_isect_pt(&node->totr, x, y);
555 int node_get_colorid(bNode *node)
557 switch (node->typeinfo->nclass) {
558 case NODE_CLASS_INPUT: return TH_NODE_IN_OUT;
559 case NODE_CLASS_OUTPUT: return (node->flag & NODE_DO_OUTPUT) ? TH_NODE_IN_OUT : TH_NODE;
560 case NODE_CLASS_CONVERTOR: return TH_NODE_CONVERTOR;
561 case NODE_CLASS_OP_COLOR:
562 case NODE_CLASS_OP_VECTOR:
563 case NODE_CLASS_OP_FILTER: return TH_NODE_OPERATOR;
564 case NODE_CLASS_GROUP: return TH_NODE_GROUP;
565 case NODE_CLASS_INTERFACE: return TH_NODE_INTERFACE;
566 case NODE_CLASS_MATTE: return TH_NODE_MATTE;
567 case NODE_CLASS_DISTORT: return TH_NODE_DISTORT;
568 default: return TH_NODE;
572 /* note: in cmp_util.c is similar code, for node_compo_pass_on()
573 * the same goes for shader and texture nodes. */
574 /* note: in node_edit.c is similar code, for untangle node */
575 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
580 glEnable(GL_LINE_SMOOTH);
582 for (link = node->internal_links.first; link; link = link->next)
583 node_draw_link_bezier(v2d, snode, link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
586 glDisable(GL_LINE_SMOOTH);
589 /* this might have some more generic use */
590 static void node_circle_draw(float x, float y, float size, float *col, int highlight)
592 /* 16 values of sin function */
593 static float si[16] = {
594 0.00000000f, 0.39435585f, 0.72479278f, 0.93775213f,
595 0.99871650f, 0.89780453f, 0.65137248f, 0.29936312f,
596 -0.10116832f, -0.48530196f, -0.79077573f, -0.96807711f,
597 -0.98846832f, -0.84864425f, -0.57126821f, -0.20129852f
599 /* 16 values of cos function */
600 static float co[16] = {
601 1.00000000f, 0.91895781f, 0.68896691f, 0.34730525f,
602 -0.05064916f, -0.44039415f, -0.75875812f, -0.95413925f,
603 -0.99486932f, -0.87434661f, -0.61210598f, -0.25065253f,
604 0.15142777f, 0.52896401f, 0.82076344f, 0.97952994f,
612 for (a = 0; a < 16; a++)
613 glVertex2f(x + size * si[a], y + size * co[a]);
618 UI_ThemeColor(TH_TEXT_HI);
622 glColor4ub(0, 0, 0, 150);
625 glEnable(GL_LINE_SMOOTH);
626 glBegin(GL_LINE_LOOP);
627 for (a = 0; a < 16; a++)
628 glVertex2f(x + size * si[a], y + size * co[a]);
630 glDisable(GL_LINE_SMOOTH);
635 void node_socket_circle_draw(const bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *sock, float size, int highlight)
637 PointerRNA ptr, node_ptr;
640 RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
641 RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
642 sock->typeinfo->draw_color((bContext *)C, &ptr, &node_ptr, color);
643 node_circle_draw(sock->locx, sock->locy, size, color, highlight);
646 /* ************** Socket callbacks *********** */
648 static void node_draw_preview_background(float tile, rctf *rect)
652 /* draw checkerboard backdrop to show alpha */
653 glColor3ub(120, 120, 120);
654 glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
655 glColor3ub(160, 160, 160);
657 for (y = rect->ymin; y < rect->ymax; y += tile * 2) {
658 for (x = rect->xmin; x < rect->xmax; x += tile * 2) {
659 float tilex = tile, tiley = tile;
661 if (x + tile > rect->xmax)
662 tilex = rect->xmax - x;
663 if (y + tile > rect->ymax)
664 tiley = rect->ymax - y;
666 glRectf(x, y, x + tilex, y + tiley);
669 for (y = rect->ymin + tile; y < rect->ymax; y += tile * 2) {
670 for (x = rect->xmin + tile; x < rect->xmax; x += tile * 2) {
671 float tilex = tile, tiley = tile;
673 if (x + tile > rect->xmax)
674 tilex = rect->xmax - x;
675 if (y + tile > rect->ymax)
676 tiley = rect->ymax - y;
678 glRectf(x, y, x + tilex, y + tiley);
684 static void node_draw_preview(bNodePreview *preview, rctf *prv)
686 float xrect = BLI_rctf_size_x(prv);
687 float yrect = BLI_rctf_size_y(prv);
688 float xscale = xrect / ((float)preview->xsize);
689 float yscale = yrect / ((float)preview->ysize);
693 /* uniform scale and offset */
695 if (xscale < yscale) {
696 float offset = 0.5f * (yrect - ((float)preview->ysize) * xscale);
697 draw_rect.ymin += offset;
698 draw_rect.ymax -= offset;
702 float offset = 0.5f * (xrect - ((float)preview->xsize) * yscale);
703 draw_rect.xmin += offset;
704 draw_rect.xmax -= offset;
708 node_draw_preview_background(BLI_rctf_size_x(prv) / 10.0f, &draw_rect);
711 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* premul graphics */
713 glColor4f(1.0, 1.0, 1.0, 1.0);
714 glPixelZoom(scale, scale);
715 glaDrawPixelsTex(draw_rect.xmin, draw_rect.ymin, preview->xsize, preview->ysize, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR, preview->rect);
716 glPixelZoom(1.0f, 1.0f);
720 UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
721 fdrawbox(draw_rect.xmin, draw_rect.ymin, draw_rect.xmax, draw_rect.ymax);
724 /* common handle function for operator buttons that need to select the node first */
725 static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv)
727 bNode *node = (bNode *)node_argv;
728 const char *opname = (const char *)op_argv;
730 /* select & activate only the button's node */
731 node_select_single(C, node);
733 WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL);
736 void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha)
738 rctf *rct = &node->totr;
740 uiSetRoundBox(UI_CNR_ALL);
741 if (node->parent == NULL)
742 ui_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT);
744 const float margin = 3.0f;
746 glColor4f(0.0f, 0.0f, 0.0f, 0.33f);
748 uiRoundBox(rct->xmin - margin, rct->ymin - margin,
749 rct->xmax + margin, rct->ymax + margin, radius + margin);
754 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
756 bNodeInstanceHash *previews = CTX_data_pointer_get(C, "node_previews").data;
758 rctf *rct = &node->totr;
760 /* float socket_size = NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
761 float iconbutw = 0.8f * UI_UNIT_X;
762 int color_id = node_get_colorid(node);
763 char showname[128]; /* 128 used below */
764 View2D *v2d = &ar->v2d;
766 /* XXX hack: copy values from linked ID data where displayed as sockets */
768 nodeSynchronizeID(node, false);
770 /* skip if out of view */
771 if (BLI_rctf_isect(&node->totr, &ar->v2d.cur, NULL) == FALSE) {
772 uiEndBlock(C, node->block);
778 node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
780 /* header uses color from backdrop, but we make it opaqie */
781 if (color_id == TH_NODE) {
783 UI_GetThemeColorShade3fv(color_id, -20, col);
784 glColor4f(col[0], col[1], col[2], 1.0f);
787 UI_ThemeColor(color_id);
789 if (node->flag & NODE_MUTED)
790 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
793 #ifdef WITH_COMPOSITOR
794 if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
795 if (COM_isHighlightedbNode(node)) {
796 UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
801 uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
802 uiRoundBox(rct->xmin, rct->ymax - NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
804 /* show/hide icons */
805 iconofs = rct->xmax - 0.35f * U.widget_unit;
808 if (node->typeinfo->flag & NODE_PREVIEW) {
811 uiBlockSetEmboss(node->block, UI_EMBOSSN);
812 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_MATERIAL,
813 iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
814 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_preview_toggle");
815 /* XXX this does not work when node is activated and the operator called right afterwards,
816 * since active ID is not updated yet (needs to process the notifier).
817 * This can only work as visual indicator!
819 // if (!(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT)))
820 // uiButSetFlag(but, UI_BUT_DISABLED);
821 uiBlockSetEmboss(node->block, UI_EMBOSS);
824 if (node->type == NODE_GROUP) {
827 uiBlockSetEmboss(node->block, UI_EMBOSSN);
828 but = uiDefIconBut(node->block, TOGBUT, B_REDR, ICON_NODETREE,
829 iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
830 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_group_edit");
831 uiBlockSetEmboss(node->block, UI_EMBOSS);
835 if (node->flag & SELECT)
836 UI_ThemeColor(TH_SELECT);
838 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
840 /* open/close entirely? */
843 int but_size = UI_UNIT_X * 0.6f;
844 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
845 uiBlockSetEmboss(node->block, UI_EMBOSSN);
846 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
847 rct->xmin + 0.5f * U.widget_unit - but_size / 2, rct->ymax - NODE_DY / 2.0f - but_size / 2,
848 but_size, but_size, NULL, 0, 0, 0, 0, "");
849 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
850 uiBlockSetEmboss(node->block, UI_EMBOSS);
852 /* custom draw function for this button */
853 UI_DrawTriIcon(rct->xmin + 0.5f * U.widget_unit, rct->ymax - NODE_DY / 2.0f, 'v');
856 /* this isn't doing anything for the label, so commenting out */
858 if (node->flag & SELECT)
859 UI_ThemeColor(TH_TEXT_HI);
861 UI_ThemeColor(TH_TEXT);
864 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
866 //if (node->flag & NODE_MUTED)
867 // BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
869 uiDefBut(node->block, LABEL, 0, showname,
870 (int)(rct->xmin + (NODE_MARGIN_X)), (int)(rct->ymax - NODE_DY),
871 (short)(iconofs - rct->xmin - 18.0f), (short)NODE_DY,
872 NULL, 0, 0, 0, 0, "");
875 if (!nodeIsRegistered(node))
876 UI_ThemeColor4(TH_REDALERT); /* use warning color to indicate undefined types */
877 else if (node->flag & NODE_CUSTOM_COLOR)
878 glColor3fv(node->color);
880 UI_ThemeColor4(TH_NODE);
882 uiSetRoundBox(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
883 uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax - NODE_DY, BASIS_RAD);
886 /* outline active and selected emphasis */
887 if (node->flag & SELECT) {
890 glEnable(GL_LINE_SMOOTH);
892 if (node->flag & NODE_ACTIVE)
893 UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
895 UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
897 uiSetRoundBox(UI_CNR_ALL);
898 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
900 glDisable(GL_LINE_SMOOTH);
905 if (node->flag & NODE_MUTED)
906 node_draw_mute_line(v2d, snode, node);
909 /* socket inputs, buttons */
910 for (sock = node->inputs.first; sock; sock = sock->next) {
911 if (nodeSocketIsHidden(sock))
914 node_socket_circle_draw(C, ntree, node, sock, NODE_SOCKSIZE, sock->flag & SELECT);
918 for (sock = node->outputs.first; sock; sock = sock->next) {
919 if (nodeSocketIsHidden(sock))
922 node_socket_circle_draw(C, ntree, node, sock, NODE_SOCKSIZE, sock->flag & SELECT);
926 if (node->flag & NODE_PREVIEW && previews) {
927 bNodePreview *preview = BKE_node_instance_hash_lookup(previews, key);
928 if (preview && (preview->xsize && preview->ysize)) {
929 if (preview->rect && !BLI_rctf_is_empty(&node->prvr)) {
930 node_draw_preview(preview, &node->prvr);
935 UI_ThemeClearColor(color_id);
937 uiEndBlock(C, node->block);
938 uiDrawBlock(C, node->block);
942 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey UNUSED(key))
945 rctf *rct = &node->totr;
946 float dx, centy = BLI_rctf_cent_y(rct);
947 float hiddenrad = BLI_rctf_size_y(rct) / 2.0f;
948 float socket_size = NODE_SOCKSIZE;
949 int color_id = node_get_colorid(node);
950 char showname[128]; /* 128 is used below */
953 node_draw_shadow(snode, node, hiddenrad, 1.0f);
956 UI_ThemeColor(color_id);
957 if (node->flag & NODE_MUTED)
958 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
960 #ifdef WITH_COMPOSITOR
961 if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
962 if (COM_isHighlightedbNode(node)) {
963 UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
970 uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
972 /* outline active and selected emphasis */
973 if (node->flag & SELECT) {
975 glEnable(GL_LINE_SMOOTH);
977 if (node->flag & NODE_ACTIVE)
978 UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
980 UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
981 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
983 glDisable(GL_LINE_SMOOTH);
987 /* custom color inline */
988 if (node->flag & NODE_CUSTOM_COLOR) {
990 glEnable(GL_LINE_SMOOTH);
992 glColor3fv(node->color);
993 uiDrawBox(GL_LINE_LOOP, rct->xmin + 1, rct->ymin + 1, rct->xmax -1, rct->ymax - 1, hiddenrad);
995 glDisable(GL_LINE_SMOOTH);
1000 if (node->flag & SELECT)
1001 UI_ThemeColor(TH_SELECT);
1003 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
1005 /* open entirely icon */
1008 int but_size = UI_UNIT_X * 0.6f;
1009 /* XXX button uses a custom triangle draw below, so make it invisible without icon */
1010 uiBlockSetEmboss(node->block, UI_EMBOSSN);
1011 but = uiDefBut(node->block, TOGBUT, B_REDR, "",
1012 rct->xmin + 10.0f - but_size / 2, centy - but_size / 2,
1013 but_size, but_size, NULL, 0, 0, 0, 0, "");
1014 uiButSetFunc(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
1015 uiBlockSetEmboss(node->block, UI_EMBOSS);
1017 /* custom draw function for this button */
1018 UI_DrawTriIcon(rct->xmin + 10.0f, centy, 'h');
1022 if (node->flag & NODE_MUTED)
1023 node_draw_mute_line(&ar->v2d, snode, node);
1025 if (node->flag & SELECT)
1026 UI_ThemeColor(TH_SELECT);
1028 UI_ThemeColor(TH_TEXT);
1030 if (node->miniwidth > 0.0f) {
1031 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
1033 //if (node->flag & NODE_MUTED)
1034 // BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
1036 uiDefBut(node->block, LABEL, 0, showname,
1037 (int)(rct->xmin + (NODE_MARGIN_X)), (int)(centy - 10),
1038 (short)(BLI_rctf_size_x(rct) - 18.0f - 12.0f), (short)NODE_DY,
1039 NULL, 0, 0, 0, 0, "");
1042 /* scale widget thing */
1043 UI_ThemeColorShade(color_id, -10);
1045 fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
1046 fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
1048 UI_ThemeColorShade(color_id, +30);
1049 dx -= snode->aspect;
1050 fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
1051 fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
1054 for (sock = node->inputs.first; sock; sock = sock->next) {
1055 if (!nodeSocketIsHidden(sock))
1056 node_socket_circle_draw(C, ntree, node, sock, socket_size, sock->flag & SELECT);
1059 for (sock = node->outputs.first; sock; sock = sock->next) {
1060 if (!nodeSocketIsHidden(sock))
1061 node_socket_circle_draw(C, ntree, node, sock, socket_size, sock->flag & SELECT);
1064 uiEndBlock(C, node->block);
1065 uiDrawBlock(C, node->block);
1069 int node_get_resize_cursor(int directions)
1071 if (directions == 0)
1073 else if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0)
1074 return CURSOR_Y_MOVE;
1075 else if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0)
1076 return CURSOR_X_MOVE;
1081 void node_set_cursor(wmWindow *win, SpaceNode *snode, float cursor[2])
1083 bNodeTree *ntree = snode->edittree;
1086 int wmcursor = CURSOR_STD;
1089 if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN | SOCK_OUT)) {
1093 /* check nodes front to back */
1094 for (node = ntree->nodes.last; node; node = node->prev) {
1095 if (BLI_rctf_isect_pt(&node->totr, cursor[0], cursor[1]))
1096 break; /* first hit on node stops */
1099 int dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]);
1100 wmcursor = node_get_resize_cursor(dir);
1105 WM_cursor_set(win, wmcursor);
1108 void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
1110 if (node->flag & NODE_HIDDEN)
1111 node_draw_hidden(C, ar, snode, ntree, node, key);
1113 node_draw_basis(C, ar, snode, ntree, node, key);
1116 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
1118 if (node->typeinfo->draw_nodetype_prepare)
1119 node->typeinfo->draw_nodetype_prepare(C, ntree, node);
1122 void node_update_nodetree(const bContext *C, bNodeTree *ntree)
1126 /* update nodes front to back, so children sizes get updated before parents */
1127 for (node = ntree->nodes.last; node; node = node->prev) {
1128 node_update(C, ntree, node);
1132 static void node_draw(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
1134 if (node->typeinfo->draw_nodetype)
1135 node->typeinfo->draw_nodetype(C, ar, snode, ntree, node, key);
1138 #define USE_DRAW_TOT_UPDATE
1140 void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNodeInstanceKey parent_key)
1146 if (ntree == NULL) return; /* groups... */
1148 #ifdef USE_DRAW_TOT_UPDATE
1149 if (ntree->nodes.first) {
1150 BLI_rctf_init_minmax(&ar->v2d.tot);
1154 /* draw background nodes, last nodes in front */
1155 for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
1156 bNodeInstanceKey key;
1158 #ifdef USE_DRAW_TOT_UPDATE
1159 /* unrelated to background nodes, update the v2d->tot,
1160 * can be anywhere before we draw the scroll bars */
1161 BLI_rctf_union(&ar->v2d.tot, &node->totr);
1164 if (!(node->flag & NODE_BACKGROUND))
1167 key = BKE_node_instance_key(parent_key, ntree, node);
1168 node->nr = a; /* index of node in list, used for exec event code */
1169 node_draw(C, ar, snode, ntree, node, key);
1174 glEnable(GL_LINE_SMOOTH);
1175 for (link = ntree->links.first; link; link = link->next) {
1176 if (!nodeLinkIsHidden(link))
1177 node_draw_link(&ar->v2d, snode, link);
1179 glDisable(GL_LINE_SMOOTH);
1180 glDisable(GL_BLEND);
1182 /* draw foreground nodes, last nodes in front */
1183 for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
1184 bNodeInstanceKey key;
1185 if (node->flag & NODE_BACKGROUND)
1188 key = BKE_node_instance_key(parent_key, ntree, node);
1189 node->nr = a; /* index of node in list, used for exec event code */
1190 node_draw(C, ar, snode, ntree, node, key);
1194 /* draw tree path info in lower left corner */
1195 static void draw_tree_path(SpaceNode *snode)
1199 ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info));
1201 UI_ThemeColor(TH_TEXT_HI);
1202 BLF_draw_default(1.5f * UI_UNIT_X, 1.5f * UI_UNIT_Y, 0.0f, info, sizeof(info));
1205 static void snode_setup_v2d(SpaceNode *snode, ARegion *ar, const float center[2])
1207 View2D *v2d = &ar->v2d;
1209 /* shift view to node tree center */
1210 UI_view2d_setcenter(v2d, center[0], center[1]);
1211 UI_view2d_view_ortho(v2d);
1213 /* aspect+font, set each time */
1214 snode->aspect = BLI_rctf_size_x(&v2d->cur) / (float)ar->winx;
1215 // XXX snode->curfont = uiSetCurFont_ext(snode->aspect);
1218 static void draw_nodetree(const bContext *C, ARegion *ar, bNodeTree *ntree, bNodeInstanceKey parent_key)
1220 SpaceNode *snode = CTX_wm_space_node(C);
1222 node_uiblocks_init(C, ntree);
1224 #ifdef WITH_COMPOSITOR
1225 if (ntree->type == NTREE_COMPOSIT) {
1226 COM_startReadHighlights();
1230 node_update_nodetree(C, ntree);
1231 node_draw_nodetree(C, ar, snode, ntree, parent_key);
1234 /* shade the parent node group and add a uiBlock to clip mouse events */
1235 static void draw_group_overlay(const bContext *C, ARegion *ar)
1237 View2D *v2d = &ar->v2d;
1238 rctf rect = v2d->cur;
1241 /* shade node groups to separate them visually */
1242 UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70);
1244 uiSetRoundBox(UI_CNR_NONE);
1245 uiDrawBox(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 0);
1246 glDisable(GL_BLEND);
1248 /* set the block bounds to clip mouse events from underlying nodes */
1249 block = uiBeginBlock(C, ar, "node tree bounds block", UI_EMBOSS);
1250 uiExplicitBoundsBlock(block, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
1251 uiBlockSetFlag(block, UI_BLOCK_CLIP_EVENTS);
1252 uiEndBlock(C, block);
1255 void drawnodespace(const bContext *C, ARegion *ar)
1257 wmWindow *win = CTX_wm_window(C);
1258 View2DScrollers *scrollers;
1259 SpaceNode *snode = CTX_wm_space_node(C);
1260 View2D *v2d = &ar->v2d;
1262 UI_ThemeClearColor(TH_BACK);
1263 glClear(GL_COLOR_BUFFER_BIT);
1265 UI_view2d_view_ortho(v2d);
1267 /* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */
1268 UI_view2d_region_to_view(&ar->v2d, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin,
1269 &snode->cursor[0], &snode->cursor[1]);
1270 snode->cursor[0] /= UI_DPI_FAC;
1271 snode->cursor[1] /= UI_DPI_FAC;
1273 ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
1276 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1277 glEnable(GL_MAP1_VERTEX_3);
1280 snode_set_context(C);
1282 /* draw parent node trees */
1283 if (snode->treepath.last) {
1284 static const int max_depth = 2;
1285 bNodeTreePath *path;
1286 int depth, curdepth;
1289 bNodeLinkDrag *nldrag;
1292 path = snode->treepath.last;
1294 /* current View2D center, will be set temporarily for parent node trees */
1295 UI_view2d_getcenter(v2d, ¢er[0], ¢er[1]);
1297 /* store new view center in path and current edittree */
1298 copy_v2_v2(path->view_center, center);
1299 if (snode->edittree)
1300 copy_v2_v2(snode->edittree->view_center, center);
1303 while (path->prev && depth < max_depth) {
1308 /* parent node trees in the background */
1309 for (curdepth = depth; curdepth > 0; path = path->next, --curdepth) {
1310 ntree = path->nodetree;
1312 snode_setup_v2d(snode, ar, path->view_center);
1314 draw_nodetree(C, ar, ntree, path->parent_key);
1316 draw_group_overlay(C, ar);
1320 /* top-level edit tree */
1321 ntree = path->nodetree;
1323 snode_setup_v2d(snode, ar, center);
1325 /* grid, uses theme color based on node path depth */
1326 UI_view2d_multi_grid_draw(v2d, (depth > 0 ? TH_NODE_GROUP : TH_BACK), U.widget_unit, 5, 2);
1329 draw_nodespace_back_pix(C, ar, snode, path->parent_key);
1331 draw_nodetree(C, ar, ntree, path->parent_key);
1334 /* temporary links */
1336 glEnable(GL_LINE_SMOOTH);
1337 for (nldrag = snode->linkdrag.first; nldrag; nldrag = nldrag->next) {
1338 for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next)
1339 node_draw_link(v2d, snode, (bNodeLink *)linkdata->data);
1341 glDisable(GL_LINE_SMOOTH);
1342 glDisable(GL_BLEND);
1344 if (snode->flag & SNODE_SHOW_GPENCIL) {
1345 /* draw grease-pencil ('canvas' strokes) */
1346 draw_gpencil_view2d(C, TRUE);
1351 UI_view2d_multi_grid_draw(v2d, TH_BACK, U.widget_unit, 5, 2);
1354 draw_nodespace_back_pix(C, ar, snode, NODE_INSTANCE_KEY_NONE);
1357 ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
1359 /* reset view matrix */
1360 UI_view2d_view_restore(C);
1362 if (snode->treepath.last) {
1363 if (snode->flag & SNODE_SHOW_GPENCIL) {
1364 /* draw grease-pencil (screen strokes, and also paintbuffer) */
1365 draw_gpencil_view2d(C, FALSE);
1369 /* tree path info */
1370 draw_tree_path(snode);
1373 scrollers = UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1374 UI_view2d_scrollers_draw(C, v2d, scrollers);
1375 UI_view2d_scrollers_free(scrollers);