/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2005 Blender Foundation. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Bob Holcomb. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/blenkernel/intern/node.c * \ingroup bke */ #include "MEM_guardedalloc.h" #include #include #include #include #include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_node_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "BLI_string.h" #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_utildefines.h" #include "BLF_translation.h" #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" #include "RNA_access.h" #include "NOD_socket.h" #include "NOD_composite.h" #include "NOD_shader.h" #include "NOD_texture.h" bNodeTreeType *ntreeGetType(int type) { static bNodeTreeType *types[NUM_NTREE_TYPES]; static int types_init = 1; if (types_init) { types[NTREE_SHADER] = &ntreeType_Shader; types[NTREE_COMPOSIT] = &ntreeType_Composite; types[NTREE_TEXTURE] = &ntreeType_Texture; types_init = 0; } if (type >= 0 && type < NUM_NTREE_TYPES) { return types[type]; } else { return NULL; } } static bNodeType *node_get_type(bNodeTree *ntree, int type) { bNodeType *ntype = ntreeGetType(ntree->type)->node_types.first; for (; ntype; ntype = ntype->next) if (ntype->type == type) return ntype; return NULL; } bNodeType *ntreeGetNodeType(bNodeTree *ntree) { return node_get_type(ntree, ntree->nodetype); } bNodeSocketType *ntreeGetSocketType(int type) { static bNodeSocketType *types[NUM_SOCKET_TYPES] = {NULL}; static int types_init = 1; if (types_init) { node_socket_type_init(types); types_init = 0; } if (type < NUM_SOCKET_TYPES) { return types[type]; } else { return NULL; } } void ntreeInitTypes(bNodeTree *ntree) { bNode *node, *next; for (node = ntree->nodes.first; node; node = next) { next = node->next; node->typeinfo = node_get_type(ntree, node->type); if (node->typeinfo == NULL) { printf("Error: Node type %s doesn't exist anymore, removed\n", node->name); nodeFreeNode(ntree, node); } } ntree->init |= NTREE_TYPE_INIT; } static bNodeSocket *make_socket(bNodeTree *UNUSED(ntree), int in_out, const char *name, int type) { bNodeSocket *sock; sock = MEM_callocN(sizeof(bNodeSocket), "sock"); BLI_strncpy(sock->name, name, NODE_MAXSTR); sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF); sock->type = type; sock->storage = NULL; sock->flag |= SOCK_COLLAPSED; sock->default_value = node_socket_make_default_value(type); node_socket_init_default_value(type, sock->default_value); return sock; } bNodeSocket *nodeAddSocket(bNodeTree *ntree, bNode *node, int in_out, const char *name, int type) { bNodeSocket *sock = make_socket(ntree, in_out, name, type); if (in_out == SOCK_IN) BLI_addtail(&node->inputs, sock); else if (in_out == SOCK_OUT) BLI_addtail(&node->outputs, sock); node->update |= NODE_UPDATE; return sock; } bNodeSocket *nodeInsertSocket(bNodeTree *ntree, bNode *node, int in_out, bNodeSocket *next_sock, const char *name, int type) { bNodeSocket *sock = make_socket(ntree, in_out, name, type); if (in_out == SOCK_IN) BLI_insertlinkbefore(&node->inputs, next_sock, sock); else if (in_out == SOCK_OUT) BLI_insertlinkbefore(&node->outputs, next_sock, sock); node->update |= NODE_UPDATE; return sock; } void nodeRemoveSocket(bNodeTree *ntree, bNode *node, bNodeSocket *sock) { bNodeLink *link, *next; for (link = ntree->links.first; link; link = next) { next = link->next; if (link->fromsock == sock || link->tosock == sock) { nodeRemLink(ntree, link); } } /* this is fast, this way we don't need an in_out argument */ BLI_remlink(&node->inputs, sock); BLI_remlink(&node->outputs, sock); node_socket_free_default_value(sock->type, sock->default_value); MEM_freeN(sock); node->update |= NODE_UPDATE; } void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) { bNodeSocket *sock; bNodeLink *link, *next; for (link = ntree->links.first; link; link = next) { next = link->next; if (link->fromnode == node || link->tonode == node) { nodeRemLink(ntree, link); } } for (sock = node->inputs.first; sock; sock = sock->next) node_socket_free_default_value(sock->type, sock->default_value); BLI_freelistN(&node->inputs); for (sock = node->outputs.first; sock; sock = sock->next) node_socket_free_default_value(sock->type, sock->default_value); BLI_freelistN(&node->outputs); node->update |= NODE_UPDATE; } /* finds a node based on its name */ bNode *nodeFindNodebyName(bNodeTree *ntree, const char *name) { return BLI_findstring(&ntree->nodes, name, offsetof(bNode, name)); } /* finds a node based on given socket */ int nodeFindNode(bNodeTree *ntree, bNodeSocket *sock, bNode **nodep, int *sockindex, int *in_out) { bNode *node; bNodeSocket *tsock; int index = 0; for (node = ntree->nodes.first; node; node = node->next) { for (index = 0, tsock = node->inputs.first; tsock; tsock = tsock->next, index++) { if (tsock == sock) { if (in_out) *in_out = SOCK_IN; break; } } if (tsock) break; for (index = 0, tsock = node->outputs.first; tsock; tsock = tsock->next, index++) { if (tsock == sock) { if (in_out) *in_out = SOCK_OUT; break; } } if (tsock) break; } if (node) { *nodep = node; if (sockindex) *sockindex = index; return 1; } *nodep = NULL; return 0; } /* ************** Add stuff ********** */ static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) { bNodeSocketTemplate *sockdef; /* bNodeSocket *sock; */ /* UNUSED */ if (ntype->inputs) { sockdef = ntype->inputs; while (sockdef->type != -1) { /* sock = */ node_add_input_from_template(ntree, node, sockdef); sockdef++; } } if (ntype->outputs) { sockdef = ntype->outputs; while (sockdef->type != -1) { /* sock = */ node_add_output_from_template(ntree, node, sockdef); sockdef++; } } } /* Find the first available, non-duplicate name for a given node */ void nodeUniqueName(bNodeTree *ntree, bNode *node) { BLI_uniquename(&ntree->nodes, node, "Node", '.', offsetof(bNode, name), sizeof(node->name)); } bNode *nodeAddNode(bNodeTree *ntree, struct bNodeTemplate *ntemp) { bNode *node; bNodeType *ntype; ntype = node_get_type(ntree, ntemp->type); if (ntype == NULL) { printf("nodeAddNodeType() error: '%d' type invalid\n", ntemp->type); return NULL; } /* validity check */ if (!nodeValid(ntree, ntemp)) return NULL; node = MEM_callocN(sizeof(bNode), "new node"); node->type = ntype->type; node->typeinfo = ntype; node->flag = NODE_SELECT | ntype->flag; node->width = ntype->width; node->miniwidth = 42.0f; node->height = ntype->height; node->color[0] = node->color[1] = node->color[2] = 0.608; /* default theme color */ node_add_sockets_from_type(ntree, node, ntype); BLI_addtail(&ntree->nodes, node); if (ntype->initfunc != NULL) ntype->initfunc(ntree, node, ntemp); /* initialize the node name with the node label. * note: do this after the initfunc so nodes get their data set which may be used in naming * (node groups for example) */ /* XXX Do not use nodeLabel() here, it returns translated content, which should *only* be used * in UI, *never* in data... * This solution may be a bit rougher than nodeLabel()'s returned string, but it's simpler * than adding a "no translate" flag to this func (and labelfunc() as well). */ BLI_strncpy(node->name, node->typeinfo->name, NODE_MAXSTR); nodeUniqueName(ntree, node); ntree->update |= NTREE_UPDATE_NODES; return node; } /* keep socket listorder identical, for copying links */ /* ntree is the target tree */ bNode *nodeCopyNode(struct bNodeTree *ntree, struct bNode *node) { bNode *nnode = MEM_callocN(sizeof(bNode), "dupli node"); bNodeSocket *sock, *oldsock; bNodeLink *link, *oldlink; *nnode = *node; /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { nodeUniqueName(ntree, nnode); BLI_addtail(&ntree->nodes, nnode); } BLI_duplicatelist(&nnode->inputs, &node->inputs); oldsock = node->inputs.first; for (sock = nnode->inputs.first; sock; sock = sock->next, oldsock = oldsock->next) { oldsock->new_sock = sock; sock->stack_index = 0; sock->default_value = node_socket_make_default_value(oldsock->type); node_socket_copy_default_value(oldsock->type, sock->default_value, oldsock->default_value); /* XXX some compositor node (e.g. image, render layers) still store * some persistent buffer data here, need to clear this to avoid dangling pointers. */ sock->cache = NULL; } BLI_duplicatelist(&nnode->outputs, &node->outputs); oldsock = node->outputs.first; for (sock = nnode->outputs.first; sock; sock = sock->next, oldsock = oldsock->next) { oldsock->new_sock = sock; sock->stack_index = 0; sock->default_value = node_socket_make_default_value(oldsock->type); node_socket_copy_default_value(oldsock->type, sock->default_value, oldsock->default_value); /* XXX some compositor node (e.g. image, render layers) still store * some persistent buffer data here, need to clear this to avoid dangling pointers. */ sock->cache = NULL; } BLI_duplicatelist(&nnode->internal_links, &node->internal_links); oldlink = node->internal_links.first; for (link = nnode->internal_links.first; link; link = link->next, oldlink = oldlink->next) { link->fromnode = nnode; link->tonode = nnode; link->fromsock = link->fromsock->new_sock; link->tosock = link->tosock->new_sock; } /* don't increase node->id users, freenode doesn't decrement either */ if (node->typeinfo->copystoragefunc) node->typeinfo->copystoragefunc(node, nnode); node->new_node = nnode; nnode->new_node = NULL; nnode->preview = NULL; if (ntree) ntree->update |= NTREE_UPDATE_NODES; return nnode; } /* also used via rna api, so we check for proper input output direction */ bNodeLink *nodeAddLink(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock) { bNodeSocket *sock; bNodeLink *link = NULL; int from = 0, to = 0; if (fromnode) { /* test valid input */ for (sock = fromnode->outputs.first; sock; sock = sock->next) if (sock == fromsock) break; if (sock) from = 1; /* OK */ else { for (sock = fromnode->inputs.first; sock; sock = sock->next) if (sock == fromsock) break; if (sock) from = -1; /* OK but flip */ } } else if (ntree) { /* check tree sockets */ for (sock = ntree->inputs.first; sock; sock = sock->next) if (sock == fromsock) break; if (sock) from = 1; /* OK */ else { for (sock = ntree->outputs.first; sock; sock = sock->next) if (sock == fromsock) break; if (sock) from = -1; /* OK but flip */ } } if (tonode) { for (sock = tonode->inputs.first; sock; sock = sock->next) if (sock == tosock) break; if (sock) to = 1; /* OK */ else { for (sock = tonode->outputs.first; sock; sock = sock->next) if (sock == tosock) break; if (sock) to = -1; /* OK but flip */ } } else if (ntree) { /* check tree sockets */ for (sock = ntree->outputs.first; sock; sock = sock->next) if (sock == tosock) break; if (sock) to = 1; /* OK */ else { for (sock = ntree->inputs.first; sock; sock = sock->next) if (sock == tosock) break; if (sock) to = -1; /* OK but flip */ } } if (from >= 0 && to >= 0) { link = MEM_callocN(sizeof(bNodeLink), "link"); if (ntree) BLI_addtail(&ntree->links, link); link->fromnode = fromnode; link->fromsock = fromsock; link->tonode = tonode; link->tosock = tosock; } else if (from <= 0 && to <= 0) { link = MEM_callocN(sizeof(bNodeLink), "link"); if (ntree) BLI_addtail(&ntree->links, link); link->fromnode = tonode; link->fromsock = tosock; link->tonode = fromnode; link->tosock = fromsock; } if (ntree) ntree->update |= NTREE_UPDATE_LINKS; return link; } void nodeRemLink(bNodeTree *ntree, bNodeLink *link) { /* can be called for links outside a node tree (e.g. clipboard) */ if (ntree) BLI_remlink(&ntree->links, link); if (link->tosock) link->tosock->link = NULL; MEM_freeN(link); if (ntree) ntree->update |= NTREE_UPDATE_LINKS; } void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock) { bNodeLink *link, *next; for (link = ntree->links.first; link; link = next) { next = link->next; if (link->fromsock == sock || link->tosock == sock) { nodeRemLink(ntree, link); } } ntree->update |= NTREE_UPDATE_LINKS; } void nodeInternalRelink(bNodeTree *ntree, bNode *node) { bNodeLink *link, *link_next; if (node->internal_links.first == NULL) return; /* store link pointers in output sockets, for efficient lookup */ for (link = node->internal_links.first; link; link = link->next) link->tosock->link = link; /* redirect downstream links */ for (link = ntree->links.first; link; link = link_next) { link_next = link->next; /* do we have internal link? */ if (link->fromnode == node) { if (link->fromsock->link) { /* get the upstream input link */ bNodeLink *fromlink = link->fromsock->link->fromsock->link; /* skip the node */ if (fromlink) { link->fromnode = fromlink->fromnode; link->fromsock = fromlink->fromsock; ntree->update |= NTREE_UPDATE_LINKS; } else nodeRemLink(ntree, link); } else nodeRemLink(ntree, link); } } /* remove remaining upstream links */ for (link = ntree->links.first; link; link = link_next) { link_next = link->next; if (link->tonode == node) nodeRemLink(ntree, link); } } void nodeToView(bNode *node, float x, float y, float *rx, float *ry) { if (node->parent) { nodeToView(node->parent, x + node->locx, y + node->locy, rx, ry); } else { *rx = x + node->locx; *ry = y + node->locy; } } void nodeFromView(bNode *node, float x, float y, float *rx, float *ry) { if (node->parent) { nodeFromView(node->parent, x, y, rx, ry); *rx -= node->locx; *ry -= node->locy; } else { *rx = x - node->locx; *ry = y - node->locy; } } int nodeAttachNodeCheck(bNode *node, bNode *parent) { bNode *parent_recurse; for (parent_recurse = node; parent_recurse; parent_recurse = parent_recurse->parent) { if (parent_recurse == parent) { return TRUE; } } return FALSE; } void nodeAttachNode(bNode *node, bNode *parent) { float locx, locy; BLI_assert(parent->type == NODE_FRAME); BLI_assert(nodeAttachNodeCheck(parent, node) == FALSE); nodeToView(node, 0.0f, 0.0f, &locx, &locy); node->parent = parent; /* transform to parent space */ nodeFromView(parent, locx, locy, &node->locx, &node->locy); } void nodeDetachNode(struct bNode *node) { float locx, locy; if (node->parent) { BLI_assert(node->parent->type == NODE_FRAME); /* transform to view space */ nodeToView(node, 0.0f, 0.0f, &locx, &locy); node->locx = locx; node->locy = locy; node->parent = NULL; } } bNodeTree *ntreeAddTree(const char *name, int type, int nodetype) { bNodeTree *ntree; bNodeType *ntype; /* trees are created as local trees if they of compositor, material or texture type, * node groups and other tree types are created as library data. */ if (ELEM3(type, NTREE_COMPOSIT, NTREE_SHADER, NTREE_TEXTURE) && nodetype == 0) { ntree = MEM_callocN(sizeof(bNodeTree), "new node tree"); *( (short *)ntree->id.name) = ID_NT; /* not "type", as that is ntree->type */ BLI_strncpy(ntree->id.name + 2, name, sizeof(ntree->id.name)); } else ntree = BKE_libblock_alloc(&G.main->nodetree, ID_NT, name); ntree->type = type; ntree->nodetype = nodetype; ntreeInitTypes(ntree); ntype = node_get_type(ntree, ntree->nodetype); if (ntype && ntype->inittreefunc) ntype->inittreefunc(ntree); return ntree; } /* Warning: this function gets called during some rather unexpected times * - this gets called when executing compositing updates (for threaded previews) * - when the nodetree datablock needs to be copied (i.e. when users get copied) * - for scene duplication use ntreeSwapID() after so we don't have stale pointers. * * do_make_extern: keep enabled for general use, only reason _not_ to enable is when * copying for internal use (threads for eg), where you wont want it to modify the * scene data. */ static bNodeTree *ntreeCopyTree_internal(bNodeTree *ntree, const short do_id_user, const short do_make_extern) { bNodeTree *newtree; bNode *node /*, *nnode */ /* UNUSED */, *last; bNodeLink *link; bNodeSocket *gsock, *oldgsock; if (ntree == NULL) return NULL; /* is ntree part of library? */ for (newtree = G.main->nodetree.first; newtree; newtree = newtree->id.next) if (newtree == ntree) break; if (newtree) { newtree = BKE_libblock_copy(&ntree->id); } else { newtree = MEM_dupallocN(ntree); BKE_libblock_copy_data(&newtree->id, &ntree->id, TRUE); /* copy animdata and ID props */ } id_us_plus((ID *)newtree->gpd); /* in case a running nodetree is copied */ newtree->execdata = NULL; newtree->nodes.first = newtree->nodes.last = NULL; newtree->links.first = newtree->links.last = NULL; last = ntree->nodes.last; for (node = ntree->nodes.first; node; node = node->next) { /* ntreeUserDecrefID inline */ if (do_id_user) { id_us_plus(node->id); } if (do_make_extern) { id_lib_extern(node->id); } node->new_node = NULL; /* nnode = */ nodeCopyNode(newtree, node); /* sets node->new */ /* make sure we don't copy new nodes again! */ if (node == last) break; } /* socket definition for group usage */ BLI_duplicatelist(&newtree->inputs, &ntree->inputs); for (gsock = newtree->inputs.first, oldgsock = ntree->inputs.first; gsock; gsock = gsock->next, oldgsock = oldgsock->next) { oldgsock->new_sock = gsock; gsock->groupsock = (oldgsock->groupsock ? oldgsock->groupsock->new_sock : NULL); gsock->default_value = node_socket_make_default_value(oldgsock->type); node_socket_copy_default_value(oldgsock->type, gsock->default_value, oldgsock->default_value); } BLI_duplicatelist(&newtree->outputs, &ntree->outputs); for (gsock = newtree->outputs.first, oldgsock = ntree->outputs.first; gsock; gsock = gsock->next, oldgsock = oldgsock->next) { oldgsock->new_sock = gsock; gsock->groupsock = (oldgsock->groupsock ? oldgsock->groupsock->new_sock : NULL); gsock->default_value = node_socket_make_default_value(oldgsock->type); node_socket_copy_default_value(oldgsock->type, gsock->default_value, oldgsock->default_value); } /* copy links */ BLI_duplicatelist(&newtree->links, &ntree->links); for (link = newtree->links.first; link; link = link->next) { link->fromnode = (link->fromnode ? link->fromnode->new_node : NULL); link->fromsock = (link->fromsock ? link->fromsock->new_sock : NULL); link->tonode = (link->tonode ? link->tonode->new_node : NULL); link->tosock = (link->tosock ? link->tosock->new_sock : NULL); /* update the link socket's pointer */ if (link->tosock) link->tosock->link = link; } /* update node->parent pointers */ for (node = newtree->nodes.first; node; node = node->next) { if (node->parent) node->parent = node->parent->new_node; } return newtree; } bNodeTree *ntreeCopyTree_ex(bNodeTree *ntree, const short do_id_user) { return ntreeCopyTree_internal(ntree, do_id_user, TRUE); } bNodeTree *ntreeCopyTree(bNodeTree *ntree) { return ntreeCopyTree_ex(ntree, TRUE); } /* use when duplicating scenes */ void ntreeSwitchID_ex(bNodeTree *ntree, ID *id_from, ID *id_to, const short do_id_user) { bNode *node; if (id_from == id_to) { /* should never happen but may as well skip if it does */ return; } /* for scene duplication only */ for (node = ntree->nodes.first; node; node = node->next) { if (node->id == id_from) { if (do_id_user) { id_us_min(id_from); id_us_plus(id_to); } node->id = id_to; } } } void ntreeSwitchID(bNodeTree *ntree, ID *id_from, ID *id_to) { ntreeSwitchID_ex(ntree, id_from, id_to, TRUE); } void ntreeUserIncrefID(bNodeTree *ntree) { bNode *node; for (node = ntree->nodes.first; node; node = node->next) { id_us_plus(node->id); } } void ntreeUserDecrefID(bNodeTree *ntree) { bNode *node; for (node = ntree->nodes.first; node; node = node->next) { id_us_min(node->id); } } /* *************** preview *********** */ /* if node->preview, then we assume the rect to exist */ void nodeFreePreview(bNode *node) { if (node->preview) { if (node->preview->rect) MEM_freeN(node->preview->rect); MEM_freeN(node->preview); node->preview = NULL; } } static void node_init_preview(bNode *node, int xsize, int ysize) { if (node->preview == NULL) { node->preview = MEM_callocN(sizeof(bNodePreview), "node preview"); // printf("added preview %s\n", node->name); } /* node previews can get added with variable size this way */ if (xsize == 0 || ysize == 0) return; /* sanity checks & initialize */ if (node->preview->rect) { if (node->preview->xsize != xsize && node->preview->ysize != ysize) { MEM_freeN(node->preview->rect); node->preview->rect = NULL; } } if (node->preview->rect == NULL) { node->preview->rect = MEM_callocN(4 * xsize + xsize * ysize * sizeof(char) * 4, "node preview rect"); node->preview->xsize = xsize; node->preview->ysize = ysize; } /* no clear, makes nicer previews */ } void ntreeInitPreview(bNodeTree *ntree, int xsize, int ysize) { bNode *node; if (ntree == NULL) return; for (node = ntree->nodes.first; node; node = node->next) { if (node->typeinfo->flag & NODE_PREVIEW) /* hrms, check for closed nodes? */ node_init_preview(node, xsize, ysize); if (node->type == NODE_GROUP && (node->flag & NODE_GROUP_EDIT)) ntreeInitPreview((bNodeTree *)node->id, xsize, ysize); } } static void nodeClearPreview(bNode *node) { if (node->preview && node->preview->rect) memset(node->preview->rect, 0, MEM_allocN_len(node->preview->rect)); } /* use it to enforce clear */ void ntreeClearPreview(bNodeTree *ntree) { bNode *node; if (ntree == NULL) return; for (node = ntree->nodes.first; node; node = node->next) { if (node->typeinfo->flag & NODE_PREVIEW) nodeClearPreview(node); if (node->type == NODE_GROUP && (node->flag & NODE_GROUP_EDIT)) ntreeClearPreview((bNodeTree *)node->id); } } /* hack warning! this function is only used for shader previews, and * since it gets called multiple times per pixel for Ztransp we only * add the color once. Preview gets cleared before it starts render though */ void nodeAddToPreview(bNode *node, const float col[4], int x, int y, int do_manage) { bNodePreview *preview = node->preview; if (preview) { if (x >= 0 && y >= 0) { if (x < preview->xsize && y < preview->ysize) { unsigned char *tar = preview->rect + 4 * ((preview->xsize * y) + x); if (do_manage) { linearrgb_to_srgb_uchar4(tar, col); } else { rgba_float_to_uchar(tar, col); } } //else printf("prv out bound x y %d %d\n", x, y); } //else printf("prv out bound x y %d %d\n", x, y); } } /* ************** Free stuff ********** */ /* goes over entire tree */ void nodeUnlinkNode(bNodeTree *ntree, bNode *node) { bNodeLink *link, *next; bNodeSocket *sock; ListBase *lb; for (link = ntree->links.first; link; link = next) { next = link->next; if (link->fromnode == node) { lb = &node->outputs; if (link->tonode) link->tonode->update |= NODE_UPDATE; } else if (link->tonode == node) lb = &node->inputs; else lb = NULL; if (lb) { for (sock = lb->first; sock; sock = sock->next) { if (link->fromsock == sock || link->tosock == sock) break; } if (sock) { nodeRemLink(ntree, link); } } } } static void node_unlink_attached(bNodeTree *ntree, bNode *parent) { bNode *node; for (node = ntree->nodes.first; node; node = node->next) { if (node->parent == parent) nodeDetachNode(node); } } /** \note caller needs to manage node->id user */ void nodeFreeNode(bNodeTree *ntree, bNode *node) { bNodeSocket *sock, *nextsock; /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { bNodeTreeType *treetype = ntreeGetType(ntree->type); /* remove all references to this node */ nodeUnlinkNode(ntree, node); node_unlink_attached(ntree, node); BLI_remlink(&ntree->nodes, node); if (treetype->free_node_cache) treetype->free_node_cache(ntree, node); } /* since it is called while free database, node->id is undefined */ if (node->typeinfo && node->typeinfo->freestoragefunc) node->typeinfo->freestoragefunc(node); for (sock = node->inputs.first; sock; sock = nextsock) { nextsock = sock->next; node_socket_free_default_value(sock->type, sock->default_value); MEM_freeN(sock); } for (sock = node->outputs.first; sock; sock = nextsock) { nextsock = sock->next; node_socket_free_default_value(sock->type, sock->default_value); MEM_freeN(sock); } BLI_freelistN(&node->internal_links); nodeFreePreview(node); MEM_freeN(node); if (ntree) ntree->update |= NTREE_UPDATE_NODES; } /* do not free ntree itself here, BKE_libblock_free calls this function too */ void ntreeFreeTree_ex(bNodeTree *ntree, const short do_id_user) { bNode *node, *next; bNodeSocket *sock; if (ntree == NULL) return; /* XXX hack! node trees should not store execution graphs at all. * This should be removed when old tree types no longer require it. * Currently the execution data for texture nodes remains in the tree * after execution, until the node tree is updated or freed. */ if (ntree->execdata) { switch (ntree->type) { case NTREE_COMPOSIT: ntreeCompositEndExecTree(ntree->execdata, 1); break; case NTREE_SHADER: ntreeShaderEndExecTree(ntree->execdata, 1); break; case NTREE_TEXTURE: ntreeTexEndExecTree(ntree->execdata, 1); break; } } BKE_free_animdata((ID *)ntree); id_us_min((ID *)ntree->gpd); BLI_freelistN(&ntree->links); /* do first, then unlink_node goes fast */ for (node = ntree->nodes.first; node; node = next) { next = node->next; /* ntreeUserIncrefID inline */ /* XXX, this is correct, however when freeing the entire database * this ends up accessing freed data which isn't properly unlinking * its self from scene nodes, SO - for now prefer invalid usercounts * on free rather then bad memory access - Campbell */ #if 0 if (do_id_user) { id_us_min(node->id); } #else (void)do_id_user; #endif nodeFreeNode(ntree, node); } for (sock = ntree->inputs.first; sock; sock = sock->next) node_socket_free_default_value(sock->type, sock->default_value); BLI_freelistN(&ntree->inputs); for (sock = ntree->outputs.first; sock; sock = sock->next) node_socket_free_default_value(sock->type, sock->default_value); BLI_freelistN(&ntree->outputs); } /* same as ntreeFreeTree_ex but always manage users */ void ntreeFreeTree(bNodeTree *ntree) { ntreeFreeTree_ex(ntree, TRUE); } void ntreeFreeCache(bNodeTree *ntree) { bNodeTreeType *treetype; if (ntree == NULL) return; treetype = ntreeGetType(ntree->type); if (treetype->free_cache) treetype->free_cache(ntree); } void ntreeSetOutput(bNodeTree *ntree) { bNode *node; /* find the active outputs, might become tree type dependent handler */ for (node = ntree->nodes.first; node; node = node->next) { if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) { bNode *tnode; int output = 0; /* we need a check for which output node should be tagged like this, below an exception */ if (node->type == CMP_NODE_OUTPUT_FILE) continue; /* there is more types having output class, each one is checked */ for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { if (tnode->typeinfo->nclass == NODE_CLASS_OUTPUT) { if (ntree->type == NTREE_COMPOSIT) { /* same type, exception for viewer */ if (tnode->type == node->type || (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER) && ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))) { if (tnode->flag & NODE_DO_OUTPUT) { output++; if (output > 1) tnode->flag &= ~NODE_DO_OUTPUT; } } } else { /* same type */ if (tnode->type == node->type) { if (tnode->flag & NODE_DO_OUTPUT) { output++; if (output > 1) tnode->flag &= ~NODE_DO_OUTPUT; } } } } } if (output == 0) node->flag |= NODE_DO_OUTPUT; } } /* here we could recursively set which nodes have to be done, * might be different for editor or for "real" use... */ } typedef struct MakeLocalCallData { ID *group_id; ID *new_id; int lib, local; } MakeLocalCallData; static void ntreeMakeLocal_CheckLocal(void *calldata, ID *owner_id, bNodeTree *ntree) { MakeLocalCallData *cd = (MakeLocalCallData *)calldata; bNode *node; /* find if group is in tree */ for (node = ntree->nodes.first; node; node = node->next) { if (node->id == cd->group_id) { if (owner_id->lib) { cd->lib = TRUE; } else { cd->local = TRUE; } } } } static void ntreeMakeLocal_LinkNew(void *calldata, ID *owner_id, bNodeTree *ntree) { MakeLocalCallData *cd = (MakeLocalCallData *)calldata; bNode *node; /* find if group is in tree */ for (node = ntree->nodes.first; node; node = node->next) { if (node->id == cd->group_id) { if (owner_id->lib == NULL) { node->id = cd->new_id; cd->new_id->us++; cd->group_id->us--; } } } } void ntreeMakeLocal(bNodeTree *ntree) { Main *bmain = G.main; bNodeTreeType *treetype = ntreeGetType(ntree->type); MakeLocalCallData cd; /* - only lib users: do nothing * - only local users: set flag * - mixed: make copy */ if (ntree->id.lib == NULL) return; if (ntree->id.us == 1) { id_clear_lib_data(bmain, (ID *)ntree); return; } /* now check users of groups... again typedepending, callback... */ cd.group_id = &ntree->id; cd.new_id = NULL; cd.local = 0; cd.lib = 0; treetype->foreach_nodetree(G.main, &cd, &ntreeMakeLocal_CheckLocal); /* if all users are local, we simply make tree local */ if (cd.local && cd.lib == 0) { id_clear_lib_data(bmain, (ID *)ntree); } else if (cd.local && cd.lib) { /* this is the mixed case, we copy the tree and assign it to local users */ bNodeTree *newtree = ntreeCopyTree(ntree); newtree->id.us = 0; cd.new_id = &newtree->id; treetype->foreach_nodetree(G.main, &cd, &ntreeMakeLocal_LinkNew); } } int ntreeNodeExists(bNodeTree *ntree, bNode *testnode) { bNode *node = ntree->nodes.first; for (; node; node = node->next) if (node == testnode) return 1; return 0; } int ntreeOutputExists(bNode *node, bNodeSocket *testsock) { bNodeSocket *sock = node->outputs.first; for (; sock; sock = sock->next) if (sock == testsock) return 1; return 0; } /* returns localized tree for execution in threads */ bNodeTree *ntreeLocalize(bNodeTree *ntree) { bNodeTreeType *ntreetype = ntreeGetType(ntree->type); bNodeTree *ltree; bNode *node; bAction *action_backup = NULL, *tmpact_backup = NULL; /* Workaround for copying an action on each render! * set action to NULL so animdata actions don't get copied */ AnimData *adt = BKE_animdata_from_id(&ntree->id); if (adt) { action_backup = adt->action; tmpact_backup = adt->tmpact; adt->action = NULL; adt->tmpact = NULL; } /* node copy func */ ltree = ntreeCopyTree_internal(ntree, FALSE, FALSE); if (adt) { AnimData *ladt = BKE_animdata_from_id(<ree->id); adt->action = ladt->action = action_backup; adt->tmpact = ladt->tmpact = tmpact_backup; if (action_backup) action_backup->id.us++; if (tmpact_backup) tmpact_backup->id.us++; } /* end animdata uglyness */ /* ensures only a single output node is enabled */ ntreeSetOutput(ntree); for (node = ntree->nodes.first; node; node = node->next) { /* store new_node pointer to original */ node->new_node->new_node = node; } if (ntreetype->localize) ntreetype->localize(ltree, ntree); return ltree; } /* sync local composite with real tree */ /* local tree is supposed to be running, be careful moving previews! */ /* is called by jobs manager, outside threads, so it doesnt happen during draw */ void ntreeLocalSync(bNodeTree *localtree, bNodeTree *ntree) { bNodeTreeType *ntreetype = ntreeGetType(ntree->type); if (ntreetype->local_sync) ntreetype->local_sync(localtree, ntree); } /* merge local tree results back, and free local tree */ /* we have to assume the editor already changed completely */ void ntreeLocalMerge(bNodeTree *localtree, bNodeTree *ntree) { bNodeTreeType *ntreetype = ntreeGetType(ntree->type); bNode *lnode; /* move over the compbufs and previews */ for (lnode = localtree->nodes.first; lnode; lnode = lnode->next) { if (ntreeNodeExists(ntree, lnode->new_node)) { if (lnode->preview && lnode->preview->rect) { nodeFreePreview(lnode->new_node); lnode->new_node->preview = lnode->preview; lnode->preview = NULL; } } } if (ntreetype->local_merge) ntreetype->local_merge(localtree, ntree); ntreeFreeTree_ex(localtree, FALSE); MEM_freeN(localtree); } /* ************ find stuff *************** */ int ntreeHasType(bNodeTree *ntree, int type) { bNode *node; if (ntree) for (node = ntree->nodes.first; node; node = node->next) if (node->type == type) return 1; return 0; } bNodeLink *nodeFindLink(bNodeTree *ntree, bNodeSocket *from, bNodeSocket *to) { bNodeLink *link; for (link = ntree->links.first; link; link = link->next) { if (link->fromsock == from && link->tosock == to) return link; if (link->fromsock == to && link->tosock == from) /* hrms? */ return link; } return NULL; } int nodeCountSocketLinks(bNodeTree *ntree, bNodeSocket *sock) { bNodeLink *link; int tot = 0; for (link = ntree->links.first; link; link = link->next) { if (link->fromsock == sock || link->tosock == sock) tot++; } return tot; } bNode *nodeGetActive(bNodeTree *ntree) { bNode *node; if (ntree == NULL) return NULL; for (node = ntree->nodes.first; node; node = node->next) if (node->flag & NODE_ACTIVE) break; return node; } /* two active flags, ID nodes have special flag for buttons display */ bNode *nodeGetActiveID(bNodeTree *ntree, short idtype) { bNode *node; if (ntree == NULL) return NULL; /* check for group edit */ for (node = ntree->nodes.first; node; node = node->next) if (node->flag & NODE_GROUP_EDIT) break; if (node) ntree = (bNodeTree *)node->id; /* now find active node with this id */ for (node = ntree->nodes.first; node; node = node->next) if (node->id && GS(node->id->name) == idtype) if (node->flag & NODE_ACTIVE_ID) break; return node; } int nodeSetActiveID(bNodeTree *ntree, short idtype, ID *id) { bNode *node; int ok = FALSE; if (ntree == NULL) return ok; /* check for group edit */ for (node = ntree->nodes.first; node; node = node->next) if (node->flag & NODE_GROUP_EDIT) break; if (node) ntree = (bNodeTree *)node->id; /* now find active node with this id */ for (node = ntree->nodes.first; node; node = node->next) { if (node->id && GS(node->id->name) == idtype) { if (id && ok == FALSE && node->id == id) { node->flag |= NODE_ACTIVE_ID; ok = TRUE; } else { node->flag &= ~NODE_ACTIVE_ID; } } } return ok; } /* two active flags, ID nodes have special flag for buttons display */ void nodeClearActiveID(bNodeTree *ntree, short idtype) { bNode *node; if (ntree == NULL) return; for (node = ntree->nodes.first; node; node = node->next) if (node->id && GS(node->id->name) == idtype) node->flag &= ~NODE_ACTIVE_ID; } void nodeClearActive(bNodeTree *ntree) { bNode *node; if (ntree == NULL) return; for (node = ntree->nodes.first; node; node = node->next) node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_ID); } /* two active flags, ID nodes have special flag for buttons display */ void nodeSetActive(bNodeTree *ntree, bNode *node) { bNode *tnode; /* make sure only one node is active, and only one per ID type */ for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { tnode->flag &= ~NODE_ACTIVE; if (node->id && tnode->id) { if (GS(node->id->name) == GS(tnode->id->name)) tnode->flag &= ~NODE_ACTIVE_ID; } if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) tnode->flag &= ~NODE_ACTIVE_TEXTURE; } node->flag |= NODE_ACTIVE; if (node->id) node->flag |= NODE_ACTIVE_ID; if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) node->flag |= NODE_ACTIVE_TEXTURE; } int nodeSocketIsHidden(bNodeSocket *sock) { return ((sock->flag & (SOCK_HIDDEN | SOCK_UNAVAIL)) != 0); } void nodeSocketSetType(bNodeSocket *sock, int type) { int old_type = sock->type; void *old_default_value = sock->default_value; sock->type = type; sock->default_value = node_socket_make_default_value(sock->type); node_socket_init_default_value(type, sock->default_value); node_socket_convert_default_value(sock->type, sock->default_value, old_type, old_default_value); node_socket_free_default_value(old_type, old_default_value); } /* ************** Node Clipboard *********** */ #define USE_NODE_CB_VALIDATE #ifdef USE_NODE_CB_VALIDATE /** * This data structure is to validate the node on creation, * otherwise we may reference missing data. * * Currently its only used for ID's, but nodes may one day * reference other pointers which need validation. */ typedef struct bNodeClipboardExtraInfo { struct bNodeClipboardExtraInfo *next, *prev; ID *id; char id_name[MAX_ID_NAME]; char library_name[FILE_MAX]; } bNodeClipboardExtraInfo; #endif /* USE_NODE_CB_VALIDATE */ typedef struct bNodeClipboard { ListBase nodes; #ifdef USE_NODE_CB_VALIDATE ListBase nodes_extra_info; #endif ListBase links; int type; } bNodeClipboard; bNodeClipboard node_clipboard = {{0}}; void BKE_node_clipboard_init(struct bNodeTree *ntree) { node_clipboard.type = ntree->type; } void BKE_node_clipboard_clear(void) { bNode *node, *node_next; bNodeLink *link, *link_next; for (link = node_clipboard.links.first; link; link = link_next) { link_next = link->next; nodeRemLink(NULL, link); } node_clipboard.links.first = node_clipboard.links.last = NULL; for (node = node_clipboard.nodes.first; node; node = node_next) { node_next = node->next; nodeFreeNode(NULL, node); } node_clipboard.nodes.first = node_clipboard.nodes.last = NULL; #ifdef USE_NODE_CB_VALIDATE BLI_freelistN(&node_clipboard.nodes_extra_info); #endif } /* return FALSE when one or more ID's are lost */ int BKE_node_clipboard_validate(void) { int ok = TRUE; #ifdef USE_NODE_CB_VALIDATE bNodeClipboardExtraInfo *node_info; bNode *node; /* lists must be aligned */ BLI_assert(BLI_countlist(&node_clipboard.nodes) == BLI_countlist(&node_clipboard.nodes_extra_info)); for (node = node_clipboard.nodes.first, node_info = node_clipboard.nodes_extra_info.first; node; node = node->next, node_info = node_info->next) { /* validate the node against the stored node info */ /* re-assign each loop since we may clear, * open a new file where the ID is valid, and paste again */ node->id = node_info->id; /* currently only validate the ID */ if (node->id) { ListBase *lb = which_libbase(G.main, GS(node_info->id_name)); BLI_assert(lb != NULL); if (BLI_findindex(lb, node_info->id) == -1) { /* may assign NULL */ node->id = BLI_findstring(lb, node_info->id_name + 2, offsetof(ID, name) + 2); if (node->id == NULL) { ok = FALSE; } } } } #endif /* USE_NODE_CB_VALIDATE */ return ok; } void BKE_node_clipboard_add_node(bNode *node) { #ifdef USE_NODE_CB_VALIDATE /* add extra info */ bNodeClipboardExtraInfo *node_info = MEM_mallocN(sizeof(bNodeClipboardExtraInfo), STRINGIFY(bNodeClipboardExtraInfo)); node_info->id = node->id; if (node->id) { BLI_strncpy(node_info->id_name, node->id->name, sizeof(node_info->id_name)); if (node->id->lib) { BLI_strncpy(node_info->library_name, node->id->lib->filepath, sizeof(node_info->library_name)); } else { node_info->library_name[0] = '\0'; } } else { node_info->id_name[0] = '\0'; node_info->library_name[0] = '\0'; } BLI_addtail(&node_clipboard.nodes_extra_info, node_info); /* end extra info */ #endif /* USE_NODE_CB_VALIDATE */ /* add node */ BLI_addtail(&node_clipboard.nodes, node); } void BKE_node_clipboard_add_link(bNodeLink *link) { BLI_addtail(&node_clipboard.links, link); } const ListBase *BKE_node_clipboard_get_nodes(void) { return &node_clipboard.nodes; } const ListBase *BKE_node_clipboard_get_links(void) { return &node_clipboard.links; } int BKE_node_clipboard_get_type(void) { return node_clipboard.type; } /* ************** dependency stuff *********** */ /* node is guaranteed to be not checked before */ static int node_get_deplist_recurs(bNodeTree *ntree, bNode *node, bNode ***nsort) { bNode *fromnode; bNodeLink *link; int level = 0xFFF; node->done = TRUE; /* check linked nodes */ for (link = ntree->links.first; link; link = link->next) { if (link->tonode == node) { fromnode = link->fromnode; if (fromnode) { if (fromnode->done == 0) fromnode->level = node_get_deplist_recurs(ntree, fromnode, nsort); if (fromnode->level <= level) level = fromnode->level - 1; } } } /* check parent node */ if (node->parent) { if (node->parent->done == 0) node->parent->level = node_get_deplist_recurs(ntree, node->parent, nsort); if (node->parent->level <= level) level = node->parent->level - 1; } if (nsort) { **nsort = node; (*nsort)++; } return level; } void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***deplist, int *totnodes) { bNode *node, **nsort; *totnodes = 0; /* first clear data */ for (node = ntree->nodes.first; node; node = node->next) { node->done = FALSE; (*totnodes)++; } if (*totnodes == 0) { *deplist = NULL; return; } nsort = *deplist = MEM_callocN((*totnodes) * sizeof(bNode *), "sorted node array"); /* recursive check */ for (node = ntree->nodes.first; node; node = node->next) { if (node->done == 0) { node->level = node_get_deplist_recurs(ntree, node, &nsort); } } } /* only updates node->level for detecting cycles links */ static void ntree_update_node_level(bNodeTree *ntree) { bNode *node; /* first clear tag */ for (node = ntree->nodes.first; node; node = node->next) { node->done = FALSE; } /* recursive check */ for (node = ntree->nodes.first; node; node = node->next) { if (node->done == 0) { node->level = node_get_deplist_recurs(ntree, node, NULL); } } } static void ntree_update_link_pointers(bNodeTree *ntree) { bNode *node; bNodeSocket *sock; bNodeLink *link; /* first clear data */ for (node = ntree->nodes.first; node; node = node->next) { for (sock = node->inputs.first; sock; sock = sock->next) { sock->link = NULL; sock->flag &= ~SOCK_IN_USE; } for (sock = node->outputs.first; sock; sock = sock->next) { sock->flag &= ~SOCK_IN_USE; } } for (sock = ntree->inputs.first; sock; sock = sock->next) { sock->flag &= ~SOCK_IN_USE; } for (sock = ntree->outputs.first; sock; sock = sock->next) { sock->link = NULL; sock->flag &= ~SOCK_IN_USE; } for (link = ntree->links.first; link; link = link->next) { link->tosock->link = link; link->fromsock->flag |= SOCK_IN_USE; link->tosock->flag |= SOCK_IN_USE; } } static void ntree_validate_links(bNodeTree *ntree) { bNodeTreeType *ntreetype = ntreeGetType(ntree->type); bNodeLink *link; for (link = ntree->links.first; link; link = link->next) { link->flag |= NODE_LINK_VALID; if (link->fromnode && link->tonode && link->fromnode->level <= link->tonode->level) link->flag &= ~NODE_LINK_VALID; else if (ntreetype->validate_link) { if (!ntreetype->validate_link(ntree, link)) link->flag &= ~NODE_LINK_VALID; } } } static void ntree_verify_nodes_cb(void *calldata, struct ID *UNUSED(owner_id), struct bNodeTree *ntree) { ID *id = (ID *)calldata; bNode *node; for (node = ntree->nodes.first; node; node = node->next) if (node->typeinfo->verifyfunc) node->typeinfo->verifyfunc(ntree, node, id); } void ntreeVerifyNodes(struct Main *main, struct ID *id) { bNodeTreeType *ntreetype; bNodeTree *ntree; int n; for (n = 0; n < NUM_NTREE_TYPES; ++n) { ntreetype = ntreeGetType(n); if (ntreetype && ntreetype->foreach_nodetree) ntreetype->foreach_nodetree(main, id, ntree_verify_nodes_cb); } for (ntree = main->nodetree.first; ntree; ntree = ntree->id.next) ntree_verify_nodes_cb(id, NULL, ntree); } void ntreeUpdateTree(bNodeTree *ntree) { bNodeTreeType *ntreetype = ntreeGetType(ntree->type); bNode *node; if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { /* set the bNodeSocket->link pointers */ ntree_update_link_pointers(ntree); /* update the node level from link dependencies */ ntree_update_node_level(ntree); } /* update individual nodes */ for (node = ntree->nodes.first; node; node = node->next) { /* node tree update tags override individual node update flags */ if ((node->update & NODE_UPDATE) || (ntree->update & NTREE_UPDATE)) { if (ntreetype->update_node) ntreetype->update_node(ntree, node); else if (node->typeinfo->updatefunc) node->typeinfo->updatefunc(ntree, node); nodeUpdateInternalLinks(ntree, node); } } /* check link validity */ if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) ntree_validate_links(ntree); /* generic tree update callback */ if (ntreetype->update) ntreetype->update(ntree); else { /* Trees can be associated with a specific node type (i.e. group nodes), * in that case a tree update function may be defined by that node type. */ bNodeType *ntype = node_get_type(ntree, ntree->nodetype); if (ntype && ntype->updatetreefunc) ntype->updatetreefunc(ntree); } /* XXX hack, should be done by depsgraph!! */ ntreeVerifyNodes(G.main, &ntree->id); /* clear update flags */ for (node = ntree->nodes.first; node; node = node->next) { node->update = 0; } ntree->update = 0; } void nodeUpdate(bNodeTree *ntree, bNode *node) { bNodeTreeType *ntreetype = ntreeGetType(ntree->type); if (ntreetype->update_node) ntreetype->update_node(ntree, node); else if (node->typeinfo->updatefunc) node->typeinfo->updatefunc(ntree, node); nodeUpdateInternalLinks(ntree, node); /* clear update flag */ node->update = 0; } int nodeUpdateID(bNodeTree *ntree, ID *id) { bNodeTreeType *ntreetype; bNode *node; int change = FALSE; if (ELEM(NULL, id, ntree)) return change; ntreetype = ntreeGetType(ntree->type); if (ntreetype->update_node) { for (node = ntree->nodes.first; node; node = node->next) { if (node->id == id) { change = TRUE; node->update |= NODE_UPDATE_ID; ntreetype->update_node(ntree, node); /* clear update flag */ node->update = 0; } } } else { for (node = ntree->nodes.first; node; node = node->next) { if (node->id == id) { change = TRUE; node->update |= NODE_UPDATE_ID; if (node->typeinfo->updatefunc) node->typeinfo->updatefunc(ntree, node); /* clear update flag */ node->update = 0; } } } for (node = ntree->nodes.first; node; node = node->next) { nodeUpdateInternalLinks(ntree, node); } return change; } void nodeUpdateInternalLinks(bNodeTree *ntree, bNode *node) { BLI_freelistN(&node->internal_links); if (node->typeinfo && node->typeinfo->update_internal_links) node->typeinfo->update_internal_links(ntree, node); } /* ************* node type access ********** */ int nodeValid(bNodeTree *ntree, bNodeTemplate *ntemp) { bNodeType *ntype = node_get_type(ntree, ntemp->type); if (ntype) { if (ntype->validfunc) return ntype->validfunc(ntree, ntemp); else return 1; } else return 0; } const char *nodeLabel(bNode *node) { if (node->label[0] != '\0') return node->label; else if (node->typeinfo->labelfunc) return node->typeinfo->labelfunc(node); else return IFACE_(node->typeinfo->name); } struct bNodeTree *nodeGroupEditGet(struct bNode *node) { if (node->typeinfo->group_edit_get) return node->typeinfo->group_edit_get(node); else return NULL; } struct bNodeTree *nodeGroupEditSet(struct bNode *node, int edit) { if (node->typeinfo->group_edit_set) return node->typeinfo->group_edit_set(node, edit); else if (node->typeinfo->group_edit_get) return node->typeinfo->group_edit_get(node); else return NULL; } void nodeGroupEditClear(struct bNode *node) { if (node->typeinfo->group_edit_clear) node->typeinfo->group_edit_clear(node); } struct bNodeTemplate nodeMakeTemplate(struct bNode *node) { bNodeTemplate ntemp; if (node->typeinfo->templatefunc) return node->typeinfo->templatefunc(node); else { ntemp.type = node->type; return ntemp; } } void node_type_base(bNodeTreeType *ttype, bNodeType *ntype, int type, const char *name, short nclass, short flag) { memset(ntype, 0, sizeof(bNodeType)); ntype->type = type; BLI_strncpy(ntype->name, name, sizeof(ntype->name)); ntype->nclass = nclass; ntype->flag = flag; /* Default muting stuff. */ if (ttype) ntype->update_internal_links = ttype->update_internal_links; /* default size values */ ntype->width = 140; ntype->minwidth = 100; ntype->maxwidth = 320; ntype->height = 100; ntype->minheight = 30; ntype->maxheight = FLT_MAX; } void node_type_socket_templates(struct bNodeType *ntype, struct bNodeSocketTemplate *inputs, struct bNodeSocketTemplate *outputs) { ntype->inputs = inputs; ntype->outputs = outputs; } void node_type_init(struct bNodeType *ntype, void (*initfunc)(struct bNodeTree *ntree, struct bNode *node, struct bNodeTemplate *ntemp)) { ntype->initfunc = initfunc; } void node_type_valid(struct bNodeType *ntype, int (*validfunc)(struct bNodeTree *ntree, struct bNodeTemplate *ntemp)) { ntype->validfunc = validfunc; } void node_type_size(struct bNodeType *ntype, int width, int minwidth, int maxwidth) { ntype->width = width; ntype->minwidth = minwidth; if (maxwidth <= minwidth) ntype->maxwidth = FLT_MAX; else ntype->maxwidth = maxwidth; } void node_type_storage(bNodeType *ntype, const char *storagename, void (*freestoragefunc)(struct bNode *), void (*copystoragefunc)(struct bNode *, struct bNode *)) { if (storagename) BLI_strncpy(ntype->storagename, storagename, sizeof(ntype->storagename)); else ntype->storagename[0] = '\0'; ntype->copystoragefunc = copystoragefunc; ntype->freestoragefunc = freestoragefunc; } void node_type_label(struct bNodeType *ntype, const char *(*labelfunc)(struct bNode *)) { ntype->labelfunc = labelfunc; } void node_type_template(struct bNodeType *ntype, struct bNodeTemplate (*templatefunc)(struct bNode *)) { ntype->templatefunc = templatefunc; } void node_type_update(struct bNodeType *ntype, void (*updatefunc)(struct bNodeTree *ntree, struct bNode *node), void (*verifyfunc)(struct bNodeTree *ntree, struct bNode *node, struct ID *id)) { ntype->updatefunc = updatefunc; ntype->verifyfunc = verifyfunc; } void node_type_tree(struct bNodeType *ntype, void (*inittreefunc)(struct bNodeTree *), void (*updatetreefunc)(struct bNodeTree *)) { ntype->inittreefunc = inittreefunc; ntype->updatetreefunc = updatetreefunc; } void node_type_group_edit(struct bNodeType *ntype, struct bNodeTree *(*group_edit_get)(struct bNode *node), struct bNodeTree *(*group_edit_set)(struct bNode *node, int edit), void (*group_edit_clear)(struct bNode *node)) { ntype->group_edit_get = group_edit_get; ntype->group_edit_set = group_edit_set; ntype->group_edit_clear = group_edit_clear; } void node_type_exec(struct bNodeType *ntype, void (*execfunc)(void *data, struct bNode *, struct bNodeStack **, struct bNodeStack **)) { ntype->execfunc = execfunc; } void node_type_exec_new(struct bNodeType *ntype, void *(*initexecfunc)(struct bNode *node), void (*freeexecfunc)(struct bNode *node, void *nodedata), void (*newexecfunc)(void *data, int thread, struct bNode *, void *nodedata, struct bNodeStack **, struct bNodeStack **)) { ntype->initexecfunc = initexecfunc; ntype->freeexecfunc = freeexecfunc; ntype->newexecfunc = newexecfunc; } void node_type_internal_links(bNodeType *ntype, void (*update_internal_links)(bNodeTree *, bNode *)) { ntype->update_internal_links = update_internal_links; } void node_type_gpu(struct bNodeType *ntype, int (*gpufunc)(struct GPUMaterial *mat, struct bNode *node, struct GPUNodeStack *in, struct GPUNodeStack *out)) { ntype->gpufunc = gpufunc; } void node_type_gpu_ext(struct bNodeType *ntype, int (*gpuextfunc)(struct GPUMaterial *mat, struct bNode *node, void *nodedata, struct GPUNodeStack *in, struct GPUNodeStack *out)) { ntype->gpuextfunc = gpuextfunc; } void node_type_compatibility(struct bNodeType *ntype, short compatibility) { ntype->compatibility = compatibility; } static bNodeType *is_nodetype_registered(ListBase *typelist, int type) { bNodeType *ntype = typelist->first; for (; ntype; ntype = ntype->next) if (ntype->type == type) return ntype; return NULL; } void nodeRegisterType(bNodeTreeType *ttype, bNodeType *ntype) { ListBase *typelist = &(ttype->node_types); bNodeType *found = is_nodetype_registered(typelist, ntype->type); if (found == NULL) BLI_addtail(typelist, ntype); } static void registerCompositNodes(bNodeTreeType *ttype) { register_node_type_frame(ttype); register_node_type_reroute(ttype); register_node_type_cmp_group(ttype); // register_node_type_cmp_forloop(ttype); // register_node_type_cmp_whileloop(ttype); register_node_type_cmp_rlayers(ttype); register_node_type_cmp_image(ttype); register_node_type_cmp_texture(ttype); register_node_type_cmp_value(ttype); register_node_type_cmp_rgb(ttype); register_node_type_cmp_curve_time(ttype); register_node_type_cmp_movieclip(ttype); register_node_type_cmp_composite(ttype); register_node_type_cmp_viewer(ttype); register_node_type_cmp_splitviewer(ttype); register_node_type_cmp_output_file(ttype); register_node_type_cmp_view_levels(ttype); register_node_type_cmp_curve_rgb(ttype); register_node_type_cmp_mix_rgb(ttype); register_node_type_cmp_hue_sat(ttype); register_node_type_cmp_brightcontrast(ttype); register_node_type_cmp_gamma(ttype); register_node_type_cmp_invert(ttype); register_node_type_cmp_alphaover(ttype); register_node_type_cmp_zcombine(ttype); register_node_type_cmp_colorbalance(ttype); register_node_type_cmp_huecorrect(ttype); register_node_type_cmp_normal(ttype); register_node_type_cmp_curve_vec(ttype); register_node_type_cmp_map_value(ttype); register_node_type_cmp_normalize(ttype); register_node_type_cmp_filter(ttype); register_node_type_cmp_blur(ttype); register_node_type_cmp_dblur(ttype); register_node_type_cmp_bilateralblur(ttype); register_node_type_cmp_vecblur(ttype); register_node_type_cmp_dilateerode(ttype); register_node_type_cmp_inpaint(ttype); register_node_type_cmp_despeckle(ttype); register_node_type_cmp_defocus(ttype); register_node_type_cmp_valtorgb(ttype); register_node_type_cmp_rgbtobw(ttype); register_node_type_cmp_setalpha(ttype); register_node_type_cmp_idmask(ttype); register_node_type_cmp_math(ttype); register_node_type_cmp_seprgba(ttype); register_node_type_cmp_combrgba(ttype); register_node_type_cmp_sephsva(ttype); register_node_type_cmp_combhsva(ttype); register_node_type_cmp_sepyuva(ttype); register_node_type_cmp_combyuva(ttype); register_node_type_cmp_sepycca(ttype); register_node_type_cmp_combycca(ttype); register_node_type_cmp_premulkey(ttype); register_node_type_cmp_diff_matte(ttype); register_node_type_cmp_distance_matte(ttype); register_node_type_cmp_chroma_matte(ttype); register_node_type_cmp_color_matte(ttype); register_node_type_cmp_channel_matte(ttype); register_node_type_cmp_color_spill(ttype); register_node_type_cmp_luma_matte(ttype); register_node_type_cmp_doubleedgemask(ttype); register_node_type_cmp_keyingscreen(ttype); register_node_type_cmp_keying(ttype); register_node_type_cmp_translate(ttype); register_node_type_cmp_rotate(ttype); register_node_type_cmp_scale(ttype); register_node_type_cmp_flip(ttype); register_node_type_cmp_crop(ttype); register_node_type_cmp_displace(ttype); register_node_type_cmp_mapuv(ttype); register_node_type_cmp_glare(ttype); register_node_type_cmp_tonemap(ttype); register_node_type_cmp_lensdist(ttype); register_node_type_cmp_transform(ttype); register_node_type_cmp_stabilize2d(ttype); register_node_type_cmp_moviedistortion(ttype); register_node_type_cmp_colorcorrection(ttype); register_node_type_cmp_boxmask(ttype); register_node_type_cmp_ellipsemask(ttype); register_node_type_cmp_bokehimage(ttype); register_node_type_cmp_bokehblur(ttype); register_node_type_cmp_switch(ttype); register_node_type_cmp_pixelate(ttype); register_node_type_cmp_mask(ttype); register_node_type_cmp_trackpos(ttype); } static void registerShaderNodes(bNodeTreeType *ttype) { register_node_type_frame(ttype); register_node_type_reroute(ttype); register_node_type_sh_group(ttype); //register_node_type_sh_forloop(ttype); //register_node_type_sh_whileloop(ttype); register_node_type_sh_output(ttype); register_node_type_sh_material(ttype); register_node_type_sh_camera(ttype); register_node_type_sh_gamma(ttype); register_node_type_sh_brightcontrast(ttype); register_node_type_sh_value(ttype); register_node_type_sh_rgb(ttype); register_node_type_sh_mix_rgb(ttype); register_node_type_sh_valtorgb(ttype); register_node_type_sh_rgbtobw(ttype); register_node_type_sh_texture(ttype); register_node_type_sh_normal(ttype); register_node_type_sh_geom(ttype); register_node_type_sh_mapping(ttype); register_node_type_sh_curve_vec(ttype); register_node_type_sh_curve_rgb(ttype); register_node_type_sh_math(ttype); register_node_type_sh_vect_math(ttype); register_node_type_sh_squeeze(ttype); register_node_type_sh_material_ext(ttype); register_node_type_sh_invert(ttype); register_node_type_sh_seprgb(ttype); register_node_type_sh_combrgb(ttype); register_node_type_sh_hue_sat(ttype); register_node_type_sh_attribute(ttype); register_node_type_sh_geometry(ttype); register_node_type_sh_light_path(ttype); register_node_type_sh_light_falloff(ttype); register_node_type_sh_object_info(ttype); register_node_type_sh_fresnel(ttype); register_node_type_sh_layer_weight(ttype); register_node_type_sh_tex_coord(ttype); register_node_type_sh_particle_info(ttype); register_node_type_sh_bump(ttype); register_node_type_sh_background(ttype); register_node_type_sh_bsdf_anisotropic(ttype); register_node_type_sh_bsdf_diffuse(ttype); register_node_type_sh_bsdf_glossy(ttype); register_node_type_sh_bsdf_glass(ttype); register_node_type_sh_bsdf_translucent(ttype); register_node_type_sh_bsdf_transparent(ttype); register_node_type_sh_bsdf_velvet(ttype); register_node_type_sh_emission(ttype); register_node_type_sh_holdout(ttype); //register_node_type_sh_volume_transparent(ttype); //register_node_type_sh_volume_isotropic(ttype); register_node_type_sh_mix_shader(ttype); register_node_type_sh_add_shader(ttype); register_node_type_sh_output_lamp(ttype); register_node_type_sh_output_material(ttype); register_node_type_sh_output_world(ttype); register_node_type_sh_tex_image(ttype); register_node_type_sh_tex_environment(ttype); register_node_type_sh_tex_sky(ttype); register_node_type_sh_tex_noise(ttype); register_node_type_sh_tex_wave(ttype); register_node_type_sh_tex_voronoi(ttype); register_node_type_sh_tex_musgrave(ttype); register_node_type_sh_tex_gradient(ttype); register_node_type_sh_tex_magic(ttype); register_node_type_sh_tex_checker(ttype); register_node_type_sh_tex_brick(ttype); } static void registerTextureNodes(bNodeTreeType *ttype) { register_node_type_frame(ttype); register_node_type_reroute(ttype); register_node_type_tex_group(ttype); // register_node_type_tex_forloop(ttype); // register_node_type_tex_whileloop(ttype); register_node_type_tex_math(ttype); register_node_type_tex_mix_rgb(ttype); register_node_type_tex_valtorgb(ttype); register_node_type_tex_rgbtobw(ttype); register_node_type_tex_valtonor(ttype); register_node_type_tex_curve_rgb(ttype); register_node_type_tex_curve_time(ttype); register_node_type_tex_invert(ttype); register_node_type_tex_hue_sat(ttype); register_node_type_tex_coord(ttype); register_node_type_tex_distance(ttype); register_node_type_tex_compose(ttype); register_node_type_tex_decompose(ttype); register_node_type_tex_output(ttype); register_node_type_tex_viewer(ttype); register_node_type_tex_checker(ttype); register_node_type_tex_texture(ttype); register_node_type_tex_bricks(ttype); register_node_type_tex_image(ttype); register_node_type_tex_rotate(ttype); register_node_type_tex_translate(ttype); register_node_type_tex_scale(ttype); register_node_type_tex_at(ttype); register_node_type_tex_proc_voronoi(ttype); register_node_type_tex_proc_blend(ttype); register_node_type_tex_proc_magic(ttype); register_node_type_tex_proc_marble(ttype); register_node_type_tex_proc_clouds(ttype); register_node_type_tex_proc_wood(ttype); register_node_type_tex_proc_musgrave(ttype); register_node_type_tex_proc_noise(ttype); register_node_type_tex_proc_stucci(ttype); register_node_type_tex_proc_distnoise(ttype); } static void free_typeinfos(ListBase *list) { bNodeType *ntype, *next; for (ntype = list->first; ntype; ntype = next) { next = ntype->next; if (ntype->needs_free) MEM_freeN(ntype); } } void init_nodesystem(void) { /* init clipboard */ node_clipboard.nodes.first = node_clipboard.nodes.last = NULL; node_clipboard.links.first = node_clipboard.links.last = NULL; registerCompositNodes(ntreeGetType(NTREE_COMPOSIT)); registerShaderNodes(ntreeGetType(NTREE_SHADER)); registerTextureNodes(ntreeGetType(NTREE_TEXTURE)); } void free_nodesystem(void) { free_typeinfos(&ntreeGetType(NTREE_COMPOSIT)->node_types); free_typeinfos(&ntreeGetType(NTREE_SHADER)->node_types); free_typeinfos(&ntreeGetType(NTREE_TEXTURE)->node_types); } /* called from BKE_scene_unlink, when deleting a scene goes over all scenes * other than the input, checks if they have render layer nodes referencing * the to-be-deleted scene, and resets them to NULL. */ /* XXX needs to get current scene then! */ void clear_scene_in_nodes(Main *bmain, Scene *sce) { Scene *sce1; bNode *node; for (sce1 = bmain->scene.first; sce1; sce1 = sce1->id.next) { if (sce1 != sce) { if (sce1->nodetree) { for (node = sce1->nodetree->nodes.first; node; node = node->next) { if (node->type == CMP_NODE_R_LAYERS) { Scene *nodesce = (Scene *)node->id; if (nodesce == sce) node->id = NULL; } } } } } }