diff options
author | Nathan Letwory <nathan@letworyinteractive.com> | 2008-02-10 02:17:15 +0300 |
---|---|---|
committer | Nathan Letwory <nathan@letworyinteractive.com> | 2008-02-10 02:17:15 +0300 |
commit | 16514e3ddb8500242931650c93f1397147442eba (patch) | |
tree | c66895704889f7a42219fbcdf1b59d81e6858529 | |
parent | a37d9470a7f4e042efb6ce873ee05308e9aec535 (diff) |
* Merge of PyNodes to trunk. Finally!
See http://wiki.blender.org/index.php/BlenderDev/PyNodes and
http://wiki.blender.org/index.php/BlenderDev/PyNodes/API
For current documentation.
Very very big thanks go to William Germano for fixing the memory issues left
and for improving on the code.
In the coming time documentation will be finalised and further stabilising
of PyNodes is to be expected.
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 34 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_utildefines.h | 20 | ||||
-rw-r--r-- | source/blender/blenkernel/SConscript | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 200 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 19 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 4 | ||||
-rw-r--r-- | source/blender/nodes/SConscript | 4 | ||||
-rw-r--r-- | source/blender/nodes/SHD_node.h | 2 | ||||
-rw-r--r-- | source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c | 645 | ||||
-rw-r--r-- | source/blender/python/SConscript | 4 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Blender.c | 2 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Node.c | 718 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Node.h | 14 | ||||
-rw-r--r-- | source/blender/src/drawnode.c | 98 | ||||
-rw-r--r-- | source/blender/src/drawtext.c | 5 | ||||
-rw-r--r-- | source/blender/src/editnode.c | 19 | ||||
-rw-r--r-- | source/blender/src/toolbox.c | 24 |
18 files changed, 1266 insertions, 549 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index f324c5d693c..bad7f0e90be 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -87,8 +87,8 @@ typedef struct bNodeType { /* for use with dynamic typedefs */ ID *id; - void *script; /* holds pointer to python script */ - void *dict; /* holds pointer to python script dictionary (scope)*/ + void *pynode; /* holds pointer to python script */ + void *pydict; /* holds pointer to python script dictionary (scope)*/ } bNodeType; @@ -103,13 +103,14 @@ typedef struct bNodeType { #define NODE_CLASS_INPUT 0 #define NODE_CLASS_OUTPUT 1 #define NODE_CLASS_OP_COLOR 3 -#define NODE_CLASS_OP_VECTOR 4 -#define NODE_CLASS_OP_FILTER 5 +#define NODE_CLASS_OP_VECTOR 4 +#define NODE_CLASS_OP_FILTER 5 #define NODE_CLASS_GROUP 6 #define NODE_CLASS_FILE 7 -#define NODE_CLASS_CONVERTOR 8 +#define NODE_CLASS_CONVERTOR 8 #define NODE_CLASS_MATTE 9 #define NODE_CLASS_DISTORT 10 +#define NODE_CLASS_OP_DYNAMIC 11 /* ************** GENERIC API, TREES *************** */ @@ -119,6 +120,7 @@ struct bNodeTree *ntreeAddTree(int type); void ntreeInitTypes(struct bNodeTree *ntree); void ntreeMakeOwnType(struct bNodeTree *ntree); +void ntreeUpdateType(struct bNodeTree *ntree, struct bNodeType *ntype); void ntreeFreeTree(struct bNodeTree *ntree); struct bNodeTree *ntreeCopyTree(struct bNodeTree *ntree, int internal_select); void ntreeMakeLocal(struct bNodeTree *ntree); @@ -144,7 +146,12 @@ void nodeVerifyType(struct bNodeTree *ntree, struct bNode *node); void nodeAddToPreview(struct bNode *, float *, int, int); void nodeUnlinkNode(struct bNodeTree *ntree, struct bNode *node); -struct bNode *nodeAddNodeType(struct bNodeTree *ntree, int type, struct bNodeTree *ngroup); +void nodeAddSockets(struct bNode *node, struct bNodeType *ntype); +struct bNode *nodeAddNodeType(struct bNodeTree *ntree, int type, struct bNodeTree *ngroup, struct ID *id); +void nodeRegisterType(struct ListBase *typelist, const struct bNodeType *ntype) ; +void nodeUpdateType(struct bNodeTree *ntree, struct bNode* node, struct bNodeType *ntype); +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); @@ -176,6 +183,7 @@ void nodeGroupSocketUseFlags(struct bNodeTree *ngroup); #define NODE_GROUP 2 #define NODE_GROUP_MENU 1000 +#define NODE_DYNAMIC_MENU 4000 extern bNodeType node_group_typeinfo; @@ -192,7 +200,7 @@ struct ShadeResult; #define SH_NODE_OUTPUT 1 #define SH_NODE_MATERIAL 100 -#define SH_NODE_RGB 101 +#define SH_NODE_RGB 101 #define SH_NODE_VALUE 102 #define SH_NODE_MIX_RGB 103 #define SH_NODE_VALTORGB 104 @@ -212,12 +220,21 @@ struct ShadeResult; #define SH_NODE_SEPRGB 120 #define SH_NODE_COMBRGB 121 #define SH_NODE_HUE_SAT 122 - +#define NODE_DYNAMIC 123 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 #define SH_NODE_MAT_SPEC 2 #define SH_NODE_MAT_NEG 4 +/* custom defines: states for Script node. These are bit indices */ +#define NODE_DYNAMIC_READY 0 /* 1 */ +#define NODE_DYNAMIC_LOADED 1 /* 2 */ +#define NODE_DYNAMIC_NEW 2 /* 4 */ +#define NODE_DYNAMIC_UPDATED 3 /* 8 */ +#define NODE_DYNAMIC_ADDEXIST 4 /* 16 */ +#define NODE_DYNAMIC_ERROR 5 /* 32 */ +#define NODE_DYNAMIC_REPARSE 6 /* 64 */ +#define NODE_DYNAMIC_SET 15 /* sign */ /* the type definitions array */ extern struct ListBase node_all_shaders; @@ -350,5 +367,6 @@ void free_compbuf(struct CompBuf *cbuf); /* internal...*/ void init_nodesystem(void); void free_nodesystem(void); +void reinit_nodesystem(void); #endif diff --git a/source/blender/blenkernel/BKE_utildefines.h b/source/blender/blenkernel/BKE_utildefines.h index fc22f6787b1..aa2be236d36 100644 --- a/source/blender/blenkernel/BKE_utildefines.h +++ b/source/blender/blenkernel/BKE_utildefines.h @@ -171,20 +171,22 @@ /* This one rotates the bytes in an int */ #define SWITCH_INT(a) { \ - char s_i, *p_i; \ - p_i= (char *)&(a); \ - s_i=p_i[0]; p_i[0]=p_i[3]; p_i[3]=s_i; \ - s_i=p_i[1]; p_i[1]=p_i[2]; p_i[2]=s_i; } + char s_i, *p_i; \ + p_i= (char *)&(a); \ + s_i=p_i[0]; p_i[0]=p_i[3]; p_i[3]=s_i; \ + s_i=p_i[1]; p_i[1]=p_i[2]; p_i[2]=s_i; } #define SWITCH_SHORT(a) { \ - char s_i, *p_i; \ - p_i= (char *)&(a); \ - s_i=p_i[0]; p_i[0]=p_i[1]; p_i[1]=s_i; } + char s_i, *p_i; \ + p_i= (char *)&(a); \ + s_i=p_i[0]; p_i[0]=p_i[1]; p_i[1]=s_i; } /* Bit operations */ -#define BTST(a,b) ( ( (a) & 1<<(b) )!=0 ) -#define BSET(a,b) ( (a) | 1<<(b) ) +#define BTST(a,b) ( ( (a) & 1<<(b) )!=0 ) +#define BNTST(a,b) ( ( (a) & 1<<(b) )==0 ) +#define BTST2(a,b,c) ( BTST( (a), (b) ) || BTST( (a), (c) ) ) +#define BSET(a,b) ( (a) | 1<<(b) ) #define BCLR(a,b) ( (a) & ~(1<<(b)) ) /* bit-row */ #define BROW(min, max) (((max)>=31? 0xFFFFFFFF: (1<<(max+1))-1) - ((min)? ((1<<(min))-1):0) ) diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index f8f2f0b9f57..1bb98239a68 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -11,6 +11,7 @@ incs += ' #/extern/bullet2/src' incs += ' #/intern/bmfont' incs += ' #/intern/opennl/extern' +incs += ' ' + env['BF_PYTHON_INC'] incs += ' ' + env['BF_OPENGL_INC'] incs += ' ' + env['BF_ZLIB_INC'] incs += ' ' + env['BF_SDL_INC'] diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 43a6f2091de..f30ad81bf0c 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -27,6 +27,7 @@ * ***** END GPL LICENSE BLOCK ***** */ +#include <Python.h> #include <stdlib.h> #include <string.h> @@ -34,6 +35,7 @@ #include "DNA_image_types.h" #include "DNA_node_types.h" #include "DNA_material_types.h" +#include "DNA_text_types.h" #include "DNA_scene_types.h" #include "BKE_blender.h" @@ -44,6 +46,7 @@ #include "BKE_main.h" #include "BKE_node.h" #include "BKE_texture.h" +#include "BKE_text.h" #include "BKE_utildefines.h" #include "BLI_arithb.h" @@ -72,7 +75,7 @@ ListBase node_all_shaders = {NULL, NULL}; /* ************** Type stuff ********** */ -static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup) +static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup, ID *id) { if(type==NODE_GROUP) { if(ngroup && GS(ngroup->id.name)==ID_NT) { @@ -83,7 +86,7 @@ static bNodeType *node_get_type(bNodeTree *ntree, int type, bNodeTree *ngroup) else { bNodeType *ntype = ntree->alltypes.first; for(; ntype; ntype= ntype->next) - if(ntype->type==type) + if(ntype->type==type && id==ntype->id ) return ntype; return NULL; @@ -105,7 +108,27 @@ void ntreeInitTypes(bNodeTree *ntree) for(node= ntree->nodes.first; node; node= next) { next= node->next; - node->typeinfo= node_get_type(ntree, node->type, (bNodeTree *)node->id); + if(node->type==NODE_DYNAMIC) { + bNodeType *stype= NULL; + if(node->id==NULL) { /* empty script node */ + stype= node_get_type(ntree, node->type, NULL, NULL); + } else { /* not an empty script node */ + stype= node_get_type(ntree, node->type, NULL, node->id); + if(!stype) { + stype= node_get_type(ntree, node->type, NULL, NULL); + /* needed info if the pynode script fails now: */ + if (node->id) node->storage= ntree; + } else { + node->custom1= 0; + node->custom1= BSET(node->custom1,NODE_DYNAMIC_ADDEXIST); + } + } + node->typeinfo= stype; + node->typeinfo->initfunc(node); + } else { + node->typeinfo= node_get_type(ntree, node->type, (bNodeTree *)node->id, NULL); + } + if(node->typeinfo==NULL) { printf("Error: Node type %s doesn't exist anymore, removed\n", node->name); nodeFreeNode(ntree, node); @@ -115,6 +138,18 @@ void ntreeInitTypes(bNodeTree *ntree) ntree->init |= NTREE_TYPE_INIT; } +/* updates node with (modified) bNodeType.. this should be done for all trees */ +void ntreeUpdateType(bNodeTree *ntree, bNodeType *ntype) +{ + bNode *node; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo== ntype) { + nodeUpdateType(ntree, node, ntype); + } + } +} + /* only used internal... we depend on type definitions! */ static bNodeSocket *node_add_socket_type(ListBase *lb, bNodeSocketType *stype) { @@ -137,7 +172,7 @@ static bNodeSocket *node_add_socket_type(ListBase *lb, bNodeSocketType *stype) if(lb) BLI_addtail(lb, sock); - + return sock; } @@ -513,7 +548,7 @@ bNode *nodeMakeGroupFromSelected(bNodeTree *ntree) ntreeMakeOwnType(ngroup); /* make group node */ - gnode= nodeAddNodeType(ntree, NODE_GROUP, ngroup); + gnode= nodeAddNodeType(ntree, NODE_GROUP, ngroup, NULL); gnode->locx= 0.5f*(min[0]+max[0]); gnode->locy= 0.5f*(min[1]+max[1]); @@ -767,29 +802,10 @@ int nodeGroupUnGroup(bNodeTree *ntree, bNode *gnode) } /* ************** Add stuff ********** */ - -bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup) +void nodeAddSockets(bNode *node, bNodeType *ntype) { - bNode *node; - bNodeType *ntype= node_get_type(ntree, type, ngroup); bNodeSocketType *stype; - - node= MEM_callocN(sizeof(bNode), "new node"); - BLI_addtail(&ntree->nodes, node); - node->typeinfo= ntype; - - if(ngroup) - BLI_strncpy(node->name, ngroup->id.name+2, NODE_MAXSTR); - else - BLI_strncpy(node->name, ntype->name, NODE_MAXSTR); - node->type= ntype->type; - node->flag= NODE_SELECT|ntype->flag; - node->width= ntype->width; - node->miniwidth= 42.0f; /* small value only, allows print of first chars */ - - if(type==NODE_GROUP) - node->id= (ID *)ngroup; - + if(ntype->inputs) { stype= ntype->inputs; while(stype->type != -1) { @@ -804,13 +820,84 @@ bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup) stype++; } } - +} + + +bNode *nodeAddNodeType(bNodeTree *ntree, int type, bNodeTree *ngroup, ID *id) +{ + bNode *node= NULL; + bNodeType *ntype= NULL; + + if(type>=NODE_DYNAMIC_MENU) { + int a=0, idx= type-NODE_DYNAMIC_MENU; + ntype= ntree->alltypes.first; + while(ntype) { + if(ntype->type==NODE_DYNAMIC) { + if(a==idx) + break; + a++; + } + ntype= ntype->next; + } + } else + ntype= node_get_type(ntree, type, ngroup, id); + + node= MEM_callocN(sizeof(bNode), "new node"); + BLI_addtail(&ntree->nodes, node); + node->typeinfo= ntype; + if(type>=NODE_DYNAMIC_MENU) + node->custom2= type; /* for node_dynamic_init */ + + if(ngroup) + BLI_strncpy(node->name, ngroup->id.name+2, NODE_MAXSTR); + else if(type>NODE_DYNAMIC_MENU) { + BLI_strncpy(node->name, ntype->id->name+2, NODE_MAXSTR); + } + else + BLI_strncpy(node->name, ntype->name, NODE_MAXSTR); + node->type= ntype->type; + node->flag= NODE_SELECT|ntype->flag; + node->width= ntype->width; + node->miniwidth= 42.0f; /* small value only, allows print of first chars */ + + if(type==NODE_GROUP) + node->id= (ID *)ngroup; + /* need init handler later? */ - /* got it-bob*/ - if(ntype->initfunc!=NULL) - ntype->initfunc(node); + /* got it-bob*/ + if(ntype->initfunc!=NULL) + ntype->initfunc(node); + + nodeAddSockets(node, ntype); + + return node; +} + +void nodeMakeDynamicType(bNode *node) +{ + /* find SH_DYNAMIC_NODE ntype */ + bNodeType *ntype= node_all_shaders.first; + while(ntype) { + if(ntype->type==NODE_DYNAMIC && ntype->id==NULL) + break; + ntype= ntype->next; + } - return node; + /* make own type struct to fill */ + if(ntype) { + /*node->typeinfo= MEM_dupallocN(ntype);*/ + bNodeType *newtype= MEM_callocN(sizeof(bNodeType), "dynamic bNodeType"); + *newtype= *ntype; + newtype->name= BLI_strdup(ntype->name); + node->typeinfo= newtype; + } +} + + +void nodeUpdateType(bNodeTree *ntree, bNode* node, bNodeType *ntype) +{ + verify_socket_list(ntree, &node->inputs, ntype->inputs); + verify_socket_list(ntree, &node->outputs, ntype->outputs); } /* keep socket listorder identical, for copying links */ @@ -1042,6 +1129,7 @@ void nodeFreeNode(bNodeTree *ntree, bNode *node) if(node->typeinfo && node->typeinfo->freestoragefunc) { node->typeinfo->freestoragefunc(node); } + MEM_freeN(node); } @@ -2322,12 +2410,12 @@ void ntreeCompositTagGenerators(bNodeTree *ntree) /* ************* node definition init ********** */ -static bNodeType *is_nodetype_registered(ListBase *typelist, int type) +static bNodeType *is_nodetype_registered(ListBase *typelist, int type, ID *id) { bNodeType *ntype= typelist->first; for(;ntype; ntype= ntype->next ) - if(ntype->type==type) + if(ntype->type==type && ntype->id==id) return ntype; return NULL; @@ -2336,10 +2424,10 @@ static bNodeType *is_nodetype_registered(ListBase *typelist, int type) /* type can be from a static array, we make copy for duplicate types (like group) */ void nodeRegisterType(ListBase *typelist, const bNodeType *ntype) { - bNodeType *found= is_nodetype_registered(typelist, ntype->type); + bNodeType *found= is_nodetype_registered(typelist, ntype->type, ntype->id); if(found==NULL) { - bNodeType *ntypen= MEM_mallocN(sizeof(bNodeType), "node type"); + bNodeType *ntypen= MEM_callocN(sizeof(bNodeType), "node type"); *ntypen= *ntype; BLI_addtail(typelist, ntypen); } @@ -2409,7 +2497,6 @@ static void registerCompositNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &cmp_node_crop); nodeRegisterType(ntypelist, &cmp_node_displace); nodeRegisterType(ntypelist, &cmp_node_mapuv); - nodeRegisterType(ntypelist, &cmp_node_glare); nodeRegisterType(ntypelist, &cmp_node_tonemap); nodeRegisterType(ntypelist, &cmp_node_lensdist); @@ -2436,12 +2523,46 @@ static void registerShaderNodes(ListBase *ntypelist) nodeRegisterType(ntypelist, &sh_node_value); nodeRegisterType(ntypelist, &sh_node_rgb); nodeRegisterType(ntypelist, &sh_node_texture); + nodeRegisterType(ntypelist, &node_dynamic_typeinfo); nodeRegisterType(ntypelist, &sh_node_invert); nodeRegisterType(ntypelist, &sh_node_seprgb); nodeRegisterType(ntypelist, &sh_node_combrgb); nodeRegisterType(ntypelist, &sh_node_hue_sat); } +static void remove_dynamic_typeinfos(ListBase *list) +{ + bNodeType *ntype= list->first; + bNodeType *next= NULL; + while(ntype) { + next= ntype->next; + if(ntype->type==NODE_DYNAMIC && ntype->id!=NULL) { + BLI_remlink(list, ntype); + if(ntype->inputs) { + bNodeSocketType *sock= ntype->inputs; + while(sock->type!=-1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(ntype->inputs); + } + if(ntype->outputs) { + bNodeSocketType *sock= ntype->outputs; + while(sock->type!=-1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(ntype->outputs); + } + if(ntype->name) { + MEM_freeN(ntype->name); + } + MEM_freeN(ntype); + } + ntype= next; + } +} + void init_nodesystem(void) { registerCompositNodes(&node_all_composit); @@ -2450,8 +2571,15 @@ void init_nodesystem(void) void free_nodesystem(void) { + /*remove_dynamic_typeinfos(&node_all_composit);*/ /* unused for now */ BLI_freelistN(&node_all_composit); + remove_dynamic_typeinfos(&node_all_shaders); BLI_freelistN(&node_all_shaders); } +void reinit_nodesystem(void) +{ + /*remove_dynamic_typeinfos(&node_all_composit);*/ /* unused for now */ + /*remove_dynamic_typeinfos(&node_all_shaders);*//*crash on undo/redo*/ +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 33fe1b36f52..fa580220799 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1453,11 +1453,18 @@ static void lib_link_nodetree(FileData *fd, Main *main) } /* verify types for nodes and groups, all data has to be read */ -static void lib_verify_nodetree(Main *main) +/* open = 0: appending/linking, open = 1: open new file (need to clean out dynamic + * typedefs*/ +static void lib_verify_nodetree(Main *main, int open) { Scene *sce; Material *ma; bNodeTree *ntree; + + /* this crashes blender on undo/redo + if(open==1) { + reinit_nodesystem(); + }*/ /* now create the own typeinfo structs an verify nodes */ /* here we still assume no groups in groups */ @@ -1494,6 +1501,12 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree) link_list(fd, &ntree->nodes); for(node= ntree->nodes.first; node; node= node->next) { + if(node->type == NODE_DYNAMIC) { + node->custom1= 0; + node->custom1= BSET(node->custom1, NODE_DYNAMIC_LOADED); + node->typeinfo= NULL; + } + node->storage= newdataadr(fd, node->storage); if(node->storage) { @@ -7546,7 +7559,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, BlendReadError *error_r) blo_join_main(&fd->mainlist); lib_link_all(fd, bfd->main); - lib_verify_nodetree(bfd->main); + lib_verify_nodetree(bfd->main, 1); fix_relpaths_library(fd->filename, bfd->main); /* make all relative paths, relative to the open blend file */ if(fg) @@ -8508,7 +8521,7 @@ static Library* library_append( Scene *scene, char* file, char *dir, int idcode, G.main= fd->mainlist.first; lib_link_all(fd, G.main); - lib_verify_nodetree(G.main); + lib_verify_nodetree(G.main, 0); fix_relpaths_library(G.sce, G.main); /* make all relative paths, relative to the open blend file */ /* give a base to loose objects. If group append, do it for objects too */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index d9766af6ba1..0804ec9689c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -452,7 +452,7 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree) writestruct(wd, DATA, "bNode", 1, node); for(node= ntree->nodes.first; node; node= node->next) { - if(node->storage) { + if(node->storage && node->type!=NODE_DYNAMIC) { /* could be handlerized at some point, now only 1 exception still */ if(ntree->type==NTREE_SHADER && (node->type==SH_NODE_CURVE_VEC || node->type==SH_NODE_CURVE_RGB)) write_curvemapping(wd, node->storage); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 0b462a46753..0e2805c88b7 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -258,6 +258,10 @@ typedef struct NodeDefocus { float fstop, maxblur, bthresh, scale; } NodeDefocus; +typedef struct NodeScriptDict { + void *dict; /* for PyObject *dict */ + void *node; /* for BPy_Node *node */ +} NodeScriptDict; /* qdn: glare node */ typedef struct NodeGlare { diff --git a/source/blender/nodes/SConscript b/source/blender/nodes/SConscript index 0460848b4a5..cc7cde6fc26 100644 --- a/source/blender/nodes/SConscript +++ b/source/blender/nodes/SConscript @@ -13,7 +13,7 @@ incs += ' ../blenloader ../quicktime' incs += ' ../blenkernel ../renderconverter ' - +incs += ' ' + env['BF_PYTHON_INC'] incs += ' ' + env['BF_OPENGL_INC'] incs += ' ' + env['BF_ZLIB_INC'] incs += ' ' + env['BF_SDL_INC'] @@ -41,6 +41,6 @@ if env['WITH_BF_QUICKTIME'] == 1: defs += ' WITH_QUICKTIME' incs += ' ' + env['BF_QUICKTIME_INC'] -defs += ' WITH_CCGSUBSURF' +defs += ' WITH_CCGSUBSURF USE_PYNODES ' env.BlenderLib ( libname = 'nodes', sources = sources, includes = Split(incs), defines = Split(defs), libtype=['core','player'], priority = [65, 20] ) diff --git a/source/blender/nodes/SHD_node.h b/source/blender/nodes/SHD_node.h index 8064a543ca0..d75d7c9f568 100644 --- a/source/blender/nodes/SHD_node.h +++ b/source/blender/nodes/SHD_node.h @@ -56,13 +56,13 @@ extern bNodeType sh_node_curve_rgb; extern bNodeType sh_node_math; extern bNodeType sh_node_vect_math; extern bNodeType sh_node_squeeze; +extern bNodeType node_dynamic_typeinfo; extern bNodeType sh_node_material_ext; extern bNodeType sh_node_invert; extern bNodeType sh_node_seprgb; extern bNodeType sh_node_combrgb; extern bNodeType sh_node_hue_sat; - #endif diff --git a/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c b/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c index 1ba777a0533..c198e73a3bf 100644 --- a/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c +++ b/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c @@ -34,6 +34,7 @@ #include "DNA_text_types.h" #include "BKE_text.h" +#include "BKE_utildefines.h" #include "api2_2x/Node.h" #include "api2_2x/gen_utils.h" @@ -41,6 +42,9 @@ #include "../SHD_util.h" +static void node_dynamic_setup(bNode *node); +static void node_dynamic_exec_cb(void *data, bNode *node, bNodeStack **in, bNodeStack **out); + static PyObject *init_dynamicdict(void) { PyObject *newscriptdict= PyDict_New(); PyDict_SetItemString(newscriptdict, "__builtins__", PyEval_GetBuiltins()); @@ -48,198 +52,575 @@ static PyObject *init_dynamicdict(void) { return newscriptdict; } +/* unused for now static void free_dynamicdict(PyObject *dict) { - if(dict!=NULL) { + if (dict!=NULL) { Py_DECREF(dict); } } +*/ -static void node_dynamic_init(bNode *node) { - NodeScriptDict *nsd= MEM_callocN(sizeof(NodeScriptDict), "node script dictionary"); - int type= node->custom2; - node->custom2= 0; - node->storage= nsd; - if(type>=NODE_DYNAMIC_MENU) { - if(type==NODE_DYNAMIC_MENU) { - nodeMakeDynamicType(node); - node->custom1= SH_NODE_DYNAMIC_NEW; - } else { - node->custom1= SH_NODE_DYNAMIC_ADDEXIST; +static bNodeType *node_dynamic_find_typeinfo(ListBase *list, ID *id) +{ + bNodeType *ntype = list->first; + + while(ntype) { + if (ntype->type == NODE_DYNAMIC && ntype->id == id) + break; + ntype = ntype->next; + } + + return ntype; /* NULL if doesn't exist */ +} + +static void node_dynamic_free_typeinfo_sockets(bNodeType *tinfo) +{ + bNodeSocketType *sock; + + if (!tinfo) return; + + if (tinfo->inputs) { + sock = tinfo->inputs; + while (sock->type != -1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(tinfo->inputs); + tinfo->inputs = NULL; + } + if (tinfo->outputs) { + sock = tinfo->outputs; + while (sock->type != -1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(tinfo->outputs); + tinfo->outputs = NULL; + } +} + +static void node_dynamic_free_typeinfo(bNodeType *tinfo) +{ + if (!tinfo) return; + + node_dynamic_free_typeinfo_sockets(tinfo); + + if (tinfo->name) { MEM_freeN(tinfo->name); } + + MEM_freeN(tinfo); +} + +static void node_dynamic_free_sockets(bNode *node) +{ + BLI_freelistN(&node->inputs); + BLI_freelistN(&node->outputs); +} + +/* For now we just remove the socket links. It's the safest + * route, since an update in the script may change completely the + * inputs and outputs. Trying to recreate the node links would be + * nicer for pynode authors, though. */ +static void node_dynamic_update_socket_links(bNode *node, bNodeTree *ntree) +{ + if (ntree) { + nodeVerifyType(ntree, node); + } + else { + Material *ma; + + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + bNode *nd; + for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) { + if (nd == node) nodeVerifyType(ma->nodetree, node); + } + } } - node->id= node->typeinfo->id; - nodeDynamicParse(node); - } else { - if(node->custom1== SH_NODE_DYNAMIC_LOADED) { - nodeMakeDynamicType(node); - nodeDynamicParse(node); - } else if(node->custom1== SH_NODE_DYNAMIC_ADDEXIST) - nodeDynamicParse(node); } } -static void node_dynamic_free(bNode *node) +static void node_dynamic_free_storage_cb(bNode *node) { - NodeScriptDict *nsd= (NodeScriptDict *)(node->storage); - BPy_Node *pynode= nsd->node; - Py_XDECREF(pynode); - free_dynamicdict((PyObject *)(nsd->dict)); + NodeScriptDict *nsd; + PyObject *pydict; + BPy_Node *pynode; + + if (!node->storage) return; + + nsd = (NodeScriptDict *)(node->storage); + pydict = nsd->dict; + if (pydict) { + Py_DECREF(pydict); + } + pynode = nsd->node; + if (pynode) { + Py_DECREF(pynode); + } MEM_freeN(node->storage); + node->storage = NULL; } -static void node_dynamic_copy(bNode *orig_node, bNode *new_node) +/* Disable pynode when its script fails */ +static void node_dynamic_disable(bNode *node) { - NodeScriptDict *nsd= (NodeScriptDict *)(orig_node->storage); - new_node->storage= MEM_dupallocN(orig_node->storage); - if(nsd->node) - Py_INCREF((PyObject *)(nsd->node)); - if(nsd->dict) - Py_INCREF((PyObject *)(nsd->dict)); + node->custom1 = 0; + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ERROR); } -static void node_dynamic_exec(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { - BPy_Node *mynode = NULL; - NodeScriptDict *nsd = NULL; - PyObject *pyresult = NULL; - PyObject *args = NULL; - ShadeInput *shi= ((ShaderCallData *)data)->shi; +/* Disable all pynodes using the given text (script) id */ +static void node_dynamic_disable_all_by_id(ID *id) +{ + Material *ma; /* XXX hardcoded for shaders */ - if(node->custom1==SH_NODE_DYNAMIC_NEW) { - nodeDynamicParse(node); - return; + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + bNode *nd; + bNodeTree *ntree = ma->nodetree; + for (nd= ntree->nodes.first; nd; nd= nd->next) { + if (nd->id == id) { + nd->custom1 = 0; + nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_ERROR); + } + } + } } +} - if(node->custom2<0) - return; +static void node_rem_socklist_links(bNodeTree *ntree, ListBase *lb) +{ + bNodeLink *link, *next; + bNodeSocket *sock; - if(node->custom1==SH_NODE_DYNAMIC_READY || node->custom1==SH_NODE_DYNAMIC_UPDATED) { - if(node->custom1== SH_NODE_DYNAMIC_UPDATED) - node->custom1= SH_NODE_DYNAMIC_READY; + if (!lb) return; - nsd = (NodeScriptDict *)node->storage; + for (sock= lb->first; sock; sock= sock->next) { + for (link= ntree->links.first; link; link= next) { + next= link->next; + if (link->fromsock==sock || link->tosock==sock) { + nodeRemLink(ntree, link); + } + } + } +} - mynode = (BPy_Node *)(nsd->node); - if(mynode && PyCallable_Check((PyObject *)mynode)) { - mynode->node= node; - Node_SetStack(mynode, in, NODE_INPUTSTACK); - Node_SetStack(mynode, out, NODE_OUTPUTSTACK); - Node_SetShi(mynode, shi); - args=Py_BuildValue("()"); - pyresult= PyObject_Call((PyObject *)mynode, args, NULL); - if(!pyresult) { - if(PyErr_Occurred()) { - PyErr_Print(); - node->custom2= -1; - } else { - printf("PyObject_Call __call__ failed\n"); +/* XXX hardcoded for shaders */ +static void node_dynamic_rem_all_links(bNodeType *tinfo) +{ + Material *ma; + int in, out; + + in = tinfo->inputs ? 1 : 0; + out = tinfo->outputs ? 1 : 0; + + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + bNode *nd; + bNodeTree *ntree = ma->nodetree; + for (nd= ntree->nodes.first; nd; nd= nd->next) { + if (nd->typeinfo == tinfo) { + if (in) + node_rem_socklist_links(ntree, &nd->inputs); + if (out) + node_rem_socklist_links(ntree, &nd->outputs); + } + } + } + } +} + +/* node_dynamic_reset: clean a pynode, getting rid of all + * data dynamically created for it. + * ntree is used only in a special case: for working pynodes + * that were saved on a .blend but fail for some reason when + * the file is opened. We need it because pynodes are initialized + * before G.main. */ +static void node_dynamic_reset(bNode *node, bNodeTree *ntree) +{ + bNodeType *tinfo, *tinfo_default; + Material *ma; + + tinfo = node->typeinfo; + tinfo_default = node_dynamic_find_typeinfo(&node_all_shaders, NULL); + + node_dynamic_rem_all_links(tinfo); + node_dynamic_free_typeinfo_sockets(tinfo); + + if (!ntree) { node_dynamic_free_sockets(node); } + + //wnode_dynamic_update_socket_links(node, ntree); + node_dynamic_free_storage_cb(node); + + /* XXX hardcoded for shaders: */ + if (node->typeinfo->id) { BLI_remlink(&node_all_shaders, tinfo); } + + node->typeinfo = tinfo_default; + + /* reset all other XXX shader nodes sharing this typeinfo */ + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + bNode *nd; + for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) { + if (nd->typeinfo == tinfo) { + node_dynamic_free_storage_cb(nd); + node_dynamic_free_sockets(nd); + nd->typeinfo = tinfo_default; } } - Py_XDECREF(pyresult); - Py_DECREF(args); } } + + node_dynamic_free_typeinfo(tinfo); } -void nodeDynamicParse(struct bNode *node) +int nodeDynamicUnlinkText(ID *txtid) { + Material *ma; + int unlinked= 0; + + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + bNode *nd, *nd2 = NULL; + for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) { + if ((nd->type == NODE_DYNAMIC) && (nd->id == txtid)) { + nd->id = NULL; + nd->custom1 = 0; + nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_NEW); + BLI_strncpy(nd->name, "Dynamic", 8); + nd2 = nd; /* so we have a ptr to one of them */ + unlinked++; + } + } + /* clean uneeded dynamic data from all nodes that shared + * this text: */ + if (nd2) node_dynamic_reset(nd2, NULL); + } + } + + return unlinked; +} + +static void node_dynamic_pyerror_print(bNode *node) +{ + fprintf(stderr, "\nError in dynamic node script \"%s\":\n", node->name); + if (PyErr_Occurred()) { PyErr_Print(); } + else { fprintf(stderr, "Not a valid dynamic node Python script.\n"); } +} + +static int node_dynamic_parse(struct bNode *node) { - BPy_Node *pynode= NULL; PyObject *dict= NULL; PyObject *key= NULL; PyObject *value= NULL; - PyObject *testinst= NULL; + PyObject *pynode= NULL; PyObject *args= NULL; - int pos = 0; - NodeScriptDict *nsd= NULL; + NodeScriptDict *nsd = NULL; PyObject *pyresult = NULL; - PyObject *pycompiled = NULL; - Text *txt = NULL; - char *buf= NULL; + char *buf = NULL; + int pos = 0, is_valid_script = 0; + + if (!node->id || !node->storage) + return 0; - if(! node->id) { + /* READY, no need to be here */ + if (BTST(node->custom1, NODE_DYNAMIC_READY)) + return 0; + + nsd = (NodeScriptDict *)node->storage; + + dict = (PyObject *)(nsd->dict); + buf = txt_to_buf((Text *)node->id); + + pyresult = PyRun_String(buf, Py_file_input, dict, dict); + + MEM_freeN(buf); + + if (!pyresult) { + node_dynamic_disable(node); + node_dynamic_pyerror_print(node); + return -1; + } + + Py_DECREF(pyresult); + + while (PyDict_Next( (PyObject *)(nsd->dict), &pos, &key, &value)) { + /* look for the node object */ + if (PyObject_TypeCheck(value, &PyType_Type)==1) { + BPy_NodeSockets *sockets = Node_CreateSockets(node); + + args = Py_BuildValue("(O)", sockets); + + /* init it to get the input and output sockets */ + pynode = PyObject_Call(value, args, NULL); + + Py_DECREF(sockets); + Py_DECREF(args); + + if (!PyErr_Occurred() && pynode && PyObject_TypeCheck(pynode, &Node_Type)==1) { + InitNode((BPy_Node *)(pynode), node); + nsd->node = pynode; + node->typeinfo->execfunc = node_dynamic_exec_cb; + is_valid_script = 1; + + /* for NEW, LOADED, REPARSE */ + if (BNTST(node->custom1, NODE_DYNAMIC_ADDEXIST)) { + node->typeinfo->pydict = dict; + node->typeinfo->pynode = pynode; + node->typeinfo->id = node->id; + nodeAddSockets(node, node->typeinfo); + if (BNTST(node->custom1, NODE_DYNAMIC_REPARSE)) { + nodeRegisterType(&node_all_shaders, node->typeinfo); + /* nodeRegisterType copied it to a new one, so we + * free the typeinfo itself, but not what it + * points to: */ + MEM_freeN(node->typeinfo); + node->typeinfo = node_dynamic_find_typeinfo(&node_all_shaders, node->id); + MEM_freeN(node->typeinfo->name); + node->typeinfo->name = BLI_strdup(node->name); + } + } + + node->custom1 = 0; + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_READY); + break; + } + break; + } + } + + if (!is_valid_script) { /* not a valid pynode script */ + node_dynamic_disable(node); + node_dynamic_pyerror_print(node); + return -1; + } + + return 0; +} + +/* node_dynamic_setup: prepare for execution (state: NODE_DYNAMIC_READY) + * pynodes already linked to a script (node->id != NULL). */ +static void node_dynamic_setup(bNode *node) +{ + NodeScriptDict *nsd = NULL; + bNodeTree *nodetree = NULL; + bNodeType *ntype = NULL; + + /* Possible cases: + * NEW + * ADDEXIST + * LOADED + * REPARSE + * ERROR + * READY + */ + + /* NEW, but not linked to a script: link default (empty) typeinfo */ + if (!node->id) { + node->typeinfo = node_dynamic_find_typeinfo(&node_all_shaders, + NULL); return; } - if(node->custom1!=SH_NODE_DYNAMIC_READY) { - txt = (Text *)node->id; - nsd = (NodeScriptDict *)node->storage; + /* READY, no need to be here */ + if (BTST(node->custom1, NODE_DYNAMIC_READY)) + return; - if(nsd->dict==NULL && (node->custom1==SH_NODE_DYNAMIC_NEW||node->custom1==SH_NODE_DYNAMIC_LOADED)) { - nsd->dict= init_dynamicdict(); - } else if(nsd->dict==NULL && node->custom1==SH_NODE_DYNAMIC_ADDEXIST) { - nsd->dict= node->typeinfo->pydict; - nsd->node= node->typeinfo->pynode; - Py_INCREF((PyObject *)(nsd->dict)); - Py_INCREF((PyObject *)(nsd->node)); - node->custom1= SH_NODE_DYNAMIC_READY; + /* ERROR, reset to (empty) defaults */ + if (BCLR(node->custom1, NODE_DYNAMIC_ERROR) == 0) { + node_dynamic_reset(node, NULL); + return; + } + + /* User asked to update this pynode, prepare it for reparsing */ + if (BTST(node->custom1, NODE_DYNAMIC_REPARSE)) { + int needs_parsing = 1; + + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_NEW); + + if (BTST(node->custom1, NODE_DYNAMIC_ERROR)) { + node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_REPARSE); + ntype = node_dynamic_find_typeinfo(&node_all_shaders, node->id); + + if (ntype) { + node->typeinfo = ntype; + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ADDEXIST); + node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_ERROR); + needs_parsing = 0; + } + else { nodeMakeDynamicType(node); } + + } else { + node_dynamic_free_typeinfo_sockets(node->typeinfo); + node_dynamic_update_socket_links(node, NULL); + node_dynamic_free_storage_cb(node); + } + + if (needs_parsing) { + nsd = MEM_callocN(sizeof(NodeScriptDict), "node script dictionary"); + nsd->dict = init_dynamicdict(); + node->storage = nsd; + /* prepared, now reparse: */ + node_dynamic_parse(node); return; } - dict= (PyObject *)(nsd->dict); + } + else if (BTST(node->custom1, NODE_DYNAMIC_LOADED)) { + /* when loading from a .blend we don't have G.main yet, so we + * quickly abuse node->storage in ntreeInitTypes (node.c) to have + * our nodetree ptr (needed if a pynode script that worked before + * saving the .blend for some reason fails upon loading): */ + nodetree = (bNodeTree *)node->storage; + node->storage = NULL; + } - if(node->custom1!=SH_NODE_DYNAMIC_ADDEXIST) { - buf = txt_to_buf( txt ); - /*printf("Running script (%s, %d)...", node->name, node->custom1);*/ - pyresult = PyRun_String(buf, Py_file_input, dict, dict); - /*printf(" done\n");*/ + if (node->storage) + fprintf(stderr, "\nDEBUG: PYNODES ERROR: non NULL node->storage in node_dynamic_setup()\n"); - MEM_freeN(buf); + nsd = MEM_callocN(sizeof(NodeScriptDict), "node script dictionary"); + node->storage = nsd; + + /* NEW, LOADED or REPARSE */ + if (BNTST(node->custom1, NODE_DYNAMIC_ADDEXIST)) { + /* check if there's already a bNodeType linked to this script */ + /* (XXX hardcoded for shader nodes for now) */ + ntype = node_dynamic_find_typeinfo(&node_all_shaders, node->id); - if(!pyresult) { - if(PyErr_Occurred()) { - PyErr_Print(); - } - Py_XDECREF(pyresult); - return; + if (ntype) { /* if so, reuse it */ + node->typeinfo = ntype; + /* so this is actually an ADDEXIST type */ + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ADDEXIST); + } + else { /* create bNodeType for this pynode */ + nodeMakeDynamicType(node); + nsd->dict = init_dynamicdict(); + if ((node_dynamic_parse(node) == -1) && nodetree) { + node_dynamic_reset(node, nodetree); } + return; + } + } - Py_DECREF(pyresult); + /* ADDEXIST: new pynode linked to an already registered dynamic type, + * we just reuse existing py dict and pynode */ + nsd->dict = node->typeinfo->pydict; + nsd->node = node->typeinfo->pynode; + Py_INCREF((PyObject *)(nsd->dict)); + Py_INCREF((PyObject *)(nsd->node)); - while(PyDict_Next( (PyObject *)(nsd->dict), &pos, &key, &value) ) { - if(PyObject_TypeCheck(value, &PyType_Type)==1) { - BPy_DefinitionMap *outputdef= Node_CreateOutputDefMap(node); - BPy_DefinitionMap *inputdef= Node_CreateInputDefMap(node); - - args= Py_BuildValue("(OO)", inputdef, outputdef); - testinst= PyObject_Call(value, args, NULL); - - Py_DECREF(outputdef); - Py_DECREF(inputdef); - if(testinst && PyObject_TypeCheck(testinst, &Node_Type)==1) { - Py_INCREF(testinst); - Py_INCREF(dict); - InitNode((BPy_Node *)(testinst), node); - nsd->node= testinst; - node->typeinfo->execfunc= node_dynamic_exec; - if(node->custom1== SH_NODE_DYNAMIC_NEW || node->custom1== SH_NODE_DYNAMIC_LOADED) { - node->typeinfo->pynode= testinst; - node->typeinfo->pydict= nsd->dict; - node->typeinfo->id= node->id; - nodeAddSockets(node, node->typeinfo); - nodeRegisterType(&node_all_shaders, node->typeinfo); - node->custom1= SH_NODE_DYNAMIC_READY; - } - break; - } - Py_DECREF(args); - } - } + if (BTST(node->custom1, NODE_DYNAMIC_NEW)) { + nodeAddSockets(node, node->typeinfo); + node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_NEW); + } + + node->custom1 = BCLR(node->custom1, NODE_DYNAMIC_ADDEXIST); + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_READY); + + return; +} + +/* node_dynamic_init_cb callback: called when a pynode is created. + * The pynode type is passed via node->custom2. It can be: + * 0: for loaded empty nodes + * NODE_DYNAMIC_MENU: for the default Dynamic node type + * > NODE_DYNAMIC_MENU: for the new types defined by scripts +*/ +static void node_dynamic_init_cb(bNode *node) { + int type = node->custom2; + + node->custom2 = 0; + + if (type >= NODE_DYNAMIC_MENU) { + node->custom1 = 0; + + if (type == NODE_DYNAMIC_MENU) { + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_NEW); + return; } + + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_ADDEXIST); + node->id = node->typeinfo->id; } + + node_dynamic_setup(node); } +/* node_dynamic_copy_cb: pynode copy callback */ +static void node_dynamic_copy_cb(bNode *orig_node, bNode *new_node) +{ + NodeScriptDict *nsd; + + if (!orig_node->storage) return; + + nsd = (NodeScriptDict *)(orig_node->storage); + new_node->storage = MEM_dupallocN(orig_node->storage); + + if (nsd->node) + Py_INCREF((PyObject *)(nsd->node)); + if (nsd->dict) + Py_INCREF((PyObject *)(nsd->dict)); +} + +/* node_dynamic_exec_cb: the execution callback called per pixel + * during rendering. */ +static void node_dynamic_exec_cb(void *data, bNode *node, bNodeStack **in, bNodeStack **out) { + BPy_Node *mynode = NULL; + NodeScriptDict *nsd = NULL; + PyObject *pyresult = NULL; + PyObject *args = NULL; + ShadeInput *shi; + + if (!node->id) + return; + + if (BTST2(node->custom1, NODE_DYNAMIC_NEW, NODE_DYNAMIC_REPARSE)) { + node_dynamic_setup(node); + return; + } + + if (BTST(node->custom1, NODE_DYNAMIC_ERROR)) { + if (node->storage) node_dynamic_setup(node); + return; + } + + if (BTST(node->custom1, NODE_DYNAMIC_READY)) { + nsd = (NodeScriptDict *)node->storage; + + mynode = (BPy_Node *)(nsd->node); + if (mynode && PyCallable_Check((PyObject *)mynode)) { + mynode->node = node; + shi = ((ShaderCallData *)data)->shi; + + Node_SetStack(mynode, in, NODE_INPUTSTACK); + Node_SetStack(mynode, out, NODE_OUTPUTSTACK); + Node_SetShi(mynode, shi); + + args=Py_BuildValue("()"); + pyresult= PyObject_Call((PyObject *)mynode, args, NULL); + Py_DECREF(args); + + if (!pyresult) { + node_dynamic_disable_all_by_id(node->id); + node_dynamic_pyerror_print(node); + node_dynamic_setup(node); + return; + } + Py_DECREF(pyresult); + } + } +} -bNodeType sh_node_dynamic = { +bNodeType node_dynamic_typeinfo = { /* next, prev */ NULL, NULL, - /* type code */ SH_NODE_DYNAMIC, + /* type code */ NODE_DYNAMIC, /* name */ "Dynamic", /* width+range */ 150, 60, 300, /* class+opts */ NODE_CLASS_OP_DYNAMIC, NODE_OPTIONS, /* input sock */ NULL, /* output sock */ NULL, /* storage */ "NodeScriptDict", - /* execfunc */ node_dynamic_exec, + /* execfunc */ node_dynamic_exec_cb, /* butfunc */ NULL, - /* initfunc */ node_dynamic_init, - /* freefunc */ node_dynamic_free, - /* copyfunc */ node_dynamic_copy, + /* initfunc */ node_dynamic_init_cb, + /* freefunc */ node_dynamic_free_storage_cb, + /* copyfunc */ node_dynamic_copy_cb, /* id */ NULL }; diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript index 9cd245394b0..f608787d66d 100644 --- a/source/blender/python/SConscript +++ b/source/blender/python/SConscript @@ -3,7 +3,7 @@ Import ('env') sources = Split('BPY_interface.c BPY_menus.c') + env.Glob('api2_2x/*.c') -incs = 'api2_2x ../blenkernel ../blenlib ../blenloader' +incs = 'api2_2x ../blenkernel ../nodes ../blenlib ../blenloader' incs += ' ../render/extern/include ../radiosity/extern/include' incs += ' ../makesdna #intern/guardedalloc #intern/bmfont ../imbuf ../include' incs += ' ' + env['BF_PYTHON_INC'] @@ -20,6 +20,8 @@ if env['WITH_BF_QUICKTIME']==1: if env['WITH_BF_OPENEXR'] == 1: defs.append('WITH_OPENEXR') +defs.append('USE_PYNODES') + if env['WITH_BF_FFMPEG'] == 1: defs.append('WITH_FFMPEG') diff --git a/source/blender/python/api2_2x/Blender.c b/source/blender/python/api2_2x/Blender.c index db47d6550c2..46f7b9da79f 100644 --- a/source/blender/python/api2_2x/Blender.c +++ b/source/blender/python/api2_2x/Blender.c @@ -85,6 +85,7 @@ struct ID; /*keep me up here */ #include "Metaball.h" #include "Modifier.h" #include "NMesh.h" +#include "Node.h" #include "Object.h" #include "Group.h" #include "Registry.h" @@ -1047,6 +1048,7 @@ void M_Blender_Init(void) PyDict_SetItemString(dict, "Geometry", Geometry_Init()); PyDict_SetItemString(dict, "Modifier", Modifier_Init()); PyDict_SetItemString(dict, "NMesh", NMesh_Init()); + PyDict_SetItemString(dict, "Node", Node_Init()); PyDict_SetItemString(dict, "Noise", Noise_Init()); PyDict_SetItemString(dict, "Object", Object_Init()); PyDict_SetItemString(dict, "Group", Group_Init()); diff --git a/source/blender/python/api2_2x/Node.c b/source/blender/python/api2_2x/Node.c index 2871eda9432..195b61a39ea 100644 --- a/source/blender/python/api2_2x/Node.c +++ b/source/blender/python/api2_2x/Node.c @@ -1,5 +1,5 @@ /* - * $Id: Node.c 10454 2007-04-04 11:27:43Z jesterking $ + * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * @@ -36,6 +36,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_utildefines.h" #include "DNA_material_types.h" @@ -48,138 +49,199 @@ static PyObject *Node_repr( BPy_Node * self ); static int Node_compare(BPy_Node *a, BPy_Node *b); static PyObject *ShadeInput_repr( BPy_ShadeInput * self ); static int ShadeInput_compare(BPy_ShadeInput *a, BPy_ShadeInput *b); +static BPy_ShadeInput *ShadeInput_CreatePyObject(ShadeInput *shi); /** - * Take the descriptions from dict and create sockets for those in socks + * Take the descriptions from list and create sockets for those in socks * socks is a socketstack from a bNodeTypeInfo */ -static int dict_socks_to_typeinfo(PyObject *dict, bNodeSocketType **socks, int len, int stage) { - int a = 0, pos = 0; - PyObject *key = NULL, *value = NULL; +static int list_socks_to_typeinfo(PyObject *tuple, bNodeSocketType **socks, int stage, int limit) { + int len = 0, a = 0, pos = 0, retval = 0; + //wPyObject *key = NULL, *value = NULL; + PyObject *item, *arg; bNodeSocketType *newsocks = NULL; + char *s_name = NULL; + int s_type = SOCK_VALUE; + float s_val[4], s_min, s_max; - if(stage!=SH_NODE_DYNAMIC_READY && stage!=SH_NODE_DYNAMIC_ADDEXIST) { - newsocks = MEM_callocN(sizeof(bNodeSocketType)*(len+1), "bNodeSocketType"); - - if (dict) { - if(PyDict_Check(dict)) { - while(PyDict_Next(dict, &pos, &key, &value)) { - if(PyTuple_Check(value) && PyTuple_Size(value)==7) { - newsocks[a].name = BLI_strdup(PyString_AsString(key)); - newsocks[a].type = (int)(PyInt_AsLong(PyTuple_GetItem(value, 0))); - newsocks[a].val1 = (float)(PyFloat_AsDouble(PyTuple_GetItem(value, 1))); - newsocks[a].val2 = (float)(PyFloat_AsDouble(PyTuple_GetItem(value, 2))); - newsocks[a].val3 = (float)(PyFloat_AsDouble(PyTuple_GetItem(value, 3))); - newsocks[a].val4 = (float)(PyFloat_AsDouble(PyTuple_GetItem(value, 4))); - newsocks[a].min = (float)(PyFloat_AsDouble(PyTuple_GetItem(value, 5))); - newsocks[a].max = (float)(PyFloat_AsDouble(PyTuple_GetItem(value, 6))); - a++; - } - } - } else { - return(EXPP_ReturnIntError( PyExc_AttributeError, "INPUT must be a dict")); - } + if (BTST2(stage, NODE_DYNAMIC_READY, NODE_DYNAMIC_ADDEXIST)) + return 0; /* already has sockets */ + + len = PyTuple_Size(tuple); + + newsocks = MEM_callocN(sizeof(bNodeSocketType)*(len+1), "bNodeSocketType in Node.c"); + + for (pos = 0, a = 0; pos< len; pos++, a++) { + /* default socket values: */ + s_name = NULL; + s_type = SOCK_VALUE; + s_min = 0.0f; + s_max = 1.0f; + s_val[0] = s_val[1] = s_val[2] = s_val[3] = 1.0f; + + item = PyTuple_GetItem(tuple, pos); + + if (!PySequence_Check(item)) { + PyErr_SetString(PyExc_AttributeError, "a socket must be a List of Lists or Tuples"); + retval = -1; + break; } - newsocks[a].type = -1; - if(*socks) { - int b = 0; - while((*socks)[b].type!=-1) { - MEM_freeN((*socks)[b].name); - (*socks)[b].name = NULL; - b++; - } - MEM_freeN(*socks); + + arg = PySequence_Tuple(item); + + if (!PyArg_ParseTuple(arg, "s|iffffff", &s_name, &s_type, + &s_min, &s_max, + &s_val[0], &s_val[1], &s_val[2], &s_val[3] )) { + PyErr_SetString(PyExc_AttributeError, "socket definitions require a string and optionally an int and 6 floats"); + retval = -1; + Py_DECREF(arg); + break; } - *socks = newsocks; + + newsocks[a].name = BLI_strdupn(s_name, NODE_MAXSTR); + newsocks[a].type = s_type; + newsocks[a].min = s_min; + newsocks[a].max = s_max; + newsocks[a].val1 = s_val[0]; + newsocks[a].val2 = s_val[1]; + newsocks[a].val3 = s_val[2]; + newsocks[a].val4 = s_val[3]; + newsocks[a].limit = limit; + + Py_DECREF(arg); } - return 0; + + newsocks[a].type = -1; + + *socks = newsocks; + + return retval; } -/* Get number of complying entries in a dict. +/* Get number of complying entries in a list. * */ -static int num_dict_sockets(PyObject *dict) { - int a = 0, pos = 0; - PyObject *key = NULL, *value = NULL; - while(PyDict_Next(dict, &pos, &key, &value)) { - if(PyTuple_Check(value) && PyTuple_Size(value)==7) - a++; +/* unused +static int num_list_sockets(PyObject *list) { + int size = 0; + int i = 0, count = 0; + PyObject *element = NULL; + + size = PyList_Size(list); + for(i = 0; i < size; i++) { + element = PyList_GetItem(list, i); + //wPy_INCREF(element); + if(PyList_Check(element) && PyList_Size(element) == 8) + count++; + //wPy_DECREF(element); } - return a; + return count; +} +*/ +static void NodeSockets_dealloc(BPy_NodeSockets *self) +{ + Py_DECREF(self->input); + Py_DECREF(self->output); + self->ob_type->tp_free((PyObject *)self); } -static int Map_socketdef(PyObject *self, PyObject *args, void *closure) +static PyObject *Map_socketdef_getter(BPy_NodeSockets *self, void *closure) { - int newincnt = 0, newoutcnt = 0; - bNode *node = NULL; - BPy_DefinitionMap *defs= NULL; + PyObject *sockets = NULL; - Py_INCREF(args); - Py_INCREF(self); + switch ((int)closure) { + case 'I': /* inputs */ + Py_INCREF(self->input); + sockets = self->input; + break; + case 'O': /* outputs */ + Py_INCREF(self->output); + sockets = self->output; + break; + default: + fprintf(stderr, "DEBUG pynodes: wrong option in Map_socketdef_getter\n"); + Py_INCREF(Py_None); + sockets = Py_None; + break; + } + + return sockets; +} + +static int Map_socketdef(BPy_NodeSockets *self, PyObject *args, void *closure) +{ + bNode *node = NULL; + PyObject *tuple = NULL; - defs= (BPy_DefinitionMap *)self; - node= defs->node; + node = self->node; if(!node) { - fprintf(stderr,"! no bNode in BPy_Node (Map_socketdef)\n"); + fprintf(stderr,"DEBUG pynodes: No bNode in BPy_Node (Map_socketdef)\n"); return 0; } - if(node->custom1==SH_NODE_DYNAMIC_READY && node->custom1==SH_NODE_DYNAMIC_ADDEXIST) + if(BTST2(node->custom1, NODE_DYNAMIC_READY, NODE_DYNAMIC_ADDEXIST)) return 0; switch((int)closure) { case 'I': if (args) { - if(PyDict_Check(args)) { - newincnt = num_dict_sockets(args); - dict_socks_to_typeinfo(args, &(node->typeinfo->inputs), newincnt, node->custom1); + if(PySequence_Check(args)) { + tuple = PySequence_Tuple(args); + list_socks_to_typeinfo(tuple, &(node->typeinfo->inputs), node->custom1, 1); + Py_DECREF(self->input); + self->input = tuple; } else { - Py_DECREF(self); - Py_DECREF(args); - return(EXPP_ReturnIntError( PyExc_AttributeError, "INPUT must be a dict")); + return(EXPP_ReturnIntError( PyExc_AttributeError, "INPUT must be a List of Lists or Tuples")); } } break; case 'O': if (args) { - if(PyDict_Check(args)) { - newoutcnt = num_dict_sockets(args); - dict_socks_to_typeinfo(args, &(node->typeinfo->outputs), newoutcnt, node->custom1); + if(PyList_Check(args)) { + tuple = PySequence_Tuple(args); + list_socks_to_typeinfo(tuple, &(node->typeinfo->outputs), node->custom1, 0); + Py_DECREF(self->output); + self->output = tuple; } else { - Py_DECREF(self); - Py_DECREF(args); - return(EXPP_ReturnIntError( PyExc_AttributeError, "OUTPUT must be a dict")); + return(EXPP_ReturnIntError( PyExc_AttributeError, "OUTPUT must be a List of Lists or Tuples")); } } break; default: - fprintf(stderr, "Hrm, why we got no dict? Todo: figure out proper complaint to scripter\n"); + fprintf(stderr,"DEBUG pynodes: got no list in Map_socketdef\n"); break; } - Py_DECREF(self); - Py_DECREF(args); return 0; } -static PyGetSetDef InputDefMap_getseters[] = { - {"definitions", (getter)NULL, (setter)Map_socketdef, - "Set the inputs definition (dictionary)", +static PyGetSetDef NodeSockets_getseters[] = { + {"input", (getter)Map_socketdef_getter, (setter)Map_socketdef, + "Set this node's input sockets (list of lists or tuples)", + (void *)'I'}, + {"i" /*alias*/, (getter)Map_socketdef_getter, (setter)Map_socketdef, + "Set this node's input sockets (list of lists or tuples)", (void *)'I'}, + {"output", (getter)Map_socketdef_getter, (setter)Map_socketdef, + "Set this node's output sockets (list of lists or tuples)", + (void *)'O'}, + {"o" /*alias*/, (getter)Map_socketdef_getter, (setter)Map_socketdef, + "Set this node's output sockets (list of lists or tuples)", + (void *)'O'}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; -PyTypeObject InputDefMap_Type = { +PyTypeObject NodeSockets_Type = { PyObject_HEAD_INIT( NULL ) /* required py macro */ 0, /* ob_size */ /* For printing, in format "<module>.<name>" */ - "Blender.Node.InputDefinitions", /* char *tp_name; */ - sizeof( BPy_DefinitionMap ), /* int tp_basicsize; */ + "Blender.Node.Sockets", /* char *tp_name; */ + sizeof( BPy_NodeSockets ), /* int tp_basicsize; */ 0, /* tp_itemsize; For allocation */ /* Methods to implement standard operations */ - NULL,/* destructor tp_dealloc; */ + (destructor)NodeSockets_dealloc,/* destructor tp_dealloc; */ NULL, /* printfunc tp_print; */ NULL, /* getattrfunc tp_getattr; */ NULL, /* setattrfunc tp_setattr; */ @@ -229,7 +291,7 @@ PyTypeObject InputDefMap_Type = { /*** Attribute descriptor and subclassing stuff ***/ 0, //BPy_MVertSeq_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ - InputDefMap_getseters, /* struct PyGetSetDef *tp_getset; */ + NodeSockets_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ @@ -251,127 +313,48 @@ PyTypeObject InputDefMap_Type = { NULL }; -BPy_DefinitionMap *Node_CreateInputDefMap(bNode *node) { - BPy_DefinitionMap *map = PyObject_NEW(BPy_DefinitionMap, &InputDefMap_Type); - map->node = node; - return map; +BPy_NodeSockets *Node_CreateSockets(bNode *node) { + BPy_NodeSockets *sockets = PyObject_NEW(BPy_NodeSockets, &NodeSockets_Type); + sockets->node = node; + sockets->input = PyList_New(0); + sockets->output = PyList_New(0); + return sockets; } /***************************************/ -static PyGetSetDef OutputDefMap_getseters[] = { - {"definitions", (getter)NULL, (setter)Map_socketdef, - "Set the outputs definition (dictionary)", - (void *)'O'}, - {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ -}; - -PyTypeObject OutputDefMap_Type = { - PyObject_HEAD_INIT( NULL ) /* required py macro */ - 0, /* ob_size */ - /* For printing, in format "<module>.<name>" */ - "Blender.Node.OutputDefinitions", /* char *tp_name; */ - sizeof( BPy_DefinitionMap ), /* int tp_basicsize; */ - 0, /* tp_itemsize; For allocation */ - - /* Methods to implement standard operations */ - - NULL,/* destructor tp_dealloc; */ - NULL, /* printfunc tp_print; */ - NULL, /* getattrfunc tp_getattr; */ - NULL, /* setattrfunc tp_setattr; */ - NULL, /* cmpfunc tp_compare; */ - NULL, /* reprfunc tp_repr; */ - - /* Method suites for standard classes */ - - NULL, /* PyNumberMethods *tp_as_number; */ - NULL, /* PySequenceMethods *tp_as_sequence; */ - NULL, /* PyMappingMethods *tp_as_mapping; */ - - /* More standard operations (here for binary compatibility) */ - - NULL, /* hashfunc tp_hash; */ - NULL, /* ternaryfunc tp_call; */ - NULL, /* reprfunc tp_str; */ - NULL, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ - - /* Functions to access object as input/output buffer */ - NULL, /* PyBufferProcs *tp_as_buffer; */ - - /*** Flags to define presence of optional/expanded features ***/ - Py_TPFLAGS_DEFAULT, /* long tp_flags; */ - - NULL, /* char *tp_doc; Documentation string */ - /*** Assigned meaning in release 2.0 ***/ - /* call function for all accessible objects */ - NULL, /* traverseproc tp_traverse; */ - - /* delete references to contained objects */ - NULL, /* inquiry tp_clear; */ - - /*** Assigned meaning in release 2.1 ***/ - /*** rich comparisons ***/ - NULL, /* richcmpfunc tp_richcompare; */ - - /*** weak reference enabler ***/ - 0, /* long tp_weaklistoffset; */ - - /*** Added in release 2.2 ***/ - /* Iterators */ - 0, //( getiterfunc) MVertSeq_getIter, /* getiterfunc tp_iter; */ - 0, //( iternextfunc ) MVertSeq_nextIter, /* iternextfunc tp_iternext; */ +static int sockinmap_len ( BPy_SockMap * self) { + bNode *node = self->node; + bNodeType *tinfo; + int a = 0; - /*** Attribute descriptor and subclassing stuff ***/ - 0, //BPy_MVertSeq_methods, /* struct PyMethodDef *tp_methods; */ - NULL, /* struct PyMemberDef *tp_members; */ - OutputDefMap_getseters, /* struct PyGetSetDef *tp_getset; */ - NULL, /* struct _typeobject *tp_base; */ - NULL, /* PyObject *tp_dict; */ - NULL, /* descrgetfunc tp_descr_get; */ - NULL, /* descrsetfunc tp_descr_set; */ - 0, /* long tp_dictoffset; */ - NULL, /* initproc tp_init; */ - NULL, /* allocfunc tp_alloc; */ - NULL, /* newfunc tp_new; */ - /* Low-level free-memory routine */ - NULL, /* freefunc tp_free; */ - /* For PyObject_IS_GC */ - NULL, /* inquiry tp_is_gc; */ - NULL, /* PyObject *tp_bases; */ - /* method resolution order */ - NULL, /* PyObject *tp_mro; */ - NULL, /* PyObject *tp_cache; */ - NULL, /* PyObject *tp_subclasses; */ - NULL, /* PyObject *tp_weaklist; */ - NULL -}; + if (!node) return 0; -BPy_DefinitionMap *Node_CreateOutputDefMap(bNode *node) { - BPy_DefinitionMap *map = PyObject_NEW(BPy_DefinitionMap, &OutputDefMap_Type); - map->node = node; - return map; -} + tinfo = node->typeinfo; -/***************************************/ + if (BNTST(node->custom1, NODE_DYNAMIC_READY)) return 0; -static int sockinmap_len ( BPy_SockMap * self) { - int a = 0; - if(self->typeinfo) { - while(self->typeinfo->inputs[a].type!=-1) + if (tinfo && tinfo->inputs) { + while(self->node->typeinfo->inputs[a].type!=-1) a++; } return a; } static int sockinmap_has_key( BPy_SockMap *self, PyObject *key) { + bNode *node = self->node; + bNodeType *tinfo; + char *strkey = NULL; int a = 0; - char *strkey = PyString_AsString(key); - if(self->typeinfo){ - while(self->typeinfo->inputs[a].type!=-1) { - if(BLI_strcaseeq(self->typeinfo->inputs[a].name, strkey)) { + if (!node) return -1; + + tinfo = node->typeinfo; + strkey = PyString_AsString(key); + + if(tinfo && tinfo->inputs){ + while(self->node->typeinfo->inputs[a].type!=-1) { + if(BLI_strcaseeq(self->node->typeinfo->inputs[a].name, strkey)) { return a; } a++; @@ -380,39 +363,46 @@ static int sockinmap_has_key( BPy_SockMap *self, PyObject *key) { return -1; } -PyObject *sockinmap_subscript(BPy_SockMap *self, PyObject *idx) { - int a, _idx; - a = sockinmap_len(self); +PyObject *sockinmap_subscript(BPy_SockMap *self, PyObject *pyidx) { + int idx; + + if (!self->node) + return EXPP_ReturnPyObjError(PyExc_RuntimeError, "no access to Blender node data!"); - if (PyString_Check(idx)) { - _idx = sockinmap_has_key( self, idx); + if (PyString_Check(pyidx)) { + idx = sockinmap_has_key(self, pyidx); } - else if(PyInt_Check(idx)) { - PyErr_SetString(PyExc_ValueError, "int index not implemented"); - Py_RETURN_NONE; + else if(PyInt_Check(pyidx)) { + int len = sockinmap_len(self); + idx = (int)PyInt_AsLong(pyidx); + if (idx < 0 || idx >= len) + return EXPP_ReturnPyObjError(PyExc_IndexError, "index out of range"); } - else if (PySlice_Check(idx)) { - PyErr_SetString(PyExc_ValueError, "slices not implemented"); - Py_RETURN_NONE; + else if (PySlice_Check(pyidx)) { + return EXPP_ReturnPyObjError(PyExc_ValueError, "slices not implemented"); } else { - PyErr_SetString(PyExc_IndexError, "Index must be string"); - Py_RETURN_NONE; + return EXPP_ReturnPyObjError(PyExc_IndexError, "index must be an int or a string"); } + if(idx<0) { /* we're not as nice as Python */ + return EXPP_ReturnPyObjError(PyExc_IndexError, "invalid socket index"); + } - switch(self->typeinfo->inputs[_idx].type) { + switch(self->node->typeinfo->inputs[idx].type) { case SOCK_VALUE: - return Py_BuildValue("f", self->stack[_idx]->vec[0]); + return Py_BuildValue("f", self->stack[idx]->vec[0]); break; case SOCK_VECTOR: - return Py_BuildValue("(fff)", self->stack[_idx]->vec[0], self->stack[_idx]->vec[1], self->stack[_idx]->vec[2]); + return Py_BuildValue("(fff)", self->stack[idx]->vec[0], self->stack[idx]->vec[1], self->stack[idx]->vec[2]); break; case SOCK_RGBA: - return Py_BuildValue("(ffff)", self->stack[_idx]->vec[0], self->stack[_idx]->vec[1], self->stack[_idx]->vec[2], self->stack[_idx]->vec[3]); + /* otherwise RGBA tuple */ + return Py_BuildValue("(ffff)", self->stack[idx]->vec[0], self->stack[idx]->vec[1], self->stack[idx]->vec[2], self->stack[idx]->vec[3]); break; default: break; } + Py_RETURN_NONE; } @@ -506,21 +496,35 @@ PyTypeObject SockInMap_Type = { }; static int sockoutmap_len ( BPy_SockMap * self) { + bNode *node = self->node; + bNodeType *tinfo; int a = 0; - if(self->typeinfo) { - while(self->typeinfo->outputs[a].type!=-1) + + if (!node) return 0; + + tinfo = node->typeinfo; + + if (tinfo && tinfo->outputs) { + while(self->node->typeinfo->outputs[a].type!=-1) a++; } return a; } static int sockoutmap_has_key( BPy_SockMap *self, PyObject *key) { + bNode *node = self->node; + bNodeType *tinfo; int a = 0; - char *strkey = PyString_AsString(key); + char *strkey = NULL; + + if (!node) return -1; + + tinfo = node->typeinfo; + strkey = PyString_AsString(key); - if(self->typeinfo){ - while(self->typeinfo->outputs[a].type!=-1) { - if(BLI_strcaseeq(self->typeinfo->outputs[a].name, strkey)) { + if(tinfo && tinfo->outputs){ + while(self->node->typeinfo->outputs[a].type!=-1) { + if(BLI_strcaseeq(self->node->typeinfo->outputs[a].name, strkey)) { return a; } a++; @@ -529,51 +533,89 @@ static int sockoutmap_has_key( BPy_SockMap *self, PyObject *key) { return -1; } -static int sockoutmap_assign_subscript(BPy_SockMap *self, PyObject *idx, PyObject *value) { - int a, _idx; - a = sockoutmap_len(self); - if(PyInt_Check(idx)) { - _idx = (int)PyInt_AsLong(idx); +static int sockoutmap_assign_subscript(BPy_SockMap *self, PyObject *pyidx, PyObject *value) { + int i, idx, len, wanted_len = 0, ret = -1; + PyObject *val; + PyObject **items; + + if (!self->node) + return EXPP_ReturnIntError(PyExc_RuntimeError, "no access to Blender node data!"); + + if (PyInt_Check(pyidx)) { + idx = (int)PyInt_AsLong(pyidx); + if (idx < 0 || idx >= sockinmap_len(self)) + return EXPP_ReturnIntError(PyExc_IndexError, "index out of range"); } - else if (PyString_Check(idx)) { - _idx = sockoutmap_has_key( self, idx); + else if (PyString_Check(pyidx)) { + idx = sockoutmap_has_key(self, pyidx); } - else if (PySlice_Check(idx)) { - PyErr_SetString(PyExc_ValueError, "slices not implemented, yet"); - return -1; - } else { - PyErr_SetString(PyExc_IndexError, "Index must be int or string"); - return -1; - } - if(_idx > -1) { - switch(self->typeinfo->outputs[_idx].type) { - case SOCK_VALUE: - if(PyTuple_Size(value)==1) - self->stack[_idx]->vec[0] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 0)); - return 0; - break; - case SOCK_VECTOR: - if(PyTuple_Size(value)==3) { - self->stack[_idx]->vec[0] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 0)); - self->stack[_idx]->vec[1] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 1)); - self->stack[_idx]->vec[2] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 2)); - } - return 0; - break; - case SOCK_RGBA: - if(PyTuple_Size(value)==4) { - self->stack[_idx]->vec[0] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 0)); - self->stack[_idx]->vec[1] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 1)); - self->stack[_idx]->vec[2] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 2)); - self->stack[_idx]->vec[3] = (float)PyFloat_AsDouble(PyTuple_GetItem(value, 3)); - } - return 0; - break; - default: - break; + else if (PySlice_Check(pyidx)) { + return EXPP_ReturnIntError(PyExc_ValueError, "slices not yet implemented"); + } else { + return EXPP_ReturnIntError(PyExc_IndexError, "index must be a positive int or a string"); + } + + if (idx < 0) + return EXPP_ReturnIntError(PyExc_IndexError, "index must be a positive int or a string"); + + val = PySequence_Fast(value, "expected a numeric tuple or list"); + if (!val) return -1; + + len = PySequence_Fast_GET_SIZE(val); + + if (len == 0) { + Py_DECREF(val); + return EXPP_ReturnIntError(PyExc_AttributeError, "expected a non-empty numeric tuple or list"); + } + + items = PySequence_Fast_ITEMS(val); + + for (i = 0; i < len; i++) { + if (!PyNumber_Check(items[i])) { + Py_DECREF(val); + return EXPP_ReturnIntError(PyExc_AttributeError, "expected a *numeric* tuple or list"); } } - return 0; + + switch(self->node->typeinfo->outputs[idx].type) { + case SOCK_VALUE: + wanted_len = 1; + if (len == 1) { + self->stack[idx]->vec[0] = (float)PyFloat_AsDouble(items[0]); + ret = 0; + } + break; + case SOCK_VECTOR: + wanted_len = 3; + if (len == 3) { + self->stack[idx]->vec[0] = (float)PyFloat_AsDouble(items[0]); + self->stack[idx]->vec[1] = (float)PyFloat_AsDouble(items[1]); + self->stack[idx]->vec[2] = (float)PyFloat_AsDouble(items[2]); + ret = 0; + } + break; + case SOCK_RGBA: + wanted_len = 4; + if (len == 4) { + self->stack[idx]->vec[0] = (float)PyFloat_AsDouble(items[0]); + self->stack[idx]->vec[1] = (float)PyFloat_AsDouble(items[1]); + self->stack[idx]->vec[2] = (float)PyFloat_AsDouble(items[2]); + self->stack[idx]->vec[3] = (float)PyFloat_AsDouble(items[3]); + ret = 0; + } + break; + default: + break; + } + + Py_DECREF(val); + + if (ret == -1) { + PyErr_SetString(PyExc_AttributeError, "wrong number of items in list or tuple"); + fprintf(stderr, "\nExpected %d numeric values, got %d.", wanted_len, len); + } + + return ret; } /* write only */ @@ -667,14 +709,14 @@ PyTypeObject SockOutMap_Type = { static BPy_SockMap *Node_CreateInputMap(bNode *node, bNodeStack **stack) { - BPy_SockMap *map= PyObject_NEW(BPy_SockMap, &SockInMap_Type); - map->typeinfo= node->typeinfo; - map->stack= stack; + BPy_SockMap *map = PyObject_NEW(BPy_SockMap, &SockInMap_Type); + map->node = node; + map->stack = stack; return map; } static PyObject *Node_GetInputMap(BPy_Node *self) { - BPy_SockMap *inmap= Node_CreateInputMap(self->node, self->in); + BPy_SockMap *inmap = Node_CreateInputMap(self->node, self->in); return (PyObject *)(inmap); } @@ -685,11 +727,11 @@ static PyObject *Node_GetInputMap(BPy_Node *self) { #define TEXTURE 4 #define PIXEL 5 #define COLOR 6 -#define SPECULAR 7 -#define MIRROR 8 -#define AMBIENT 9 -#define AMBIENTFACTOR 10 -#define EMITFACTOR 11 +#define SPECULAR_COLOR 7 +#define MIRROR_COLOR 8 +#define AMBIENT_COLOR 9 +#define AMBIENT 10 +#define EMIT 11 #define DISPLACE 12 #define STRAND 13 #define STRESS 14 @@ -704,80 +746,80 @@ static PyObject *Node_GetInputMap(BPy_Node *self) { #define STRAND_D 37 static PyObject *ShadeInput_getAttribute(BPy_ShadeInput *self, void *type) { - PyObject *obj= NULL; + PyObject *obj = NULL; if(self->shi) { switch((int)type) { case SURFACEVIEWVECTOR: - obj= Py_BuildValue("(fff)", self->shi->view[0], self->shi->view[1], self->shi->view[2]); + obj = Py_BuildValue("(fff)", self->shi->view[0], self->shi->view[1], self->shi->view[2]); break; case VIEWNORMAL: - obj= Py_BuildValue("(fff)", self->shi->vn[0], self->shi->vn[1], self->shi->vn[2]); + obj = Py_BuildValue("(fff)", self->shi->vn[0], self->shi->vn[1], self->shi->vn[2]); break; case SURFACENORMAL: - obj= Py_BuildValue("(fff)", self->shi->facenor[0], self->shi->facenor[1], self->shi->facenor[2]); + obj = Py_BuildValue("(fff)", self->shi->facenor[0], self->shi->facenor[1], self->shi->facenor[2]); break; case GLOBALTEXTURE: - obj= Py_BuildValue("(fff)", self->shi->gl[0], self->shi->gl[1], self->shi->gl[2]); + obj = Py_BuildValue("(fff)", self->shi->gl[0], self->shi->gl[1], self->shi->gl[2]); break; case TEXTURE: - obj= Py_BuildValue("(fff)", self->shi->lo[0], self->shi->lo[1], self->shi->lo[2]); + obj = Py_BuildValue("(fff)", self->shi->lo[0], self->shi->lo[1], self->shi->lo[2]); break; case PIXEL: - obj= Py_BuildValue("(ii)", self->shi->xs, self->shi->ys); + obj = Py_BuildValue("(ii)", self->shi->xs, self->shi->ys); break; case COLOR: - obj= Py_BuildValue("(fff)", self->shi->r, self->shi->g, self->shi->b); + obj = Py_BuildValue("(fff)", self->shi->r, self->shi->g, self->shi->b); break; - case SPECULAR: - obj= Py_BuildValue("(fff)", self->shi->specr, self->shi->specg, self->shi->specb); + case SPECULAR_COLOR: + obj = Py_BuildValue("(fff)", self->shi->specr, self->shi->specg, self->shi->specb); break; - case MIRROR: - obj= Py_BuildValue("(fff)", self->shi->mirr, self->shi->mirg, self->shi->mirb); + case MIRROR_COLOR: + obj = Py_BuildValue("(fff)", self->shi->mirr, self->shi->mirg, self->shi->mirb); break; - case AMBIENT: - obj= Py_BuildValue("(fff)", self->shi->ambr, self->shi->ambg, self->shi->ambb); + case AMBIENT_COLOR: + obj = Py_BuildValue("(fff)", self->shi->ambr, self->shi->ambg, self->shi->ambb); break; - case AMBIENTFACTOR: - obj= PyFloat_FromDouble((double)(self->shi->amb)); + case AMBIENT: + obj = PyFloat_FromDouble((double)(self->shi->amb)); break; - case EMITFACTOR: - obj= PyFloat_FromDouble((double)(self->shi->emit)); + case EMIT: + obj = PyFloat_FromDouble((double)(self->shi->emit)); break; case DISPLACE: - obj= Py_BuildValue("(fff)", self->shi->displace[0], self->shi->displace[1], self->shi->displace[2]); + obj = Py_BuildValue("(fff)", self->shi->displace[0], self->shi->displace[1], self->shi->displace[2]); break; case STRAND: - obj= PyFloat_FromDouble((double)(self->shi->strand)); + obj = PyFloat_FromDouble((double)(self->shi->strandco)); break; case STRESS: - obj= PyFloat_FromDouble((double)(self->shi->stress)); + obj = PyFloat_FromDouble((double)(self->shi->stress)); break; case TANGENT: - obj= Py_BuildValue("(fff)", self->shi->tang[0], self->shi->tang[1], self->shi->tang[2]); + obj = Py_BuildValue("(fff)", self->shi->tang[0], self->shi->tang[1], self->shi->tang[2]); break; case SURFACE_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxco[0], self->shi->dxco[1], self->shi->dxco[2], self->shi->dyco[0], self->shi->dyco[1], self->shi->dyco[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxco[0], self->shi->dxco[1], self->shi->dxco[2], self->shi->dyco[0], self->shi->dyco[1], self->shi->dyco[2]); break; case TEXTURE_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxlo[0], self->shi->dxlo[1], self->shi->dxlo[2], self->shi->dylo[0], self->shi->dylo[1], self->shi->dylo[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxlo[0], self->shi->dxlo[1], self->shi->dxlo[2], self->shi->dylo[0], self->shi->dylo[1], self->shi->dylo[2]); break; case GLOBALTEXTURE_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxgl[0], self->shi->dxgl[1], self->shi->dxgl[2], self->shi->dygl[0], self->shi->dygl[1], self->shi->dygl[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxgl[0], self->shi->dxgl[1], self->shi->dxgl[2], self->shi->dygl[0], self->shi->dygl[1], self->shi->dygl[2]); break; case REFLECTION_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxref[0], self->shi->dxref[1], self->shi->dxref[2], self->shi->dyref[0], self->shi->dyref[1], self->shi->dyref[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxref[0], self->shi->dxref[1], self->shi->dxref[2], self->shi->dyref[0], self->shi->dyref[1], self->shi->dyref[2]); break; case NORMAL_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxno[0], self->shi->dxno[1], self->shi->dxno[2], self->shi->dyno[0], self->shi->dyno[1], self->shi->dyno[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxno[0], self->shi->dxno[1], self->shi->dxno[2], self->shi->dyno[0], self->shi->dyno[1], self->shi->dyno[2]); break; case STICKY_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxsticky[0], self->shi->dxsticky[1], self->shi->dxsticky[2], self->shi->dysticky[0], self->shi->dysticky[1], self->shi->dysticky[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxsticky[0], self->shi->dxsticky[1], self->shi->dxsticky[2], self->shi->dysticky[0], self->shi->dysticky[1], self->shi->dysticky[2]); break; case REFRACT_D: - obj= Py_BuildValue("(fff)(fff)", self->shi->dxrefract[0], self->shi->dxrefract[1], self->shi->dxrefract[2], self->shi->dyrefract[0], self->shi->dyrefract[1], self->shi->dyrefract[2]); + obj = Py_BuildValue("(fff)(fff)", self->shi->dxrefract[0], self->shi->dxrefract[1], self->shi->dxrefract[2], self->shi->dyrefract[0], self->shi->dyrefract[1], self->shi->dyrefract[2]); break; case STRAND_D: - obj= Py_BuildValue("(ff)", self->shi->dxstrand, self->shi->dystrand); + obj = Py_BuildValue("(ff)", self->shi->dxstrand, self->shi->dystrand); break; default: break; @@ -792,27 +834,26 @@ static PyObject *ShadeInput_getAttribute(BPy_ShadeInput *self, void *type) { static BPy_SockMap *Node_CreateOutputMap(bNode *node, bNodeStack **stack) { BPy_SockMap *map = PyObject_NEW(BPy_SockMap, &SockOutMap_Type); - map->typeinfo= node->typeinfo; - map->stack= stack; + map->node = node; + map->stack = stack; return map; } static PyObject *Node_GetOutputMap(BPy_Node *self) { - BPy_SockMap *outmap= Node_CreateOutputMap(self->node, self->out); + BPy_SockMap *outmap = Node_CreateOutputMap(self->node, self->out); return (PyObject *)outmap; } static PyObject *Node_GetShi(BPy_Node *self) { - BPy_ShadeInput *shi= ShadeInput_CreatePyObject(self->shi); + BPy_ShadeInput *shi = ShadeInput_CreatePyObject(self->shi); return (PyObject *)shi; - } static PyObject *node_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; assert(type!=NULL && type->tp_alloc!=NULL); - self= type->tp_alloc(type, 1); + self = type->tp_alloc(type, 1); return self; } @@ -822,17 +863,29 @@ static int node_init(BPy_Node *self, PyObject *args, PyObject *kwds) } static PyGetSetDef BPy_Node_getseters[] = { - {"ins", + {"input", + (getter)Node_GetInputMap, (setter)NULL, + "Get the input sockets mapping (dictionary)", + NULL}, + {"i", /* alias */ (getter)Node_GetInputMap, (setter)NULL, - "Get the ShadeInput mapping (dictionary)", + "Get the input sockets mapping (dictionary)", + NULL}, + {"output", + (getter)Node_GetOutputMap, (setter)NULL, + "Get the output sockets mapping (dictionary)", NULL}, - {"outs", + {"o", /* alias */ (getter)Node_GetOutputMap, (setter)NULL, - "Get the ShadeInput mapping (dictionary)", + "Get the output sockets mapping (dictionary)", NULL}, {"shi", (getter)Node_GetShi, (setter)NULL, - "Get the ShadeInput (ShadeInput)", + "Get the Shade Input data (ShadeInput)", + NULL}, + {"s", /* alias */ + (getter)Node_GetShi, (setter)NULL, + "Get the Shade Input data (ShadeInput)", NULL}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; @@ -842,19 +895,19 @@ static PyGetSetDef BPy_ShadeInput_getseters[] = { (getter)ShadeInput_getAttribute, (setter)NULL, "Get the current texture coordinate (tuple)", (void*)TEXTURE}, - {"texture_global", + {"textureGlobal", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the current global texture coordinate (tuple)", (void*)GLOBALTEXTURE}, - {"surface_normal", + {"surfaceNormal", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the current surface normal (tuple)", (void*)SURFACENORMAL}, - {"view_normal", + {"viewNormal", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the current view normal (tuple)", (void*)VIEWNORMAL}, - {"surface_view_vec", + {"surfaceViewVector", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the vector pointing to the viewpoint from the point being shaded (tuple)", (void*)SURFACEVIEWVECTOR}, @@ -866,26 +919,26 @@ static PyGetSetDef BPy_ShadeInput_getseters[] = { (getter)ShadeInput_getAttribute, (setter)NULL, "Get the color for the point being shaded (tuple)", (void*)COLOR}, - {"specular", + {"specularColor", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the specular color for the point being shaded (tuple)", - (void*)SPECULAR}, - {"mirror", + (void*)SPECULAR_COLOR}, + {"mirrorColor", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the mirror color for the point being shaded (tuple)", - (void*)MIRROR}, - {"ambient", + (void*)MIRROR_COLOR}, + {"ambientColor", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the ambient color for the point being shaded (tuple)", - (void*)AMBIENT}, - {"ambient_factor", + (void*)AMBIENT_COLOR}, + {"ambient", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the ambient factor for the point being shaded (float)", - (void*)AMBIENTFACTOR}, - {"emit_factor", + (void*)AMBIENT}, + {"emit", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the emit factor for the point being shaded (float)", - (void*)EMITFACTOR}, + (void*)EMIT}, {"displace", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the displace vector for the point being shaded (tuple)", @@ -902,38 +955,38 @@ static PyGetSetDef BPy_ShadeInput_getseters[] = { (getter)ShadeInput_getAttribute, (setter)NULL, "Get the tangent vector (tuple)", (void*)TANGENT}, - {"surface_d", + {"surfaceD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the surface d (tuple of tuples)", (void*)SURFACE_D}, - {"texture_d", + {"textureD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the texture d (tuple of tuples)", (void*)TEXTURE_D}, - {"texture_global_d", + {"textureGlobalD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the global texture d (tuple of tuples)", (void*)GLOBALTEXTURE_D}, - {"reflection_d", + {"reflectionD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the reflection d (tuple of tuples)", (void*)REFLECTION_D}, - {"normal_d", + {"normalD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the normal d (tuple of tuples)", (void*)NORMAL_D}, - {"sticky_d", + {"stickyD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the sticky d (tuple of tuples)", (void*)STICKY_D}, - {"refract_d", + {"refractD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the refract d (tuple of tuples)", (void*)REFRACT_D}, - {"strand_d", + {"strandD", (getter)ShadeInput_getAttribute, (setter)NULL, "Get the strand d (tuple)", - (void*)REFRACT_D}, + (void*)STRAND_D}, {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; @@ -1101,7 +1154,6 @@ PyTypeObject ShadeInput_Type = { NULL }; - /* Initialise Node module */ PyObject *Node_Init(void) { @@ -1111,9 +1163,7 @@ PyObject *Node_Init(void) return NULL; if( PyType_Ready( &ShadeInput_Type ) < 0 ) return NULL; - if( PyType_Ready( &OutputDefMap_Type ) < 0 ) - return NULL; - if( PyType_Ready( &InputDefMap_Type ) < 0 ) + if( PyType_Ready( &NodeSockets_Type ) < 0 ) return NULL; if( PyType_Ready( &SockInMap_Type ) < 0 ) return NULL; @@ -1135,7 +1185,7 @@ PyObject *Node_Init(void) static int Node_compare(BPy_Node *a, BPy_Node *b) { bNode *pa = a->node, *pb = b->node; - return (pa==pb) ? 0 : -1; + return (pa == pb) ? 0 : -1; } static PyObject *Node_repr(BPy_Node *self) @@ -1154,13 +1204,13 @@ BPy_Node *Node_CreatePyObject(bNode *node) return (BPy_Node *)(EXPP_ReturnPyObjError(PyExc_MemoryError, "couldn't create BPy_Node object")); } - pynode->node= node; + pynode->node = node; return pynode; } void InitNode(BPy_Node *self, bNode *node) { - self->node= node; + self->node = node; } bNode *Node_FromPyObject(PyObject *pyobj) @@ -1170,16 +1220,16 @@ bNode *Node_FromPyObject(PyObject *pyobj) void Node_SetStack(BPy_Node *self, bNodeStack **stack, int type) { - if(type==NODE_INPUTSTACK) { - self->in= stack; - } else if(type==NODE_OUTPUTSTACK) { - self->out= stack; + if(type == NODE_INPUTSTACK) { + self->in = stack; + } else if(type == NODE_OUTPUTSTACK) { + self->out = stack; } } void Node_SetShi(BPy_Node *self, ShadeInput *shi) { - self->shi= shi; + self->shi = shi; } /*********************/ @@ -1187,12 +1237,12 @@ void Node_SetShi(BPy_Node *self, ShadeInput *shi) static int ShadeInput_compare(BPy_ShadeInput *a, BPy_ShadeInput *b) { ShadeInput *pa = a->shi, *pb = b->shi; - return (pa==pb) ? 0 : -1; + return (pa == pb) ? 0 : -1; } static PyObject *ShadeInput_repr(BPy_ShadeInput *self) { - return PyString_FromFormat( "[ShadeInput @ \"%p\"]", self); + return PyString_FromFormat( "[ShadeInput at \"%p\"]", self); } BPy_ShadeInput *ShadeInput_CreatePyObject(ShadeInput *shi) diff --git a/source/blender/python/api2_2x/Node.h b/source/blender/python/api2_2x/Node.h index 434d0aa684a..ce42527342a 100644 --- a/source/blender/python/api2_2x/Node.h +++ b/source/blender/python/api2_2x/Node.h @@ -1,5 +1,5 @@ /* - * $Id: Node.h 10449 2007-04-03 11:24:11Z jesterking $ + * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * @@ -56,14 +56,16 @@ typedef struct BPy_ShadeInput { typedef struct { PyObject_VAR_HEAD - bNodeType *typeinfo; + bNode* node; bNodeStack **stack; } BPy_SockMap; typedef struct { PyObject_HEAD bNode *node; -} BPy_DefinitionMap; + PyObject *input; + PyObject *output; +} BPy_NodeSockets; typedef struct BPy_Node { PyObject_HEAD @@ -76,13 +78,9 @@ typedef struct BPy_Node { extern PyObject *Node_Init(void); extern void InitNode(BPy_Node *self, bNode *node); extern BPy_Node *Node_CreatePyObject(bNode *node); -extern BPy_DefinitionMap *Node_CreateOutputDefMap(bNode *node); -extern BPy_DefinitionMap *Node_CreateInputDefMap(bNode *node); +extern BPy_NodeSockets *Node_CreateSockets(bNode *node); extern void Node_SetStack(BPy_Node *self, bNodeStack **stack, int type); extern void Node_SetShi(BPy_Node *self, ShadeInput *shi); -extern BPy_ShadeInput *ShadeInput_CreatePyObject(ShadeInput *shi); -extern void Node_dealloc(BPy_Node *self); -extern void ShadeInput_dealloc(BPy_ShadeInput *self); #define NODE_INPUTSTACK 0 #define NODE_OUTPUTSTACK 1 diff --git a/source/blender/src/drawnode.c b/source/blender/src/drawnode.c index 561c193dfdb..5c426cdb395 100644 --- a/source/blender/src/drawnode.c +++ b/source/blender/src/drawnode.c @@ -48,6 +48,7 @@ #include "DNA_space_types.h" #include "DNA_screen_types.h" #include "DNA_texture_types.h" +#include "DNA_text_types.h" #include "DNA_userdef_types.h" #include "BKE_global.h" @@ -58,6 +59,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_texture.h" +#include "BKE_text.h" #include "BKE_utildefines.h" #include "CMP_node.h" @@ -442,6 +444,36 @@ static void node_browse_tex_cb(void *ntree_v, void *node_v) node->menunr= 0; } +static void node_dynamic_update_cb(void *ntree_v, void *node_v) +{ + Material *ma; + bNode *node= (bNode *)node_v; + ID *id= node->id; + int error= 0; + + if (BTST(node->custom1, NODE_DYNAMIC_ERROR)) error= 1; + + /* Users only have to press the "update" button in one pynode + * and we also update all others sharing the same script */ + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + bNode *nd; + for (nd= ma->nodetree->nodes.first; nd; nd= nd->next) { + if ((nd->type == NODE_DYNAMIC) && (nd->id == id)) { + nd->custom1= 0; + nd->custom1= BSET(nd->custom1, NODE_DYNAMIC_REPARSE); + nd->menunr= 0; + if (error) + nd->custom1= BSET(nd->custom1, NODE_DYNAMIC_ERROR); + } + } + } + } + + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWNODE, 0); +} + static int node_buts_texture(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *butr) { if(block) { @@ -483,6 +515,31 @@ static int node_buts_math(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *b /* ****************** BUTTON CALLBACKS FOR SHADER NODES ***************** */ +static void node_browse_text_cb(void *ntree_v, void *node_v) +{ + bNodeTree *ntree= ntree_v; + bNode *node= node_v; + ID *oldid; + + if(node->menunr<1) return; + + if(node->id) { + node->id->us--; + } + oldid= node->id; + node->id= BLI_findlink(&G.main->text, node->menunr-1); + id_us_plus(node->id); + BLI_strncpy(node->name, node->id->name+2, 21); /* huh? why 21? */ + + node->custom1= BSET(node->custom1, NODE_DYNAMIC_NEW); + + nodeSetActive(ntree, node); + + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWNODE, 0); + + node->menunr= 0; +} static void node_mat_alone_cb(void *node_v, void *unused) { @@ -709,6 +766,42 @@ static int node_shader_buts_geometry(uiBlock *block, bNodeTree *ntree, bNode *no return 40; } +static int node_shader_buts_dynamic(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *butr) +{ + if (block) { + uiBut *bt; + SpaceNode *snode= curarea->spacedata.first; + short dy= (short)butr->ymin; + int xoff=0; + + /* B_NODE_EXEC is handled in butspace.c do_node_buts */ + if(!node->id) { + char *strp; + IDnames_to_pupstring(&strp, NULL, "", &(G.main->text), NULL, NULL); + node->menunr= 0; + bt= uiDefButS(block, MENU, B_NODE_EXEC/*+node->nr*/, strp, + butr->xmin, dy, 19, 19, + &node->menunr, 0, 0, 0, 0, "Browses existing choices"); + uiButSetFunc(bt, node_browse_text_cb, ntree, node); + xoff=19; + if(strp) MEM_freeN(strp); + } + else { + bt = uiDefBut(block, BUT, B_NOP, "Update", + butr->xmin+xoff, butr->ymin+20, 50, 19, + &node->menunr, 0.0, 19.0, 0, 0, "Refresh this node (and all others that use the same script)"); + uiButSetFunc(bt, node_dynamic_update_cb, ntree, node); + + if (BTST(node->custom1, NODE_DYNAMIC_ERROR)) { + BIF_ThemeColor(TH_REDALERT); + ui_rasterpos_safe(butr->xmin + xoff, butr->ymin + 5, snode->aspect); + snode_drawstring(snode, "Error! Check console...", butr->xmax - butr->xmin); + } + } + } + return 20+19; +} + /* only once called */ static void node_shader_set_butfunc(bNodeType *ntype) { @@ -755,6 +848,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_GEOMETRY: ntype->butfunc= node_shader_buts_geometry; break; + case NODE_DYNAMIC: + ntype->butfunc= node_shader_buts_dynamic; + break; default: ntype->butfunc= NULL; } @@ -2680,7 +2776,7 @@ static void node_draw_basis(ScrArea *sa, SpaceNode *snode, bNode *node) uiSetRoundBox(8); uiRoundBox(rct->xmin, rct->ymin, rct->xmax, rct->ymax-NODE_DY, BASIS_RAD); glDisable(GL_BLEND); - + /* scaling indicator */ node_scaling_widget(TH_NODE, snode->aspect, rct->xmax-BASIS_RAD*snode->aspect, rct->ymin, rct->xmax, rct->ymin+BASIS_RAD*snode->aspect); diff --git a/source/blender/src/drawtext.c b/source/blender/src/drawtext.c index b4026746a35..fff1d768b93 100644 --- a/source/blender/src/drawtext.c +++ b/source/blender/src/drawtext.c @@ -62,6 +62,7 @@ #include "BKE_text.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -1180,6 +1181,10 @@ void unlink_text(Text *text) if (BPY_check_all_scriptlinks (text)) { allqueue(REDRAWBUTSSCRIPT, 0); } + /* equivalently for pynodes: */ + if (nodeDynamicUnlinkText ((ID*)text)) { + allqueue(REDRAWNODE, 0); + } for (scr= G.main->screen.first; scr; scr= scr->id.next) { for (area= scr->areabase.first; area; area= area->next) { diff --git a/source/blender/src/editnode.c b/source/blender/src/editnode.c index 8fc11c68891..a4e3137b242 100644 --- a/source/blender/src/editnode.c +++ b/source/blender/src/editnode.c @@ -336,10 +336,10 @@ void node_shader_default(Material *ma) ma->nodetree= ntreeAddTree(NTREE_SHADER); - out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL); + out= nodeAddNodeType(ma->nodetree, SH_NODE_OUTPUT, NULL, NULL); out->locx= 300.0f; out->locy= 300.0f; - in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL); + in= nodeAddNodeType(ma->nodetree, SH_NODE_MATERIAL, NULL, NULL); in->locx= 10.0f; in->locy= 300.0f; nodeSetActive(ma->nodetree, in); @@ -366,10 +366,10 @@ void node_composit_default(Scene *sce) sce->nodetree= ntreeAddTree(NTREE_COMPOSIT); - out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL); + out= nodeAddNodeType(sce->nodetree, CMP_NODE_COMPOSITE, NULL, NULL); out->locx= 300.0f; out->locy= 400.0f; - in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL); + in= nodeAddNodeType(sce->nodetree, CMP_NODE_R_LAYERS, NULL, NULL); in->locx= 10.0f; in->locy= 400.0f; nodeSetActive(sce->nodetree, in); @@ -624,7 +624,7 @@ static void node_addgroup(SpaceNode *snode) if(val>=0) { ngroup= BLI_findlink(&G.main->nodetree, val); if(ngroup) { - bNode *node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup); + bNode *node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL); /* generics */ if(node) { @@ -1523,7 +1523,10 @@ bNode *node_add_node(SpaceNode *snode, int type, float locx, float locy) node_deselectall(snode, 0); - if(type>=NODE_GROUP_MENU) { + if(type>=NODE_DYNAMIC_MENU) { + node= nodeAddNodeType(snode->edittree, type, NULL, NULL); + } + else if(type>=NODE_GROUP_MENU) { if(snode->edittree!=snode->nodetree) { error("Can not add a Group in a Group"); return NULL; @@ -1531,11 +1534,11 @@ bNode *node_add_node(SpaceNode *snode, int type, float locx, float locy) else { bNodeTree *ngroup= BLI_findlink(&G.main->nodetree, type-NODE_GROUP_MENU); if(ngroup) - node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup); + node= nodeAddNodeType(snode->edittree, NODE_GROUP, ngroup, NULL); } } else - node= nodeAddNodeType(snode->edittree, type, NULL); + node= nodeAddNodeType(snode->edittree, type, NULL, NULL); /* generics */ if(node) { diff --git a/source/blender/src/toolbox.c b/source/blender/src/toolbox.c index 671628bf5c7..1ad355b5e81 100644 --- a/source/blender/src/toolbox.c +++ b/source/blender/src/toolbox.c @@ -1570,6 +1570,7 @@ static TBitem tb_node_addsh[]= { { 0, "Vector", 4, NULL}, { 0, "Convertor", 5, NULL}, { 0, "Group", 6, NULL}, + { 0, "Dynamic", 7, NULL}, { -1, "", 0, NULL}}; static TBitem tb_node_addcomp[]= { @@ -1582,6 +1583,7 @@ static TBitem tb_node_addcomp[]= { { 0, "Matte", 7, NULL}, { 0, "Distort", 8, NULL}, { 0, "Group", 9, NULL}, + { 0, "Dynamic", 10, NULL}, { -1, "", 0, NULL}}; /* do_node_addmenu() in header_node.c, prototype in BSE_headerbuttons.h */ @@ -1630,11 +1632,21 @@ static TBitem *node_add_sublevel(ListBase *storage, bNodeTree *ntree, int nodecl } } else { - bNodeType *ntype= ntree->alltypes.first; - for(a=0; ntype; ntype= ntype->next) { - if( ntype->nclass == nodeclass ) { - addmenu[a].name= ntype->name; - addmenu[a].retval= ntype->type; + bNodeType *type= ntree->alltypes.first; + int script=0; + for(a=0; type; type= type->next) { + if( type->nclass == nodeclass ) { + if(type->type == NODE_DYNAMIC) { + if(type->id) + addmenu[a].name= type->id->name+2; + else + addmenu[a].name= type->name; + addmenu[a].retval= NODE_DYNAMIC_MENU+script; + script++; + } else { + addmenu[a].name= type->name; + addmenu[a].retval= type->type; + } a++; } } @@ -2130,6 +2142,7 @@ void toolbox_n(void) menu1[3].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_OP_VECTOR); menu1[4].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_CONVERTOR); menu1[5].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_GROUP); + menu1[6].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_OP_DYNAMIC); } else if(snode->treetype==NTREE_COMPOSIT) { menu1[0].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_INPUT); @@ -2141,6 +2154,7 @@ void toolbox_n(void) menu1[6].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_MATTE); menu1[7].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_DISTORT); menu1[8].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_GROUP); + menu1[9].poin= node_add_sublevel(&storage, snode->nodetree, NODE_CLASS_OP_DYNAMIC); } |