diff options
-rw-r--r-- | source/blender/blenkernel/BKE_blender.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 16 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 1099 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 28 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 6 | ||||
-rw-r--r-- | source/blender/editors/space_node/drawnode.c | 9 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_draw.c | 350 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_edit.c | 339 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_intern.h | 4 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_ops.c | 4 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_select.c | 4 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 34 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_enum_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_main_api.c | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 72 |
15 files changed, 1230 insertions, 741 deletions
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index 7c6d7d81f2b..f6bb4fbb635 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -44,7 +44,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather then defining with quotes */ #define BLENDER_VERSION 256 -#define BLENDER_SUBVERSION 1 +#define BLENDER_SUBVERSION 2 #define BLENDER_MINVERSION 250 #define BLENDER_MINSUBVERSION 0 diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 0896dfbabb1..b637f5ab18e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -71,9 +71,6 @@ typedef struct bNodeSocketType { /* after this line is used internal only */ struct bNodeSocket *sock; /* used during verify_types */ - struct bNodeSocket *internsock; /* group nodes, the internal socket counterpart */ - int own_index; /* verify group nodes */ - } bNodeSocketType; typedef struct bNodeType { @@ -130,6 +127,10 @@ typedef struct bNodeType { #define NODE_CLASS_PATTERN 12 #define NODE_CLASS_TEXTURE 13 +/* enum values for input/output */ +#define SOCK_IN 1 +#define SOCK_OUT 2 + /* ************** GENERIC API, TREES *************** */ void ntreeVerifyTypes(struct bNodeTree *ntree); @@ -137,7 +138,7 @@ void ntreeVerifyTypes(struct bNodeTree *ntree); struct bNodeTree *ntreeAddTree(const char *name, int type, const short is_group); void ntreeInitTypes(struct bNodeTree *ntree); -void ntreeMakeOwnType(struct bNodeTree *ntree); +//void ntreeMakeGroupSockets(struct bNodeTree *ntree); void ntreeUpdateType(struct bNodeTree *ntree, struct bNodeType *ntype); void ntreeFreeTree(struct bNodeTree *ntree); struct bNodeTree *ntreeCopyTree(struct bNodeTree *ntree); @@ -178,7 +179,7 @@ void nodeUpdateType(struct bNodeTree *ntree, struct bNode* node, struct bNodeT void nodeMakeDynamicType(struct bNode *node); int nodeDynamicUnlinkText(struct ID *txtid); void nodeFreeNode(struct bNodeTree *ntree, struct bNode *node); -struct bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node, int internal); +struct bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node); struct bNodeLink *nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock); void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link); @@ -210,6 +211,11 @@ void nodeGroupSocketUseFlags(struct bNodeTree *ngroup); void nodeCopyGroup(struct bNode *gnode); +struct bNodeSocket *nodeAddGroupSocket(struct bNodeTree *ngroup, const char *name, int type, int in_out); +struct bNodeSocket *nodeAddGroupSocketCopy(struct bNodeTree *ngroup, struct bNodeSocket *copy, int in_out); +void nodeAddAllGroupSockets(struct bNodeTree *ngroup); +void nodeRemGroupSocket(struct bNodeTree *ngroup, struct bNodeSocket *gsock, int in_out); + /* ************** COMMON NODES *************** */ /* Init a new node type struct with default values and callbacks */ diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 057d90dc8bb..cebbab45928 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -31,12 +31,17 @@ #include <Python.h> #endif +#include "MEM_guardedalloc.h" + #include <stdlib.h> #include <stddef.h> #include <string.h> #include "DNA_anim_types.h" #include "DNA_action_types.h" +#include "DNA_node_types.h" + +#include "BLI_listbase.h" #include "RNA_access.h" @@ -45,6 +50,7 @@ #include "BKE_fcurve.h" #include "BKE_node.h" #include "BKE_utildefines.h" +#include "BKE_node.h" #include "PIL_time.h" @@ -64,22 +70,14 @@ ListBase node_all_textures = {NULL, NULL}; /* ************** Type stuff ********** */ -static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup, ID *id) +static bNodeType *node_get_type(bNodeTree *ntree, int type, ID *id) { - if(type==NODE_GROUP) { - if(ngroup && GS(ngroup->id.name)==ID_NT) { - return ngroup->owntype; - } - return NULL; - } - else { - bNodeType *ntype = ntree->alltypes.first; - for(; ntype; ntype= ntype->next) - if(ntype->type==type && id==ntype->id ) - return ntype; - - return NULL; - } + bNodeType *ntype = ntree->alltypes.first; + for(; ntype; ntype= ntype->next) + if(ntype->type==type && id==ntype->id ) + return ntype; + + return NULL; } void ntreeInitTypes(bNodeTree *ntree) @@ -102,11 +100,11 @@ void ntreeInitTypes(bNodeTree *ntree) if(node->type==NODE_DYNAMIC) { bNodeType *stype= NULL; if(node->id==NULL) { /* empty script node */ - stype= node_get_type(ntree, node->type, NULL, NULL); + stype= node_get_type(ntree, node->type, NULL); } else { /* not an empty script node */ - stype= node_get_type(ntree, node->type, NULL, node->id); + stype= node_get_type(ntree, node->type, node->id); if(!stype) { - stype= node_get_type(ntree, node->type, NULL, NULL); + stype= node_get_type(ntree, node->type, NULL); /* needed info if the pynode script fails now: */ if (node->id) node->storage= ntree; } else { @@ -118,7 +116,7 @@ void ntreeInitTypes(bNodeTree *ntree) if(node->typeinfo) node->typeinfo->initfunc(node); } else { - node->typeinfo= node_get_type(ntree, node->type, (bNodeTree *)node->id, NULL); + node->typeinfo= node_get_type(ntree, node->type, NULL); } if(node->typeinfo==NULL) { @@ -152,9 +150,6 @@ static bNodeSocket *node_add_socket_type(ListBase *lb, bNodeSocketType *stype) else sock->limit= stype->limit; sock->type= stype->type; - sock->to_index= stype->own_index; - sock->tosock= stype->internsock; - sock->ns.vec[0]= stype->val1; sock->ns.vec[1]= stype->val2; sock->ns.vec[2]= stype->val3; @@ -168,6 +163,30 @@ static bNodeSocket *node_add_socket_type(ListBase *lb, bNodeSocketType *stype) return sock; } +static bNodeSocket *node_add_group_socket(ListBase *lb, bNodeSocket *gsock) +{ + bNodeSocket *sock= MEM_callocN(sizeof(bNodeSocket), "sock"); + + /* make a copy of the group socket */ + *sock = *gsock; + sock->link = NULL; + sock->next = sock->prev = NULL; + sock->new_sock = NULL; + sock->ns.data = NULL; + + sock->own_index = gsock->own_index; + sock->groupsock = gsock; + /* XXX hack: group socket input/output roles are inverted internally, + * need to change the limit value when making actual node sockets from them. + */ + sock->limit = (gsock->limit==1 ? 0xFFF : 1); + + if(lb) + BLI_addtail(lb, sock); + + return sock; +} + static void node_rem_socket(bNodeTree *ntree, ListBase *lb, bNodeSocket *sock) { bNodeLink *link, *next; @@ -188,18 +207,16 @@ static bNodeSocket *verify_socket(ListBase *lb, bNodeSocketType *stype) bNodeSocket *sock; for(sock= lb->first; sock; sock= sock->next) { - /* both indices are zero for non-groups, otherwise it's a unique index */ - if(sock->to_index==stype->own_index) - if(strncmp(sock->name, stype->name, NODE_MAXSTR)==0) - break; + if(strncmp(sock->name, stype->name, NODE_MAXSTR)==0) + break; } if(sock) { sock->type= stype->type; /* in future, read this from tydefs! */ if(stype->limit==0) sock->limit= 0xFFF; else sock->limit= stype->limit; + sock->ns.min= stype->min; sock->ns.max= stype->max; - sock->tosock= stype->internsock; BLI_remlink(lb, sock); @@ -210,6 +227,37 @@ static bNodeSocket *verify_socket(ListBase *lb, bNodeSocketType *stype) } } +static bNodeSocket *verify_group_socket(ListBase *lb, bNodeSocket *gsock) +{ + bNodeSocket *sock; + + for(sock= lb->first; sock; sock= sock->next) { + if(sock->own_index==gsock->own_index) + break; + } + if(sock) { + sock->groupsock = gsock; + + strcpy(sock->name, gsock->name); + sock->type= gsock->type; + + /* XXX hack: group socket input/output roles are inverted internally, + * need to change the limit value when making actual node sockets from them. + */ + sock->limit = (gsock->limit==1 ? 0xFFF : 1); + + sock->ns.min= gsock->ns.min; + sock->ns.max= gsock->ns.max; + + BLI_remlink(lb, sock); + + return sock; + } + else { + return node_add_group_socket(NULL, gsock); + } +} + static void verify_socket_list(bNodeTree *ntree, ListBase *lb, bNodeSocketType *stype_first) { bNodeSocketType *stype; @@ -238,15 +286,41 @@ static void verify_socket_list(bNodeTree *ntree, ListBase *lb, bNodeSocketType * } } -void nodeVerifyType(bNodeTree *ntree, bNode *node) +static void verify_group_socket_list(bNodeTree *ntree, ListBase *lb, ListBase *glb) { - bNodeType *ntype= node->typeinfo; + bNodeSocket *gsock; - if(ntype) { - /* might add some other verify stuff here */ - - verify_socket_list(ntree, &node->inputs, ntype->inputs); - verify_socket_list(ntree, &node->outputs, ntype->outputs); + /* step by step compare */ + for (gsock= glb->first; gsock; gsock=gsock->next) { + /* abusing new_sock pointer for verification here! only used inside this function */ + gsock->new_sock= verify_group_socket(lb, gsock); + } + /* leftovers are removed */ + while(lb->first) + node_rem_socket(ntree, lb, lb->first); + /* and we put back the verified sockets */ + for (gsock= glb->first; gsock; gsock=gsock->next) { + BLI_addtail(lb, gsock->new_sock); + gsock->new_sock = NULL; + } +} + +void nodeVerifyType(bNodeTree *ntree, bNode *node) +{ + /* node groups don't have static sock lists, but use external sockets from the tree instead */ + if (node->type==NODE_GROUP) { + bNodeTree *ngroup= (bNodeTree*)node->id; + if (ngroup) { + verify_group_socket_list(ntree, &node->inputs, &ngroup->inputs); + verify_group_socket_list(ntree, &node->outputs, &ngroup->outputs); + } + } + else { + bNodeType *ntype= node->typeinfo; + if(ntype) { + verify_socket_list(ntree, &node->inputs, ntype->inputs); + verify_socket_list(ntree, &node->outputs, ntype->outputs); + } } } @@ -283,176 +357,20 @@ void register_node_type_group(ListBase *lb) nodeRegisterType(lb, &ntype_group); } -/* tag internal sockets */ -static void group_tag_internal_sockets(bNodeTree *ngroup) +static bNodeSocket *find_group_node_input(bNode *gnode, bNodeSocket *gsock) { - bNode *node; bNodeSocket *sock; - bNodeLink *link; - - /* clear intern tag, but check already for hidden sockets */ - for(node= ngroup->nodes.first; node; node= node->next) { - for(sock= node->inputs.first; sock; sock= sock->next) - sock->intern= sock->flag & SOCK_HIDDEN; - for(sock= node->outputs.first; sock; sock= sock->next) - sock->intern= sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL); - } - /* set tag */ - for(link= ngroup->links.first; link; link= link->next) { - link->fromsock->intern= 1; - link->tosock->intern= 1; - } - - /* remove link pointer to external links (only happens on create group) */ - for(node= ngroup->nodes.first; node; node= node->next) { - for(sock= node->inputs.first; sock; sock= sock->next) - if(sock->intern==0) - sock->link= NULL; - } - - /* set all intern sockets to own_index zero, makes sure that later use won't mixup */ - for(node= ngroup->nodes.first; node; node= node->next) { - for(sock= node->inputs.first; sock; sock= sock->next) - if(sock->intern) - sock->own_index= 0; - for(sock= node->outputs.first; sock; sock= sock->next) - if(sock->intern) - sock->own_index= 0; - } -} - -/* after editing group, new sockets are zero */ -/* this routine ensures unique identifiers for zero sockets that are exposed */ -static void group_verify_own_indices(bNodeTree *ngroup) -{ - bNode *node; - bNodeSocket *sock; - - for(node= ngroup->nodes.first; node; node= node->next) { - for(sock= node->inputs.first; sock; sock= sock->next) - if(sock->own_index==0 && sock->intern==0) - sock->own_index= ++(ngroup->cur_index); - for(sock= node->outputs.first; sock; sock= sock->next) - if(sock->own_index==0 && sock->intern==0) - sock->own_index= ++(ngroup->cur_index); - } - //printf("internal index %d\n", ngroup->cur_index); -} - - -/* nodetrees can be used as groups, so we need typeinfo structs generated */ -void ntreeMakeOwnType(bNodeTree *ngroup) -{ - bNode *node; - bNodeSocket *sock; - int totin= 0, totout=0, a; - - /* tags socket when internal linked */ - group_tag_internal_sockets(ngroup); - - /* ensure all sockets have own unique id */ - group_verify_own_indices(ngroup); - - /* counting stats */ - for(node= ngroup->nodes.first; node; node= node->next) { - if(node->type==NODE_GROUP) - break; - for(sock= node->inputs.first; sock; sock= sock->next) - if(sock->intern==0) - totin++; - for(sock= node->outputs.first; sock; sock= sock->next) - if(sock->intern==0) - totout++; - } - /* debug: nodetrees in nodetrees not handled yet */ - if(node) { - printf("group in group, not supported yet\n"); - return; - } - - /* free own type struct */ - if(ngroup->owntype) { - if(ngroup->owntype->inputs) - MEM_freeN(ngroup->owntype->inputs); - if(ngroup->owntype->outputs) - MEM_freeN(ngroup->owntype->outputs); - MEM_freeN(ngroup->owntype); - } - - /* make own type struct */ - ngroup->owntype= MEM_callocN(sizeof(bNodeType), "group type"); - *ngroup->owntype= ntype_group; /* copy data, for init */ - - /* input type arrays */ - if(totin) { - bNodeSocketType *stype; - bNodeSocketType *inputs= MEM_callocN(sizeof(bNodeSocketType)*(totin+1), "bNodeSocketType"); - a= 0; - - for(node= ngroup->nodes.first; node; node= node->next) { - /* nodes are presumed fully verified, stype and socket list are in sync */ - stype= node->typeinfo->inputs; - for(sock= node->inputs.first; sock; sock= sock->next, stype++) { - if(sock->intern==0) { - /* debug only print */ - if(stype==NULL || stype->type==-1) printf("group verification error %s\n", ngroup->id.name); - - inputs[a]= *stype; - inputs[a].own_index= sock->own_index; - inputs[a].internsock= sock; - a++; - } - } - } - inputs[a].type= -1; /* terminator code */ - ngroup->owntype->inputs= inputs; - } - - /* output type arrays */ - if(totout) { - bNodeSocketType *stype; - bNodeSocketType *outputs= MEM_callocN(sizeof(bNodeSocketType)*(totout+1), "bNodeSocketType"); - a= 0; - - for(node= ngroup->nodes.first; node; node= node->next) { - /* nodes are presumed fully verified, stype and socket list are in sync */ - stype= node->typeinfo->outputs; - for(sock= node->outputs.first; sock; sock= sock->next, stype++) { - if(sock->intern==0) { - /* debug only print */ - if(stype==NULL || stype->type==-1) printf("group verification error %s\n", ngroup->id.name); - - outputs[a]= *stype; - outputs[a].own_index= sock->own_index; - outputs[a].internsock= sock; - a++; - } - } - } - outputs[a].type= -1; /* terminator code */ - ngroup->owntype->outputs= outputs; - } - - /* voila, the nodetree has the full definition for generating group-node instances! */ -} - - -static bNodeSocket *groupnode_find_tosock(bNode *gnode, int index) -{ - bNodeSocket *sock; - - for(sock= gnode->inputs.first; sock; sock= sock->next) - if(sock->to_index==index) + for (sock=gnode->inputs.first; sock; sock=sock->next) + if (sock->groupsock == gsock) return sock; return NULL; } -static bNodeSocket *groupnode_find_fromsock(bNode *gnode, int index) +static bNodeSocket *find_group_node_output(bNode *gnode, bNodeSocket *gsock) { bNodeSocket *sock; - - for(sock= gnode->outputs.first; sock; sock= sock->next) - if(sock->to_index==index) + for (sock=gnode->outputs.first; sock; sock=sock->next) + if (sock->groupsock == gsock) return sock; return NULL; } @@ -461,8 +379,8 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) { bNodeLink *link, *linkn; bNode *node, *gnode, *nextn; - bNodeSocket *sock; bNodeTree *ngroup; + bNodeSocket *gsock; ListBase anim_basepaths = {NULL, NULL}; float min[2], max[2]; int totnode=0; @@ -485,9 +403,9 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) /* check if all connections are OK, no unselected node has both inputs and outputs to a selection */ for(link= ntree->links.first; link; link= link->next) { - if(link->fromnode->flag & NODE_SELECT) + if(link->fromnode && link->tonode && link->fromnode->flag & NODE_SELECT) link->tonode->done |= 1; - if(link->tonode->flag & NODE_SELECT) + if(link->fromnode && link->tonode && link->tonode->flag & NODE_SELECT) link->fromnode->done |= 2; } @@ -526,26 +444,9 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) node->locx-= 0.5f*(min[0]+max[0]); node->locy-= 0.5f*(min[1]+max[1]); - - /* set socket own_index to zero since it can still have a value - * from being in a group before, otherwise it doesn't get a unique - * index in group_verify_own_indices */ - for(sock= node->inputs.first; sock; sock= sock->next) - sock->own_index= 0; - for(sock= node->outputs.first; sock; sock= sock->next) - sock->own_index= 0; } } - /* move links over */ - for(link= ntree->links.first; link; link= linkn) { - linkn= link->next; - if(link->fromnode->flag & link->tonode->flag & NODE_SELECT) { - BLI_remlink(&ntree->links, link); - BLI_addtail(&ngroup->links, link); - } - } - /* move animation data over */ if (ntree->adt) { LinkData *ld, *ldn=NULL; @@ -561,9 +462,6 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) } } - /* now we can make own group typeinfo */ - ntreeMakeOwnType(ngroup); - /* make group node */ gnode= nodeAddNodeType(ntree, NODE_GROUP, ngroup, NULL); gnode->locx= 0.5f*(min[0]+max[0]); @@ -573,35 +471,29 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) for(link= ntree->links.first; link; link= linkn) { linkn= link->next; - if(link->tonode->flag & NODE_SELECT) { - link->tonode= gnode; - sock= groupnode_find_tosock(gnode, link->tosock->own_index); - if(sock==NULL) { - nodeRemLink(ntree, link); - printf("Removed link, cannot mix internal and external sockets in group\n"); - } - else link->tosock= sock; + if(link->fromnode && link->tonode && (link->fromnode->flag & link->tonode->flag & NODE_SELECT)) { + BLI_remlink(&ntree->links, link); + BLI_addtail(&ngroup->links, link); } - else if(link->fromnode->flag & NODE_SELECT) { - link->fromnode= gnode; - sock= groupnode_find_fromsock(gnode, link->fromsock->own_index); - if(sock==NULL) { - nodeRemLink(ntree, link); - printf("Removed link, cannot mix internal and external sockets in group\n"); - } - else link->fromsock= sock; + else if(link->tonode && (link->tonode->flag & NODE_SELECT)) { + gsock = nodeAddGroupSocketCopy(ngroup, link->tosock, SOCK_IN); + link->tosock->link = nodeAddLink(ngroup, NULL, gsock, link->tonode, link->tosock); + link->tosock = node_add_group_socket(&gnode->inputs, gsock); + link->tonode = gnode; } - } - - /* initialize variables of unused input sockets */ - for(node= ngroup->nodes.first; node; node= node->next) { - for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->intern==0) { - bNodeSocket *nsock= groupnode_find_tosock(gnode, sock->own_index); - if(nsock) { - QUATCOPY(nsock->ns.vec, sock->ns.vec); - } + else if(link->fromnode && (link->fromnode->flag & NODE_SELECT)) { + /* search for existing group node socket */ + for (gsock=ngroup->outputs.first; gsock; gsock=gsock->next) + if (gsock->link && gsock->link->fromsock==link->fromsock) + break; + if (!gsock) { + gsock = nodeAddGroupSocketCopy(ngroup, link->fromsock, SOCK_OUT); + gsock->link = nodeAddLink(ngroup, link->fromnode, link->fromsock, NULL, gsock); + link->fromsock = node_add_group_socket(&gnode->outputs, gsock); } + else + link->fromsock = find_group_node_output(gnode, gsock); + link->fromnode = gnode; } } @@ -611,41 +503,21 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) return gnode; } -/* note: ungroup: group_indices zero! */ - /* here's a nasty little one, need to check users... */ /* should become callbackable... */ void nodeVerifyGroup(bNodeTree *ngroup) { - /* XXX nodeVerifyGroup is sometimes called for non-group trees. - * This is not the best way to check if a tree is a group, - * trees should get their own flag for this! - */ - if (!ngroup->owntype) - return; - /* group changed, so we rebuild the type definition */ - ntreeMakeOwnType(ngroup); +// ntreeMakeGroupSockets(ngroup); if(ngroup->type==NTREE_SHADER) { Material *ma; for(ma= G.main->mat.first; ma; ma= ma->id.next) { if(ma->nodetree) { bNode *node; - - /* find if group is in tree */ for(node= ma->nodetree->nodes.first; node; node= node->next) if(node->id == (ID *)ngroup) - break; - - if(node) { - /* set all type pointers OK */ - ntreeInitTypes(ma->nodetree); - - for(node= ma->nodetree->nodes.first; node; node= node->next) - if(node->id == (ID *)ngroup) - nodeVerifyType(ma->nodetree, node); - } + nodeVerifyType(ma->nodetree, node); } } } @@ -654,20 +526,9 @@ void nodeVerifyGroup(bNodeTree *ngroup) for(sce= G.main->scene.first; sce; sce= sce->id.next) { if(sce->nodetree) { bNode *node; - - /* find if group is in tree */ for(node= sce->nodetree->nodes.first; node; node= node->next) if(node->id == (ID *)ngroup) - break; - - if(node) { - /* set all type pointers OK */ - ntreeInitTypes(sce->nodetree); - - for(node= sce->nodetree->nodes.first; node; node= node->next) - if(node->id == (ID *)ngroup) - nodeVerifyType(sce->nodetree, node); - } + nodeVerifyType(sce->nodetree, node); } } } @@ -676,20 +537,9 @@ void nodeVerifyGroup(bNodeTree *ngroup) for(tx= G.main->tex.first; tx; tx= tx->id.next) { if(tx->nodetree) { bNode *node; - - /* find if group is in tree */ for(node= tx->nodetree->nodes.first; node; node= node->next) if(node->id == (ID *)ngroup) - break; - - if(node) { - /* set all type pointers OK */ - ntreeInitTypes(tx->nodetree); - - for(node= tx->nodetree->nodes.first; node; node= node->next) - if(node->id == (ID *)ngroup) - nodeVerifyType(tx->nodetree, node); - } + nodeVerifyType(tx->nodetree, node); } } } @@ -716,15 +566,15 @@ void nodeGroupSocketUseFlags(bNodeTree *ngroup) for(ma= G.main->mat.first; ma; ma= ma->id.next) { if(ma->nodetree) { for(node= ma->nodetree->nodes.first; node; node= node->next) { - if(node->id==(ID *)ngroup) { + if(node->id==&ngroup->id) { for(sock= node->inputs.first; sock; sock= sock->next) if(sock->link) - if(sock->tosock) - sock->tosock->flag |= SOCK_IN_USE; + if(sock->groupsock) + sock->groupsock->flag |= SOCK_IN_USE; for(sock= node->outputs.first; sock; sock= sock->next) if(nodeCountSocketLinks(ma->nodetree, sock)) - if(sock->tosock) - sock->tosock->flag |= SOCK_IN_USE; + if(sock->groupsock) + sock->groupsock->flag |= SOCK_IN_USE; } } } @@ -738,12 +588,12 @@ void nodeGroupSocketUseFlags(bNodeTree *ngroup) if(node->id==(ID *)ngroup) { for(sock= node->inputs.first; sock; sock= sock->next) if(sock->link) - if(sock->tosock) - sock->tosock->flag |= SOCK_IN_USE; + if(sock->groupsock) + sock->groupsock->flag |= SOCK_IN_USE; for(sock= node->outputs.first; sock; sock= sock->next) if(nodeCountSocketLinks(sce->nodetree, sock)) - if(sock->tosock) - sock->tosock->flag |= SOCK_IN_USE; + if(sock->groupsock) + sock->groupsock->flag |= SOCK_IN_USE; } } } @@ -757,12 +607,12 @@ void nodeGroupSocketUseFlags(bNodeTree *ngroup) if(node->id==(ID *)ngroup) { for(sock= node->inputs.first; sock; sock= sock->next) if(sock->link) - if(sock->tosock) - sock->tosock->flag |= SOCK_IN_USE; + if(sock->groupsock) + sock->groupsock->flag |= SOCK_IN_USE; for(sock= node->outputs.first; sock; sock= sock->next) if(nodeCountSocketLinks(tx->nodetree, sock)) - if(sock->tosock) - sock->tosock->flag |= SOCK_IN_USE; + if(sock->groupsock) + sock->groupsock->flag |= SOCK_IN_USE; } } } @@ -813,7 +663,6 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) bNode *node, *nextn; bNodeTree *ngroup, *wgroup; ListBase anim_basepaths = {NULL, NULL}; - int index; ngroup= (bNodeTree *)gnode->id; if(ngroup==NULL) return 0; @@ -855,7 +704,66 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) node->flag |= NODE_SELECT; } - /* and the internal links */ + + /* restore external links to and from the gnode */ + for(link= ntree->links.first; link; link= link->next) { + if (link->fromnode==gnode) { + if (link->fromsock->groupsock) { + bNodeSocket *gsock= link->fromsock->groupsock; + if (gsock->link) { + if (gsock->link->fromnode) { + /* NB: using the new internal copies here! the groupsock pointer still maps to the old tree */ + link->fromnode = (gsock->link->fromnode ? gsock->link->fromnode->new_node : NULL); + link->fromsock = gsock->link->fromsock->new_sock; + } + else { + /* group output directly maps to group input */ + bNodeSocket *insock= find_group_node_input(gnode, gsock->link->fromsock); + if (insock->link) { + link->fromnode = insock->link->fromnode; + link->fromsock = insock->link->fromsock; + } + } + } + else { + /* constant group output: copy the stack value to the external socket. + * the link is kept here until all possible external users have been fixed. + */ + QUATCOPY(link->tosock->ns.vec, gsock->ns.vec); + } + } + } + } + /* remove internal output links, these are not used anymore */ + for(link=wgroup->links.first; link; link= linkn) { + linkn = link->next; + if (!link->tonode) + nodeRemLink(wgroup, link); + } + /* restore links from internal nodes */ + for(link= wgroup->links.first; link; link= link->next) { + /* indicates link to group input */ + if (!link->fromnode) { + /* NB: can't use find_group_node_input here, + * because gnode sockets still point to the old tree! + */ + bNodeSocket *insock; + for (insock= gnode->inputs.first; insock; insock= insock->next) + if (insock->groupsock->new_sock == link->fromsock) + break; + if (insock->link) { + link->fromnode = insock->link->fromnode; + link->fromsock = insock->link->fromsock; + } + else { + /* uses group constant input. copy the input value and remove the dead link. */ + QUATCOPY(link->tosock->ns.vec, insock->ns.vec); + nodeRemLink(wgroup, link); + } + } + } + + /* add internal links to the ntree */ for(link= wgroup->links.first; link; link= linkn) { linkn= link->next; BLI_remlink(&wgroup->links, link); @@ -884,37 +792,15 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) /* free temp action too */ free_libblock(&G.main->action, waction); } - - /* restore links to and from the gnode */ - for(link= ntree->links.first; link; link= link->next) { - if(link->tonode==gnode) { - /* link->tosock->tosock is on the node we look for */ - nodeFindNode(ngroup, link->tosock->tosock, &nextn, &index); - if(nextn==NULL) printf("wrong stuff!\n"); - else if(nextn->new_node==NULL) printf("wrong stuff too!\n"); - else { - link->tonode= nextn->new_node; - link->tosock= BLI_findlink(&link->tonode->inputs, index); - } - } - else if(link->fromnode==gnode) { - /* link->fromsock->tosock is on the node we look for */ - nodeFindNode(ngroup, link->fromsock->tosock, &nextn, &index); - if(nextn==NULL) printf("1 wrong stuff!\n"); - else if(nextn->new_node==NULL) printf("1 wrong stuff too!\n"); - else { - link->fromnode= nextn->new_node; - link->fromsock= BLI_findlink(&link->fromnode->outputs, index); - } - } - } - - /* remove the gnode & work tree */ - free_libblock(&G.main->nodetree, wgroup); + /* delete the group instance. this also removes old input links! */ nodeFreeNode(ntree, gnode); + /* free the group tree (takes care of user count) */ + free_libblock(&G.main->nodetree, wgroup); + /* solve order goes fine, but the level tags not... doing it twice works for now. solve this once */ + /* XXX is this still necessary with new groups? it may have been caused by non-updated sock->link pointers. lukas */ ntreeSolveOrder(ntree); ntreeSolveOrder(ntree); @@ -930,34 +816,126 @@ void nodeCopyGroup(bNode *gnode) /* new_sock was set in nodeCopyNode */ for(sock=gnode->inputs.first; sock; sock=sock->next) - if(sock->tosock) - sock->tosock= sock->tosock->new_sock; + if(sock->groupsock) + sock->groupsock= sock->groupsock->new_sock; for(sock=gnode->outputs.first; sock; sock=sock->next) - if(sock->tosock) - sock->tosock= sock->tosock->new_sock; + if(sock->groupsock) + sock->groupsock= sock->groupsock->new_sock; +} + +bNodeSocket *nodeAddGroupSocket(bNodeTree *ngroup, const char *name, int type, int in_out) +{ + bNodeSocket *gsock = MEM_callocN(sizeof(bNodeSocket), "bNodeSocket"); + + strncpy(gsock->name, name, sizeof(gsock->name)); + gsock->type = type; + gsock->ns.min = INT_MIN; + gsock->ns.max = INT_MAX; + zero_v4(gsock->ns.vec); + gsock->ns.data = NULL; + gsock->flag = 0; + + gsock->next = gsock->prev = NULL; + gsock->new_sock = NULL; + gsock->link = NULL; + gsock->ns.data = NULL; + /* assign new unique index */ + gsock->own_index = ngroup->cur_index++; + gsock->limit = (in_out==SOCK_IN ? 0xFFF : 1); + + BLI_addtail(in_out==SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock); + + return gsock; +} + +bNodeSocket *nodeAddGroupSocketCopy(bNodeTree *ngroup, bNodeSocket *copy, int in_out) +{ + bNodeSocket *gsock = MEM_callocN(sizeof(bNodeSocket), "bNodeSocket"); + + /* copy name type and data */ + strcpy(gsock->name, copy->name); + gsock->type = copy->type; + gsock->ns = copy->ns; + gsock->ns.data = NULL; + gsock->flag = copy->flag; + + gsock->next = gsock->prev = NULL; + gsock->new_sock = NULL; + gsock->groupsock = NULL; + gsock->link = NULL; + /* assign new unique index */ + gsock->own_index = ngroup->cur_index++; + gsock->limit = (in_out==SOCK_IN ? 0xFFF : 1); + + BLI_addtail(in_out==SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock); + + return gsock; +} + +void nodeAddAllGroupSockets(bNodeTree *ngroup) +{ + bNode *node; + bNodeSocket *sock, *gsock; + + for (node=ngroup->nodes.first; node; node=node->next) { + for (sock=node->inputs.first; sock; sock=sock->next) { + if (!sock->link && !(sock->flag & SOCK_HIDDEN)) { + gsock = nodeAddGroupSocketCopy(ngroup, sock, SOCK_IN); + sock->link = nodeAddLink(ngroup, NULL, gsock, node, sock); + } + } + for (sock=node->outputs.first; sock; sock=sock->next) { + if (nodeCountSocketLinks(ngroup, sock)==0 && !(sock->flag & SOCK_HIDDEN)) { + gsock = nodeAddGroupSocketCopy(ngroup, sock, SOCK_OUT); + gsock->link = nodeAddLink(ngroup, node, sock, NULL, gsock); + } + } + } +} + +void nodeRemGroupSocket(bNodeTree *ngroup, bNodeSocket *gsock, int in_out) +{ + nodeRemSocketLinks(ngroup, gsock); + switch (in_out) { + case SOCK_IN: BLI_remlink(&ngroup->inputs, gsock); break; + case SOCK_OUT: BLI_remlink(&ngroup->outputs, gsock); break; + } } /* ************** Add stuff ********** */ void nodeAddSockets(bNode *node, bNodeType *ntype) { - bNodeSocketType *stype; - - if(ntype->inputs) { - stype= ntype->inputs; - while(stype->type != -1) { - node_add_socket_type(&node->inputs, stype); - stype++; + if (node->type==NODE_GROUP) { + bNodeTree *ntree= (bNodeTree*)node->id; + if (ntree) { + bNodeSocket *gsock; + for (gsock=ntree->inputs.first; gsock; gsock=gsock->next) + node_add_group_socket(&node->inputs, gsock); + for (gsock=ntree->outputs.first; gsock; gsock=gsock->next) + node_add_group_socket(&node->outputs, gsock); } } - if(ntype->outputs) { - stype= ntype->outputs; - while(stype->type != -1) { - node_add_socket_type(&node->outputs, stype); - stype++; + else { + bNodeSocketType *stype; + + if(ntype->inputs) { + stype= ntype->inputs; + while(stype->type != -1) { + node_add_socket_type(&node->inputs, stype); + stype++; + } + } + if(ntype->outputs) { + stype= ntype->outputs; + while(stype->type != -1) { + node_add_socket_type(&node->outputs, stype); + stype++; + } } } } + /* Find the first available, non-duplicate name for a given node */ void nodeUniqueName(bNodeTree *ntree, bNode *node) { @@ -986,7 +964,7 @@ bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup, ID *id) ntype= ntype->next; } } else - ntype= node_get_type(ntree, type, ngroup, id); + ntype= node_get_type(ntree, type, id); node= MEM_callocN(sizeof(bNode), "new node"); BLI_addtail(&ntree->nodes, node); @@ -1050,7 +1028,7 @@ void nodeUpdateType(bNodeTree *ntree, bNode* node, bNodeType *ntype) /* keep socket listorder identical, for copying links */ /* ntree is the target tree */ -bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node, int internal) +bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node) { bNode *nnode= MEM_callocN(sizeof(bNode), "dupli node"); bNodeSocket *sock, *oldsock; @@ -1064,15 +1042,11 @@ bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node, int internal) oldsock= node->inputs.first; for(sock= nnode->inputs.first; sock; sock= sock->next, oldsock= oldsock->next) { oldsock->new_sock= sock; - if(internal) - sock->own_index= 0; } BLI_duplicatelist(&nnode->outputs, &node->outputs); oldsock= node->outputs.first; for(sock= nnode->outputs.first; sock; sock= sock->next, oldsock= oldsock->next) { - if(internal) - sock->own_index= 0; sock->stack_index= 0; sock->ns.data= NULL; oldsock->new_sock= sock; @@ -1098,7 +1072,7 @@ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNodeLink *link= NULL; int from= 0, to= 0; - if(fromsock) { + if(fromnode) { /* test valid input */ for(sock= fromnode->outputs.first; sock; sock= sock->next) if(sock==fromsock) @@ -1113,7 +1087,7 @@ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, from= -1; /* OK but flip */ } } - if(tosock) { + if(tonode) { for(sock= tonode->inputs.first; sock; sock= sock->next) if(sock==tosock) break; @@ -1199,9 +1173,8 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree) { bNodeTree *newtree; bNode *node, *nnode, *last; - bNodeLink *link, *nlink; - bNodeSocket *sock; - int a; + bNodeLink *link; + bNodeSocket *gsock, *oldgsock; if(ntree==NULL) return NULL; @@ -1220,45 +1193,34 @@ bNodeTree *ntreeCopyTree(bNodeTree *ntree) last = ntree->nodes.last; for(node= ntree->nodes.first; node; node= node->next) { node->new_node= NULL; - nnode= nodeCopyNode(newtree, node, 0); /* sets node->new */ - - /* make sure we don't copy new nodes again! */ - if (node==last) - break; + nnode= nodeCopyNode(newtree, node); /* sets node->new */ + if(node==last) break; } - /* check for copying links */ - for(link= ntree->links.first; link; link= link->next) { - if(link->fromnode==NULL || link->tonode==NULL); - else if(link->fromnode->new_node && link->tonode->new_node) { - nlink= nodeAddLink(newtree, link->fromnode->new_node, NULL, link->tonode->new_node, NULL); - /* sockets were copied in order */ - for(a=0, sock= link->fromnode->outputs.first; sock; sock= sock->next, a++) { - if(sock==link->fromsock) - break; - } - nlink->fromsock= BLI_findlink(&link->fromnode->new_node->outputs, a); - - for(a=0, sock= link->tonode->inputs.first; sock; sock= sock->next, a++) { - if(sock==link->tosock) - break; - } - nlink->tosock= BLI_findlink(&link->tonode->new_node->inputs, a); - } + /* socket definition for group usage */ + BLI_duplicatelist(&newtree->inputs, &ntree->inputs); + for(gsock= newtree->inputs.first, oldgsock= ntree->inputs.first; gsock; gsock=gsock->next, oldgsock=oldgsock->next) { + oldgsock->new_sock= gsock; + gsock->groupsock = (oldgsock->groupsock ? oldgsock->groupsock->new_sock : NULL); } - /* own type definition for group usage */ - if(ntree->owntype) { - newtree->owntype= MEM_dupallocN(ntree->owntype); - if(ntree->owntype->inputs) - newtree->owntype->inputs= MEM_dupallocN(ntree->owntype->inputs); - if(ntree->owntype->outputs) - newtree->owntype->outputs= MEM_dupallocN(ntree->owntype->outputs); + BLI_duplicatelist(&newtree->outputs, &ntree->outputs); + for(gsock= newtree->outputs.first, oldgsock= ntree->outputs.first; gsock; gsock=gsock->next, oldgsock=oldgsock->next) { + oldgsock->new_sock= gsock; + gsock->groupsock = (oldgsock->groupsock ? oldgsock->groupsock->new_sock : NULL); + } + + /* copy links */ + BLI_duplicatelist(&newtree->links, &ntree->links); + for(link= newtree->links.first; link; link= link->next) { + link->fromnode = (link->fromnode ? link->fromnode->new_node : NULL); + link->fromsock = (link->fromsock ? link->fromsock->new_sock : NULL); + link->tonode = (link->tonode ? link->tonode->new_node : NULL); + link->tosock = (link->tosock ? link->tosock->new_sock : NULL); + /* update the link socket's pointer */ + if (link->tosock) + link->tosock->link = link; } - - /* weird this is required... there seem to be link pointers wrong still? */ - /* anyhoo, doing this solves crashes on copying entire tree (copy scene) and delete nodes */ - ntreeSolveOrder(newtree); return newtree; } @@ -1397,7 +1359,8 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node) if(link->fromnode==node) { lb= &node->outputs; - NodeTagChanged(ntree, link->tonode); + if (link->tonode) + NodeTagChanged(ntree, link->tonode); } else if(link->tonode==node) lb= &node->inputs; @@ -1467,13 +1430,8 @@ void ntreeFreeTree(bNodeTree *ntree) nodeFreeNode(ntree, node); } - if(ntree->owntype) { - if(ntree->owntype->inputs) - MEM_freeN(ntree->owntype->inputs); - if(ntree->owntype->outputs) - MEM_freeN(ntree->owntype->outputs); - MEM_freeN(ntree->owntype); - } + BLI_freelistN(&ntree->inputs); + BLI_freelistN(&ntree->outputs); } void ntreeFreeCache(bNodeTree *ntree) @@ -1807,7 +1765,7 @@ static int node_recurs_check(bNode *node, bNode ***nsort, int level) if(sock->link) { has_inputlinks= 1; fromnode= sock->link->fromnode; - if(fromnode->done==0) { + if(fromnode && fromnode->done==0) { fromnode->level= node_recurs_check(fromnode, nsort, level); } } @@ -1893,6 +1851,9 @@ void ntreeSolveOrder(bNodeTree *ntree) for(sock= node->inputs.first; sock; sock= sock->next) sock->link= NULL; } + /* clear group socket links */ + for(sock= ntree->outputs.first; sock; sock= sock->next) + sock->link= NULL; if(totnode==0) return; @@ -1970,33 +1931,40 @@ int NodeTagIDChanged(bNodeTree *ntree, ID *id) /* ******************* executing ************* */ +/* for a given socket, find the actual stack entry */ +static bNodeStack *get_socket_stack(bNodeStack *stack, bNodeSocket *sock, bNodeStack **gin) +{ + switch (sock->stack_type) { + case SOCK_STACK_LOCAL: + return stack + sock->stack_index; + case SOCK_STACK_EXTERN: + return (gin ? gin[sock->stack_index] : NULL); + case SOCK_STACK_CONST: + return sock->stack_ptr; + } + return NULL; +} + /* see notes at ntreeBeginExecTree */ -static void group_node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out, bNodeStack **gin, bNodeStack **gout) +static void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out, bNodeStack **gin) { bNodeSocket *sock; /* build pointer stack */ - for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->intern) { - /* yep, intern can have link or is hidden socket */ - if(sock->link) - *(in++)= stack + sock->link->fromsock->stack_index; - else - *(in++)= &sock->ns; + if (in) { + for(sock= node->inputs.first; sock; sock= sock->next) { + *(in++) = get_socket_stack(stack, sock, gin); } - else - *(in++)= gin[sock->stack_index_ext]; } - for(sock= node->outputs.first; sock; sock= sock->next) { - if(sock->intern) - *(out++)= stack + sock->stack_index; - else - *(out++)= gout[sock->stack_index_ext]; + if (out) { + for(sock= node->outputs.first; sock; sock= sock->next) { + *(out++) = get_socket_stack(stack, sock, gin); + } } } -static void node_group_execute(bNodeStack *stack, void *data, bNode *gnode, bNodeStack **in, bNodeStack **out) +static void node_group_execute(bNodeStack *stack, void *data, bNode *gnode, bNodeStack **in) { bNode *node; bNodeTree *ntree= (bNodeTree *)gnode->id; @@ -2009,7 +1977,7 @@ static void node_group_execute(bNodeStack *stack, void *data, bNode *gnode, bNod for(node= ntree->nodes.first; node; node= node->next) { if(node->typeinfo->execfunc) { - group_node_get_stack(node, stack, nsin, nsout, in, out); + node_get_stack(node, stack, nsin, nsout, in); /* for groups, only execute outputs for edited group */ if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) { @@ -2020,25 +1988,92 @@ static void node_group_execute(bNodeStack *stack, void *data, bNode *gnode, bNod node->typeinfo->execfunc(data, node, nsin, nsout); } } +} + +static int set_stack_indexes_default(bNode *node, int index) +{ + bNodeSocket *sock; - /* free internal group output nodes */ - if(ntree->type==NTREE_COMPOSIT) { - for(node= ntree->nodes.first; node; node= node->next) { - if(node->typeinfo->execfunc) { - bNodeSocket *sock; - - for(sock= node->outputs.first; sock; sock= sock->next) { - if(sock->intern) { - bNodeStack *ns= stack + sock->stack_index; - if(ns->data) { - free_compbuf(ns->data); - ns->data= NULL; - } - } - } + for (sock=node->inputs.first; sock; sock=sock->next) { + if (sock->link && sock->link->fromsock) { + sock->stack_type = sock->link->fromsock->stack_type; + sock->stack_index = sock->link->fromsock->stack_index; + sock->stack_ptr = sock->link->fromsock->stack_ptr; + } + else { + sock->stack_type = SOCK_STACK_CONST; + sock->stack_index = -1; + sock->stack_ptr = &sock->ns; + } + } + + for (sock=node->outputs.first; sock; sock=sock->next) { + sock->stack_type = SOCK_STACK_LOCAL; + sock->stack_index = index++; + sock->stack_ptr = NULL; + } + + return index; +} + +static int ntree_begin_exec_tree(bNodeTree *ntree); +static int set_stack_indexes_group(bNode *node, int index) +{ + bNodeTree *ngroup= (bNodeTree*)node->id; + bNodeSocket *sock; + + if((ngroup->init & NTREE_TYPE_INIT)==0) + ntreeInitTypes(ngroup); + + node->stack_index = index; + index += ntree_begin_exec_tree(ngroup); + + for (sock=node->inputs.first; sock; sock=sock->next) { + if (sock->link && sock->link->fromsock) { + sock->stack_type = sock->link->fromsock->stack_type; + sock->stack_index = sock->link->fromsock->stack_index; + sock->stack_ptr = sock->link->fromsock->stack_ptr; + } + else { + sock->stack_type = SOCK_STACK_CONST; + sock->stack_index = -1; + sock->stack_ptr = &sock->ns; + } + } + + /* identify group node outputs from internal group sockets */ + for(sock= node->outputs.first; sock; sock= sock->next) { + if (sock->groupsock) { + bNodeSocket *insock, *gsock = sock->groupsock; + switch (gsock->stack_type) { + case SOCK_STACK_EXTERN: + /* extern stack is resolved for this group node instance */ + insock= find_group_node_input(node, gsock->link->fromsock); + sock->stack_type = insock->stack_type; + sock->stack_index = insock->stack_index; + sock->stack_ptr = insock->stack_ptr; + break; + case SOCK_STACK_LOCAL: + sock->stack_type = SOCK_STACK_LOCAL; + /* local stack index must be offset by group node instance */ + sock->stack_index = gsock->stack_index + node->stack_index; + sock->stack_ptr = NULL; + break; + case SOCK_STACK_CONST: + sock->stack_type = SOCK_STACK_CONST; + sock->stack_index = -1; + sock->stack_ptr = gsock->stack_ptr; + break; } } + else { + sock->stack_type = SOCK_STACK_LOCAL; + sock->stack_index = index++; + sock->stack_ptr = NULL; + } } + + return index; } /* recursively called for groups */ @@ -2047,31 +2082,42 @@ static void node_group_execute(bNodeStack *stack, void *data, bNode *gnode, bNod static int ntree_begin_exec_tree(bNodeTree *ntree) { bNode *node; - bNodeSocket *sock; - int index= 0, index_in= 0, index_out= 0; + bNodeSocket *gsock; + int index= 0, i; if((ntree->init & NTREE_TYPE_INIT)==0) ntreeInitTypes(ntree); + /* group inputs are numbered 0..totinputs, so external stack can easily be addressed */ + i = 0; + for(gsock=ntree->inputs.first; gsock; gsock = gsock->next) { + gsock->stack_type = SOCK_STACK_EXTERN; + gsock->stack_index = i++; + gsock->stack_ptr = NULL; + } + /* create indices for stack, check preview */ for(node= ntree->nodes.first; node; node= node->next) { - - for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->intern==0) - sock->stack_index_ext= index_in++; - } - - for(sock= node->outputs.first; sock; sock= sock->next) { - sock->stack_index= index++; - if(sock->intern==0) - sock->stack_index_ext= index_out++; + /* XXX can this be done by a generic one-for-all function? + * otherwise should use node-type callback. + */ + if(node->type==NODE_GROUP) + index = set_stack_indexes_group(node, index); + else + index = set_stack_indexes_default(node, index); + } + + /* group outputs */ + for(gsock=ntree->outputs.first; gsock; gsock = gsock->next) { + if (gsock->link && gsock->link->fromsock) { + gsock->stack_type = gsock->link->fromsock->stack_type; + gsock->stack_index = gsock->link->fromsock->stack_index; + gsock->stack_ptr = gsock->link->fromsock->stack_ptr; } - - if(node->type==NODE_GROUP) { - if(node->id) { - node->stack_index= index; - index+= ntree_begin_exec_tree((bNodeTree *)node->id); - } + else { + gsock->stack_type = SOCK_STACK_CONST; + gsock->stack_index = -1; + gsock->stack_ptr = &gsock->ns; } } @@ -2079,7 +2125,7 @@ static int ntree_begin_exec_tree(bNodeTree *ntree) } /* copy socket compbufs to stack, initialize usage of curve nodes */ -static void composit_begin_exec(bNodeTree *ntree, int is_group) +static void composit_begin_exec(bNodeTree *ntree, bNodeStack *stack) { bNode *node; bNodeSocket *sock; @@ -2089,16 +2135,14 @@ static void composit_begin_exec(bNodeTree *ntree, int is_group) /* initialize needed for groups */ node->exec= 0; - if(is_group==0) { - for(sock= node->outputs.first; sock; sock= sock->next) { - bNodeStack *ns= ntree->stack + sock->stack_index; - - if(sock->ns.data) { - ns->data= sock->ns.data; - sock->ns.data= NULL; - } + for(sock= node->outputs.first; sock; sock= sock->next) { + bNodeStack *ns= get_socket_stack(stack, sock, NULL); + if(ns && sock->ns.data) { + ns->data= sock->ns.data; + sock->ns.data= NULL; } } + /* cannot initialize them while using in threads */ if(ELEM4(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT)) { curvemapping_initialize(node->storage); @@ -2106,52 +2150,39 @@ static void composit_begin_exec(bNodeTree *ntree, int is_group) curvemapping_premultiply(node->storage, 0); } if(node->type==NODE_GROUP) - composit_begin_exec((bNodeTree *)node->id, 1); + composit_begin_exec((bNodeTree *)node->id, stack + node->stack_index); } } /* copy stack compbufs to sockets */ -static void composit_end_exec(bNodeTree *ntree, int is_group) +static void composit_end_exec(bNodeTree *ntree, bNodeStack *stack) { bNode *node; bNodeStack *ns; - int a; for(node= ntree->nodes.first; node; node= node->next) { - if(is_group==0) { - bNodeSocket *sock; + bNodeSocket *sock; - for(sock= node->outputs.first; sock; sock= sock->next) { - ns= ntree->stack + sock->stack_index; - if(ns->data) { - sock->ns.data= ns->data; - ns->data= NULL; - } + for(sock= node->outputs.first; sock; sock= sock->next) { + ns = get_socket_stack(stack, sock, NULL); + if(ns && ns->data) { + sock->ns.data= ns->data; + ns->data= NULL; } } + if(node->type==CMP_NODE_CURVE_RGB) curvemapping_premultiply(node->storage, 1); if(node->type==NODE_GROUP) - composit_end_exec((bNodeTree *)node->id, 1); + composit_end_exec((bNodeTree *)node->id, stack + node->stack_index); node->need_exec= 0; } - - if(is_group==0) { - /* internally, group buffers are not stored */ - for(ns= ntree->stack, a=0; a<ntree->stacksize; a++, ns++) { - if(ns->data) { - printf("freed leftover buffer from stack\n"); - free_compbuf(ns->data); - ns->data= NULL; - } - } - } } -static void group_tag_used_outputs(bNode *gnode, bNodeStack *stack) +static void group_tag_used_outputs(bNode *gnode, bNodeStack *stack, bNodeStack **gin) { bNodeTree *ntree= (bNodeTree *)gnode->id; bNode *node; @@ -2163,15 +2194,8 @@ static void group_tag_used_outputs(bNode *gnode, bNodeStack *stack) bNodeSocket *sock; for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->intern) { - if(sock->link) { - bNodeStack *ns= stack + sock->link->fromsock->stack_index; - ns->hasoutput= 1; - ns->sockettype= sock->link->fromsock->type; - } - else - sock->ns.sockettype= sock->type; - } + bNodeStack *ns = get_socket_stack(stack, sock, gin); + ns->hasoutput= 1; } } } @@ -2231,6 +2255,8 @@ static void tex_end_exec(bNodeTree *ntree) void ntreeBeginExecTree(bNodeTree *ntree) { + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + /* let's make it sure */ if(ntree->init & NTREE_EXEC_INIT) return; @@ -2262,13 +2288,9 @@ void ntreeBeginExecTree(bNodeTree *ntree) node->need_exec= 1; for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->link) { - ns= ntree->stack + sock->link->fromsock->stack_index; - ns->hasoutput= 1; - ns->sockettype= sock->link->fromsock->type; - } - else - sock->ns.sockettype= sock->type; + ns = get_socket_stack(ntree->stack, sock, NULL); + if (ns) + ns->hasoutput = 1; if(sock->link) { bNodeLink *link= sock->link; @@ -2282,13 +2304,14 @@ void ntreeBeginExecTree(bNodeTree *ntree) } } - if(node->type==NODE_GROUP && node->id) - group_tag_used_outputs(node, ntree->stack); - + if(node->type==NODE_GROUP && node->id) { + node_get_stack(node, ntree->stack, nsin, NULL, NULL); + group_tag_used_outputs(node, ntree->stack, nsin); + } } if(ntree->type==NTREE_COMPOSIT) - composit_begin_exec(ntree, 0); + composit_begin_exec(ntree, ntree->stack); } ntree->init |= NTREE_EXEC_INIT; @@ -2296,14 +2319,24 @@ void ntreeBeginExecTree(bNodeTree *ntree) void ntreeEndExecTree(bNodeTree *ntree) { + bNodeStack *ns; if(ntree->init & NTREE_EXEC_INIT) { bNodeThreadStack *nts; int a; /* another callback candidate! */ - if(ntree->type==NTREE_COMPOSIT) - composit_end_exec(ntree, 0); + if(ntree->type==NTREE_COMPOSIT) { + composit_end_exec(ntree, ntree->stack); + + for(ns= ntree->stack, a=0; a<ntree->stacksize; a++, ns++) { + if(ns->data) { + printf("freed leftover buffer from stack\n"); + free_compbuf(ns->data); + ns->data= NULL; + } + } + } else if(ntree->type==NTREE_TEXTURE) tex_end_exec(ntree); @@ -2327,23 +2360,6 @@ void ntreeEndExecTree(bNodeTree *ntree) } } -static void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out) -{ - bNodeSocket *sock; - - /* build pointer stack */ - for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->link) - *(in++)= stack + sock->link->fromsock->stack_index; - else - *(in++)= &sock->ns; - } - - for(sock= node->outputs.first; sock; sock= sock->next) { - *(out++)= stack + sock->stack_index; - } -} - /* nodes are presorted, so exec is in order of list */ void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread) { @@ -2356,7 +2372,7 @@ void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread) /* only when initialized */ if((ntree->init & NTREE_EXEC_INIT)==0) ntreeBeginExecTree(ntree); - + /* composite does 1 node per thread, so no multiple stacks needed */ if(ntree->type==NTREE_COMPOSIT) { stack= ntree->stack; @@ -2369,12 +2385,12 @@ void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread) for(node= ntree->nodes.first; node; node= node->next) { if(node->need_exec) { if(node->typeinfo->execfunc) { - node_get_stack(node, stack, nsin, nsout); + node_get_stack(node, stack, nsin, nsout, NULL); node->typeinfo->execfunc(callerdata, node, nsin, nsout); } else if(node->type==NODE_GROUP && node->id) { - node_get_stack(node, stack, nsin, nsout); - node_group_execute(stack, callerdata, node, nsin, nsout); + node_get_stack(node, stack, nsin, NULL, NULL); + node_group_execute(stack, callerdata, node, nsin); } } } @@ -2420,7 +2436,7 @@ static void *exec_composite_node(void *node_v) bNode *node= node_v; ThreadData *thd= (ThreadData *)node->threaddata; - node_get_stack(node, thd->stack, nsin, nsout); + node_get_stack(node, thd->stack, nsin, nsout, NULL); if((node->flag & NODE_MUTED) && (!node_only_value(node))) { /* viewers we execute, for feedback to user */ @@ -2433,7 +2449,7 @@ static void *exec_composite_node(void *node_v) node->typeinfo->execfunc(thd->rd, node, nsin, nsout); } else if(node->type==NODE_GROUP && node->id) { - node_group_execute(thd->stack, thd->rd, node, nsin, nsout); + node_group_execute(thd->stack, thd->rd, node, nsin); } node->exec |= NODE_READY; @@ -2461,7 +2477,7 @@ static int setExecutableNodes(bNodeTree *ntree, ThreadData *thd) for(node= ntree->nodes.first; node; node= node->next) { int a; - node_get_stack(node, thd->stack, nsin, nsout); + node_get_stack(node, thd->stack, nsin, nsout, NULL); /* test the outputs */ /* skip value-only nodes (should be in type!) */ @@ -2526,7 +2542,7 @@ static int setExecutableNodes(bNodeTree *ntree, ThreadData *thd) for(node= ntree->nodes.first; node; node= node->next) { if(node->need_exec==0 && node_only_value(node)) { if(node->typeinfo->execfunc) { - node_get_stack(node, thd->stack, nsin, nsout); + node_get_stack(node, thd->stack, nsin, nsout, NULL); node->typeinfo->execfunc(thd->rd, node, nsin, nsout); } } @@ -2564,8 +2580,8 @@ static void freeExecutableNode(bNodeTree *ntree) for(node= ntree->nodes.first; node; node= node->next) { if(node->exec & NODE_FREEBUFS) { for(sock= node->outputs.first; sock; sock= sock->next) { - bNodeStack *ns= ntree->stack + sock->stack_index; - if(ns->data) { + bNodeStack *ns= get_socket_stack(ntree->stack, sock, NULL); + if(ns && ns->data) { free_compbuf(ns->data); ns->data= NULL; // printf("freed buf node %s \n", node->name); @@ -2585,7 +2601,7 @@ static bNode *getExecutableNode(bNodeTree *ntree) /* input sockets should be ready */ for(sock= node->inputs.first; sock; sock= sock->next) { - if(sock->link) + if(sock->link && sock->link->fromnode) if((sock->link->fromnode->exec & NODE_READY)==0) break; } @@ -2656,7 +2672,6 @@ void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview) if(BLI_available_threads(&threads)) { node= getExecutableNode(ntree); if(node) { - if(ntree->progress && totnode) ntree->progress(ntree->prh, (1.0 - curnode/(float)totnode)); if(ntree->stats_draw) { @@ -2923,7 +2938,7 @@ static void data_from_gpu_stack(ListBase *sockets, bNodeStack **ns, GPUNodeStack } } -static void gpu_node_group_execute(bNodeStack *stack, GPUMaterial *mat, bNode *gnode, bNodeStack **in, bNodeStack **out) +static void gpu_node_group_execute(bNodeStack *stack, GPUMaterial *mat, bNode *gnode, bNodeStack **in) { bNode *node; bNodeTree *ntree= (bNodeTree *)gnode->id; @@ -2938,7 +2953,7 @@ static void gpu_node_group_execute(bNodeStack *stack, GPUMaterial *mat, bNode *g for(node= ntree->nodes.first; node; node= node->next) { if(node->typeinfo->gpufunc) { - group_node_get_stack(node, stack, nsin, nsout, in, out); + node_get_stack(node, stack, nsin, nsout, in); doit = 0; @@ -2976,15 +2991,15 @@ void ntreeGPUMaterialNodes(bNodeTree *ntree, GPUMaterial *mat) for(node= ntree->nodes.first; node; node= node->next) { if(node->typeinfo->gpufunc) { - node_get_stack(node, stack, nsin, nsout); + node_get_stack(node, stack, nsin, nsout, NULL); gpu_from_node_stack(&node->inputs, nsin, gpuin); gpu_from_node_stack(&node->outputs, nsout, gpuout); if(node->typeinfo->gpufunc(mat, node, gpuin, gpuout)) data_from_gpu_stack(&node->outputs, nsout, gpuout); } else if(node->type==NODE_GROUP && node->id) { - node_get_stack(node, stack, nsin, nsout); - gpu_node_group_execute(stack, mat, node, nsin, nsout); + node_get_stack(node, stack, nsin, nsout, NULL); + gpu_node_group_execute(stack, mat, node, nsin); } } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c8ab53da177..3f722cc8cf3 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2046,8 +2046,7 @@ static void lib_verify_nodetree(Main *main, int UNUSED(open)) /* now create the own typeinfo structs an verify nodes */ /* here we still assume no groups in groups */ for(ntree= main->nodetree.first; ntree; ntree= ntree->id.next) { - ntreeVerifyTypes(ntree); /* internal nodes, no groups! */ - ntreeMakeOwnType(ntree); /* for group usage */ + ntreeVerifyTypes(ntree); /* internal nodes, no groups! */ } /* now verify all types in material trees, groups are set OK now */ @@ -2078,7 +2077,6 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree) bNodeLink *link; ntree->init= 0; /* to set callbacks and force setting types */ - ntree->owntype= NULL; ntree->progress= NULL; ntree->adt= newdataadr(fd, ntree->adt); @@ -2116,6 +2114,10 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree) } link_list(fd, &ntree->links); + /* external sockets */ + link_list(fd, &ntree->inputs); + link_list(fd, &ntree->outputs); + /* and we connect the rest */ for(node= ntree->nodes.first; node; node= node->next) { node->preview= newimaadr(fd, node->preview); @@ -2125,13 +2127,16 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree) for(sock= node->outputs.first; sock; sock= sock->next) sock->ns.data= NULL; } + for(sock= ntree->outputs.first; sock; sock= sock->next) + sock->link= newdataadr(fd, sock->link); + for(link= ntree->links.first; link; link= link->next) { link->fromnode= newdataadr(fd, link->fromnode); link->tonode= newdataadr(fd, link->tonode); link->fromsock= newdataadr(fd, link->fromsock); link->tosock= newdataadr(fd, link->tosock); } - + /* type verification is in lib-link */ } @@ -11363,8 +11368,19 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } + if (main->versionfile < 256 || (main->versionfile == 256 && main->subversionfile < 2)) { + bNodeTree *ntree; + + /* node sockets are not exposed automatically any more, + * this mimics the old behaviour by adding all unlinked sockets to groups. + */ + for (ntree=main->nodetree.first; ntree; ntree=ntree->id.next) { + nodeAddAllGroupSockets(ntree); + } + } + /* put compatibility code here until next subversion bump */ - + { bScreen *sc; @@ -11377,7 +11393,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } } - + /* WATCH IT!!!: pointers from libdata have not been converted yet here! */ /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index e8275dde2af..8d0656e45e1 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -670,6 +670,12 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree) for(link= ntree->links.first; link; link= link->next) writestruct(wd, DATA, "bNodeLink", 1, link); + + /* external sockets */ + for(sock= ntree->inputs.first; sock; sock= sock->next) + writestruct(wd, DATA, "bNodeSocket", 1, sock); + for(sock= ntree->outputs.first; sock; sock= sock->next) + writestruct(wd, DATA, "bNodeSocket", 1, sock); } static void current_screen_compat(Main *mainvar, bScreen **screen) diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 203e6a618a5..ac1c819c9e4 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1604,11 +1604,11 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) int do_shaded= 0, th_col1= TH_HEADER, th_col2= TH_HEADER; int do_triple= 0, th_col3= TH_WIRE; - if(link->fromnode==NULL && link->tonode==NULL) + if(link->fromsock==NULL && link->tosock==NULL) return; /* new connection */ - if(link->fromnode==NULL || link->tonode==NULL) { + if(!link->fromsock || !link->tosock) { th_col1 = TH_ACTIVE; do_triple = 1; } @@ -1620,8 +1620,9 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) return; /* a bit ugly... but thats how we detect the internal group links */ - if(link->fromnode==link->tonode) { - th_col1 = TH_GRID; + if(!link->fromnode || !link->tonode) { + UI_ThemeColorBlend(TH_BACK, TH_WIRE, 0.5f); + do_shaded= 0; } else { /* check cyclic */ diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 7b9a33c521f..9e93773c271 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -30,6 +30,8 @@ #include <stdio.h> #include <string.h> +#include "MEM_guardedalloc.h" + #include "DNA_node_types.h" #include "DNA_material_types.h" #include "DNA_object_types.h" @@ -68,6 +70,9 @@ #include "node_intern.h" +/* width of socket columns in group display */ +#define NODE_GROUP_FRAME 120 + // XXX interface.h extern void ui_dropshadow(rctf *rct, float radius, float aspect, int select); @@ -380,9 +385,10 @@ static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode) { bNodeTree *ngroup= (bNodeTree *)gnode->id; bNode *node; - bNodeSocket *nsock; + bNodeSocket *sock, *gsock; rctf *rect= &gnode->totr; int counter; + int dy; /* center them, is a bit of abuse of locx and locy though */ for(node= ngroup->nodes.first; node; node= node->next) { @@ -405,6 +411,11 @@ static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode) else BLI_union_rctf(rect, &node->totr); } + + /* add some room for links to group sockets */ + rect->xmin -= 3*NODE_DY; + rect->xmax += 3*NODE_DY; + if(counter==1) return; /* should be prevented? */ rect->xmin-= NODE_DY; @@ -412,16 +423,22 @@ static void node_update_group(const bContext *C, bNodeTree *ntree, bNode *gnode) rect->xmax+= NODE_DY; rect->ymax+= NODE_DY; - /* output sockets */ - for(nsock= gnode->outputs.first; nsock; nsock= nsock->next) { - nsock->locx= rect->xmax; - nsock->locy= nsock->tosock->locy; + /* input sockets */ + dy = 0.5f*(rect->ymin+rect->ymax) + NODE_DY*(BLI_countlist(&gnode->inputs)-1); + for(gsock=ngroup->inputs.first, sock=gnode->inputs.first; gsock; gsock=gsock->next, sock=sock->next) { + gsock->locx = rect->xmin; + sock->locx = rect->xmin - NODE_GROUP_FRAME; + sock->locy = gsock->locy = dy; + dy -= 2*NODE_DY; } - /* input sockets */ - for(nsock= gnode->inputs.first; nsock; nsock= nsock->next) { - nsock->locx= rect->xmin; - nsock->locy= nsock->tosock->locy; + /* output sockets */ + dy = 0.5f*(rect->ymin+rect->ymax) + NODE_DY*(BLI_countlist(&gnode->outputs)-1); + for(gsock=ngroup->outputs.first, sock=gnode->outputs.first; gsock; gsock=gsock->next, sock=sock->next) { + gsock->locx = rect->xmax; + sock->locx = rect->xmax + NODE_GROUP_FRAME; + sock->locy = gsock->locy = dy - NODE_DYS; + dy -= 2*NODE_DY; } } @@ -549,29 +566,6 @@ static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v) /* ************** Socket callbacks *********** */ -/* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */ -static uiBlock *socket_vector_menu(bContext *C, ARegion *ar, void *socket_v) -{ - bNodeSocket *sock= socket_v; - uiBlock *block; - - SpaceNode *snode= CTX_wm_space_node(C); - bNodeTree *ntree = snode->nodetree; - PointerRNA ptr; - uiLayout *layout; - - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr); - - block= uiBeginBlock(C, ar, "socket menu", UI_EMBOSS); - uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN); - - layout= uiLayoutColumn(uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, sock->locx, sock->locy-8, 140, 20, U.uistyles.first), 0); - - uiItemR(layout, &ptr, "default_value", UI_ITEM_R_EXPAND, "", ICON_NULL); - - return block; -} - /* not a callback */ static void node_draw_preview(bNodePreview *preview, rctf *prv) { @@ -626,16 +620,91 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv) } +typedef struct SocketVectorMenuArgs { + PointerRNA ptr; + int x, y, width; + uiButHandleFunc cb; + void *arg1, *arg2; +} SocketVectorMenuArgs; + +/* NOTE: this is a block-menu, needs 0 events, otherwise the menu closes */ +static uiBlock *socket_vector_menu(bContext *C, ARegion *ar, void *args_v) +{ + SocketVectorMenuArgs *args= (SocketVectorMenuArgs*)args_v; + uiBlock *block; + uiLayout *layout; + + block= uiBeginBlock(C, ar, "socket menu", UI_EMBOSS); + uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN); + + layout= uiLayoutColumn(uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, args->x, args->y+2, args->width, 20, U.uistyles.first), 0); + + uiItemR(layout, &args->ptr, "default_value", UI_ITEM_R_EXPAND, "", ICON_NULL); + + return block; +} + +static void node_draw_socket_button(bNodeTree *ntree, bNodeSocket *sock, const char *name, + uiBlock *block, int x, int y, int width, + uiButHandleFunc cb, void *arg1, void *arg2) +{ + uiBut *bt= NULL; + PointerRNA ptr; + int labelw; + SocketVectorMenuArgs *args; + + RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr); + + switch (sock->type) { + case SOCK_VALUE: + bt=uiDefButR(block, NUM, B_NODE_EXEC, name, + x, y+1, width, 17, + &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL); + if (cb) + uiButSetFunc(bt, cb, arg1, arg2); + break; + + case SOCK_VECTOR: + args= MEM_callocN(sizeof(SocketVectorMenuArgs), "SocketVectorMenuArgs"); + + args->ptr = ptr; + args->x = x; + args->y = y; + args->width = width; + args->cb = cb; + args->arg1 = arg1; + args->arg2 = arg2; + + uiDefBlockButN(block, socket_vector_menu, args, name, + x, y+1, width, 17, + ""); + break; + + case SOCK_RGBA: + labelw= width - 40; + + bt=uiDefButR(block, COL, B_NODE_EXEC, "", + x, y+2, (labelw>0 ? 40 : width), 15, + &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL); + if (cb) + uiButSetFunc(bt, cb, arg1, arg2); + + if (name[0]!='\0' && labelw>0) + uiDefBut(block, LABEL, 0, name, + x + 40, y+2, labelw, 15, + NULL, 0, 0, 0, 0, ""); + break; + } +} + static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node) { bNodeSocket *sock; - uiBut *bt; rctf *rct= &node->totr; - float /*slen,*/ iconofs; - int /*ofs,*/ color_id= node_get_colorid(node); + float iconofs; + int color_id= node_get_colorid(node); char showname[128]; /* 128 used below */ View2D *v2d = &ar->v2d; - PointerRNA ptr; /* hurmf... another candidate for callback, have to see how this works first */ if(node->id && node->block && snode->treetype==NTREE_SHADER) @@ -765,38 +834,10 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) { socket_circle_draw(sock, NODE_SOCKSIZE); - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr); - if(node->block && sock->link==NULL) { - - if(sock->type==SOCK_VALUE) { - bt=uiDefButR(node->block, NUM, B_NODE_EXEC, sock->name, - (short)sock->locx+NODE_DYS, (short)(sock->locy)-9, (short)node->width-NODE_DY, 17, - &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL); - uiButSetFunc(bt, node_sync_cb, snode, node); - } - else if(sock->type==SOCK_VECTOR) { - uiDefBlockBut(node->block, socket_vector_menu, sock, sock->name, - (short)sock->locx+NODE_DYS, (short)sock->locy-9, (short)node->width-NODE_DY, 17, - ""); - } - else if(node->block && sock->type==SOCK_RGBA) { - short labelw= (short)node->width-NODE_DY-40, width; - - if(labelw>0) width= 40; else width= (short)node->width-NODE_DY; - - bt=uiDefButR(node->block, COL, B_NODE_EXEC, "", - (short)sock->locx+NODE_DYS, (short)(sock->locy)-8, width, 15, - &ptr, "default_value", 0, sock->ns.min, sock->ns.max, -1, -1, NULL); - uiButSetFunc(bt, node_sync_cb, snode, node); - - if(labelw>0) uiDefBut(node->block, LABEL, 0, sock->name, - (short)(sock->locx+NODE_DYS) + 40, (short)sock->locy-8, labelw, 15, - NULL, 0, 0, 0, 0, ""); - } + node_draw_socket_button(ntree, sock, sock->name, node->block, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY, node_sync_cb, snode, node); } else { - uiDefBut(node->block, LABEL, 0, sock->name, (short)(sock->locx+7), (short)(sock->locy-9.0f), (short)(node->width-NODE_DY), NODE_DY, NULL, 0, 0, 0, 0, ""); } @@ -969,39 +1010,11 @@ static void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, } } -/* fake links from groupnode to internal nodes */ -static void node_draw_group_links(View2D *v2d, SpaceNode *snode, bNode *gnode) +static void group_verify_cb(bContext *UNUSED(C), void *UNUSED(snode_v), void *ngroup_v) { - bNodeLink fakelink; - bNodeSocket *sock; - - glEnable(GL_BLEND); - glEnable(GL_LINE_SMOOTH); - - fakelink.tonode= fakelink.fromnode= gnode; - - for(sock= gnode->inputs.first; sock; sock= sock->next) { - if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) { - if(sock->tosock) { - fakelink.fromsock= sock; - fakelink.tosock= sock->tosock; - node_draw_link(v2d, snode, &fakelink); - } - } - } - - for(sock= gnode->outputs.first; sock; sock= sock->next) { - if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) { - if(sock->tosock) { - fakelink.tosock= sock; - fakelink.fromsock= sock->tosock; - node_draw_link(v2d, snode, &fakelink); - } - } - } + bNodeTree *ngroup= (bNodeTree*)ngroup_v; - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); + nodeVerifyGroup(ngroup); } /* groups are, on creation, centered around 0,0 */ @@ -1010,25 +1023,51 @@ static void node_draw_group(const bContext *C, ARegion *ar, SpaceNode *snode, bN bNodeTree *ngroup= (bNodeTree *)gnode->id; bNodeSocket *sock; rctf rect= gnode->totr; + int index; uiLayout *layout; PointerRNA ptr; + uiBut *bt; /* backdrop header */ glEnable(GL_BLEND); uiSetRoundBox(3); UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70); - uiDrawBox(GL_POLYGON, rect.xmin, rect.ymax, rect.xmax, rect.ymax+26, BASIS_RAD); + uiDrawBox(GL_POLYGON, rect.xmin-NODE_GROUP_FRAME, rect.ymax, rect.xmax+NODE_GROUP_FRAME, rect.ymax+26, BASIS_RAD); /* backdrop body */ UI_ThemeColorShadeAlpha(TH_BACK, -8, -70); - uiSetRoundBox(12); + uiSetRoundBox(0); uiDrawBox(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, BASIS_RAD); - - /* selection outline */ + + /* input column */ + UI_ThemeColorShadeAlpha(TH_BACK, 10, -50); + uiSetRoundBox(8); + uiDrawBox(GL_POLYGON, rect.xmin-NODE_GROUP_FRAME, rect.ymin, rect.xmin, rect.ymax, BASIS_RAD); + + /* output column */ + UI_ThemeColorShadeAlpha(TH_BACK, 10, -50); + uiSetRoundBox(4); + uiDrawBox(GL_POLYGON, rect.xmax, rect.ymin, rect.xmax+NODE_GROUP_FRAME, rect.ymax, BASIS_RAD); + + /* input column separator */ + glColor4ub(200, 200, 200, 140); + glBegin(GL_LINES); + glVertex2f(rect.xmin, rect.ymin); + glVertex2f(rect.xmin, rect.ymax); + glEnd(); + + /* output column separator */ + glColor4ub(200, 200, 200, 140); + glBegin(GL_LINES); + glVertex2f(rect.xmax, rect.ymin); + glVertex2f(rect.xmax, rect.ymax); + glEnd(); + + /* group node outline */ uiSetRoundBox(15); glColor4ub(200, 200, 200, 140); glEnable( GL_LINE_SMOOTH ); - uiDrawBox(GL_LINE_LOOP, rect.xmin, rect.ymin, rect.xmax, rect.ymax+26, BASIS_RAD); + uiDrawBox(GL_LINE_LOOP, rect.xmin-NODE_GROUP_FRAME, rect.ymin, rect.xmax+NODE_GROUP_FRAME, rect.ymax+26, BASIS_RAD); glDisable( GL_LINE_SMOOTH ); glDisable(GL_BLEND); @@ -1041,26 +1080,101 @@ static void node_draw_group(const bContext *C, ARegion *ar, SpaceNode *snode, bN uiTemplateIDBrowse(layout, (bContext*)C, &ptr, "node_tree", NULL, NULL, NULL); uiBlockLayoutResolve(gnode->block, NULL, NULL); - uiEndBlock(C, gnode->block); - uiDrawBlock(C, gnode->block); - gnode->block= NULL; - + /* draw the internal tree nodes and links */ + node_draw_nodetree(C, ar, snode, ngroup); - /* links from groupsockets to the internal nodes */ - node_draw_group_links(&ar->v2d, snode, gnode); - /* group sockets */ - for(sock= gnode->inputs.first; sock; sock= sock->next) - if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) - socket_circle_draw(sock, NODE_SOCKSIZE); - for(sock= gnode->outputs.first; sock; sock= sock->next) - if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) - socket_circle_draw(sock, NODE_SOCKSIZE); - - + for(sock=ngroup->inputs.first, index=0; sock; sock=sock->next, ++index) { + socket_circle_draw(sock, NODE_SOCKSIZE); + /* small hack to use socket_circle_draw function with offset */ + sock->locx -= NODE_GROUP_FRAME; + socket_circle_draw(sock, NODE_SOCKSIZE); + sock->locx += NODE_GROUP_FRAME; + + bt = uiDefBut(gnode->block, TEX, 0, "", + sock->locx-114, sock->locy+1, 72, NODE_DY, + sock->name, 0, 31, 0, 0, ""); + uiButSetFunc(bt, group_verify_cb, snode, ngroup); + + node_draw_socket_button(ngroup, sock, "", gnode->block, + sock->locx-114, sock->locy-NODE_DY, 72, + NULL, NULL, NULL); + + uiBlockSetDirection(gnode->block, UI_TOP); + uiBlockBeginAlign(gnode->block); + bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_up", 0, ICON_TRIA_UP, + sock->locx-40, sock->locy, 16, 16, ""); + if (!sock->prev) + uiButSetFlag(bt, UI_BUT_DISABLED); + RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index); + RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_IN); + bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_down", 0, ICON_TRIA_DOWN, + sock->locx-40, sock->locy-16, 16, 16, ""); + if (!sock->next) + uiButSetFlag(bt, UI_BUT_DISABLED); + RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index); + RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_IN); + uiBlockEndAlign(gnode->block); + uiBlockSetDirection(gnode->block, 0); + + uiBlockSetEmboss(gnode->block, UI_EMBOSSN); + bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_remove", 0, ICON_X, + sock->locx-22, sock->locy-8, 16, 16, ""); + RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index); + RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_IN); + uiBlockSetEmboss(gnode->block, UI_EMBOSS); + } - /* and finally the whole tree */ - node_draw_nodetree(C, ar, snode, ngroup); + for(sock=ngroup->outputs.first, index=0; sock; sock=sock->next, ++index) { + socket_circle_draw(sock, NODE_SOCKSIZE); + /* small hack to use socket_circle_draw function with offset */ + sock->locx += NODE_GROUP_FRAME; + socket_circle_draw(sock, NODE_SOCKSIZE); + sock->locx -= NODE_GROUP_FRAME; + + uiBlockSetEmboss(gnode->block, UI_EMBOSSN); + bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_remove", 0, ICON_X, + sock->locx+6, sock->locy-8, 16, 16, ""); + RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index); + RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_OUT); + uiBlockSetEmboss(gnode->block, UI_EMBOSS); + + uiBlockSetDirection(gnode->block, UI_TOP); + uiBlockBeginAlign(gnode->block); + bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_up", 0, ICON_TRIA_UP, + sock->locx+24, sock->locy, 16, 16, ""); + if (!sock->prev) + uiButSetFlag(bt, UI_BUT_DISABLED); + RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index); + RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_OUT); + bt = uiDefIconButO(gnode->block, BUT, "NODE_OT_group_socket_move_down", 0, ICON_TRIA_DOWN, + sock->locx+24, sock->locy-16, 16, 16, ""); + if (!sock->next) + uiButSetFlag(bt, UI_BUT_DISABLED); + RNA_int_set(uiButGetOperatorPtrRNA(bt), "index", index); + RNA_enum_set(uiButGetOperatorPtrRNA(bt), "in_out", SOCK_OUT); + uiBlockEndAlign(gnode->block); + uiBlockSetDirection(gnode->block, 0); + + if (sock->link) { + bt = uiDefBut(gnode->block, TEX, 0, "", + sock->locx+42, sock->locy-NODE_DYS+1, 72, NODE_DY, + sock->name, 0, 31, 0, 0, ""); + uiButSetFunc(bt, group_verify_cb, snode, ngroup); + } + else { + bt = uiDefBut(gnode->block, TEX, 0, "", + sock->locx+42, sock->locy+1, 72, NODE_DY, + sock->name, 0, 31, 0, 0, ""); + uiButSetFunc(bt, group_verify_cb, snode, ngroup); + + node_draw_socket_button(ngroup, sock, "", gnode->block, sock->locx+42, sock->locy-NODE_DY, 72, NULL, NULL, NULL); + } + } + + uiEndBlock(C, gnode->block); + uiDrawBlock(C, gnode->block); + gnode->block= NULL; } void drawnodespace(const bContext *C, ARegion *ar, View2D *v2d) diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 385dfbcd9a8..c0c1b7dc72b 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -77,10 +77,15 @@ #include "IMB_imbuf.h" +#include "RNA_enum_types.h" + #include "node_intern.h" -#define SOCK_IN 1 -#define SOCK_OUT 2 +static EnumPropertyItem socket_in_out_items[] = { + { SOCK_IN, "IN", 0, "In", "" }, + { SOCK_OUT, "OUT", 0, "Out", "" }, + { 0, NULL, 0, NULL, NULL} +}; /* ***************** composite job manager ********************** */ @@ -634,6 +639,241 @@ void NODE_OT_group_edit(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } +/* ***************** Add Group Socket operator ************* */ + +static int node_group_socket_add_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + int in_out= -1; + char name[32]= ""; + int type= SOCK_VALUE; + bNodeTree *ngroup= snode->edittree; + bNodeSocket *sock; + + ED_preview_kill_jobs(C); + + if (RNA_property_is_set(op->ptr, "name")) + RNA_string_get(op->ptr, "name", name); + + if (RNA_property_is_set(op->ptr, "type")) + type = RNA_enum_get(op->ptr, "type"); + + if (RNA_property_is_set(op->ptr, "in_out")) + in_out = RNA_enum_get(op->ptr, "in_out"); + else + return OPERATOR_CANCELLED; + + sock = nodeAddGroupSocket(ngroup, name, type, in_out); + + node_tree_verify_groups(snode->nodetree); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_socket_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Group Socket"; + ot->description = "Add node group socket"; + ot->idname = "NODE_OT_group_socket_add"; + + /* api callbacks */ + ot->exec = node_group_socket_add_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output"); + RNA_def_string(ot->srna, "name", "", 32, "Name", "Group socket name"); + RNA_def_enum(ot->srna, "type", node_socket_type_items, SOCK_VALUE, "Type", "Type of the group socket"); +} + +/* ***************** Remove Group Socket operator ************* */ + +static int node_group_socket_remove_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + int index= -1; + int in_out= -1; + bNodeTree *ngroup= snode->edittree; + bNodeSocket *sock; + + ED_preview_kill_jobs(C); + + if (RNA_property_is_set(op->ptr, "index")) + index = RNA_int_get(op->ptr, "index"); + else + return OPERATOR_CANCELLED; + + if (RNA_property_is_set(op->ptr, "in_out")) + in_out = RNA_enum_get(op->ptr, "in_out"); + else + return OPERATOR_CANCELLED; + + sock = (bNodeSocket*)BLI_findlink(in_out==SOCK_IN ? &ngroup->inputs : &ngroup->outputs, index); + if (sock) { + nodeRemGroupSocket(ngroup, sock, in_out); + node_tree_verify_groups(snode->nodetree); + + snode_notify(C, snode); + } + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_socket_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Group Socket"; + ot->description = "Removed node group socket"; + ot->idname = "NODE_OT_group_socket_remove"; + + /* api callbacks */ + ot->exec = node_group_socket_remove_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX); + RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output"); +} + +/* ***************** Move Group Socket Up operator ************* */ + +static int node_group_socket_move_up_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + int index= -1; + int in_out= -1; + bNodeTree *ngroup= snode->edittree; + bNodeSocket *sock, *prev; + + ED_preview_kill_jobs(C); + + if (RNA_property_is_set(op->ptr, "index")) + index = RNA_int_get(op->ptr, "index"); + else + return OPERATOR_CANCELLED; + + if (RNA_property_is_set(op->ptr, "in_out")) + in_out = RNA_enum_get(op->ptr, "in_out"); + else + return OPERATOR_CANCELLED; + + /* swap */ + if (in_out==SOCK_IN) { + sock = (bNodeSocket*)BLI_findlink(&ngroup->inputs, index); + prev = sock->prev; + /* can't move up the first socket */ + if (!prev) + return OPERATOR_CANCELLED; + BLI_remlink(&ngroup->inputs, sock); + BLI_insertlinkbefore(&ngroup->inputs, prev, sock); + } + else if (in_out==SOCK_OUT) { + sock = (bNodeSocket*)BLI_findlink(&ngroup->outputs, index); + prev = sock->prev; + /* can't move up the first socket */ + if (!prev) + return OPERATOR_CANCELLED; + BLI_remlink(&ngroup->outputs, sock); + BLI_insertlinkbefore(&ngroup->outputs, prev, sock); + } + node_tree_verify_groups(snode->nodetree); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_socket_move_up(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Move Group Socket Up"; + ot->description = "Move up node group socket"; + ot->idname = "NODE_OT_group_socket_move_up"; + + /* api callbacks */ + ot->exec = node_group_socket_move_up_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX); + RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output"); +} + +/* ***************** Move Group Socket Up operator ************* */ + +static int node_group_socket_move_down_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + int index= -1; + int in_out= -1; + bNodeTree *ngroup= snode->edittree; + bNodeSocket *sock, *next; + + ED_preview_kill_jobs(C); + + if (RNA_property_is_set(op->ptr, "index")) + index = RNA_int_get(op->ptr, "index"); + else + return OPERATOR_CANCELLED; + + if (RNA_property_is_set(op->ptr, "in_out")) + in_out = RNA_enum_get(op->ptr, "in_out"); + else + return OPERATOR_CANCELLED; + + /* swap */ + if (in_out==SOCK_IN) { + sock = (bNodeSocket*)BLI_findlink(&ngroup->inputs, index); + next = sock->next; + /* can't move down the last socket */ + if (!next) + return OPERATOR_CANCELLED; + BLI_remlink(&ngroup->inputs, sock); + BLI_insertlinkafter(&ngroup->inputs, next, sock); + } + else if (in_out==SOCK_OUT) { + sock = (bNodeSocket*)BLI_findlink(&ngroup->outputs, index); + next = sock->next; + /* can't move down the last socket */ + if (!next) + return OPERATOR_CANCELLED; + BLI_remlink(&ngroup->outputs, sock); + BLI_insertlinkafter(&ngroup->outputs, next, sock); + } + node_tree_verify_groups(snode->nodetree); + + snode_notify(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_group_socket_move_down(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Move Group Socket Down"; + ot->description = "Move down node group socket"; + ot->idname = "NODE_OT_group_socket_move_down"; + + /* api callbacks */ + ot->exec = node_group_socket_move_down_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX); + RNA_def_enum(ot->srna, "in_out", socket_in_out_items, SOCK_IN, "Socket Type", "Input or Output"); +} + /* ******************** Ungroup operator ********************** */ static int node_group_ungroup_exec(bContext *C, wmOperator *op) @@ -1433,6 +1673,33 @@ static int find_indicated_socket(SpaceNode *snode, bNode **nodep, bNodeSocket ** } } } + + /* check group sockets + * NB: using ngroup->outputs as input sockets and vice versa here! + */ + if(in_out & SOCK_IN) { + for(sock= snode->edittree->outputs.first; sock; sock= sock->next) { + if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) { + if(BLI_in_rctf(&rect, sock->locx, sock->locy)) { + *nodep= NULL; /* NULL node pointer indicates group socket */ + *sockp= sock; + return 1; + } + } + } + } + if(in_out & SOCK_OUT) { + for(sock= snode->edittree->inputs.first; sock; sock= sock->next) { + if(!(sock->flag & (SOCK_HIDDEN|SOCK_UNAVAIL))) { + if(BLI_in_rctf(&rect, sock->locx, sock->locy)) { + *nodep= NULL; /* NULL node pointer indicates group socket */ + *sockp= sock; + return 1; + } + } + } + } + return 0; } @@ -1473,6 +1740,16 @@ static int node_socket_hilights(SpaceNode *snode, int in_out) return redraw; } +static int outside_group_rect(SpaceNode *snode) +{ + bNode *gnode= node_tree_get_editgroup(snode->nodetree); + if (gnode) { + return (snode->mx < gnode->totr.xmin || snode->mx >= gnode->totr.xmax + || snode->my < gnode->totr.ymin || snode->my >= gnode->totr.ymax); + } + return 0; +} + /* ****************** Add *********************** */ @@ -1701,7 +1978,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) last = ntree->nodes.last; for(node= ntree->nodes.first; node; node= node->next) { if(node->flag & SELECT) { - newnode = nodeCopyNode(ntree, node, 1); + newnode = nodeCopyNode(ntree, node); /* deselect old node, select the copy instead */ node->flag &= ~(NODE_SELECT|NODE_ACTIVE); @@ -1767,19 +2044,24 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeL break; } if(tlink) { - /* is there a free input socket with same type? */ - for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) { - if(sock->type==tlink->fromsock->type) - if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit) - break; - } - if(sock) { - tlink->tosock= sock; - sock->flag &= ~SOCK_HIDDEN; + /* try to move the existing link to the next available socket */ + if (tlink->tonode) { + /* is there a free input socket with same type? */ + for(sock= tlink->tonode->inputs.first; sock; sock= sock->next) { + if(sock->type==tlink->fromsock->type) + if(nodeCountSocketLinks(snode->edittree, sock) < sock->limit) + break; + } + if(sock) { + tlink->tosock= sock; + sock->flag &= ~SOCK_HIDDEN; + } + else { + nodeRemLink(snode->edittree, tlink); + } } - else { + else nodeRemLink(snode->edittree, tlink); - } } } } @@ -1810,7 +2092,7 @@ static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event) if(in_out==SOCK_OUT) { if(find_indicated_socket(snode, &tnode, &tsock, SOCK_IN)) { if(nodeFindLink(snode->edittree, sock, tsock)==NULL) { - if(tnode!=node && link->tonode!=tnode && link->tosock!= tsock) { + if( link->tosock!= tsock && (!tnode || (tnode!=node && link->tonode!=tnode)) ) { link->tonode= tnode; link->tosock= tsock; ntreeSolveOrder(snode->edittree); /* for interactive red line warning */ @@ -1826,7 +2108,7 @@ static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event) if(find_indicated_socket(snode, &tnode, &tsock, SOCK_OUT)) { if(nodeFindLink(snode->edittree, sock, tsock)==NULL) { if(nodeCountSocketLinks(snode->edittree, tsock) < tsock->limit) { - if(tnode!=node && link->fromnode!=tnode && link->fromsock!= tsock) { + if( link->fromsock!= tsock && (!tnode || (tnode!=node && link->fromnode!=tnode)) ) { link->fromnode= tnode; link->fromsock= tsock; ntreeSolveOrder(snode->edittree); /* for interactive red line warning */ @@ -1847,19 +2129,28 @@ static int node_link_modal(bContext *C, wmOperator *op, wmEvent *event) case LEFTMOUSE: case RIGHTMOUSE: case MIDDLEMOUSE: - - /* remove link? */ - if(link->tonode==NULL || link->fromnode==NULL) { - nodeRemLink(snode->edittree, link); - } - else { + if(link->tosock && link->fromsock) { /* send changed events for original tonode and new */ - if(link->tonode) + if(link->tonode) NodeTagChanged(snode->edittree, link->tonode); /* we might need to remove a link */ - if(in_out==SOCK_OUT) node_remove_extra_links(snode, link->tosock, link); + if(in_out==SOCK_OUT) + node_remove_extra_links(snode, link->tosock, link); } + else if (outside_group_rect(snode) && (link->tonode || link->fromnode)) { + /* automatically add new group socket */ + if (link->tonode && link->tosock) { + link->fromsock = nodeAddGroupSocketCopy(snode->edittree, link->tosock, SOCK_IN); + link->fromnode = NULL; + } + else if (link->fromnode && link->fromsock) { + link->tosock = nodeAddGroupSocketCopy(snode->edittree, link->fromsock, SOCK_OUT); + link->tonode = NULL; + } + } + else + nodeRemLink(snode->edittree, link); ntreeSolveOrder(snode->edittree); node_tree_verify_groups(snode->nodetree); diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index eb7e1209801..532ca58905e 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -103,6 +103,10 @@ void NODE_OT_links_cut(struct wmOperatorType *ot); void NODE_OT_group_make(struct wmOperatorType *ot); void NODE_OT_group_ungroup(struct wmOperatorType *ot); void NODE_OT_group_edit(struct wmOperatorType *ot); +void NODE_OT_group_socket_add(struct wmOperatorType *ot); +void NODE_OT_group_socket_remove(struct wmOperatorType *ot); +void NODE_OT_group_socket_move_up(struct wmOperatorType *ot); +void NODE_OT_group_socket_move_down(struct wmOperatorType *ot); void NODE_OT_mute_toggle(struct wmOperatorType *ot); void NODE_OT_hide_toggle(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index 9c2b6f0274d..be5436cc2c8 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -74,6 +74,10 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_group_make); WM_operatortype_append(NODE_OT_group_ungroup); WM_operatortype_append(NODE_OT_group_edit); + WM_operatortype_append(NODE_OT_group_socket_add); + WM_operatortype_append(NODE_OT_group_socket_remove); + WM_operatortype_append(NODE_OT_group_socket_move_up); + WM_operatortype_append(NODE_OT_group_socket_move_down); WM_operatortype_append(NODE_OT_link_viewer); diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index b53ac07aab3..48e70be51e5 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -288,7 +288,7 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op)) node->flag &= ~NODE_TEST; for (link=snode->edittree->links.first; link; link=link->next) { - if (link->fromnode->flag & NODE_SELECT) + if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT)) link->tonode->flag |= NODE_TEST; } @@ -328,7 +328,7 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op)) node->flag &= ~NODE_TEST; for(link=snode->edittree->links.first; link; link=link->next) { - if(link->tonode->flag & NODE_SELECT) + if(link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT)) link->fromnode->flag |= NODE_TEST; } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index a9f688e29b9..0a4c672273b 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -70,21 +70,27 @@ typedef struct bNodeSocket { char name[32]; bNodeStack ns; /* custom data for inputs, only UI writes in this */ - short type, flag; /* type is copy from socket type struct */ - short limit, stack_index; /* limit for dependency sort, stack_index for exec */ - short intern; /* intern = tag for group nodes */ - short stack_index_ext; /* for groups, to find the caller stack index */ - int pad1; + short type, flag; + short limit; /* max. number of links */ + + /* stack data info (only during execution!) */ + short stack_type; /* type of stack reference */ + /* XXX only one of stack_ptr or stack_index is used (depending on stack_type). + * could store the index in the pointer with SET_INT_IN_POINTER (a bit ugly). + * (union won't work here, not supported by DNA) + */ + struct bNodeStack *stack_ptr; /* constant input value */ + short stack_index; /* local stack index or external input number */ + short pad; float locx, locy; /* internal data to retrieve relations and groups */ - int own_index, to_index; /* group socket identifiers, to find matching pairs after reading files */ + int own_index; /* group socket identifiers, to find matching pairs after reading files */ + struct bNodeSocket *groupsock; - struct bNodeSocket *tosock; /* group-node sockets point to the internal group counterpart sockets, set after read file */ struct bNodeLink *link; /* a link pointer, set in nodeSolveOrder() */ - } bNodeSocket; /* sock->type */ @@ -99,11 +105,16 @@ typedef struct bNodeSocket { #define SOCK_IN_USE 4 /* unavailable is for dynamic sockets */ #define SOCK_UNAVAIL 8 -# -# + +/* sock->stack_type */ +#define SOCK_STACK_LOCAL 1 /* part of the local tree stack */ +#define SOCK_STACK_EXTERN 2 /* use input stack pointer */ +#define SOCK_STACK_CONST 3 /* use pointer to constant input value */ + typedef struct bNodePreview { unsigned char *rect; short xsize, ysize; + int pad; } bNodePreview; @@ -185,7 +196,7 @@ typedef struct bNodeTree { int flag, pad; ListBase alltypes; /* type definitions */ - struct bNodeType *owntype; /* for groups or dynamic trees, no read/write */ + ListBase inputs, outputs; /* external sockets for group nodes */ int pad2[2]; @@ -208,6 +219,7 @@ typedef struct bNodeTree { /* ntree->flag */ #define NTREE_DS_EXPAND 1 /* for animation editors */ +#define NTREE_AUTO_EXPOSE 2 /* automatically make unhidden, unlinked group sockets external */ /* data structs, for node->storage */ diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 2f6a6df9a55..b9fb7c89468 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -100,6 +100,8 @@ extern EnumPropertyItem property_unit_items[]; extern EnumPropertyItem viewport_shade_items[]; +extern EnumPropertyItem node_socket_type_items[]; + extern EnumPropertyItem node_blend_type_items[]; extern EnumPropertyItem node_math_items[]; extern EnumPropertyItem node_vec_math_items[]; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index ce9785819ca..b9ad9eaf15e 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -205,7 +205,7 @@ struct bNodeTree *rna_Main_nodetree_new(Main *bmain, const char *name, int type) { bNodeTree *tree = ntreeAddTree(name, type, TRUE); - ntreeMakeOwnType(tree); +// ntreeMakeGroupSockets(tree); id_us_min(&tree->id); return tree; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 98739874db8..35374d07a5f 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -47,6 +47,13 @@ #include "MEM_guardedalloc.h" + +EnumPropertyItem node_socket_type_items[] = { + {SOCK_VALUE, "VALUE", 0, "Value", ""}, + {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, + {SOCK_RGBA, "RGBA", 0, "RGBA", ""}, + {0, NULL, 0, NULL, NULL}}; + EnumPropertyItem node_blend_type_items[] = { { 0, "MIX", 0, "Mix", ""}, { 1, "ADD", 0, "Add", ""}, @@ -185,12 +192,22 @@ static char *rna_NodeSocket_path(PointerRNA *ptr) bNode *node; int socketindex; + /* group sockets */ + socketindex = BLI_findindex(&ntree->inputs, sock); + if (socketindex != -1) + return BLI_sprintfN("inputs[%d]", socketindex); + + socketindex = BLI_findindex(&ntree->outputs, sock); + if (socketindex != -1) + return BLI_sprintfN("outputs[%d]", socketindex); + + /* node sockets */ if (!nodeFindNode(ntree, sock, &node, NULL)) return NULL; - + socketindex = BLI_findindex(&node->inputs, sock); if (socketindex != -1) return BLI_sprintfN("nodes[\"%s\"].inputs[%d]", node->name, socketindex); - + socketindex = BLI_findindex(&node->outputs, sock); if (socketindex != -1) return BLI_sprintfN("nodes[\"%s\"].outputs[%d]", node->name, socketindex); @@ -354,6 +371,18 @@ static void rna_NodeSocket_update(Main *bmain, Scene *scene, PointerRNA *ptr) node_update(bmain, scene, ntree, node); } +static void rna_NodeGroupSocket_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bNodeTree *ntree= (bNodeTree*)ptr->id.data; + bNodeSocket *sock= (bNodeSocket*)ptr->data; + bNode *node; + + nodeVerifyGroup(ntree); + + if (nodeFindNode(ntree, sock, &node, NULL)) + node_update(bmain, scene, ntree, node); +} + static void rna_NodeSocket_defvalue_range(PointerRNA *ptr, float *min, float *max) { bNodeSocket *sock= (bNodeSocket*)ptr->data; @@ -2473,12 +2502,6 @@ static void rna_def_node_socket(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static EnumPropertyItem node_socket_type_items[] = { - {SOCK_VALUE, "VALUE", 0, "Value", ""}, - {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, - {SOCK_RGBA, "RGBA", 0, "RGBA", ""}, - {0, NULL, 0, NULL, NULL}}; - srna = RNA_def_struct(brna, "NodeSocket", NULL); RNA_def_struct_ui_text(srna, "Node Socket", "Input or output socket of a node"); RNA_def_struct_refine_func(srna, "rna_NodeSocketType_refine"); @@ -2487,9 +2510,11 @@ static void rna_def_node_socket(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_NodeSocket_path"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); + /* XXX must be editable for group sockets. if necessary use a special rna definition for these */ +// RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Name", "Socket name"); RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_NodeGroupSocket_update"); /* can add back if there is any use in reading them */ #if 0 @@ -2671,29 +2696,22 @@ static void rna_def_nodetree(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "GreasePencil"); RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil datablock"); - - /* these are too much like operators, better to have data level access - * ngroup = bpy.data.node_groups.new() - * ngroup.nodes.new(....) etc. */ -#if 0 - func= RNA_def_function(srna, "group_add", "nodeMakeGroupFromSelected"); - RNA_def_function_ui_description(func, "Make a group from the active nodes."); - /* return */ - parm= RNA_def_pointer(func, "group", "Node", "", "New group."); - RNA_def_function_return(func, parm); - - func= RNA_def_function(srna, "ungroup", "nodeGroupUnGroup"); - RNA_def_function_ui_description(func, "Ungroup node group"); - parm= RNA_def_pointer(func, "group", "Node", "", "The group to ungroup."); - RNA_def_property_flag(parm, PROP_REQUIRED); - parm= RNA_def_int(func, "bool", 0, 0, 1, "Bool", "", 0, 1); - RNA_def_function_return(func, parm); -#endif prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_items(prop, nodetree_type_items); RNA_def_property_ui_text(prop, "Type", "Node Tree type"); + + /* group sockets */ + prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "inputs", NULL); + RNA_def_property_struct_type(prop, "NodeSocket"); + RNA_def_property_ui_text(prop, "Inputs", ""); + + prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "outputs", NULL); + RNA_def_property_struct_type(prop, "NodeSocket"); + RNA_def_property_ui_text(prop, "Outputs", ""); } static void rna_def_composite_nodetree(BlenderRNA *brna) |