Made minor revisions (no functional changes).
[blender.git] / source / blender / editors / space_node / node_draw.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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. 
8  *
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.
13  *
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.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  * Contributor(s): Nathan Letwory
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/space_node/node_draw.c
28  *  \ingroup spnode
29  */
30
31
32 #include <math.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_node_types.h"
39 #include "DNA_lamp_types.h"
40 #include "DNA_material_types.h"
41 #include "DNA_object_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_world_types.h"
46
47 #include "BLI_math.h"
48 #include "BLI_blenlib.h"
49 #include "BLI_threads.h"
50 #include "BLI_utildefines.h"
51
52 #include "BKE_context.h"
53 #include "BKE_depsgraph.h"
54 #include "BKE_main.h"
55 #include "BKE_node.h"
56
57 #include "BIF_gl.h"
58 #include "BIF_glutil.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62
63 #include "ED_node.h"
64 #include "ED_gpencil.h"
65
66 #include "UI_interface.h"
67 #include "UI_interface_icons.h"
68 #include "UI_resources.h"
69 #include "UI_view2d.h"
70
71 #include "RNA_access.h"
72
73 #include "NOD_composite.h"
74 #include "NOD_shader.h"
75
76 #include "intern/node_util.h"
77
78 #include "node_intern.h"
79
80 /* width of socket columns in group display */
81 #define NODE_GROUP_FRAME                120
82
83 // XXX interface.h
84 extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
85
86 /* XXX update functions for node editor are a mess, needs a clear concept */
87 void ED_node_tree_update(SpaceNode *snode, Scene *scene)
88 {
89         snode_set_context(snode, scene);
90         
91         if(snode->nodetree && snode->nodetree->id.us==0)
92                 snode->nodetree->id.us= 1;
93 }
94
95 void ED_node_changed_update(ID *id, bNode *node)
96 {
97         bNodeTree *nodetree, *edittree;
98         int treetype;
99
100         node_tree_from_ID(id, &nodetree, &edittree, &treetype);
101
102         if(treetype==NTREE_SHADER) {
103                 DAG_id_tag_update(id, 0);
104
105                 if(GS(id->name) == ID_MA)
106                         WM_main_add_notifier(NC_MATERIAL|ND_SHADING_DRAW, id);
107                 else if(GS(id->name) == ID_LA)
108                         WM_main_add_notifier(NC_LAMP|ND_LIGHTING_DRAW, id);
109                 else if(GS(id->name) == ID_WO)
110                         WM_main_add_notifier(NC_WORLD|ND_WORLD_DRAW, id);
111         }
112         else if(treetype==NTREE_COMPOSIT) {
113                 if(node)
114                         nodeUpdate(edittree, node);
115                 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
116                         
117                 node= node_tree_get_editgroup(nodetree);
118                 if(node)
119                         nodeUpdateID(nodetree, node->id);
120
121                 WM_main_add_notifier(NC_SCENE|ND_NODES, id);
122         }                       
123         else if(treetype==NTREE_TEXTURE) {
124                 DAG_id_tag_update(id, 0);
125                 WM_main_add_notifier(NC_TEXTURE|ND_NODES, id);
126         }
127 }
128
129 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
130 {
131         bNode *node;
132         
133         if(ntree == lookup)
134                 return 1;
135         
136         for(node=ntree->nodes.first; node; node=node->next)
137                 if(node->type == NODE_GROUP && node->id)
138                         if(has_nodetree((bNodeTree*)node->id, lookup))
139                                 return 1;
140         
141         return 0;
142 }
143
144 typedef struct NodeUpdateCalldata {
145         bNodeTree *ntree;
146         bNode *node;
147 } NodeUpdateCalldata;
148 static void node_generic_update_cb(void *calldata, ID *owner_id, bNodeTree *ntree)
149 {
150         NodeUpdateCalldata *cd= (NodeUpdateCalldata*)calldata;
151         /* check if nodetree uses the group stored in calldata */
152         if (has_nodetree(ntree, cd->ntree))
153                 ED_node_changed_update(owner_id, cd->node);
154 }
155 void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node)
156 {
157         bNodeTreeType *tti= ntreeGetType(ntree->type);
158         NodeUpdateCalldata cd;
159         cd.ntree = ntree;
160         cd.node = node;
161         /* look through all datablocks, to support groups */
162         tti->foreach_nodetree(bmain, &cd, node_generic_update_cb);
163         
164         if(ntree->type == NTREE_TEXTURE)
165                 ntreeTexCheckCyclics(ntree);
166 }
167
168 static void do_node_internal_buttons(bContext *C, void *node_v, int event)
169 {
170         if(event==B_NODE_EXEC) {
171                 SpaceNode *snode= CTX_wm_space_node(C);
172                 if(snode && snode->id)
173                         ED_node_changed_update(snode->id, node_v);
174         }
175 }
176
177
178 static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax)
179 {
180         float dx;
181         float dy;
182         
183         dx= 0.5f*(xmax-xmin);
184         dy= 0.5f*(ymax-ymin);
185         
186         UI_ThemeColorShade(color_id, +30);      
187         fdrawline(xmin, ymin, xmax, ymax);
188         fdrawline(xmin+dx, ymin, xmax, ymax-dy);
189         
190         UI_ThemeColorShade(color_id, -10);
191         fdrawline(xmin, ymin+aspect, xmax, ymax+aspect);
192         fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect);
193 }
194
195 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
196 {
197         bNode *node;
198         char str[32];
199         
200         /* add node uiBlocks in reverse order - prevents events going to overlapping nodes */
201         
202         /* process selected nodes first so they're at the start of the uiblocks list */
203         for(node= ntree->nodes.last; node; node= node->prev) {
204                 
205                 if (node->flag & NODE_SELECT) {
206                         /* ui block */
207                         sprintf(str, "node buttons %p", (void *)node);
208                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
209                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
210                 }
211         }
212         
213         /* then the rest */
214         for(node= ntree->nodes.last; node; node= node->prev) {
215                 
216                 if (!(node->flag & (NODE_GROUP_EDIT|NODE_SELECT))) {
217                         /* ui block */
218                         sprintf(str, "node buttons %p", (void *)node);
219                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
220                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
221                 }
222         }
223 }
224
225 /* based on settings in node, sets drawing rect info. each redraw! */
226 static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
227 {
228         uiLayout *layout;
229         PointerRNA ptr;
230         bNodeSocket *nsock;
231         float locx, locy;
232         float dy;
233         int buty;
234         
235         /* get "global" coords */
236         nodeSpaceCoords(node, &locx, &locy);
237         dy= locy;
238         
239         /* header */
240         dy-= NODE_DY;
241         
242         /* little bit space in top */
243         if(node->outputs.first)
244                 dy-= NODE_DYS/2;
245
246         /* output sockets */
247         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
248                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
249                         nsock->locx= locx + node->width;
250                         nsock->locy= dy - NODE_DYS;
251                         dy-= NODE_DY;
252                 }
253         }
254
255         node->prvr.xmin= locx + NODE_DYS;
256         node->prvr.xmax= locx + node->width- NODE_DYS;
257
258         /* preview rect? */
259         if(node->flag & NODE_PREVIEW) {
260                 /* only recalculate size when there's a preview actually, otherwise we use stored result */
261                 BLI_lock_thread(LOCK_PREVIEW);
262
263                 if(node->preview && node->preview->rect) {
264                         float aspect= 1.0f;
265                         
266                         if(node->preview && node->preview->xsize && node->preview->ysize) 
267                                 aspect= (float)node->preview->ysize/(float)node->preview->xsize;
268                         
269                         dy-= NODE_DYS/2;
270                         node->prvr.ymax= dy;
271                         
272                         if(aspect <= 1.0f)
273                                 node->prvr.ymin= dy - aspect*(node->width-NODE_DY);
274                         else {
275                                 float dx= (node->width - NODE_DYS) - (node->width- NODE_DYS)/aspect;    /* width correction of image */
276                                 
277                                 node->prvr.ymin= dy - (node->width-NODE_DY);
278                                 
279                                 node->prvr.xmin+= 0.5f*dx;
280                                 node->prvr.xmax-= 0.5f*dx;
281                         }
282
283                         dy= node->prvr.ymin - NODE_DYS/2;
284
285                         /* make sure that maximums are bigger or equal to minimums */
286                         if(node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
287                         if(node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
288                 }
289                 else {
290                         float oldh= node->prvr.ymax - node->prvr.ymin;
291                         if(oldh==0.0f)
292                                 oldh= 0.6f*node->width-NODE_DY;
293                         dy-= NODE_DYS/2;
294                         node->prvr.ymax= dy;
295                         node->prvr.ymin= dy - oldh;
296                         dy= node->prvr.ymin - NODE_DYS/2;
297                 }
298
299                 BLI_unlock_thread(LOCK_PREVIEW);
300         }
301
302         /* buttons rect? */
303         if((node->flag & NODE_OPTIONS) && node->typeinfo->uifunc) {
304                 dy-= NODE_DYS/2;
305
306                 /* set this for uifunc() that don't use layout engine yet */
307                 node->butr.xmin= 0;
308                 node->butr.xmax= node->width - 2*NODE_DYS;
309                 node->butr.ymin= 0;
310                 node->butr.ymax= 0;
311                 
312                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
313                 
314                 layout= uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
315                                                           locx+NODE_DYS, dy, node->butr.xmax, NODE_DY, UI_GetStyle());
316                 
317                 node->typeinfo->uifunc(layout, (bContext *)C, &ptr);
318                 
319                 uiBlockEndAlign(node->block);
320                 uiBlockLayoutResolve(node->block, NULL, &buty);
321                 
322                 dy= buty - NODE_DYS/2;
323         }
324
325         /* input sockets */
326         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
327                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
328                         nsock->locx= locx;
329                         nsock->locy= dy - NODE_DYS;
330                         dy-= NODE_DY;
331                 }
332         }
333         
334         /* little bit space in end */
335         if(node->inputs.first || (node->flag & (NODE_OPTIONS|NODE_PREVIEW))==0 )
336                 dy-= NODE_DYS/2;
337
338         node->totr.xmin= locx;
339         node->totr.xmax= locx + node->width;
340         node->totr.ymax= locy;
341         node->totr.ymin= MIN2(dy, locy-2*NODE_DY);
342 }
343
344 /* based on settings in node, sets drawing rect info. each redraw! */
345 static void node_update_hidden(bNode *node)
346 {
347         bNodeSocket *nsock;
348         float locx, locy;
349         float rad, drad, hiddenrad= HIDDEN_RAD;
350         int totin=0, totout=0, tot;
351         
352         /* get "global" coords */
353         nodeSpaceCoords(node, &locx, &locy);
354
355         /* calculate minimal radius */
356         for(nsock= node->inputs.first; nsock; nsock= nsock->next)
357                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
358                         totin++;
359         for(nsock= node->outputs.first; nsock; nsock= nsock->next)
360                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
361                         totout++;
362         
363         tot= MAX2(totin, totout);
364         if(tot>4) {
365                 hiddenrad += 5.0f*(float)(tot-4);
366         }
367         
368         node->totr.xmin= locx;
369         node->totr.xmax= locx + 3*hiddenrad + node->miniwidth;
370         node->totr.ymax= locy + (hiddenrad - 0.5f*NODE_DY);
371         node->totr.ymin= node->totr.ymax - 2*hiddenrad;
372         
373         /* output sockets */
374         rad=drad= (float)M_PI/(1.0f + (float)totout);
375         
376         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
377                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
378                         nsock->locx= node->totr.xmax - hiddenrad + (float)sin(rad)*hiddenrad;
379                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
380                         rad+= drad;
381                 }
382         }
383         
384         /* input sockets */
385         rad=drad= - (float)M_PI/(1.0f + (float)totin);
386         
387         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
388                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
389                         nsock->locx= node->totr.xmin + hiddenrad + (float)sin(rad)*hiddenrad;
390                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
391                         rad+= drad;
392                 }
393         }
394 }
395
396 void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
397 {
398         if(node->flag & NODE_HIDDEN)
399                 node_update_hidden(node);
400         else
401                 node_update_basis(C, ntree, node);
402 }
403
404 static int node_get_colorid(bNode *node)
405 {
406         if(node->typeinfo->nclass==NODE_CLASS_INPUT)
407                 return TH_NODE_IN_OUT;
408         if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) {
409                 if(node->flag & NODE_DO_OUTPUT)
410                         return TH_NODE_IN_OUT;
411                 else
412                         return TH_NODE;
413         }
414         if(node->typeinfo->nclass==NODE_CLASS_CONVERTOR)
415                 return TH_NODE_CONVERTOR;
416         if(ELEM3(node->typeinfo->nclass, NODE_CLASS_OP_COLOR, NODE_CLASS_OP_VECTOR, NODE_CLASS_OP_FILTER))
417                 return TH_NODE_OPERATOR;
418         if(node->typeinfo->nclass==NODE_CLASS_GROUP)
419                 return TH_NODE_GROUP;
420         return TH_NODE;
421 }
422
423 /* note: in cmp_util.c is similar code, for node_compo_pass_on()
424  *       the same goes for shader and texture nodes. */
425 /* note: in node_edit.c is similar code, for untangle node */
426 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
427 {
428         ListBase links;
429         LinkInOutsMuteNode *lnk;
430         bNodeLink link= {NULL};
431         int i;
432
433         if(node->typeinfo->mutelinksfunc == NULL)
434                 return;
435
436         /* Get default muting links (as bNodeSocket pointers). */
437         links = node->typeinfo->mutelinksfunc(snode->edittree, node, NULL, NULL, NULL, NULL);
438
439         glEnable(GL_BLEND);
440         glEnable(GL_LINE_SMOOTH);
441
442         link.fromnode = link.tonode = node;
443         for(lnk = links.first; lnk; lnk = lnk->next) {
444                 for(i = 0; i < lnk->num_outs; i++) {
445                         link.fromsock = (bNodeSocket*)(lnk->in);
446                         link.tosock   = (bNodeSocket*)(lnk->outs)+i;
447                         node_draw_link_bezier(v2d, snode, &link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
448                 }
449                 /* If num_outs > 1, lnk->outs was an allocated table of pointers... */
450                 if(i > 1)
451                         MEM_freeN(lnk->outs);
452         }
453
454         glDisable(GL_BLEND);
455         glDisable(GL_LINE_SMOOTH);
456
457         BLI_freelistN(&links);
458 }
459
460 /* this might have some more generic use */
461 static void node_circle_draw(float x, float y, float size, char *col)
462 {
463         /* 16 values of sin function */
464         static float si[16] = {
465                 0.00000000f, 0.39435585f,0.72479278f,0.93775213f,
466                 0.99871650f,0.89780453f,0.65137248f,0.29936312f,
467                 -0.10116832f,-0.48530196f,-0.79077573f,-0.96807711f,
468                 -0.98846832f,-0.84864425f,-0.57126821f,-0.20129852f
469         };
470         /* 16 values of cos function */
471         static float co[16] ={
472                 1.00000000f,0.91895781f,0.68896691f,0.34730525f,
473                 -0.05064916f,-0.44039415f,-0.75875812f,-0.95413925f,
474                 -0.99486932f,-0.87434661f,-0.61210598f,-0.25065253f,
475                 0.15142777f,0.52896401f,0.82076344f,0.97952994f,
476         };
477         int a;
478         
479         glColor3ub(col[0], col[1], col[2]);
480         
481         glBegin(GL_POLYGON);
482         for(a=0; a<16; a++)
483                 glVertex2f(x+size*si[a], y+size*co[a]);
484         glEnd();
485         
486         glColor4ub(0, 0, 0, 150);
487         glEnable(GL_BLEND);
488         glEnable( GL_LINE_SMOOTH );
489         glBegin(GL_LINE_LOOP);
490         for(a=0; a<16; a++)
491                 glVertex2f(x+size*si[a], y+size*co[a]);
492         glEnd();
493         glDisable( GL_LINE_SMOOTH );
494         glDisable(GL_BLEND);
495 }
496
497 void node_socket_circle_draw(bNodeTree *UNUSED(ntree), bNodeSocket *sock, float size)
498 {
499         bNodeSocketType *stype = ntreeGetSocketType(sock->type);
500         node_circle_draw(sock->locx, sock->locy, size, stype->ui_color);
501 }
502
503 /* **************  Socket callbacks *********** */
504
505 /* not a callback */
506 static void node_draw_preview(bNodePreview *preview, rctf *prv)
507 {
508         float xscale= (prv->xmax-prv->xmin)/((float)preview->xsize);
509         float yscale= (prv->ymax-prv->ymin)/((float)preview->ysize);
510         float tile= (prv->xmax - prv->xmin) / 10.0f;
511         float x, y;
512         
513         /* draw checkerboard backdrop to show alpha */
514         glColor3ub(120, 120, 120);
515         glRectf(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
516         glColor3ub(160, 160, 160);
517         
518         for(y=prv->ymin; y<prv->ymax; y+=tile*2) {
519                 for(x=prv->xmin; x<prv->xmax; x+=tile*2) {
520                         float tilex= tile, tiley= tile;
521
522                         if(x+tile > prv->xmax)
523                                 tilex= prv->xmax-x;
524                         if(y+tile > prv->ymax)
525                                 tiley= prv->ymax-y;
526
527                         glRectf(x, y, x + tilex, y + tiley);
528                 }
529         }
530         for(y=prv->ymin+tile; y<prv->ymax; y+=tile*2) {
531                 for(x=prv->xmin+tile; x<prv->xmax; x+=tile*2) {
532                         float tilex= tile, tiley= tile;
533
534                         if(x+tile > prv->xmax)
535                                 tilex= prv->xmax-x;
536                         if(y+tile > prv->ymax)
537                                 tiley= prv->ymax-y;
538
539                         glRectf(x, y, x + tilex, y + tiley);
540                 }
541         }
542         
543         glPixelZoom(xscale, yscale);
544
545         glEnable(GL_BLEND);
546         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );    /* premul graphics */
547         
548         glColor4f(1.0, 1.0, 1.0, 1.0);
549         glaDrawPixelsTex(prv->xmin, prv->ymin, preview->xsize, preview->ysize, GL_UNSIGNED_BYTE, preview->rect);
550         
551         glDisable(GL_BLEND);
552         glPixelZoom(1.0f, 1.0f);
553
554         UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
555         fdrawbox(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
556         
557 }
558
559 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
560 {
561         bNodeSocket *sock;
562         rctf *rct= &node->totr;
563         float iconofs;
564         /* float socket_size= NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
565         float iconbutw= 0.8f*UI_UNIT_X;
566         int color_id= node_get_colorid(node);
567         char showname[128]; /* 128 used below */
568         View2D *v2d = &ar->v2d;
569         
570         /* hurmf... another candidate for callback, have to see how this works first */
571         if(node->id && node->block && snode->treetype==NTREE_SHADER)
572                 nodeShaderSynchronizeID(node, 0);
573         
574         /* skip if out of view */
575         if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
576                         node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
577                 
578                 uiEndBlock(C, node->block);
579                 node->block= NULL;
580                 return;
581         }
582         
583         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT);
584         ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
585         
586         /* header */
587         if(color_id==TH_NODE)
588                 UI_ThemeColorShade(color_id, -20);
589         else
590                 UI_ThemeColor(color_id);
591         
592         if(node->flag & NODE_MUTED)
593                 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
594
595         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
596         uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
597         
598         /* show/hide icons, note this sequence is copied in do_header_node() node_state.c */
599         iconofs= rct->xmax - 7.0f;
600         
601         if(node->typeinfo->flag & NODE_PREVIEW) {
602                 int icon_id;
603                 
604                 if(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT))
605                         icon_id= ICON_MATERIAL;
606                 else
607                         icon_id= ICON_MATERIAL_DATA;
608                 iconofs-=iconbutw;
609                 uiDefIconBut(node->block, LABEL, B_REDR, icon_id, iconofs, rct->ymax-NODE_DY,
610                                          iconbutw, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
611         }
612         if(node->type == NODE_GROUP) {
613                 
614                 iconofs-=iconbutw;
615                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_NODETREE, iconofs, rct->ymax-NODE_DY,
616                                          iconbutw, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
617         }
618         if(node->typeinfo->flag & NODE_OPTIONS) {
619                 iconofs-=iconbutw;
620                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_BUTS, iconofs, rct->ymax-NODE_DY,
621                                          iconbutw, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
622         }
623         {       /* always hide/reveal unused sockets */ 
624                 // XXX re-enable
625                 /* int shade;
626                 if(node_has_hidden_sockets(node))
627                         shade= -40;
628                 else
629                         shade= -90; */
630
631                 iconofs-=iconbutw;
632
633                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_PLUS, iconofs, rct->ymax-NODE_DY,
634                                                   iconbutw, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
635         }
636         
637         /* title */
638         if(node->flag & SELECT) 
639                 UI_ThemeColor(TH_TEXT_HI);
640         else
641                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
642         
643         /* open/close entirely? */
644         UI_DrawTriIcon(rct->xmin+10.0f, rct->ymax-NODE_DY/2.0f, 'v');
645         
646         /* this isn't doing anything for the label, so commenting out
647         if(node->flag & SELECT) 
648                 UI_ThemeColor(TH_TEXT_HI);
649         else
650                 UI_ThemeColor(TH_TEXT); */
651         
652         BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
653         
654         //if(node->flag & NODE_MUTED)
655         //      sprintf(showname, "[%s]", showname);
656         
657         uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY), 
658                          (int)(iconofs - rct->xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
659
660         /* body */
661         UI_ThemeColor4(TH_NODE);
662         glEnable(GL_BLEND);
663         uiSetRoundBox(UI_CNR_BOTTOM_LEFT);
664         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
665         glDisable(GL_BLEND);
666
667         /* scaling indicator */
668         node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
669
670         /* outline active and selected emphasis */
671         if( node->flag & (NODE_ACTIVE|SELECT) ) {
672                 glEnable(GL_BLEND);
673                 glEnable( GL_LINE_SMOOTH );
674                         /* using different shades of TH_TEXT_HI for the empasis, like triangle */
675                         if( node->flag & NODE_ACTIVE ) 
676                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
677                         else
678                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
679                         uiSetRoundBox(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_LEFT); // round all corners except lower right
680                         uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
681                         
682                 glDisable( GL_LINE_SMOOTH );
683                 glDisable(GL_BLEND);
684         }
685         
686         /* disable lines */
687         if(node->flag & NODE_MUTED)
688                 node_draw_mute_line(v2d, snode, node);
689
690         
691         /* socket inputs, buttons */
692         for(sock= node->inputs.first; sock; sock= sock->next) {
693                 bNodeSocketType *stype= ntreeGetSocketType(sock->type);
694                 
695                 if(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
696                         continue;
697                 
698                 node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE);
699                 
700                 if (sock->link || (sock->flag & SOCK_HIDE_VALUE)) {
701                         uiDefBut(node->block, LABEL, 0, sock->name, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY, NODE_DY,
702                                          NULL, 0, 0, 0, 0, "");
703                 }
704                 else {
705                         if (stype->buttonfunc)
706                                 stype->buttonfunc(C, node->block, ntree, node, sock, sock->name, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY);
707                 }
708         }
709         
710         /* socket outputs */
711         for(sock= node->outputs.first; sock; sock= sock->next) {
712                 PointerRNA sockptr;
713                 float slen;
714                 int ofs;
715                 
716                 RNA_pointer_create((ID*)ntree, &RNA_NodeSocket, sock, &sockptr);
717                 
718                 if(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))
719                         continue;
720                 
721                 node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE);
722                 
723                 ofs= 0;
724                 UI_ThemeColor(TH_TEXT);
725                 slen= snode->aspect*UI_GetStringWidth(sock->name);
726                 while(slen > node->width) {
727                         ofs++;
728                         slen= snode->aspect*UI_GetStringWidth(sock->name+ofs);
729                 }
730                 uiDefBut(node->block, LABEL, 0, sock->name+ofs, (short)(sock->locx-15.0f-slen), (short)(sock->locy-9.0f), 
731                                  (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
732         }
733         
734         /* preview */
735         if(node->flag & NODE_PREVIEW) {
736                 BLI_lock_thread(LOCK_PREVIEW);
737                 if(node->preview && node->preview->rect && !BLI_rctf_is_empty(&node->prvr))
738                         node_draw_preview(node->preview, &node->prvr);
739                 BLI_unlock_thread(LOCK_PREVIEW);
740         }
741         
742         UI_ThemeClearColor(color_id);
743                 
744         uiEndBlock(C, node->block);
745         uiDrawBlock(C, node->block);
746         node->block= NULL;
747 }
748
749 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *node)
750 {
751         bNodeSocket *sock;
752         rctf *rct= &node->totr;
753         float dx, centy= 0.5f*(rct->ymax+rct->ymin);
754         float hiddenrad= 0.5f*(rct->ymax-rct->ymin);
755         float socket_size= NODE_SOCKSIZE*U.dpi/72;
756         int color_id= node_get_colorid(node);
757         char showname[128];     /* 128 is used below */
758         
759         /* shadow */
760         uiSetRoundBox(UI_CNR_ALL);
761         ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
762
763         /* body */
764         UI_ThemeColor(color_id);
765         if(node->flag & NODE_MUTED)
766                 UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
767         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
768         
769         /* outline active and selected emphasis */
770         if( node->flag & (NODE_ACTIVE|SELECT) ) {
771                 glEnable(GL_BLEND);
772                 glEnable( GL_LINE_SMOOTH );
773                         /* using different shades of TH_TEXT_HI for the empasis, like triangle */
774                         if( node->flag & NODE_ACTIVE ) 
775                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, 0, -40);
776                         else
777                                 UI_ThemeColorShadeAlpha(TH_TEXT_HI, -20, -120);
778                         uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
779                 glDisable( GL_LINE_SMOOTH );
780                 glDisable(GL_BLEND);
781         }
782         
783         /* title */
784         if(node->flag & SELECT) 
785                 UI_ThemeColor(TH_TEXT_HI);
786         else
787                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
788         
789         /* open entirely icon */
790         UI_DrawTriIcon(rct->xmin+10.0f, centy, 'h');    
791         
792         /* disable lines */
793         if(node->flag & NODE_MUTED)
794                 node_draw_mute_line(&ar->v2d, snode, node);     
795         
796         if(node->flag & SELECT) 
797                 UI_ThemeColor(TH_TEXT_HI);
798         else
799                 UI_ThemeColor(TH_TEXT);
800         
801         if(node->miniwidth>0.0f) {
802                 BLI_strncpy(showname, nodeLabel(node), sizeof(showname));
803                 
804                 //if(node->flag & NODE_MUTED)
805                 //      sprintf(showname, "[%s]", showname);
806
807                 uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(centy-10), 
808                                  (int)(rct->xmax - rct->xmin-18.0f -12.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
809         }       
810
811         /* scale widget thing */
812         UI_ThemeColorShade(color_id, -10);      
813         dx= 10.0f;
814         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
815         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
816         
817         UI_ThemeColorShade(color_id, +30);
818         dx-= snode->aspect;
819         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
820         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
821         
822         /* sockets */
823         for(sock= node->inputs.first; sock; sock= sock->next) {
824                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
825                         node_socket_circle_draw(snode->nodetree, sock, socket_size);
826         }
827         
828         for(sock= node->outputs.first; sock; sock= sock->next) {
829                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
830                         node_socket_circle_draw(snode->nodetree, sock, socket_size);
831         }
832         
833         uiEndBlock(C, node->block);
834         uiDrawBlock(C, node->block);
835         node->block= NULL;
836 }
837
838 void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
839 {
840         if(node->flag & NODE_HIDDEN)
841                 node_draw_hidden(C, ar, snode, node);
842         else
843                 node_draw_basis(C, ar, snode, ntree, node);
844 }
845
846 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
847 {
848         if (node->typeinfo->drawupdatefunc)
849                 node->typeinfo->drawupdatefunc(C, ntree, node);
850 }
851
852 void node_update_nodetree(const bContext *C, bNodeTree *ntree, float offsetx, float offsety)
853 {
854         bNode *node;
855         
856         for(node= ntree->nodes.first; node; node= node->next) {
857                 /* XXX little hack */
858                 node->locx += offsetx;
859                 node->locy += offsety;
860                 
861                 node_update(C, ntree, node);
862                 
863                 node->locx -= offsetx;
864                 node->locy -= offsety;
865         }
866 }
867
868 static void node_draw(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
869 {
870         if (node->typeinfo->drawfunc)
871                 node->typeinfo->drawfunc(C, ar, snode, ntree, node);
872 }
873
874 void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree)
875 {
876         bNode *node;
877         bNodeLink *link;
878         int a;
879         
880         if(ntree==NULL) return;         /* groups... */
881         
882         /* node lines */
883         glEnable(GL_BLEND);
884         glEnable(GL_LINE_SMOOTH);
885         for(link= ntree->links.first; link; link= link->next)
886                 node_draw_link(&ar->v2d, snode, link);
887         glDisable(GL_LINE_SMOOTH);
888         glDisable(GL_BLEND);
889         
890         /* draw nodes, last nodes in front */
891         for(a=0, node= ntree->nodes.first; node; node=node->next, a++) {
892                 node->nr= a;            /* index of node in list, used for exec event code */
893                 node_draw(C, ar, snode, ntree, node);
894         }
895 }
896
897 void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d)
898 {
899         View2DScrollers *scrollers;
900         SpaceNode *snode= CTX_wm_space_node(C);
901         Scene *scene= CTX_data_scene(C);
902         int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
903         bNodeLinkDrag *nldrag;
904         
905         UI_ThemeClearColor(TH_BACK);
906         glClear(GL_COLOR_BUFFER_BIT);
907
908         UI_view2d_view_ortho(v2d);
909         
910         //uiFreeBlocksWin(&sa->uiblocks, sa->win);
911
912         /* only set once */
913         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
914         glEnable(GL_MAP1_VERTEX_3);
915
916         /* aspect+font, set each time */
917         snode->aspect= (v2d->cur.xmax - v2d->cur.xmin)/((float)ar->winx);
918         // XXX snode->curfont= uiSetCurFont_ext(snode->aspect);
919
920         UI_view2d_constant_grid_draw(v2d);
921         /* backdrop */
922         draw_nodespace_back_pix(ar, snode, color_manage);
923         
924         /* nodes */
925         snode_set_context(snode, CTX_data_scene(C));
926         
927         if(snode->nodetree) {
928                 bNode *node;
929                 
930                 /* init ui blocks for opened node group trees first 
931                  * so they're in the correct depth stack order */
932                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
933                         if(node->flag & NODE_GROUP_EDIT)
934                                 node_uiblocks_init(C, (bNodeTree *)node->id);
935                 }
936                 
937                 node_uiblocks_init(C, snode->nodetree);
938                 
939                 node_update_nodetree(C, snode->nodetree, 0.0f, 0.0f);
940                 node_draw_nodetree(C, ar, snode, snode->nodetree);
941                 
942                 #if 0
943                 /* active group */
944                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
945                         if(node->flag & NODE_GROUP_EDIT)
946                                 node_draw_group(C, ar, snode, snode->nodetree, node);
947                 }
948                 #endif
949         }
950         
951         /* temporary links */
952         glEnable(GL_BLEND);
953         glEnable(GL_LINE_SMOOTH);
954         for(nldrag= snode->linkdrag.first; nldrag; nldrag= nldrag->next)
955                 node_draw_link(&ar->v2d, snode, nldrag->link);
956         glDisable(GL_LINE_SMOOTH);
957         glDisable(GL_BLEND);
958         
959         /* draw grease-pencil ('canvas' strokes) */
960         if (/*(snode->flag & SNODE_DISPGP) &&*/ (snode->nodetree))
961                 draw_gpencil_view2d((bContext*)C, 1);
962         
963         /* reset view matrix */
964         UI_view2d_view_restore(C);
965         
966         /* draw grease-pencil (screen strokes, and also paintbuffer) */
967         if (/*(snode->flag & SNODE_DISPGP) && */(snode->nodetree))
968                 draw_gpencil_view2d((bContext*)C, 0);
969         
970         /* scrollers */
971         scrollers= UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
972         UI_view2d_scrollers_draw(C, v2d, scrollers);
973         UI_view2d_scrollers_free(scrollers);
974 }