4b703e60358e253934fa9bba6616618098f628c6
[blender.git] / source / blender / editors / space_node / node_draw.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  * Contributor(s): Nathan Letwory
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <math.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 #include "DNA_node_types.h"
34 #include "DNA_material_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_screen_types.h"
39
40 #include "BLI_math.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_threads.h"
43
44 #include "BKE_context.h"
45 #include "BKE_depsgraph.h"
46 #include "BKE_main.h"
47
48 #include "BIF_gl.h"
49 #include "BIF_glutil.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "ED_gpencil.h"
55
56 #include "UI_interface.h"
57 #include "UI_interface_icons.h"
58 #include "UI_resources.h"
59 #include "UI_view2d.h"
60
61 #include "RNA_access.h"
62
63 #include "CMP_node.h"
64 #include "SHD_node.h"
65
66 #include "node_intern.h"
67
68 // XXX interface.h
69 extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select);
70 extern void ui_draw_tria_icon(float x, float y, char dir);
71
72 void ED_node_changed_update(ID *id, bNode *node)
73 {
74         bNodeTree *nodetree, *edittree;
75         int treetype;
76
77         node_tree_from_ID(id, &nodetree, &edittree, &treetype);
78
79         if(treetype==NTREE_SHADER) {
80                 DAG_id_tag_update(id, 0);
81                 WM_main_add_notifier(NC_MATERIAL|ND_SHADING_DRAW, id);
82         }
83         else if(treetype==NTREE_COMPOSIT) {
84                 NodeTagChanged(edittree, node);
85                 /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */
86                         
87                 node= node_tree_get_editgroup(nodetree);
88                 if(node)
89                         NodeTagIDChanged(nodetree, node->id);
90
91                 WM_main_add_notifier(NC_SCENE|ND_NODES, id);
92         }                       
93         else if(treetype==NTREE_TEXTURE) {
94                 DAG_id_tag_update(id, 0);
95                 WM_main_add_notifier(NC_TEXTURE|ND_NODES, id);
96         }
97 }
98
99 static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup)
100 {
101         bNode *node;
102         
103         if(ntree == lookup)
104                 return 1;
105         
106         for(node=ntree->nodes.first; node; node=node->next)
107                 if(node->type == NODE_GROUP && node->id)
108                         if(has_nodetree((bNodeTree*)node->id, lookup))
109                                 return 1;
110         
111         return 0;
112 }
113
114 void ED_node_generic_update(Main *bmain, bNodeTree *ntree, bNode *node)
115 {
116         Material *ma;
117         Tex *tex;
118         Scene *sce;
119         
120         /* look through all datablocks, to support groups */
121         for(ma=bmain->mat.first; ma; ma=ma->id.next)
122                 if(ma->nodetree && ma->use_nodes && has_nodetree(ma->nodetree, ntree))
123                         ED_node_changed_update(&ma->id, node);
124         
125         for(tex=bmain->tex.first; tex; tex=tex->id.next)
126                 if(tex->nodetree && tex->use_nodes && has_nodetree(tex->nodetree, ntree))
127                         ED_node_changed_update(&tex->id, node);
128         
129         for(sce=bmain->scene.first; sce; sce=sce->id.next)
130                 if(sce->nodetree && sce->use_nodes && has_nodetree(sce->nodetree, ntree))
131                         ED_node_changed_update(&sce->id, node);
132         
133         if(ntree->type == NTREE_TEXTURE)
134                 ntreeTexCheckCyclics(ntree);
135 }
136
137 static void do_node_internal_buttons(bContext *C, void *node_v, int event)
138 {
139         if(event==B_NODE_EXEC) {
140                 SpaceNode *snode= CTX_wm_space_node(C);
141                 if(snode && snode->id)
142                         ED_node_changed_update(snode->id, node_v);
143         }
144 }
145
146
147 static void node_scaling_widget(int color_id, float aspect, float xmin, float ymin, float xmax, float ymax)
148 {
149         float dx;
150         float dy;
151         
152         dx= 0.5f*(xmax-xmin);
153         dy= 0.5f*(ymax-ymin);
154         
155         UI_ThemeColorShade(color_id, +30);      
156         fdrawline(xmin, ymin, xmax, ymax);
157         fdrawline(xmin+dx, ymin, xmax, ymax-dy);
158         
159         UI_ThemeColorShade(color_id, -10);
160         fdrawline(xmin, ymin+aspect, xmax, ymax+aspect);
161         fdrawline(xmin+dx, ymin+aspect, xmax, ymax-dy+aspect);
162 }
163
164 static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
165 {
166         bNode *node;
167         char str[32];
168         
169         /* add node uiBlocks in reverse order - prevents events going to overlapping nodes */
170         
171         /* process selected nodes first so they're at the start of the uiblocks list */
172         for(node= ntree->nodes.last; node; node= node->prev) {
173                 
174                 if (node->flag & NODE_SELECT) {
175                         /* ui block */
176                         sprintf(str, "node buttons %p", (void *)node);
177                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
178                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
179                 }
180         }
181         
182         /* then the rest */
183         for(node= ntree->nodes.last; node; node= node->prev) {
184                 
185                 if (!(node->flag & (NODE_GROUP_EDIT|NODE_SELECT))) {
186                         /* ui block */
187                         sprintf(str, "node buttons %p", (void *)node);
188                         node->block= uiBeginBlock(C, CTX_wm_region(C), str, UI_EMBOSS);
189                         uiBlockSetHandleFunc(node->block, do_node_internal_buttons, node);
190                 }
191         }
192 }
193
194 /* based on settings in node, sets drawing rect info. each redraw! */
195 static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
196 {
197         uiLayout *layout;
198         PointerRNA ptr;
199         bNodeSocket *nsock;
200         float dy= node->locy;
201         int buty;
202         
203         /* header */
204         dy-= NODE_DY;
205         
206         /* little bit space in top */
207         if(node->outputs.first)
208                 dy-= NODE_DYS/2;
209
210         /* output sockets */
211         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
212                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
213                         nsock->locx= node->locx + node->width;
214                         nsock->locy= dy - NODE_DYS;
215                         dy-= NODE_DY;
216                 }
217         }
218
219         node->prvr.xmin= node->locx + NODE_DYS;
220         node->prvr.xmax= node->locx + node->width- NODE_DYS;
221
222         /* preview rect? */
223         if(node->flag & NODE_PREVIEW) {
224                 /* only recalculate size when there's a preview actually, otherwise we use stored result */
225                 BLI_lock_thread(LOCK_PREVIEW);
226
227                 if(node->preview && node->preview->rect) {
228                         float aspect= 1.0f;
229                         
230                         if(node->preview && node->preview->xsize && node->preview->ysize) 
231                                 aspect= (float)node->preview->ysize/(float)node->preview->xsize;
232                         
233                         dy-= NODE_DYS/2;
234                         node->prvr.ymax= dy;
235                         
236                         if(aspect <= 1.0f)
237                                 node->prvr.ymin= dy - aspect*(node->width-NODE_DY);
238                         else {
239                                 float dx= (node->width - NODE_DYS) - (node->width- NODE_DYS)/aspect;    /* width correction of image */
240                                 
241                                 node->prvr.ymin= dy - (node->width-NODE_DY);
242                                 
243                                 node->prvr.xmin+= 0.5f*dx;
244                                 node->prvr.xmax-= 0.5f*dx;
245                         }
246
247                         dy= node->prvr.ymin - NODE_DYS/2;
248
249                         /* make sure that maximums are bigger or equal to minimums */
250                         if(node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
251                         if(node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
252                 }
253                 else {
254                         float oldh= node->prvr.ymax - node->prvr.ymin;
255                         if(oldh==0.0f)
256                                 oldh= 0.6f*node->width-NODE_DY;
257                         dy-= NODE_DYS/2;
258                         node->prvr.ymax= dy;
259                         node->prvr.ymin= dy - oldh;
260                         dy= node->prvr.ymin - NODE_DYS/2;
261                 }
262
263                 BLI_unlock_thread(LOCK_PREVIEW);
264         }
265
266         /* XXX ugly hack, typeinfo for group is generated */
267         if(node->type == NODE_GROUP)
268                 node->typeinfo->uifunc= node_buts_group;
269         
270         /* buttons rect? */
271         if((node->flag & NODE_OPTIONS) && node->typeinfo->uifunc) {
272                 dy-= NODE_DYS/2;
273
274                 /* set this for uifunc() that don't use layout engine yet */
275                 node->butr.xmin= 0;
276                 node->butr.xmax= node->width - 2*NODE_DYS;
277                 node->butr.ymin= 0;
278                 node->butr.ymax= 0;
279
280                 RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
281
282                 layout= uiBlockLayout(node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
283                         node->locx+NODE_DYS, dy, node->butr.xmax, 20, U.uistyles.first);
284
285                 node->typeinfo->uifunc(layout, (bContext *)C, &ptr);
286                 uiBlockEndAlign(node->block);
287                 uiBlockLayoutResolve(node->block, NULL, &buty);
288
289                 dy= buty - NODE_DYS/2;
290         }
291
292         /* input sockets */
293         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
294                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
295                         nsock->locx= node->locx;
296                         nsock->locy= dy - NODE_DYS;
297                         dy-= NODE_DY;
298                 }
299         }
300         
301         /* little bit space in end */
302         if(node->inputs.first || (node->flag & (NODE_OPTIONS|NODE_PREVIEW))==0 )
303                 dy-= NODE_DYS/2;
304
305         node->totr.xmin= node->locx;
306         node->totr.xmax= node->locx + node->width;
307         node->totr.ymax= node->locy;
308         node->totr.ymin= dy;
309 }
310
311 /* based on settings in node, sets drawing rect info. each redraw! */
312 static void node_update_hidden(bNode *node)
313 {
314         bNodeSocket *nsock;
315         float rad, drad, hiddenrad= HIDDEN_RAD;
316         int totin=0, totout=0, tot;
317         
318         /* calculate minimal radius */
319         for(nsock= node->inputs.first; nsock; nsock= nsock->next)
320                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
321                         totin++;
322         for(nsock= node->outputs.first; nsock; nsock= nsock->next)
323                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
324                         totout++;
325         
326         tot= MAX2(totin, totout);
327         if(tot>4) {
328                 hiddenrad += 5.0f*(float)(tot-4);
329         }
330         
331         node->totr.xmin= node->locx;
332         node->totr.xmax= node->locx + 3*hiddenrad + node->miniwidth;
333         node->totr.ymax= node->locy + (hiddenrad - 0.5f*NODE_DY);
334         node->totr.ymin= node->totr.ymax - 2*hiddenrad;
335         
336         /* output sockets */
337         rad=drad= (float)M_PI/(1.0f + (float)totout);
338         
339         for(nsock= node->outputs.first; nsock; nsock= nsock->next) {
340                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
341                         nsock->locx= node->totr.xmax - hiddenrad + (float)sin(rad)*hiddenrad;
342                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
343                         rad+= drad;
344                 }
345         }
346         
347         /* input sockets */
348         rad=drad= - (float)M_PI/(1.0f + (float)totin);
349         
350         for(nsock= node->inputs.first; nsock; nsock= nsock->next) {
351                 if(!(nsock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
352                         nsock->locx= node->totr.xmin + hiddenrad + (float)sin(rad)*hiddenrad;
353                         nsock->locy= node->totr.ymin + hiddenrad + (float)cos(rad)*hiddenrad;
354                         rad+= drad;
355                 }
356         }
357 }
358
359 static int node_get_colorid(bNode *node)
360 {
361         if(node->typeinfo->nclass==NODE_CLASS_INPUT)
362                 return TH_NODE_IN_OUT;
363         if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) {
364                 if(node->flag & NODE_DO_OUTPUT)
365                         return TH_NODE_IN_OUT;
366                 else
367                         return TH_NODE;
368         }
369         if(node->typeinfo->nclass==NODE_CLASS_CONVERTOR)
370                 return TH_NODE_CONVERTOR;
371         if(ELEM3(node->typeinfo->nclass, NODE_CLASS_OP_COLOR, NODE_CLASS_OP_VECTOR, NODE_CLASS_OP_FILTER))
372                 return TH_NODE_OPERATOR;
373         if(node->typeinfo->nclass==NODE_CLASS_GROUP)
374                 return TH_NODE_GROUP;
375         return TH_NODE;
376 }
377
378 /* based on settings in node, sets drawing rect info. each redraw! */
379 /* note: this assumes only 1 group at a time is drawn (linked data) */
380 /* in node->totr the entire boundbox for the group is stored */
381 static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode)
382 {
383         bNodeTree *ngroup= (bNodeTree *)gnode->id;
384         bNode *node;
385         bNodeSocket *nsock;
386         rctf *rect= &gnode->totr;
387         int counter;
388         
389         /* center them, is a bit of abuse of locx and locy though */
390         for(node= ngroup->nodes.first; node; node= node->next) {
391                 node->locx+= gnode->locx;
392                 node->locy+= gnode->locy;
393                 
394                 if(node->flag & NODE_HIDDEN)
395                         node_update_hidden(node);
396                 else
397                         node_update(C, ntree, node);
398                 node->locx-= gnode->locx;
399                 node->locy-= gnode->locy;
400         }
401         counter= 1;
402         for(node= ngroup->nodes.first; node; node= node->next) {
403                 if(counter) {
404                         *rect= node->totr;
405                         counter= 0;
406                 }
407                 else
408                         BLI_union_rctf(rect, &node->totr);
409         }
410         if(counter==1) return;  /* should be prevented? */
411         
412         rect->xmin-= NODE_DY;
413         rect->ymin-= NODE_DY;
414         rect->xmax+= NODE_DY;
415         rect->ymax+= NODE_DY;
416         
417         /* output sockets */
418         for(nsock= gnode->outputs.first; nsock; nsock= nsock->next) {
419                 nsock->locx= rect->xmax;
420                 nsock->locy= nsock->tosock->locy;
421         }
422         
423         /* input sockets */
424         for(nsock= gnode->inputs.first; nsock; nsock= nsock->next) {
425                 nsock->locx= rect->xmin;
426                 nsock->locy= nsock->tosock->locy;
427         }
428 }
429
430 /* note: in cmp_util.c is similar code, for node_compo_pass_on() */
431 static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
432 {
433         bNodeSocket *valsock= NULL, *colsock= NULL, *vecsock= NULL;
434         bNodeSocket *sock;
435         bNodeLink link= {0};
436         int a;
437         
438         /* connect the first value buffer in with first value out */
439         /* connect the first RGBA buffer in with first RGBA out */
440         
441         /* test the inputs */
442         for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) {
443                 if(nodeCountSocketLinks(snode->edittree, sock)) {
444                         if(sock->type==SOCK_VALUE && valsock==NULL) valsock= sock;
445                         if(sock->type==SOCK_VECTOR && vecsock==NULL) vecsock= sock;
446                         if(sock->type==SOCK_RGBA && colsock==NULL) colsock= sock;
447                 }
448         }
449         
450         /* outputs, draw lines */
451         UI_ThemeColor(TH_REDALERT);
452         glEnable(GL_BLEND);
453         glEnable( GL_LINE_SMOOTH );
454         
455         if(valsock || colsock || vecsock) {
456                 for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) {
457                         if(nodeCountSocketLinks(snode->edittree, sock)) {
458                                 link.tosock= sock;
459                                 
460                                 if(sock->type==SOCK_VALUE && valsock) {
461                                         link.fromsock= valsock;
462                                         node_draw_link_bezier(v2d, snode, &link, TH_WIRE, TH_WIRE, 0);
463                                         valsock= NULL;
464                                 }
465                                 if(sock->type==SOCK_VECTOR && vecsock) {
466                                         link.fromsock= vecsock;
467                                         node_draw_link_bezier(v2d, snode, &link, TH_WIRE, TH_WIRE, 0);
468                                         vecsock= NULL;
469                                 }
470                                 if(sock->type==SOCK_RGBA && colsock) {
471                                         link.fromsock= colsock;
472                                         node_draw_link_bezier(v2d, snode, &link, TH_WIRE, TH_WIRE, 0);
473                                         colsock= NULL;
474                                 }
475                         }
476                 }
477         }
478         glDisable(GL_BLEND);
479         glDisable( GL_LINE_SMOOTH );
480 }
481
482 /* nice AA filled circle */
483 /* this might have some more generic use */
484 static void circle_draw(float x, float y, float size, int col[3])
485 {
486         /* 16 values of sin function */
487         static float si[16] = {
488                 0.00000000f, 0.39435585f,0.72479278f,0.93775213f,
489                 0.99871650f,0.89780453f,0.65137248f,0.29936312f,
490                 -0.10116832f,-0.48530196f,-0.79077573f,-0.96807711f,
491                 -0.98846832f,-0.84864425f,-0.57126821f,-0.20129852f
492         };
493         /* 16 values of cos function */
494         static float co[16] ={
495                 1.00000000f,0.91895781f,0.68896691f,0.34730525f,
496                 -0.05064916f,-0.44039415f,-0.75875812f,-0.95413925f,
497                 -0.99486932f,-0.87434661f,-0.61210598f,-0.25065253f,
498                 0.15142777f,0.52896401f,0.82076344f,0.97952994f,
499         };
500         int a;
501         
502         glColor3ub(col[0], col[1], col[2]);
503         
504         glBegin(GL_POLYGON);
505         for(a=0; a<16; a++)
506                 glVertex2f(x+size*si[a], y+size*co[a]);
507         glEnd();
508         
509         glColor4ub(0, 0, 0, 150);
510         glEnable(GL_BLEND);
511         glEnable( GL_LINE_SMOOTH );
512         glBegin(GL_LINE_LOOP);
513         for(a=0; a<16; a++)
514                 glVertex2f(x+size*si[a], y+size*co[a]);
515         glEnd();
516         glDisable( GL_LINE_SMOOTH );
517         glDisable(GL_BLEND);
518 }
519
520 static void socket_circle_draw(bNodeSocket *sock, float size)
521 {
522         int col[3];
523         
524         if(sock->type==-1) {
525                 col[0]= 0; col[1]= 0; col[2]= 0;
526         }
527         else if(sock->type==SOCK_VALUE) {
528                 col[0]= 160; col[1]= 160; col[2]= 160;
529         }
530         else if(sock->type==SOCK_VECTOR) {
531                 col[0]= 100; col[1]= 100; col[2]= 200;
532         }
533         else if(sock->type==SOCK_RGBA) {
534                 col[0]= 200; col[1]= 200; col[2]= 40;
535         }
536         else { 
537                 col[0]= 100; col[1]= 200; col[2]= 100;
538         }
539
540         circle_draw(sock->locx, sock->locy, size, col);
541 }
542
543 static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v)
544 {
545         SpaceNode *snode= snode_v;
546         
547         if(snode->treetype==NTREE_SHADER) {
548                 nodeShaderSynchronizeID(node_v, 1);
549                 // allqueue(REDRAWBUTSSHADING, 0);
550         }
551 }
552
553 /* **************  Socket callbacks *********** */
554
555 /* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */
556 static uiBlock *socket_vector_menu(bContext *C, ARegion *ar, void *socket_v)
557 {
558         bNodeSocket *sock= socket_v;
559         uiBlock *block;
560         
561         SpaceNode *snode= CTX_wm_space_node(C);
562         bNodeTree *ntree = snode->nodetree;
563         PointerRNA ptr;
564         uiLayout *layout;
565         
566         RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
567         
568         block= uiBeginBlock(C, ar, "socket menu", UI_EMBOSS);
569         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN);
570         
571         layout= uiLayoutColumn(uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, sock->locx, sock->locy-8, 140, 20, U.uistyles.first), 0);
572         
573         uiItemR(layout, &ptr, "default_value", UI_ITEM_R_EXPAND, "", 0);
574         
575         return block;
576 }
577
578 /* not a callback */
579 static void node_draw_preview(bNodePreview *preview, rctf *prv)
580 {
581         float xscale= (prv->xmax-prv->xmin)/((float)preview->xsize);
582         float yscale= (prv->ymax-prv->ymin)/((float)preview->ysize);
583         float tile= (prv->xmax - prv->xmin) / 10.0f;
584         float x, y;
585         
586         /* draw checkerboard backdrop to show alpha */
587         glColor3ub(120, 120, 120);
588         glRectf(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
589         glColor3ub(160, 160, 160);
590         
591         for(y=prv->ymin; y<prv->ymax; y+=tile*2) {
592                 for(x=prv->xmin; x<prv->xmax; x+=tile*2) {
593                         float tilex= tile, tiley= tile;
594
595                         if(x+tile > prv->xmax)
596                                 tilex= prv->xmax-x;
597                         if(y+tile > prv->ymax)
598                                 tiley= prv->ymax-y;
599
600                         glRectf(x, y, x + tilex, y + tiley);
601                 }
602         }
603         for(y=prv->ymin+tile; y<prv->ymax; y+=tile*2) {
604                 for(x=prv->xmin+tile; x<prv->xmax; x+=tile*2) {
605                         float tilex= tile, tiley= tile;
606
607                         if(x+tile > prv->xmax)
608                                 tilex= prv->xmax-x;
609                         if(y+tile > prv->ymax)
610                                 tiley= prv->ymax-y;
611
612                         glRectf(x, y, x + tilex, y + tiley);
613                 }
614         }
615         
616         glPixelZoom(xscale, yscale);
617
618         glEnable(GL_BLEND);
619         glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );  /* premul graphics */
620         
621         glColor4f(1.0, 1.0, 1.0, 1.0);
622         glaDrawPixelsTex(prv->xmin, prv->ymin, preview->xsize, preview->ysize, GL_UNSIGNED_BYTE, preview->rect);
623         
624         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
625         glDisable(GL_BLEND);
626         glPixelZoom(1.0f, 1.0f);
627
628         UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
629         fdrawbox(prv->xmin, prv->ymin, prv->xmax, prv->ymax);
630         
631 }
632
633 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node)
634 {
635         bNodeSocket *sock;
636         uiBut *bt;
637         rctf *rct= &node->totr;
638         float /*slen,*/ iconofs;
639         int /*ofs,*/ color_id= node_get_colorid(node);
640         char showname[128]; /* 128 used below */
641         View2D *v2d = &ar->v2d;
642         PointerRNA ptr;
643         
644         /* hurmf... another candidate for callback, have to see how this works first */
645         if(node->id && node->block && snode->treetype==NTREE_SHADER)
646                 nodeShaderSynchronizeID(node, 0);
647         
648         /* skip if out of view */
649         if (node->totr.xmax < ar->v2d.cur.xmin || node->totr.xmin > ar->v2d.cur.xmax ||
650                         node->totr.ymax < ar->v2d.cur.ymin || node->totr.ymin > ar->v2d.cur.ymax) {
651                 
652                 uiEndBlock(C, node->block);
653                 node->block= NULL;
654                 return;
655         }
656         
657         uiSetRoundBox(15-4);
658         ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
659         
660         /* header */
661         if(color_id==TH_NODE)
662                 UI_ThemeColorShade(color_id, -20);
663         else
664                 UI_ThemeColor(color_id);
665                 
666         uiSetRoundBox(3);
667         uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
668         
669         /* show/hide icons, note this sequence is copied in editnode.c */
670         iconofs= rct->xmax;
671         
672         if(node->typeinfo->flag & NODE_PREVIEW) {
673                 int icon_id;
674                 
675                 if(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT))
676                         icon_id= ICON_MATERIAL;
677                 else
678                         icon_id= ICON_MATERIAL_DATA;
679                 iconofs-=22.0f;
680                 uiDefIconBut(node->block, LABEL, B_REDR, icon_id, iconofs, rct->ymax-NODE_DY,
681                                          UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
682         }
683         if(node->type == NODE_GROUP) {
684                 
685                 iconofs-=15.0f;
686                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_NODETREE, iconofs, rct->ymax-NODE_DY,
687                                          UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
688         }
689         if(node->typeinfo->flag & NODE_OPTIONS) {
690                 iconofs-=15.0f;
691                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_BUTS, iconofs, rct->ymax-NODE_DY,
692                                          UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
693         }
694         {       /* always hide/reveal unused sockets */ 
695                 int shade;
696
697                 iconofs-=15.0f;
698                 // XXX re-enable
699                 /*if(node_has_hidden_sockets(node))
700                         shade= -40;
701                 else*/
702                         shade= -90;
703                 uiDefIconBut(node->block, LABEL, B_REDR, ICON_PLUS, iconofs, rct->ymax-NODE_DY,
704                                                   UI_UNIT_X, UI_UNIT_Y, NULL, 0.0, 0.0, 1.0, 0.5, "");
705         }
706         
707         /* title */
708         if(node->flag & SELECT) 
709                 UI_ThemeColor(TH_TEXT_HI);
710         else
711                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
712         
713         /* open/close entirely? */
714         ui_draw_tria_icon(rct->xmin+8.0f, rct->ymax-NODE_DY+4.0f, 'v');
715         
716         if(node->flag & SELECT) 
717                 UI_ThemeColor(TH_TEXT_HI);
718         else
719                 UI_ThemeColor(TH_TEXT);
720         
721         if(node->flag & NODE_CUSTOM_NAME)
722                 BLI_strncpy(showname, node->name, sizeof(showname));
723         else
724                 /* todo: auto name display for node types */
725                 BLI_strncpy(showname, node->name, sizeof(showname));
726
727         //if(node->flag & NODE_MUTED)
728         //      sprintf(showname, "[%s]", showname);
729         
730         uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY), 
731                          (int)(iconofs - rct->xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
732
733         /* body */
734         UI_ThemeColor4(TH_NODE);
735         glEnable(GL_BLEND);
736         uiSetRoundBox(8);
737         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
738         glDisable(GL_BLEND);
739
740         /* scaling indicator */
741         node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
742
743         /* outline active emphasis */
744         if(node->flag & NODE_ACTIVE) {
745                 glEnable(GL_BLEND);
746                 glColor4ub(200, 200, 200, 140);
747                 uiSetRoundBox(15-4);
748                 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
749                 glDisable(GL_BLEND);
750         }
751         
752         /* disable lines */
753         if(node->flag & NODE_MUTED)
754                 node_draw_mute_line(v2d, snode, node);
755
756         
757         /* socket inputs, buttons */
758         for(sock= node->inputs.first; sock; sock= sock->next) {
759                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
760                         socket_circle_draw(sock, NODE_SOCKSIZE);
761                         
762                         RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
763                         
764                         if(node->block && sock->link==NULL) {
765                         
766                                 if(sock->type==SOCK_VALUE) {
767                                         bt=uiDefButR(node->block, NUM, B_NODE_EXEC, sock->name,
768                                                          (short)sock->locx+NODE_DYS, (short)(sock->locy)-9, (short)node->width-NODE_DY, 17, 
769                                                           &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL);
770                                         uiButSetFunc(bt, node_sync_cb, snode, node);
771                                 }
772                                 else if(sock->type==SOCK_VECTOR) {
773                                         uiDefBlockBut(node->block, socket_vector_menu, sock, sock->name, 
774                                                   (short)sock->locx+NODE_DYS, (short)sock->locy-9, (short)node->width-NODE_DY, 17, 
775                                                   "");
776                                 }
777                                 else if(node->block && sock->type==SOCK_RGBA) {
778                                         short labelw= (short)node->width-NODE_DY-40, width;
779                                         
780                                         if(labelw>0) width= 40; else width= (short)node->width-NODE_DY;
781                                         
782                                         bt=uiDefButR(node->block, COL, B_NODE_EXEC, "",
783                                                                  (short)sock->locx+NODE_DYS, (short)(sock->locy)-8, width, 15, 
784                                                                  &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL);
785                                         uiButSetFunc(bt, node_sync_cb, snode, node);
786                                         
787                                         if(labelw>0) uiDefBut(node->block, LABEL, 0, sock->name, 
788                                                                                    (short)(sock->locx+NODE_DYS) + 40, (short)sock->locy-8, labelw, 15, 
789                                                                                    NULL, 0, 0, 0, 0, "");
790                                 }
791                         }
792                         else {
793                                 
794                                 uiDefBut(node->block, LABEL, 0, sock->name, (short)(sock->locx+7), (short)(sock->locy-9.0f), 
795                                                  (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
796                         }
797                 }
798         }
799         
800         /* socket outputs */
801         for(sock= node->outputs.first; sock; sock= sock->next) {
802                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
803                         float slen;
804                         int ofs= 0;
805                         
806                         socket_circle_draw(sock, NODE_SOCKSIZE);
807                         
808                         UI_ThemeColor(TH_TEXT);
809                         slen= snode->aspect*UI_GetStringWidth(sock->name);
810                         while(slen > node->width) {
811                                 ofs++;
812                                 slen= snode->aspect*UI_GetStringWidth(sock->name+ofs);
813                         }
814                         
815                         uiDefBut(node->block, LABEL, 0, sock->name+ofs, (short)(sock->locx-15.0f-slen), (short)(sock->locy-9.0f), 
816                                          (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
817                 }
818         }
819         
820         /* preview */
821         if(node->flag & NODE_PREVIEW) {
822                 BLI_lock_thread(LOCK_PREVIEW);
823                 if(node->preview && node->preview->rect && !BLI_rctf_is_empty(&node->prvr))
824                         node_draw_preview(node->preview, &node->prvr);
825                 BLI_unlock_thread(LOCK_PREVIEW);
826         }
827         
828         UI_ThemeClearColor(color_id);
829                 
830         uiEndBlock(C, node->block);
831         uiDrawBlock(C, node->block);
832         node->block= NULL;
833 }
834
835 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *node)
836 {
837         bNodeSocket *sock;
838         rctf *rct= &node->totr;
839         float dx, centy= 0.5f*(rct->ymax+rct->ymin);
840         float hiddenrad= 0.5f*(rct->ymax-rct->ymin);
841         int color_id= node_get_colorid(node);
842         char showname[128];     /* 128 is used below */
843         
844         /* shadow */
845         uiSetRoundBox(15);
846         ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
847
848         /* body */
849         UI_ThemeColor(color_id);        
850         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
851         
852         /* outline active emphasis */
853         if(node->flag & NODE_ACTIVE) {
854                 glEnable(GL_BLEND);
855                 glColor4ub(200, 200, 200, 140);
856                 uiDrawBox(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
857                 glDisable(GL_BLEND);
858         }
859         
860         /* title */
861         if(node->flag & SELECT) 
862                 UI_ThemeColor(TH_TEXT_HI);
863         else
864                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
865         
866         /* open entirely icon */
867         ui_draw_tria_icon(rct->xmin+9.0f, centy-6.0f, 'h');     
868         
869         /* disable lines */
870         if(node->flag & NODE_MUTED)
871                 node_draw_mute_line(&ar->v2d, snode, node);     
872         
873         if(node->flag & SELECT) 
874                 UI_ThemeColor(TH_TEXT_HI);
875         else
876                 UI_ThemeColor(TH_TEXT);
877         
878         if(node->miniwidth>0.0f) {
879
880
881                 if(node->flag & NODE_CUSTOM_NAME)
882                         BLI_strncpy(showname, node->name, sizeof(showname));
883                 else
884                         /* todo: auto name display */
885                         BLI_strncpy(showname, node->name, sizeof(showname));
886         
887                 //if(node->flag & NODE_MUTED)
888                 //      sprintf(showname, "[%s]", showname);
889
890                 uiDefBut(node->block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(centy-10), 
891                                  (int)(rct->xmax - rct->xmin-18.0f -12.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
892         }       
893
894         /* scale widget thing */
895         UI_ThemeColorShade(color_id, -10);      
896         dx= 10.0f;
897         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
898         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
899         
900         UI_ThemeColorShade(color_id, +30);
901         dx-= snode->aspect;
902         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
903         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
904         
905         /* sockets */
906         for(sock= node->inputs.first; sock; sock= sock->next) {
907                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
908                         socket_circle_draw(sock, NODE_SOCKSIZE);
909         }
910         
911         for(sock= node->outputs.first; sock; sock= sock->next) {
912                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
913                         socket_circle_draw(sock, NODE_SOCKSIZE);
914         }
915         
916         uiEndBlock(C, node->block);
917         uiDrawBlock(C, node->block);
918         node->block= NULL;
919 }
920
921 static void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree)
922 {
923         bNode *node;
924         bNodeLink *link;
925         int a;
926         
927         if(ntree==NULL) return;         /* groups... */
928         
929         /* node lines */
930         glEnable(GL_BLEND);
931         glEnable(GL_LINE_SMOOTH);
932         for(link= ntree->links.first; link; link= link->next)
933                 node_draw_link(&ar->v2d, snode, link);
934         glDisable(GL_LINE_SMOOTH);
935         glDisable(GL_BLEND);
936         
937         /* not selected first */
938         for(a=0, node= ntree->nodes.first; node; node= node->next, a++) {
939                 node->nr= a;            /* index of node in list, used for exec event code */
940                 if(!(node->flag & SELECT)) {
941                         if(node->flag & NODE_GROUP_EDIT);
942                         else if(node->flag & NODE_HIDDEN)
943                                 node_draw_hidden(C, ar, snode, node);
944                         else
945                                 node_draw_basis(C, ar, snode, ntree, node);
946                 }
947         }
948         
949         /* selected */
950         for(node= ntree->nodes.first; node; node= node->next) {
951                 if(node->flag & SELECT) {
952                         if(node->flag & NODE_GROUP_EDIT);
953                         else if(node->flag & NODE_HIDDEN)
954                                 node_draw_hidden(C, ar, snode, node);
955                         else
956                                 node_draw_basis(C, ar, snode, ntree, node);
957                 }
958         }       
959 }
960
961 /* fake links from groupnode to internal nodes */
962 static void node_draw_group_links(View2D *v2d, SpaceNode *snode, bNode *gnode)
963 {
964         bNodeLink fakelink;
965         bNodeSocket *sock;
966         
967         glEnable(GL_BLEND);
968         glEnable(GL_LINE_SMOOTH);
969         
970         fakelink.tonode= fakelink.fromnode= gnode;
971         
972         for(sock= gnode->inputs.first; sock; sock= sock->next) {
973                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
974                         if(sock->tosock) {
975                                 fakelink.fromsock= sock;
976                                 fakelink.tosock= sock->tosock;
977                                 node_draw_link(v2d, snode, &fakelink);
978                         }
979                 }
980         }
981         
982         for(sock= gnode->outputs.first; sock; sock= sock->next) {
983                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
984                         if(sock->tosock) {
985                                 fakelink.tosock= sock;
986                                 fakelink.fromsock= sock->tosock;
987                                 node_draw_link(v2d, snode, &fakelink);
988                         }
989                 }
990         }
991         
992         glDisable(GL_BLEND);
993         glDisable(GL_LINE_SMOOTH);
994 }
995
996 /* groups are, on creation, centered around 0,0 */
997 static void node_draw_group(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *gnode)
998 {
999         bNodeTree *ngroup= (bNodeTree *)gnode->id;
1000         bNodeSocket *sock;
1001         rctf rect= gnode->totr;
1002         char showname[128];
1003         
1004         /* backdrop header */
1005         glEnable(GL_BLEND);
1006         uiSetRoundBox(3);
1007         UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70);
1008         uiDrawBox(GL_POLYGON, rect.xmin, rect.ymax, rect.xmax, rect.ymax+NODE_DY, BASIS_RAD);
1009         
1010         /* backdrop body */
1011         UI_ThemeColorShadeAlpha(TH_BACK, -8, -70);
1012         uiSetRoundBox(12);
1013         uiDrawBox(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, BASIS_RAD);
1014         
1015         /* selection outline */
1016         uiSetRoundBox(15);
1017         glColor4ub(200, 200, 200, 140);
1018         glEnable( GL_LINE_SMOOTH );
1019         uiDrawBox(GL_LINE_LOOP, rect.xmin, rect.ymin, rect.xmax, rect.ymax+NODE_DY, BASIS_RAD);
1020         glDisable( GL_LINE_SMOOTH );
1021         glDisable(GL_BLEND);
1022         
1023         /* backdrop title */
1024         UI_ThemeColor(TH_TEXT_HI);
1025
1026         if (gnode->flag & NODE_CUSTOM_NAME)
1027                 BLI_strncpy(showname, gnode->name, sizeof(showname));
1028         else
1029                 BLI_strncpy(showname, ngroup->id.name+2, sizeof(showname));
1030
1031
1032         uiDefBut(gnode->block, LABEL, 0, showname, (short)(rect.xmin+15), (short)(rect.ymax), 
1033                          (int)(rect.xmax - rect.xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
1034         uiEndBlock(C, gnode->block);
1035         uiDrawBlock(C, gnode->block);
1036         gnode->block= NULL;
1037
1038
1039         /* links from groupsockets to the internal nodes */
1040         node_draw_group_links(&ar->v2d, snode, gnode);
1041         
1042         /* group sockets */
1043         for(sock= gnode->inputs.first; sock; sock= sock->next)
1044                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
1045                         socket_circle_draw(sock, NODE_SOCKSIZE);
1046         for(sock= gnode->outputs.first; sock; sock= sock->next)
1047                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
1048                         socket_circle_draw(sock, NODE_SOCKSIZE);
1049
1050
1051         
1052         /* and finally the whole tree */
1053         node_draw_nodetree(C, ar, snode, ngroup);
1054 }
1055
1056 void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d)
1057 {
1058         View2DScrollers *scrollers;
1059         SpaceNode *snode= CTX_wm_space_node(C);
1060         Scene *scene= CTX_data_scene(C);
1061         int color_manage = scene->r.color_mgt_flag & R_COLOR_MANAGEMENT;
1062         
1063         UI_ThemeClearColor(TH_BACK);
1064         glClear(GL_COLOR_BUFFER_BIT);
1065
1066         UI_view2d_view_ortho(v2d);
1067         
1068         //uiFreeBlocksWin(&sa->uiblocks, sa->win);
1069
1070         /* only set once */
1071         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1072         glEnable(GL_MAP1_VERTEX_3);
1073
1074         /* aspect+font, set each time */
1075         snode->aspect= (v2d->cur.xmax - v2d->cur.xmin)/((float)ar->winx);
1076         // XXX snode->curfont= uiSetCurFont_ext(snode->aspect);
1077
1078         UI_view2d_constant_grid_draw(v2d);
1079         /* backdrop */
1080         draw_nodespace_back_pix(ar, snode, color_manage);
1081         
1082         /* nodes */
1083         snode_set_context(snode, CTX_data_scene(C));
1084         
1085         if(snode->nodetree) {
1086                 bNode *node;
1087                 
1088                 /* init ui blocks for opened node group trees first 
1089                  * so they're in the correct depth stack order */
1090                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1091                         if(node->flag & NODE_GROUP_EDIT)
1092                                 node_uiblocks_init(C, (bNodeTree *)node->id);
1093                 }
1094
1095                 node_uiblocks_init(C, snode->nodetree);
1096                 
1097                 
1098                 /* for now, we set drawing coordinates on each redraw */
1099                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1100                         if(node->flag & NODE_GROUP_EDIT)
1101                                 node_update_group(C, snode->nodetree, node);
1102                         else if(node->flag & NODE_HIDDEN)
1103                                 node_update_hidden(node);
1104                         else
1105                                 node_update(C, snode->nodetree, node);
1106                 }
1107
1108                 node_draw_nodetree(C, ar, snode, snode->nodetree);
1109                         
1110                 /* active group */
1111                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1112                         if(node->flag & NODE_GROUP_EDIT)
1113                                 node_draw_group(C, ar, snode, node);
1114                 }
1115         }
1116         
1117         /* draw grease-pencil ('canvas' strokes) */
1118         if (/*(snode->flag & SNODE_DISPGP) &&*/ (snode->nodetree))
1119                 draw_gpencil_view2d((bContext*)C, 1);
1120         
1121         /* reset view matrix */
1122         UI_view2d_view_restore(C);
1123         
1124         /* draw grease-pencil (screen strokes, and also paintbuffer) */
1125         if (/*(snode->flag & SNODE_DISPGP) && */(snode->nodetree))
1126                 draw_gpencil_view2d((bContext*)C, 0);
1127         
1128         /* scrollers */
1129         scrollers= UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1130         UI_view2d_scrollers_draw(C, v2d, scrollers);
1131         UI_view2d_scrollers_free(scrollers);
1132 }