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