diff options
Diffstat (limited to 'source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c')
-rw-r--r-- | source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c | 808 |
1 files changed, 668 insertions, 140 deletions
diff --git a/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c b/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c index 1ba777a0533..4fcfe3a789a 100644 --- a/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c +++ b/source/blender/nodes/intern/SHD_nodes/SHD_dynamic.c @@ -27,221 +27,749 @@ * ***** END GPL LICENSE BLOCK ***** */ -#ifdef USE_PYNODES /* note: won't work without patch */ - +#ifndef DISABLE_PYTHON #include <Python.h> +#include <compile.h> #include <eval.h> +#endif #include "DNA_text_types.h" #include "BKE_text.h" +#include "BKE_utildefines.h" +// XXX +#if 0 +#ifndef DISABLE_PYTHON #include "api2_2x/Node.h" #include "api2_2x/gen_utils.h" #include "BPY_extern.h" +#endif +#endif #include "../SHD_util.h" +// XXX +#if 0 +static void node_dynamic_setup(bNode *node); +static void node_dynamic_exec_cb(void *data, bNode *node, bNodeStack **in, bNodeStack **out); +static void node_dynamic_free_storage_cb(bNode *node); + +#ifndef DISABLE_PYTHON static PyObject *init_dynamicdict(void) { - PyObject *newscriptdict= PyDict_New(); + PyObject *newscriptdict; + PyGILState_STATE gilstate = PyGILState_Ensure(); + + newscriptdict= PyDict_New(); + PyDict_SetItemString(newscriptdict, "__builtins__", PyEval_GetBuiltins()); EXPP_dict_set_item_str(newscriptdict, "__name__", PyString_FromString("__main__")); + + PyGILState_Release(gilstate); + return newscriptdict; } +#endif + +static bNodeType *node_dynamic_find_typeinfo(ListBase *list, ID *id) +{ + bNodeType *ntype = list->first; -static void free_dynamicdict(PyObject *dict) { - if(dict!=NULL) { - Py_DECREF(dict); + 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_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 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)); +#ifndef DISABLE_PYTHON + 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); + } +#endif 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) +{ +#ifndef DISABLE_PYTHON + 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); + } + } + } } +#endif +} - 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; + + if (!in && !out) 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->typeinfo == tinfo) { + if (in) + node_rem_socklist_links(ntree, &nd->inputs); + if (out) + node_rem_socklist_links(ntree, &nd->outputs); } } - Py_XDECREF(pyresult); - Py_DECREF(args); } } } -void nodeDynamicParse(struct bNode *node) +/* node_dynamic_reset: clean a pynode, getting rid of all + * data dynamically created for it. */ +static void node_dynamic_reset(bNode *node, int unlink_text) +{ + bNodeType *tinfo, *tinfo_default; + Material *ma; + + tinfo = node->typeinfo; + tinfo_default = node_dynamic_find_typeinfo(&node_all_shaders, NULL); + + if ((tinfo == tinfo_default) && unlink_text) { + ID *textID = node->id; + /* already at default (empty) state, which happens if this node's + * script failed to parse at the first stage: definition. We're here + * because its text was removed from Blender. */ + 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->id == textID) { + nd->id = NULL; + nd->custom1 = 0; + nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_NEW); + BLI_strncpy(nd->name, "Dynamic", 8); + return; + } + } + } + } + } + + node_dynamic_rem_all_links(tinfo); + node_dynamic_free_typeinfo_sockets(tinfo); + + /* 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); + //node_dynamic_update_socket_links(nd, ma->nodetree); + nd->typeinfo = tinfo_default; + if (unlink_text) { + nd->id = NULL; + nd->custom1 = 0; + nd->custom1 = BSET(nd->custom1, NODE_DYNAMIC_NEW); + BLI_strncpy(nd->name, "Dynamic", 8); + } + } + } + } + } + + /* XXX hardcoded for shaders: */ + if (tinfo->id) { BLI_remlink(&node_all_shaders, tinfo); } + node_dynamic_free_typeinfo(tinfo); +} + +/* Special case of the above function: for working pynodes + * that were saved on a .blend but fail for some reason when + * the file is opened. We need this because pynodes are initialized + * before G.main. */ +static void node_dynamic_reset_loaded(bNode *node) +{ + bNodeType *tinfo = node->typeinfo; + + node_dynamic_rem_all_links(tinfo); + node_dynamic_free_typeinfo_sockets(tinfo); + node_dynamic_free_storage_cb(node); + /* XXX hardcoded for shaders: */ + if (tinfo->id) { BLI_remlink(&node_all_shaders, tinfo); } + + node_dynamic_free_typeinfo(tinfo); + node->typeinfo = node_dynamic_find_typeinfo(&node_all_shaders, NULL); +} + +int nodeDynamicUnlinkText(ID *txtid) { + Material *ma; + bNode *nd; + + /* find one node that uses this text */ + for (ma= G.main->mat.first; ma; ma= ma->id.next) { + if (ma->nodetree) { + for (nd= ma->nodetree->nodes.first; nd; nd = nd->next) { + if ((nd->type == NODE_DYNAMIC) && (nd->id == txtid)) { + node_dynamic_reset(nd, 1); /* found, reset all */ + return 1; + } + } + } + } + return 0; /* no pynodes used this text */ +} + +static void node_dynamic_pyerror_print(bNode *node) +{ +#ifndef DISABLE_PYTHON + PyGILState_STATE gilstate = PyGILState_Ensure(); + + 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"); } + + PyGILState_Release(gilstate); +#endif +} + +static void node_dynamic_register_type(bNode *node) +{ + 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); +} + +#ifndef DISABLE_PYTHON +/* node_dynamic_get_pynode: + * Find the pynode definition from the script */ +static PyObject *node_dynamic_get_pynode(PyObject *dict) { - BPy_Node *pynode= NULL; - PyObject *dict= NULL; PyObject *key= NULL; - PyObject *value= NULL; - PyObject *testinst= NULL; + Py_ssize_t pos = 0; + PyObject *value = NULL; + + /* script writer specified a node? */ + value = PyDict_GetItemString(dict, "__node__"); + + if (value) { + if (PyObject_TypeCheck(value, &PyType_Type)) { + Py_INCREF(value); + return value; + } + else { + PyErr_SetString(PyExc_TypeError, + "expected class object derived from Scripted node"); + return NULL; + } + } + + /* case not, search for it in the script's global dictionary */ + while (PyDict_Next(dict, &pos, &key, &value)) { + /* skip names we know belong to other available objects */ + if (strcmp("Socket", PyString_AsString(key)) == 0) + continue; + else if (strcmp("Scripted", PyString_AsString(key)) == 0) + continue; + /* naive: we grab the first ob of type 'type': */ + else if (PyObject_TypeCheck(value, &PyType_Type)) { + Py_INCREF(value); + return value; + } + } + + PyErr_SetString(PyExc_TypeError, + "no PyNode definition found in the script!"); + return NULL; +} +#endif /* DISABLE_PYTHON */ + +static int node_dynamic_parse(struct bNode *node) +{ +#ifdef DISABLE_PYTHON + return -1; +#else + PyObject *dict= NULL; + PyObject *pynode_data= 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 is_valid_script = 0; + PyGILState_STATE gilstate; + + if (!node->id || !node->storage) + return 0; + + /* READY, no need to be here */ + if (BTST(node->custom1, NODE_DYNAMIC_READY)) + return 0; + + /* for threading */ + gilstate = PyGILState_Ensure(); + + 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) { + if (BTST(node->custom1, NODE_DYNAMIC_LOADED)) { + node_dynamic_disable(node); + } else { + node_dynamic_disable_all_by_id(node->id); + } + node_dynamic_pyerror_print(node); + PyGILState_Release(gilstate); + return -1; + } + + Py_DECREF(pyresult); + + pynode_data = node_dynamic_get_pynode(dict); + + if (pynode_data) { + BPy_NodeSocketLists *socklists = Node_CreateSocketLists(node); + + args = Py_BuildValue("(O)", socklists); + + /* init it to get the input and output sockets */ + pynode = PyObject_Call(pynode_data, args, NULL); + + Py_DECREF(pynode_data); + Py_DECREF(socklists); + Py_DECREF(args); + + if (!PyErr_Occurred() && pynode && pytype_is_pynode(pynode)) { + 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; + if (BNTST(node->custom1, NODE_DYNAMIC_LOADED)) + nodeAddSockets(node, node->typeinfo); + if (BNTST(node->custom1, NODE_DYNAMIC_REPARSE)) + node_dynamic_register_type(node); + } + + node->custom1 = 0; + node->custom1 = BSET(node->custom1, NODE_DYNAMIC_READY); + } + } + + PyGILState_Release(gilstate); + + if (!is_valid_script) { /* not a valid pynode script */ + node_dynamic_disable_all_by_id(node->id); + node_dynamic_pyerror_print(node); + return -1; + } + + return 0; +#endif +} + +/* 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) +{ +#ifndef DISABLE_PYTHON + NodeScriptDict *nsd = NULL; + bNodeTree *nodetree = NULL; + bNodeType *ntype = NULL; + PyGILState_STATE gilstate; - if(! node->id) { + /* 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; + + gilstate = PyGILState_Ensure(); + + /* ERROR, reset to (empty) defaults */ + if (BCLR(node->custom1, NODE_DYNAMIC_ERROR) == 0) { + node_dynamic_reset(node, 0); + PyGILState_Release(gilstate); + 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_rem_all_links(node->typeinfo); + node_dynamic_free_typeinfo_sockets(node->typeinfo); + node_dynamic_update_socket_links(node, NULL); + node_dynamic_free_storage_cb(node); + } - 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; + 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); + PyGILState_Release(gilstate); 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_loaded(node); } + PyGILState_Release(gilstate); + 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; - 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); - } - } + Py_INCREF((PyObject *)(nsd->dict)); + Py_INCREF((PyObject *)(nsd->node)); + + 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); + + PyGILState_Release(gilstate); +#endif /* DISABLE_PYTHON */ + 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) +{ +#ifdef DISABLE_PYTHON + return; +#else + NodeScriptDict *nsd; + PyGILState_STATE gilstate; + + if (!orig_node->storage) return; + + nsd = (NodeScriptDict *)(orig_node->storage); + new_node->storage = MEM_dupallocN(orig_node->storage); + + gilstate = PyGILState_Ensure(); + + if (nsd->node) + Py_INCREF((PyObject *)(nsd->node)); + if (nsd->dict) + Py_INCREF((PyObject *)(nsd->dict)); + + PyGILState_Release(gilstate); +#endif } +/* 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) { +#ifdef DISABLE_PYTHON + return; +#else + BPy_Node *mynode = NULL; + NodeScriptDict *nsd = NULL; + PyObject *pyresult = NULL; + PyObject *args = NULL; + ShadeInput *shi; + PyGILState_STATE gilstate; + + if (!node->id) + return; + + /*if (G.scene->r.threads > 1) + 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)) { + + gilstate = PyGILState_Ensure(); + + 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); -bNodeType sh_node_dynamic = { + if (!pyresult) { + PyGILState_Release(gilstate); + node_dynamic_disable_all_by_id(node->id); + node_dynamic_pyerror_print(node); + node_dynamic_setup(node); + return; + } + Py_DECREF(pyresult); + PyGILState_Release(gilstate); + } + } +#endif +} + +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 }; -#endif /* USE_PYNODES */ +#else + +bNodeType node_dynamic_typeinfo = {NULL}; + +#endif + |