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