Fix for my previous fix.
[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= snode_get_editgroup(snode);
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                         ntreeTexUpdatePreviews(snode->nodetree);
613                 }
614         }
615         
616 }
617
618 static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *node)
619 {
620         bNodeSocket *sock;
621         uiBlock *block;
622         uiBut *bt;
623         rctf *rct= &node->totr;
624         float /*slen,*/ iconofs;
625         int /*ofs,*/ color_id= node_get_colorid(node);
626         char showname[128]; /* 128 used below */
627         View2D *v2d = &ar->v2d;
628         char str[32];
629         
630         /* make unique block name, also used for handling blocks in editnode.c */
631         sprintf(str, "node buttons %p", node);
632         block= uiBeginBlock(C, ar, str, UI_EMBOSS);
633         uiBlockSetHandleFunc(block, do_node_internal_buttons, node);
634         
635         uiSetRoundBox(15-4);
636         ui_dropshadow(rct, BASIS_RAD, snode->aspect, node->flag & SELECT);
637         
638         /* header */
639         if(color_id==TH_NODE)
640                 UI_ThemeColorShade(color_id, -20);
641         else
642                 UI_ThemeColor(color_id);
643                 
644         uiSetRoundBox(3);
645         uiRoundBox(rct->xmin, rct->ymax-NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
646         
647         /* show/hide icons, note this sequence is copied in editnode.c */
648         iconofs= rct->xmax;
649         
650         if(node->typeinfo->flag & NODE_PREVIEW) {
651                 int icon_id;
652                 
653                 if(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT))
654                         icon_id= ICON_MATERIAL;
655                 else
656                         icon_id= ICON_MATERIAL_DATA;
657                 iconofs-= 18.0f;
658                 glEnable(GL_BLEND);
659                 UI_icon_draw_aspect_blended(iconofs, rct->ymax-NODE_DY+2, icon_id, snode->aspect, -60);
660                 glDisable(GL_BLEND);
661         }
662         if(node->type == NODE_GROUP) {
663                 
664                 iconofs-= 18.0f;
665                 glEnable(GL_BLEND);
666                 if(node->id->lib) {
667                         glPixelTransferf(GL_GREEN_SCALE, 0.7f);
668                         glPixelTransferf(GL_BLUE_SCALE, 0.3f);
669                         UI_icon_draw_aspect(iconofs, rct->ymax-NODE_DY+2, ICON_NODE, snode->aspect);
670                         glPixelTransferf(GL_GREEN_SCALE, 1.0f);
671                         glPixelTransferf(GL_BLUE_SCALE, 1.0f);
672                 }
673                 else {
674                         UI_icon_draw_aspect_blended(iconofs, rct->ymax-NODE_DY+2, ICON_NODE, snode->aspect, -60);
675                 }
676                 glDisable(GL_BLEND);
677         }
678         if(node->typeinfo->flag & NODE_OPTIONS) {
679                 iconofs-= 18.0f;
680                 glEnable(GL_BLEND);
681                 UI_icon_draw_aspect_blended(iconofs, rct->ymax-NODE_DY+2, ICON_BUTS, snode->aspect, -60);
682                 glDisable(GL_BLEND);
683         }
684         {       /* always hide/reveal unused sockets */ 
685                 int shade;
686
687                 iconofs-= 18.0f;
688                 // XXX re-enable
689                 /*if(node_has_hidden_sockets(node))
690                         shade= -40;
691                 else*/
692                         shade= -90;
693                 glEnable(GL_BLEND);
694                 UI_icon_draw_aspect_blended(iconofs, rct->ymax-NODE_DY+2, ICON_PLUS, snode->aspect, shade);
695                 glDisable(GL_BLEND);
696         }
697         
698         /* title */
699         if(node->flag & SELECT) 
700                 UI_ThemeColor(TH_TEXT_HI);
701         else
702                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
703         
704         /* open/close entirely? */
705         ui_draw_tria_icon(rct->xmin+8.0f, rct->ymax-NODE_DY+4.0f, snode->aspect, 'v');
706         
707         if(node->flag & SELECT) 
708                 UI_ThemeColor(TH_TEXT_HI);
709         else
710                 UI_ThemeColor(TH_TEXT);
711         
712         if(node->flag & NODE_MUTED)
713                 sprintf(showname, "[%s]", node->name);
714         else if(node->username[0])
715                 sprintf(showname, "(%s) %s", node->username, node->name);
716         else
717                 BLI_strncpy(showname, node->name, 128);
718         
719         uiDefBut(block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(rct->ymax-NODE_DY), 
720                          (int)(iconofs - rct->xmin-18.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
721
722         /* body */
723         UI_ThemeColor4(TH_NODE);
724         glEnable(GL_BLEND);
725         uiSetRoundBox(8);
726         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD);
727         glDisable(GL_BLEND);
728
729         /* scaling indicator */
730         node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect);
731
732         /* outline active emphasis */
733         if(node->flag & NODE_ACTIVE) {
734                 glEnable(GL_BLEND);
735                 glColor4ub(200, 200, 200, 140);
736                 uiSetRoundBox(15-4);
737                 gl_round_box(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
738                 glDisable(GL_BLEND);
739         }
740         
741         /* disable lines */
742         if(node->flag & NODE_MUTED)
743                 node_draw_mute_line(v2d, snode, node);
744
745         
746         /* hurmf... another candidate for callback, have to see how this works first */
747         if(node->id && block && snode->treetype==NTREE_SHADER)
748                 nodeShaderSynchronizeID(node, 0);
749         
750         /* socket inputs, buttons */
751         for(sock= node->inputs.first; sock; sock= sock->next) {
752                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
753                         socket_circle_draw(sock, NODE_SOCKSIZE);
754                         
755                         if(block && sock->link==NULL) {
756                                 float *butpoin= sock->ns.vec;
757                                 
758                                 if(sock->type==SOCK_VALUE) {
759                                         bt= uiDefButF(block, NUM, B_NODE_EXEC, sock->name, 
760                                                   (short)sock->locx+NODE_DYS, (short)(sock->locy)-9, (short)node->width-NODE_DY, 17, 
761                                                   butpoin, sock->ns.min, sock->ns.max, 10, 2, "");
762                                         uiButSetFunc(bt, node_sync_cb, snode, node);
763                                 }
764                                 else if(sock->type==SOCK_VECTOR) {
765                                         uiDefBlockBut(block, socket_vector_menu, sock, sock->name, 
766                                                   (short)sock->locx+NODE_DYS, (short)sock->locy-9, (short)node->width-NODE_DY, 17, 
767                                                   "");
768                                 }
769                                 else if(block && sock->type==SOCK_RGBA) {
770                                         short labelw= (short)node->width-NODE_DY-40, width;
771                                         
772                                         if(labelw>0) width= 40; else width= (short)node->width-NODE_DY;
773                                         
774                                         bt= uiDefButF(block, COL, B_NODE_EXEC, "", 
775                                                 (short)(sock->locx+NODE_DYS), (short)sock->locy-8, width, 15, 
776                                                    butpoin, 0, 0, 0, 0, "");
777                                         uiButSetFunc(bt, node_sync_cb, snode, node);
778                                         
779                                         if(labelw>0) uiDefBut(block, LABEL, 0, sock->name, 
780                                                                                    (short)(sock->locx+NODE_DYS) + 40, (short)sock->locy-8, labelw, 15, 
781                                                                                    NULL, 0, 0, 0, 0, "");
782                                 }
783                         }
784                         else {
785                                 
786                                 uiDefBut(block, LABEL, 0, sock->name, (short)(sock->locx+3.0f), (short)(sock->locy-9.0f), 
787                                                  (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
788                         }
789                 }
790         }
791         
792         /* socket outputs */
793         for(sock= node->outputs.first; sock; sock= sock->next) {
794                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
795                         float slen;
796                         int ofs= 0;
797                         
798                         socket_circle_draw(sock, NODE_SOCKSIZE);
799                         
800                         UI_ThemeColor(TH_TEXT);
801                         slen= snode->aspect*UI_GetStringWidth(sock->name);
802                         while(slen > node->width) {
803                                 ofs++;
804                                 slen= snode->aspect*UI_GetStringWidth(sock->name+ofs);
805                         }
806                         
807                         uiDefBut(block, LABEL, 0, sock->name+ofs, (short)(sock->locx-15.0f-slen), (short)(sock->locy-9.0f), 
808                                          (short)(node->width-NODE_DY), NODE_DY,  NULL, 0, 0, 0, 0, "");
809                 }
810         }
811         
812         /* preview */
813         if(node->flag & NODE_PREVIEW)
814                 if(node->preview && node->preview->rect)
815                         node_draw_preview(node->preview, &node->prvr);
816                 
817         /* buttons */
818         if(node->flag & NODE_OPTIONS) {
819                 if(block) {
820                         if(node->typeinfo->butfunc) {
821                                 node->typeinfo->butfunc(block, snode->nodetree, node, &node->butr);
822                         }
823                 }
824         }
825         
826         uiEndBlock(C, block);
827         uiDrawBlock(C, block);
828 }
829
830 static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *node)
831 {
832         uiBlock *block;
833         bNodeSocket *sock;
834         rctf *rct= &node->totr;
835         float dx, centy= 0.5f*(rct->ymax+rct->ymin);
836         float hiddenrad= 0.5f*(rct->ymax-rct->ymin);
837         int color_id= node_get_colorid(node);
838         char str[32], showname[128];    /* 128 is used below */
839         
840         /* make unique block name, also used for handling blocks in editnode.c */
841         sprintf(str, "node buttons %p", node);
842         block= uiBeginBlock(C, ar, str, UI_EMBOSS);
843         uiBlockSetHandleFunc(block, do_node_internal_buttons, node);
844         
845         /* shadow */
846         uiSetRoundBox(15);
847         ui_dropshadow(rct, hiddenrad, snode->aspect, node->flag & SELECT);
848
849         /* body */
850         UI_ThemeColor(color_id);        
851         uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
852         
853         /* outline active emphasis */
854         if(node->flag & NODE_ACTIVE) {
855                 glEnable(GL_BLEND);
856                 glColor4ub(200, 200, 200, 140);
857                 gl_round_box(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
858                 glDisable(GL_BLEND);
859         }
860         
861         /* title */
862         if(node->flag & SELECT) 
863                 UI_ThemeColor(TH_TEXT_HI);
864         else
865                 UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
866         
867         /* open entirely icon */
868         ui_draw_tria_icon(rct->xmin+9.0f, centy-6.0f, snode->aspect, 'h');      
869         
870         /* disable lines */
871         if(node->flag & NODE_MUTED)
872                 node_draw_mute_line(&ar->v2d, snode, node);     
873         
874         if(node->flag & SELECT) 
875                 UI_ThemeColor(TH_TEXT_HI);
876         else
877                 UI_ThemeColor(TH_TEXT);
878         
879         if(node->miniwidth>0.0f) {
880
881                 if(node->flag & NODE_MUTED)
882                         sprintf(showname, "[%s]", node->name);
883                 else if(node->username[0])
884                         sprintf(showname, "(%s)%s", node->username, node->name);
885                 else
886                         BLI_strncpy(showname, node->name, 128);
887
888                 uiDefBut(block, LABEL, 0, showname, (short)(rct->xmin+15), (short)(centy-10), 
889                                  (int)(rct->xmax - rct->xmin-18.0f -12.0f), NODE_DY,  NULL, 0, 0, 0, 0, "");
890         }       
891
892         /* scale widget thing */
893         UI_ThemeColorShade(color_id, -10);      
894         dx= 10.0f;
895         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
896         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
897         
898         UI_ThemeColorShade(color_id, +30);
899         dx-= snode->aspect;
900         fdrawline(rct->xmax-dx, centy-4.0f, rct->xmax-dx, centy+4.0f);
901         fdrawline(rct->xmax-dx-3.0f*snode->aspect, centy-4.0f, rct->xmax-dx-3.0f*snode->aspect, centy+4.0f);
902         
903         /* sockets */
904         for(sock= node->inputs.first; sock; sock= sock->next) {
905                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
906                         socket_circle_draw(sock, NODE_SOCKSIZE);
907         }
908         
909         for(sock= node->outputs.first; sock; sock= sock->next) {
910                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
911                         socket_circle_draw(sock, NODE_SOCKSIZE);
912         }
913         
914         uiEndBlock(C, block);
915         uiDrawBlock(C, block);
916
917 }
918
919 static void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree)
920 {
921         bNode *node;
922         bNodeLink *link;
923         int a;
924         
925         if(ntree==NULL) return;         /* groups... */
926         
927         /* node lines */
928         glEnable(GL_BLEND);
929         glEnable(GL_LINE_SMOOTH);
930         for(link= ntree->links.first; link; link= link->next)
931                 node_draw_link(&ar->v2d, snode, link);
932         glDisable(GL_LINE_SMOOTH);
933         glDisable(GL_BLEND);
934         
935         /* not selected first */
936         for(a=0, node= ntree->nodes.first; node; node= node->next, a++) {
937                 node->nr= a;            /* index of node in list, used for exec event code */
938                 if(!(node->flag & SELECT)) {
939                         if(node->flag & NODE_GROUP_EDIT);
940                         else if(node->flag & NODE_HIDDEN)
941                                 node_draw_hidden(C, ar, snode, node);
942                         else
943                                 node_draw_basis(C, ar, snode, node);
944                 }
945         }
946         
947         /* selected */
948         for(node= ntree->nodes.first; node; node= node->next) {
949                 if(node->flag & SELECT) {
950                         if(node->flag & NODE_GROUP_EDIT);
951                         else if(node->flag & NODE_HIDDEN)
952                                 node_draw_hidden(C, ar, snode, node);
953                         else
954                                 node_draw_basis(C, ar, snode, node);
955                 }
956         }       
957 }
958
959 /* fake links from groupnode to internal nodes */
960 static void node_draw_group_links(View2D *v2d, SpaceNode *snode, bNode *gnode)
961 {
962         bNodeLink fakelink;
963         bNodeSocket *sock;
964         
965         glEnable(GL_BLEND);
966         glEnable(GL_LINE_SMOOTH);
967         
968         fakelink.tonode= fakelink.fromnode= gnode;
969         
970         for(sock= gnode->inputs.first; sock; sock= sock->next) {
971                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
972                         if(sock->tosock) {
973                                 fakelink.fromsock= sock;
974                                 fakelink.tosock= sock->tosock;
975                                 node_draw_link(v2d, snode, &fakelink);
976                         }
977                 }
978         }
979         
980         for(sock= gnode->outputs.first; sock; sock= sock->next) {
981                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) {
982                         if(sock->tosock) {
983                                 fakelink.tosock= sock;
984                                 fakelink.fromsock= sock->tosock;
985                                 node_draw_link(v2d, snode, &fakelink);
986                         }
987                 }
988         }
989         
990         glDisable(GL_BLEND);
991         glDisable(GL_LINE_SMOOTH);
992 }
993
994 /* groups are, on creation, centered around 0,0 */
995 static void node_draw_group(const bContext *C, ARegion *ar, SpaceNode *snode, bNode *gnode)
996 {
997         bNodeTree *ngroup= (bNodeTree *)gnode->id;
998         bNodeSocket *sock;
999         rctf rect= gnode->totr;
1000         char showname[128];
1001         
1002         /* backdrop header */
1003         glEnable(GL_BLEND);
1004         uiSetRoundBox(3);
1005         UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70);
1006         gl_round_box(GL_POLYGON, rect.xmin, rect.ymax, rect.xmax, rect.ymax+NODE_DY, BASIS_RAD);
1007         
1008         /* backdrop body */
1009         UI_ThemeColorShadeAlpha(TH_BACK, -8, -70);
1010         uiSetRoundBox(12);
1011         gl_round_box(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, BASIS_RAD);
1012         
1013         /* selection outline */
1014         uiSetRoundBox(15);
1015         glColor4ub(200, 200, 200, 140);
1016         glEnable( GL_LINE_SMOOTH );
1017         gl_round_box(GL_LINE_LOOP, rect.xmin, rect.ymin, rect.xmax, rect.ymax+NODE_DY, BASIS_RAD);
1018         glDisable( GL_LINE_SMOOTH );
1019         glDisable(GL_BLEND);
1020         
1021         /* backdrop title */
1022         UI_ThemeColor(TH_TEXT_HI);
1023
1024         if(gnode->username[0]) {
1025                 strcpy(showname,"(");
1026                 strcat(showname, gnode->username);
1027                 strcat(showname,") ");
1028                 strcat(showname, ngroup->id.name+2);
1029         }
1030         else
1031                 strcpy(showname, ngroup->id.name+2);
1032
1033         UI_DrawString(rect.xmin+8.0f, rect.ymax+5.0f, showname);
1034         
1035         /* links from groupsockets to the internal nodes */
1036         node_draw_group_links(&ar->v2d, snode, gnode);
1037         
1038         /* group sockets */
1039         for(sock= gnode->inputs.first; sock; sock= sock->next)
1040                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
1041                         socket_circle_draw(sock, NODE_SOCKSIZE);
1042         for(sock= gnode->outputs.first; sock; sock= sock->next)
1043                 if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL)))
1044                         socket_circle_draw(sock, NODE_SOCKSIZE);
1045
1046         /* and finally the whole tree */
1047         node_draw_nodetree(C, ar, snode, ngroup);
1048 }
1049
1050 void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d)
1051 {
1052         float col[3];
1053         View2DScrollers *scrollers;
1054         SpaceNode *snode= CTX_wm_space_node(C);
1055         
1056         UI_GetThemeColor3fv(TH_BACK, col);
1057         glClearColor(col[0], col[1], col[2], 0);
1058         glClear(GL_COLOR_BUFFER_BIT);
1059
1060         UI_view2d_view_ortho(C, v2d);
1061         
1062         //uiFreeBlocksWin(&sa->uiblocks, sa->win);
1063
1064         /* only set once */
1065         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1066         glEnable(GL_MAP1_VERTEX_3);
1067
1068         /* aspect+font, set each time */
1069         snode->aspect= (v2d->cur.xmax - v2d->cur.xmin)/((float)ar->winx);
1070         // XXX snode->curfont= uiSetCurFont_ext(snode->aspect);
1071
1072         UI_view2d_constant_grid_draw(C, v2d);
1073         /* backdrop */
1074         draw_nodespace_back_pix(ar, snode);
1075         
1076         /* nodes */
1077         snode_set_context(snode, CTX_data_scene(C));
1078         
1079         if(snode->nodetree) {
1080                 bNode *node;
1081                 
1082                 /* for now, we set drawing coordinates on each redraw */
1083                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1084                         if(node->flag & NODE_GROUP_EDIT)
1085                                 node_update_group(node);
1086                         else if(node->flag & NODE_HIDDEN)
1087                                 node_update_hidden(node);
1088                         else
1089                                 node_update(node);
1090                 }
1091
1092                 node_draw_nodetree(C, ar, snode, snode->nodetree);
1093                         
1094                 /* active group */
1095                 for(node= snode->nodetree->nodes.first; node; node= node->next) {
1096                         if(node->flag & NODE_GROUP_EDIT)
1097                                 node_draw_group(C, ar, snode, node);
1098                 }
1099         }
1100         
1101         /* draw grease-pencil ('canvas' strokes) */
1102         /*if ((snode->flag & SNODE_DISPGP) && (snode->nodetree))
1103                 draw_gpencil_2dview(sa, 1);*/
1104         
1105         /* restore viewport (not needed yet) */
1106         /*mywinset(sa->win);*/
1107
1108         /* ortho at pixel level curarea */
1109         /*myortho2(-0.375, sa->winx-0.375, -0.375, sa->winy-0.375);*/
1110         
1111         /* draw grease-pencil (screen strokes) */
1112         /*if ((snode->flag & SNODE_DISPGP) && (snode->nodetree))
1113                 draw_gpencil_2dview(sa, 0);*/
1114
1115         //draw_area_emboss(sa);
1116         
1117         /* it is important to end a view in a transform compatible with buttons */
1118         /*bwin_scalematrix(sa->win, snode->blockscale, snode->blockscale, snode->blockscale);
1119         nodes_blockhandlers(sa);*/
1120         
1121         //curarea->win_swap= WIN_BACK_OK;
1122         
1123         /* in the end, this is a delayed previewrender test, to allow buttons to be first */
1124         /*if(snode->flag & SNODE_DO_PREVIEW) {
1125                 addafterqueue(sa->win, RENDERPREVIEW, 1);
1126                 snode->flag &= ~SNODE_DO_PREVIEW;
1127         }*/
1128         
1129         
1130         
1131         /* reset view matrix */
1132         UI_view2d_view_restore(C);
1133         
1134         /* scrollers */
1135         scrollers= UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
1136         UI_view2d_scrollers_draw(C, v2d, scrollers);
1137         UI_view2d_scrollers_free(scrollers);
1138 }