diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 0c7bfc2bc50..0c2eeb71d38 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -35,11 +35,18 @@ struct bNodeTree; struct bNode; +struct bNodeLink; +struct bNodeSocket; struct ListBase; -void nodeFreeNode(struct bNodeTree *ntree, struct bNode *node); -void nodeFreeTree(struct bNodeTree *ntree); -struct bNode *nodeAddNode(struct bNodeTree *ntree, char *name); +void nodeFreeNode(struct bNodeTree *ntree, struct bNode *node); +void nodeFreeTree(struct bNodeTree *ntree); +struct bNode *nodeAddNode(struct bNodeTree *ntree, char *name); +struct bNodeLink *nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock); +struct bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node); + +struct bNodeLink *nodeFindLink(struct bNodeTree *ntree, struct bNodeSocket *from, struct bNodeSocket *to); + #endif diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index e3be25d86ef..254740f0d2d 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -44,11 +44,40 @@ bNode *nodeAddNode(struct bNodeTree *ntree, char *name) { bNode *node= MEM_callocN(sizeof(bNode), "new node"); + BLI_addtail(&ntree->nodes, node); BLI_strncpy(node->name, name, NODE_MAXSTR); return node; } +bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node) +{ + bNode *nnode= MEM_callocN(sizeof(bNode), "dupli node"); + + *nnode= *node; + BLI_addtail(&ntree->nodes, nnode); + + duplicatelist(&nnode->inputs, &node->inputs); + duplicatelist(&nnode->outputs, &node->outputs); + if(nnode->id) + nnode->id->us++; + + return nnode; + +} + +bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock) +{ + bNodeLink *link= MEM_callocN(sizeof(bNodeLink), "link"); + + BLI_addtail(&ntree->links, link); + link->fromnode= fromnode; + link->fromsock= fromsock; + link->tonode= tonode; + link->tosock= tosock; + + return link; +} /* ************** Free stuff ********** */ @@ -64,6 +93,9 @@ void nodeFreeNode(bNodeTree *ntree, bNode *node) if(ntree) node_unlink_node(ntree, node); + if(node->id) + node->id->us--; + BLI_freelistN(&node->inputs); BLI_freelistN(&node->outputs); @@ -85,3 +117,18 @@ void nodeFreeTree(bNodeTree *ntree) MEM_freeN(ntree); } +/* ************ find stuff *************** */ + +bNodeLink *nodeFindLink(bNodeTree *ntree, bNodeSocket *from, bNodeSocket *to) +{ + bNodeLink *link; + + for(link= ntree->links.first; link; link= link->next) { + if(link->fromsock==from && link->tosock==to) + return link; + if(link->fromsock==to && link->tosock==from) /* hrms? */ + return link; + } + return NULL; +} + diff --git a/source/blender/include/BSE_node.h b/source/blender/include/BSE_node.h index cd159f9e448..e87d3742dec 100644 --- a/source/blender/include/BSE_node.h +++ b/source/blender/include/BSE_node.h @@ -30,7 +30,10 @@ #ifndef BSE_NODE_H #define BSE_NODE_H +struct SpaceNode; +void node_deselectall(struct SpaceNode *snode, int swap); +void node_transform_ext(int mode, int unused); #endif diff --git a/source/blender/include/interface.h b/source/blender/include/interface.h index 7a45e6566b4..1c26a714c51 100644 --- a/source/blender/include/interface.h +++ b/source/blender/include/interface.h @@ -85,14 +85,13 @@ typedef struct { void *xl, *large, *medium, *small; } uiFont; -typedef struct uiLinkLine uiLinkLine; -struct uiLinkLine { /* only for draw/edit */ - uiLinkLine *next, *prev; +typedef struct uiLinkLine { /* only for draw/edit */ + struct uiLinkLine *next, *prev; short flag, pad; uiBut *from, *to; -}; +} uiLinkLine; typedef struct { void **poin; /* pointer to original pointer */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 2b54b434624..9b404b230b2 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -69,7 +69,8 @@ typedef struct bNode { typedef struct bNodeLink { struct bNodeLink *next, *prev; - bNode *from, *to; + bNode *fromnode, *tonode; + bNodeSocket *fromsock, *tosock; } bNodeLink; diff --git a/source/blender/src/drawnode.c b/source/blender/src/drawnode.c index b3646737397..aeee3217db4 100644 --- a/source/blender/src/drawnode.c +++ b/source/blender/src/drawnode.c @@ -84,6 +84,73 @@ static void get_nodetree(SpaceNode *snode) } +static void node_draw_link(SpaceNode *snode, bNodeLink *link) +{ + float vec[4][3]; + float dist, spline_step, mx=0.0f, my=0.0f; + int curve_res; + + if(link->fromnode==NULL && link->tonode==NULL) + return; + + /* this is dragging link */ + if(link->fromnode==NULL || link->tonode==NULL) { + short mval[2]; + getmouseco_areawin(mval); + areamouseco_to_ipoco(G.v2d, mval, &mx, &my); + } + + BIF_ThemeColor(TH_WIRE); + + vec[0][2]= vec[1][2]= vec[2][2]= vec[3][2]= 0.0; /* only 2d spline, set the Z to 0*/ + + /* in v0 and v3 we put begin/end points */ + if(link->fromnode) { + vec[0][0]= link->fromsock->locx; + vec[0][1]= link->fromsock->locy; + } + else { + vec[0][0]= mx; + vec[0][1]= my; + } + if(link->tonode) { + vec[3][0]= link->tosock->locx; + vec[3][1]= link->tosock->locy; + } + else { + vec[3][0]= mx; + vec[3][1]= my; + } + + dist= 0.5*VecLenf(vec[0], vec[3]); + + /* check direction later, for top sockets */ + vec[1][0]= vec[0][0]+dist; + vec[1][1]= vec[0][1]; + + vec[2][0]= vec[3][0]-dist; + vec[2][1]= vec[3][1]; + + if( MIN4(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > G.v2d->cur.xmax); /* clipped */ + else if ( MAX4(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < G.v2d->cur.xmin); /* clipped */ + else { + curve_res = 24; + + /* we can reuse the dist variable here to increment the GL curve eval amount*/ + dist = 1.0f/curve_res; + spline_step = 0.0f; + + glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, vec[0]); + glBegin(GL_LINE_STRIP); + while (spline_step < 1.000001f) { + glEvalCoord1f(spline_step); + spline_step += dist; + } + glEnd(); + } +} + + void drawnodespace(ScrArea *sa, void *spacedata) { SpaceNode *snode= sa->spacedata.first; @@ -101,6 +168,7 @@ void drawnodespace(ScrArea *sa, void *spacedata) /* only set once */ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable(GL_MAP1_VERTEX_3); /* aspect+font, set each time */ snode->aspect= (snode->v2d.cur.xmax - snode->v2d.cur.xmin)/((float)sa->winx); @@ -113,9 +181,25 @@ void drawnodespace(ScrArea *sa, void *spacedata) get_nodetree(snode); if(snode->nodetree) { bNode *node; - for(node= snode->nodetree->nodes.first; node; node= node->next) { - node->drawfunc(snode, node); - } + bNodeLink *link; + + /* node lines */ + glEnable(GL_BLEND); + glEnable( GL_LINE_SMOOTH ); + for(link= snode->nodetree->links.first; link; link= link->next) + node_draw_link(snode, link); + glDisable(GL_BLEND); + glDisable( GL_LINE_SMOOTH ); + + /* not selected */ + for(node= snode->nodetree->nodes.first; node; node= node->next) + if(!(node->flag & SELECT)) + node->drawfunc(snode, node); + /* selected */ + for(node= snode->nodetree->nodes.first; node; node= node->next) + if(node->flag & SELECT) + node->drawfunc(snode, node); + } /* restore viewport */ diff --git a/source/blender/src/editnode.c b/source/blender/src/editnode.c index 3a53602e18e..d40ffc106fa 100644 --- a/source/blender/src/editnode.c +++ b/source/blender/src/editnode.c @@ -51,6 +51,7 @@ #include "BKE_material.h" #include "BKE_utildefines.h" +#include "BIF_editview.h" #include "BIF_gl.h" #include "BIF_interface.h" #include "BIF_language.h" @@ -62,19 +63,26 @@ #include "BSE_drawipo.h" #include "BSE_headerbuttons.h" +#include "BSE_node.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" +#include "BDR_editobject.h" + #include "blendef.h" #include "interface.h" /* urm... for rasterpos_safe, roundbox */ - +#include "PIL_time.h" #include "mydevice.h" -/* ***************************** */ +/* **************** NODE draw callbacks ************* */ #define NODE_DY 20 +#define NODE_SOCK 5 + +#define SOCK_IN 1 +#define SOCK_OUT 2 static void nodeshadow(rctf *rct, int select) { @@ -165,7 +173,7 @@ static int node_basis_draw(SpaceNode *snode, bNode *node) BIF_DrawString(snode->curfont, node->name, trans); for(sock= node->inputs.first; sock; sock= sock->next) { - socket_circle_draw(sock->locx, sock->locy, 5.0f); + socket_circle_draw(sock->locx, sock->locy, NODE_SOCK); BIF_ThemeColor(TH_TEXT); ui_rasterpos_safe(sock->locx+8.0f, sock->locy-5.0f, snode->aspect); @@ -173,7 +181,7 @@ static int node_basis_draw(SpaceNode *snode, bNode *node) } for(sock= node->outputs.first; sock; sock= sock->next) { - socket_circle_draw(sock->locx, sock->locy, 5.0f); + socket_circle_draw(sock->locx, sock->locy, NODE_SOCK); BIF_ThemeColor(TH_TEXT); slen= snode->aspect*BIF_GetStringWidth(snode->curfont, sock->name, trans); @@ -184,29 +192,7 @@ static int node_basis_draw(SpaceNode *snode, bNode *node) return 0; } -static void node_deselectall(SpaceNode *snode, int swap) -{ - bNode *node; - - if(swap) { - for(node= snode->nodetree->nodes.first; node; node= node->next) - if(node->flag & SELECT) - break; - if(node==NULL) { - for(node= snode->nodetree->nodes.first; node; node= node->next) - node->flag |= SELECT; - allqueue(REDRAWNODE, 0); - return; - } - /* else pass on to deselect */ - } - - for(node= snode->nodetree->nodes.first; node; node= node->next) - node->flag &= ~SELECT; - - allqueue(REDRAWNODE, 0); -} - +/* ************************** Node generic ************** */ /* based on settings in tree and node, - it fills it with appropriate callbacks @@ -250,6 +236,172 @@ void node_update(bNodeTree *ntree, bNode *node) node->tot.ymax= node->locy + dy; } +/* ********************* transform ****************** */ + +/* releases on event, only intern (for extern see below) */ +static void transform_nodes(SpaceNode *snode, char *undostr) +{ + bNode *node; + float mxstart, mystart, mx, my, *oldlocs, *ol; + int cont=1, tot=0, cancel=0, firsttime=1; + short mval[2], mvalo[2]; + char str[64]; + + /* count total */ + for(node= snode->nodetree->nodes.first; node; node= node->next) + if(node->flag & SELECT) tot++; + + if(tot==0) return; + + /* store oldlocs */ + ol= oldlocs= MEM_mallocN(sizeof(float)*2*tot, "oldlocs transform"); + for(node= snode->nodetree->nodes.first; node; node= node->next) { + if(node->flag & SELECT) { + ol[0]= node->locx; ol[1]= node->locy; + ol+= 2; + } + } + + getmouseco_areawin(mvalo); + areamouseco_to_ipoco(G.v2d, mvalo, &mxstart, &mystart); + + while(cont) { + + getmouseco_areawin(mval); + if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || firsttime) { + firsttime= 0; + + areamouseco_to_ipoco(G.v2d, mval, &mx, &my); + mvalo[0]= mval[0]; + mvalo[1]= mval[1]; + + for(ol= oldlocs, node= snode->nodetree->nodes.first; node; node= node->next) { + if(node->flag & SELECT) { + node->locx= ol[0] + mx-mxstart; + node->locy= ol[1] + my-mystart; + node_update(snode->nodetree, node); + ol+= 2; + } + } + + sprintf(str, "X: %.1f Y: %.1f", mx-mxstart, my-mystart); + headerprint(str); + force_draw(0); + } + else + PIL_sleep_ms(10); + + while (qtest()) { + short val; + unsigned short event= extern_qread(&val); + + switch (event) { + case LEFTMOUSE: + case SPACEKEY: + case RETKEY: + cont=0; + break; + case ESCKEY: + case RIGHTMOUSE: + if(val) { + cancel=1; + cont=0; + } + break; + default: + if(val) arrows_move_cursor(event); + break; + } + } + + } + + if(cancel) { + for(ol= oldlocs, node= snode->nodetree->nodes.first; node; node= node->next) { + if(node->flag & SELECT) { + node->locx= ol[0]; + node->locy= ol[1]; + ol+= 2; + node_update(snode->nodetree, node); + } + } + + } + else + BIF_undo_push(undostr); + + allqueue(REDRAWNODE, 1); + MEM_freeN(oldlocs); +} + +/* external call, also for callback */ +void node_transform_ext(int mode, int unused) +{ + transform_nodes(curarea->spacedata.first, "Translate node"); +} + +/* ********************** select ******************** */ + +/* no undo here! */ +void node_deselectall(SpaceNode *snode, int swap) +{ + bNode *node; + + if(swap) { + for(node= snode->nodetree->nodes.first; node; node= node->next) + if(node->flag & SELECT) + break; + if(node==NULL) { + for(node= snode->nodetree->nodes.first; node; node= node->next) + node->flag |= SELECT; + allqueue(REDRAWNODE, 0); + return; + } + /* else pass on to deselect */ + } + + for(node= snode->nodetree->nodes.first; node; node= node->next) + node->flag &= ~SELECT; + + allqueue(REDRAWNODE, 0); +} + + +static void node_mouse_select(SpaceNode *snode) +{ + bNode *node; + float mx, my; + short mval[2]; + + getmouseco_areawin(mval); + areamouseco_to_ipoco(G.v2d, mval, &mx, &my); + + if((G.qual & LR_SHIFTKEY)==0) + node_deselectall(snode, 0); + + for(node= snode->nodetree->nodes.first; node; node= node->next) { + if(BLI_in_rctf(&node->tot, mx, my)) { + if(G.qual & LR_SHIFTKEY) { + if(node->flag & SELECT) + node->flag &= ~SELECT; + else + node->flag |= SELECT; + } + else + node->flag |= SELECT; + + break; + } + } + + /* not so nice (no event), but function below delays redraw otherwise */ + force_draw(0); + + std_rmouse_transform(node_transform_ext); /* does undo push for select */ +} + +/* ****************** Add *********************** */ + /* editor context */ static void node_add_menu(SpaceNode *snode) { @@ -283,30 +435,178 @@ static void node_add_menu(SpaceNode *snode) node_update(snode->nodetree, node); + /* fake links */ +// if(snode->nodetree->nodes.first!=snode->nodetree->nodes.last) { +// bNode *first= snode->nodetree->nodes.first; +// +// nodeAddLink(snode->nodetree, first, first->outputs.first, node, node->inputs.first); +// } + allqueue(REDRAWNODE, 0); } + + BIF_undo_push("Add Node"); + } -static void node_select(SpaceNode *snode) +void node_adduplicate(SpaceNode *snode) +{ + bNode *node, *nnode; + + /* backwards, we add to list end */ + for(node= snode->nodetree->nodes.last; node; node= node->prev) { + if(node->flag & SELECT) { + nnode= nodeCopyNode(snode->nodetree, node); + node->flag &= ~SELECT; + nnode->flag |= SELECT; + } + } + + transform_nodes(snode, "Duplicate"); +} + +/* checks mouse position, and returns found node/socket */ +/* type is SOCK_IN and/or SOCK_OUT */ +static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, int type) { bNode *node; - float mx, my; + bNodeSocket *sock; + rctf rect; short mval[2]; getmouseco_areawin(mval); - areamouseco_to_ipoco(G.v2d, mval, &mx, &my); + areamouseco_to_ipoco(G.v2d, mval, &rect.xmin, &rect.ymin); - if((G.qual & LR_SHIFTKEY)==0) - node_deselectall(snode, 0); + rect.xmin -= NODE_SOCK+3; + rect.ymin -= NODE_SOCK+3; + rect.xmax = rect.xmin + 2*NODE_SOCK+6; + rect.ymax = rect.ymin + 2*NODE_SOCK+6; + /* check if we click in a socket */ for(node= snode->nodetree->nodes.first; node; node= node->next) { - if(BLI_in_rctf(&node->tot, mx, my)) { - node->flag |= SELECT; + if(type & SOCK_IN) { + for(sock= node->inputs.first; sock; sock= sock->next) { + if(BLI_in_rctf(&rect, sock->locx, sock->locy)) { + *nodep= node; + *sockp= sock; + return 1; + } + } + } + if(type & SOCK_OUT) { + for(sock= node->outputs.first; sock; sock= sock->next) { + if(BLI_in_rctf(&rect, sock->locx, sock->locy)) { + *nodep= node; + *sockp= sock; + return 1; + } + } } } - allqueue(REDRAWNODE, 0); + return 0; } +/* loop that adds a link node, called by function below though */ +static int node_draw_link_drag(SpaceNode *snode, bNode *node, bNodeSocket *sock, int type) +{ + bNode *tnode; + bNodeSocket *tsock; + bNodeLink *link= NULL; + short mval[2], mvalo[2]; + + /* we make a temporal link */ + if(type==SOCK_OUT) + link= nodeAddLink(snode->nodetree, node, sock, NULL, NULL); + else + link= nodeAddLink(snode->nodetree, NULL, NULL, node, sock); + + getmouseco_areawin(mvalo); + while (get_mbut() & L_MOUSE) { + + getmouseco_areawin(mval); + if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1]) { + + mvalo[0]= mval[0]; + mvalo[1]= mval[1]; + + if(type==SOCK_OUT) { + if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) { + if(nodeFindLink(snode->nodetree, sock, tsock)==NULL) { + if(tnode!=node) { + link->tonode= tnode; + link->tosock= tsock; + } + } + } + else { + link->tonode= NULL; + link->tosock= NULL; + } + } + else { + if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) { + if(nodeFindLink(snode->nodetree, sock, tsock)==NULL) { + if(tnode!=node) { + link->fromnode= tnode; + link->fromsock= tsock; + } + } + } + else { + link->fromnode= NULL; + link->fromsock= NULL; + } + } + + force_draw(0); + } + else BIF_wait_for_statechange(); + } + + if(link->tonode==NULL || link->fromnode==NULL) { + BLI_remlink(&snode->nodetree->links, link); + MEM_freeN(link); + } + + allqueue(REDRAWNODE, 0); + + return 1; +} + +static int node_draw_link(SpaceNode *snode) +{ + bNode *node; + bNodeSocket *sock; + + /* we're going to draw an output */ + if(find_indicated_socket(snode, &node, &sock, SOCK_OUT)) { + return node_draw_link_drag(snode, node, sock, SOCK_OUT); + } + if(find_indicated_socket(snode, &node, &sock, SOCK_IN)) { + bNodeLink *link; + + /* find if we break a link */ + for(link= snode->nodetree->links.first; link; link= link->next) { + if(link->tosock==sock) + break; + } + if(link) { + node= link->fromnode; + sock= link->fromsock; + BLI_remlink(&snode->nodetree->links, link); + MEM_freeN(link); + return node_draw_link_drag(snode, node, sock, SOCK_OUT); + } + else { + /* link from input to output */ + return node_draw_link_drag(snode, node, sock, SOCK_IN); + } + } + + return 0; +} + +/* ******************** main event loop ****************** */ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt) { @@ -323,11 +623,12 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt) switch(event) { case LEFTMOUSE: - node_select(snode); + if(node_draw_link(snode)==0) + node_mouse_select(snode); break; case RIGHTMOUSE: - node_select(snode); + node_mouse_select(snode); break; case MIDDLEMOUSE: @@ -356,14 +657,19 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt) case AKEY: if(G.qual==LR_SHIFTKEY) node_add_menu(snode); - else if(G.qual==0) + else if(G.qual==0) { node_deselectall(snode, 1); + BIF_undo_push("Deselect all nodes"); + } break; case DKEY: + if(G.qual==LR_SHIFTKEY) + node_adduplicate(snode); break; case CKEY: break; case GKEY: + transform_nodes(snode, "Translate Node"); break; case DELKEY: case XKEY: diff --git a/source/blender/src/editobject.c b/source/blender/src/editobject.c index 129e0fe2c66..027092a8b48 100644 --- a/source/blender/src/editobject.c +++ b/source/blender/src/editobject.c @@ -3537,9 +3537,14 @@ void std_rmouse_transform(void (*xf_func)(int, int)) short timer=0; short mousebut; - /* check for left mouse select/right mouse select user pref */ - if (U.flag & USER_LMOUSESELECT) mousebut = L_MOUSE; - else mousebut = R_MOUSE; + /* check for left mouse select/right mouse select */ + + if(curarea->spacetype==SPACE_NODE) + mousebut = L_MOUSE|R_MOUSE; + else if (U.flag & USER_LMOUSESELECT) + mousebut = L_MOUSE; + else + mousebut = R_MOUSE; getmouseco_areawin(mval); xo= mval[0]; diff --git a/source/blender/src/space.c b/source/blender/src/space.c index 899acdb4136..63bc2856a50 100644 --- a/source/blender/src/space.c +++ b/source/blender/src/space.c @@ -5248,7 +5248,7 @@ SpaceType *spacenode_get_type(void) if (!st) { st= spacetype_new("Node"); - spacetype_set_winfuncs(st, drawnodespace, NULL, winqreadnodespace); + spacetype_set_winfuncs(st, drawnodespace, changeview2dspace, winqreadnodespace); } return st;