Big node groups improvement patch. Node group trees now have their own lists of input...
authorLukas Toenne <lukas.toenne@googlemail.com>
Mon, 21 Feb 2011 13:47:49 +0000 (13:47 +0000)
committerLukas Toenne <lukas.toenne@googlemail.com>
Mon, 21 Feb 2011 13:47:49 +0000 (13:47 +0000)
More details can be found in the patch tracker description (#24883) and on the code.blender.org development blog.

15 files changed:
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_draw.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_intern.h
source/blender/editors/space_node/node_ops.c
source/blender/editors/space_node/node_select.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_nodetree.c

index 7c6d7d81f2b16accfe8b3b900ce8f8c5447fcd22..f6bb4fbb6352b8a96ea7b274bfeb67ccad9bfef2 100644 (file)
@@ -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
index 0896dfbabb1ef47789228f84c8af27f21cedf4ef..b637f5ab18e2b0077ba55b29e040573d3b90ef2b 100644 (file)
@@ -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 */
index 057d90dc8bbd467e7f4d59d8ef0f4ea4e3144062..cebbab459284c36c7a2352a6d1d3468b06b58be9 100644 (file)
 #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);
                }
        }
 
index c8ab53da177cdf7450a05db61c658849a3dc2a4c..3f722cc8cf3293a03a37c49abc692dd4e43cb009 100644 (file)
@@ -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! */
 
index e8275dde2af71ea77c1c36df5fbfa3755fdf09a9..8d0656e45e1e9d530b5d14a37a965ffa32011763 100644 (file)
@@ -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)
index 203e6a618a58d0678006cd869ca2dbae45560c2d..ac1c819c9e48170d2d47c6c94d909a7763875ece 100644 (file)
@@ -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 */
index 7b9a33c521f8daf80108d3c5881f79fd5ca1d53a..9e93773c271345b248437ae31f67afff108480a2 100644 (file)
@@ -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)
index 385dfbcd9a8e3aeac59e7b47c92d7a2c27e8f747..c0c1b7dc72ba13a9ce3d419ceafa737625355daf 100644 (file)
 
 #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);
index eb7e120980189f13d5ca75ebe5a2a805392e551a..532ca58905e5faf1128688a43aafed0e014f2314 100644 (file)
@@ -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);
index 9c2b6f0274db2a9efe8bf9faf5e218d7549e3ca1..be5436cc2c84c272045f05bdd951f2755ef3017e 100644 (file)
@@ -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);
        
index b53ac07aab3e3aabf712a7d0c799ea69cae7ad8a..48e70be51e57d1d187263cd3d87bb8470e45842a 100644 (file)
@@ -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;
        }
        
index a9f688e29b90ad8a337be89f712cda27435acc9c..0a4c672273b585716516e23a23bcdd7687a35786 100644 (file)
@@ -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 */
 
index 2f6a6df9a55c20c96a361c27fcd82821f2ac2768..b9fb7c89468483bb5b3bd03434c149ef57bef24f 100644 (file)
@@ -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[];
index ce9785819ca3121c493beaed03587cbf3e3748b1..b9ad9eaf15ef42ea5baf3d651d076957e508cd6c 100644 (file)
@@ -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;
index 98739874db8f0a6c75346c2e7fe027f58f1e573b..35374d07a5f4ca631c0ec868eb4b9dcd5059d621 100644 (file)
 
 #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)