Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/nodes/composite/node_composite_tree.c')
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c811
1 files changed, 811 insertions, 0 deletions
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
new file mode 100644
index 00000000000..1a2fb04be8c
--- /dev/null
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -0,0 +1,811 @@
+/**
+ * ***** 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) 2007 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s):
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/node_composite_tree.c
+ * \ingroup nodes
+ */
+
+
+#include <stdio.h>
+
+#include "DNA_anim_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_node_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_threads.h"
+
+#include "BKE_animsys.h"
+#include "BKE_colortools.h"
+#include "BKE_fcurve.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_utildefines.h"
+
+#include "node_exec.h"
+#include "node_util.h"
+
+#include "PIL_time.h"
+
+#include "RNA_access.h"
+
+#include "NOD_composite.h"
+#include "node_composite_util.h"
+
+static void foreach_nodetree(Main *main, void *calldata, bNodeTreeCallback func)
+{
+ Scene *sce;
+ for(sce= main->scene.first; sce; sce= sce->id.next) {
+ if(sce->nodetree) {
+ func(calldata, &sce->id, sce->nodetree);
+ }
+ }
+}
+
+static void free_node_cache(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock;
+
+ for(sock= node->outputs.first; sock; sock= sock->next) {
+ if(sock->cache) {
+ free_compbuf(sock->cache);
+ sock->cache= NULL;
+ }
+ }
+}
+
+static void free_cache(bNodeTree *ntree)
+{
+ bNode *node;
+ for(node= ntree->nodes.first; node; node= node->next)
+ free_node_cache(ntree, node);
+}
+
+static void update_node(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *sock;
+
+ for(sock= node->outputs.first; sock; sock= sock->next) {
+ if(sock->cache) {
+ //free_compbuf(sock->cache);
+ //sock->cache= NULL;
+ }
+ }
+ node->need_exec= 1;
+
+ /* individual node update call */
+ if (node->typeinfo->updatefunc)
+ node->typeinfo->updatefunc(ntree, node);
+}
+
+/* local tree then owns all compbufs */
+static void localize(bNodeTree *UNUSED(localtree), bNodeTree *ntree)
+{
+ bNode *node;
+ bNodeSocket *sock;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+ /* ensure new user input gets handled ok */
+ node->need_exec= 0;
+
+ /* move over the compbufs */
+ /* right after ntreeCopyTree() oldsock pointers are valid */
+
+ if(ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
+ if(node->id) {
+ if(node->flag & NODE_DO_OUTPUT)
+ node->new_node->id= (ID *)copy_image((Image *)node->id);
+ else
+ node->new_node->id= NULL;
+ }
+ }
+
+ for(sock= node->outputs.first; sock; sock= sock->next) {
+ sock->new_sock->cache= sock->cache;
+ compbuf_set_node(sock->new_sock->cache, node->new_node);
+
+ sock->cache= NULL;
+ sock->new_sock->new_sock= sock;
+ }
+ }
+}
+
+static void local_sync(bNodeTree *localtree, bNodeTree *ntree)
+{
+ bNode *lnode;
+
+ /* move over the compbufs and previews */
+ for(lnode= localtree->nodes.first; lnode; lnode= lnode->next) {
+ if( (lnode->exec & NODE_READY) && !(lnode->exec & NODE_SKIPPED) ) {
+ 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;
+ }
+ }
+ }
+ }
+}
+
+static void local_merge(bNodeTree *localtree, bNodeTree *ntree)
+{
+ bNode *lnode;
+ bNodeSocket *lsock;
+
+ /* move over the compbufs and previews */
+ for(lnode= localtree->nodes.first; lnode; lnode= lnode->next) {
+ if(ntreeNodeExists(ntree, lnode->new_node)) {
+ if(ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
+ if(lnode->id && (lnode->flag & NODE_DO_OUTPUT)) {
+ /* image_merge does sanity check for pointers */
+ BKE_image_merge((Image *)lnode->new_node->id, (Image *)lnode->id);
+ }
+ }
+
+ for(lsock= lnode->outputs.first; lsock; lsock= lsock->next) {
+ if(ntreeOutputExists(lnode->new_node, lsock->new_sock)) {
+ lsock->new_sock->cache= lsock->cache;
+ compbuf_set_node(lsock->new_sock->cache, lnode->new_node);
+ lsock->cache= NULL;
+ lsock->new_sock= NULL;
+ }
+ }
+ }
+ }
+}
+
+bNodeTreeType ntreeType_Composite = {
+ /* type */ NTREE_COMPOSIT,
+ /* idname */ "NTCompositing Nodetree",
+
+ /* node_types */ { NULL, NULL },
+
+ /* free_cache */ free_cache,
+ /* free_node_cache */ free_node_cache,
+ /* foreach_nodetree */ foreach_nodetree,
+ /* localize */ localize,
+ /* local_sync */ local_sync,
+ /* local_merge */ local_merge,
+ /* update */ NULL,
+ /* update_node */ update_node
+};
+
+
+struct bNodeTreeExec *ntreeCompositBeginExecTree(bNodeTree *ntree)
+{
+ bNodeTreeExec *exec;
+ bNode *node;
+ bNodeSocket *sock;
+
+ /* XXX hack: prevent exec data from being generated twice.
+ * this should be handled by the renderer!
+ */
+ if (ntree->execdata)
+ return ntree->execdata;
+
+ /* ensures only a single output node is enabled */
+ ntreeSetOutput(ntree);
+
+ exec = ntree_exec_begin(ntree);
+
+ for(node= exec->nodetree->nodes.first; node; node= node->next) {
+ /* initialize needed for groups */
+ node->exec= 0;
+
+ for(sock= node->outputs.first; sock; sock= sock->next) {
+ bNodeStack *ns= node_get_socket_stack(exec->stack, sock);
+ if(ns && sock->cache) {
+ ns->data= sock->cache;
+ sock->cache= NULL;
+ }
+ }
+ /* cannot initialize them while using in threads */
+ if(ELEM4(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT)) {
+ curvemapping_initialize(node->storage);
+ if(node->type==CMP_NODE_CURVE_RGB)
+ curvemapping_premultiply(node->storage, 0);
+ }
+ }
+
+ /* XXX this should not be necessary, but is still used for cmp/sha/tex nodes,
+ * which only store the ntree pointer. Should be fixed at some point!
+ */
+ ntree->execdata = exec;
+
+ return exec;
+}
+
+void ntreeCompositEndExecTree(bNodeTreeExec *exec)
+{
+ if(exec) {
+ bNodeTree *ntree= exec->nodetree;
+ bNode *node;
+ bNodeStack *ns;
+
+ for(node= exec->nodetree->nodes.first; node; node= node->next) {
+ bNodeSocket *sock;
+
+ for(sock= node->outputs.first; sock; sock= sock->next) {
+ ns = node_get_socket_stack(exec->stack, sock);
+ if(ns && ns->data) {
+ sock->cache= ns->data;
+ ns->data= NULL;
+ }
+ }
+ if(node->type==CMP_NODE_CURVE_RGB)
+ curvemapping_premultiply(node->storage, 1);
+
+ node->need_exec= 0;
+ }
+
+ ntree_exec_end(exec);
+
+ /* XXX clear nodetree backpointer to exec data, same problem as noted in ntreeBeginExecTree */
+ ntree->execdata = NULL;
+ }
+}
+
+/* ***************************** threaded version for execute composite nodes ************* */
+/* these are nodes without input, only giving values */
+/* or nodes with only value inputs */
+static int node_only_value(bNode *node)
+{
+ bNodeSocket *sock;
+
+ if(ELEM3(node->type, CMP_NODE_TIME, CMP_NODE_VALUE, CMP_NODE_RGB))
+ return 1;
+
+ /* doing this for all node types goes wrong. memory free errors */
+ if(node->inputs.first && node->type==CMP_NODE_MAP_VALUE) {
+ int retval= 1;
+ for(sock= node->inputs.first; sock; sock= sock->next) {
+ if(sock->link)
+ retval &= node_only_value(sock->link->fromnode);
+ }
+ return retval;
+ }
+ return 0;
+}
+
+/* not changing info, for thread callback */
+typedef struct ThreadData {
+ bNodeStack *stack;
+ RenderData *rd;
+} ThreadData;
+
+static void *exec_composite_node(void *nodeexec_v)
+{
+ bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */
+ bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */
+ bNodeExec *nodeexec= nodeexec_v;
+ bNode *node= nodeexec->node;
+ ThreadData *thd= (ThreadData *)node->threaddata;
+
+ node_get_stack(node, thd->stack, nsin, nsout);
+
+ if((node->flag & NODE_MUTED) && (!node_only_value(node))) {
+ /* viewers we execute, for feedback to user */
+ if(ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER))
+ node->typeinfo->execfunc(thd->rd, node, nsin, nsout);
+ else
+ node_compo_pass_on(node, nsin, nsout);
+ }
+ else if(node->typeinfo->execfunc)
+ node->typeinfo->execfunc(thd->rd, node, nsin, nsout);
+ else if (node->typeinfo->newexecfunc)
+ node->typeinfo->newexecfunc(thd->rd, 0, node, nodeexec->data, nsin, nsout);
+
+ node->exec |= NODE_READY;
+ return 0;
+}
+
+/* return total of executable nodes, for timecursor */
+static int setExecutableNodes(bNodeTree *ntree, ThreadData *thd)
+{
+ bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */
+ bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */
+ bNode *node;
+ bNodeSocket *sock;
+ int totnode= 0, group_edit= 0;
+
+ /* note; do not add a dependency sort here, the stack was created already */
+
+ /* if we are in group edit, viewer nodes get skipped when group has viewer */
+ for(node= ntree->nodes.first; node; node= node->next)
+ if(node->type==NODE_GROUP && (node->flag & NODE_GROUP_EDIT))
+ if(ntreeHasType((bNodeTree *)node->id, CMP_NODE_VIEWER))
+ group_edit= 1;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+ int a;
+
+ node_get_stack(node, thd->stack, nsin, nsout);
+
+ /* test the outputs */
+ /* skip value-only nodes (should be in type!) */
+ if(!node_only_value(node)) {
+ for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) {
+ if(nsout[a]->data==NULL && nsout[a]->hasoutput) {
+ node->need_exec= 1;
+ break;
+ }
+ }
+ }
+
+ /* test the inputs */
+ for(a=0, sock= node->inputs.first; sock; sock= sock->next, a++) {
+ /* skip viewer nodes in bg render or group edit */
+ if( ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER) && (G.background || group_edit))
+ node->need_exec= 0;
+ /* is sock in use? */
+ else if(sock->link) {
+ bNodeLink *link= sock->link;
+
+ /* this is the test for a cyclic case */
+ if(link->fromnode==NULL || link->tonode==NULL);
+ else if(link->fromnode->level >= link->tonode->level && link->tonode->level!=0xFFF) {
+ if(link->fromnode->need_exec) {
+ node->need_exec= 1;
+ break;
+ }
+ }
+ else {
+ node->need_exec= 0;
+ printf("Node %s skipped, cyclic dependency\n", node->name);
+ }
+ }
+ }
+
+ if(node->need_exec) {
+
+ /* free output buffers */
+ for(a=0, sock= node->outputs.first; sock; sock= sock->next, a++) {
+ if(nsout[a]->data) {
+ free_compbuf(nsout[a]->data);
+ nsout[a]->data= NULL;
+ }
+ }
+ totnode++;
+ /* printf("node needs exec %s\n", node->name); */
+
+ /* tag for getExecutableNode() */
+ node->exec= 0;
+ }
+ else {
+ /* tag for getExecutableNode() */
+ node->exec= NODE_READY|NODE_FINISHED|NODE_SKIPPED;
+
+ }
+ }
+
+ /* last step: set the stack values for only-value nodes */
+ /* just does all now, compared to a full buffer exec this is nothing */
+ if(totnode) {
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if(node->need_exec==0 && node_only_value(node)) {
+ if(node->typeinfo->execfunc) {
+ node_get_stack(node, thd->stack, nsin, nsout);
+ node->typeinfo->execfunc(thd->rd, node, nsin, nsout);
+ }
+ }
+ }
+ }
+
+ return totnode;
+}
+
+/* while executing tree, free buffers from nodes that are not needed anymore */
+static void freeExecutableNode(bNodeTree *ntree, bNodeTreeExec *exec)
+{
+ /* node outputs can be freed when:
+ - not a render result or image node
+ - when node outputs go to nodes all being set NODE_FINISHED
+ */
+ bNode *node;
+ bNodeSocket *sock;
+
+ /* set exec flag for finished nodes that might need freed */
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if(node->type!=CMP_NODE_R_LAYERS)
+ if(node->exec & NODE_FINISHED)
+ node->exec |= NODE_FREEBUFS;
+ }
+ /* clear this flag for input links that are not done yet */
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if((node->exec & NODE_FINISHED)==0) {
+ for(sock= node->inputs.first; sock; sock= sock->next)
+ if(sock->link)
+ sock->link->fromnode->exec &= ~NODE_FREEBUFS;
+ }
+ }
+ /* now we can free buffers */
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if(node->exec & NODE_FREEBUFS) {
+ for(sock= node->outputs.first; sock; sock= sock->next) {
+ bNodeStack *ns= node_get_socket_stack(exec->stack, sock);
+ if(ns && ns->data) {
+ free_compbuf(ns->data);
+ ns->data= NULL;
+ // printf("freed buf node %s \n", node->name);
+ }
+ }
+ }
+ }
+}
+
+static bNodeExec *getExecutableNode(bNodeTreeExec *exec)
+{
+ bNodeExec *nodeexec;
+ bNodeSocket *sock;
+ int n;
+
+ for(n=0, nodeexec=exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) {
+ if(nodeexec->node->exec==0) {
+ /* input sockets should be ready */
+ for(sock= nodeexec->node->inputs.first; sock; sock= sock->next) {
+ if(sock->link && sock->link->fromnode)
+ if((sock->link->fromnode->exec & NODE_READY)==0)
+ break;
+ }
+ if(sock==NULL)
+ return nodeexec;
+ }
+ }
+ return NULL;
+}
+
+/* check if texture nodes need exec or end */
+static void ntree_composite_texnode(bNodeTree *ntree, int init)
+{
+ bNode *node;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if(node->type==CMP_NODE_TEXTURE && node->id) {
+ Tex *tex= (Tex *)node->id;
+ if(tex->nodetree && tex->use_nodes) {
+ /* has internal flag to detect it only does it once */
+ if(init) {
+ if (!tex->nodetree->execdata)
+ tex->nodetree->execdata = ntreeTexBeginExecTree(tex->nodetree);
+ }
+ else
+ ntreeTexEndExecTree(tex->nodetree->execdata);
+ tex->nodetree->execdata = NULL;
+ }
+ }
+ }
+
+}
+
+/* optimized tree execute test for compositing */
+void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview)
+{
+ bNodeExec *nodeexec;
+ bNode *node;
+ ListBase threads;
+ ThreadData thdata;
+ int totnode, curnode, rendering= 1, n;
+ bNodeTreeExec *exec= NULL;
+
+ if(ntree==NULL) return;
+
+ if(do_preview)
+ ntreeInitPreview(ntree, 0, 0);
+
+ if (!ntree->execdata)
+ exec = ntreeCompositBeginExecTree(ntree);
+ ntree_composite_texnode(ntree, 1);
+
+ /* prevent unlucky accidents */
+ if(G.background)
+ rd->scemode &= ~R_COMP_CROP;
+
+ /* setup callerdata for thread callback */
+ thdata.rd= rd;
+ thdata.stack= exec->stack;
+
+ /* fixed seed, for example noise texture */
+ BLI_srandom(rd->cfra);
+
+ /* sets need_exec tags in nodes */
+ curnode = totnode= setExecutableNodes(ntree, &thdata);
+
+ BLI_init_threads(&threads, exec_composite_node, rd->threads);
+
+ while(rendering) {
+
+ if(BLI_available_threads(&threads)) {
+ nodeexec= getExecutableNode(exec);
+ if(nodeexec) {
+ node = nodeexec->node;
+ if(ntree->progress && totnode)
+ ntree->progress(ntree->prh, (1.0 - curnode/(float)totnode));
+ if(ntree->stats_draw) {
+ char str[64];
+ sprintf(str, "Compositing %d %s", curnode, node->name);
+ ntree->stats_draw(ntree->sdh, str);
+ }
+ curnode--;
+
+ node->threaddata = &thdata;
+ node->exec= NODE_PROCESSING;
+ BLI_insert_thread(&threads, nodeexec);
+ }
+ else
+ PIL_sleep_ms(50);
+ }
+ else
+ PIL_sleep_ms(50);
+
+ rendering= 0;
+ /* test for ESC */
+ if(ntree->test_break && ntree->test_break(ntree->tbh)) {
+ for(node= ntree->nodes.first; node; node= node->next)
+ node->exec |= NODE_READY;
+ }
+
+ /* check for ready ones, and if we need to continue */
+ for(n=0, nodeexec=exec->nodeexec; n < exec->totnodes; ++n, ++nodeexec) {
+ node = nodeexec->node;
+ if(node->exec & NODE_READY) {
+ if((node->exec & NODE_FINISHED)==0) {
+ BLI_remove_thread(&threads, nodeexec); /* this waits for running thread to finish btw */
+ node->exec |= NODE_FINISHED;
+
+ /* freeing unused buffers */
+ if(rd->scemode & R_COMP_FREE)
+ freeExecutableNode(ntree, exec);
+ }
+ }
+ else rendering= 1;
+ }
+ }
+
+ BLI_end_threads(&threads);
+
+ ntreeCompositEndExecTree(exec);
+}
+
+/* *********************************************** */
+
+/* clumsy checking... should do dynamic outputs once */
+static void force_hidden_passes(bNode *node, int passflag)
+{
+ bNodeSocket *sock;
+
+ for(sock= node->outputs.first; sock; sock= sock->next)
+ sock->flag &= ~SOCK_UNAVAIL;
+
+ sock= BLI_findlink(&node->outputs, RRES_OUT_Z);
+ if(!(passflag & SCE_PASS_Z)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_NORMAL);
+ if(!(passflag & SCE_PASS_NORMAL)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_VEC);
+ if(!(passflag & SCE_PASS_VECTOR)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_UV);
+ if(!(passflag & SCE_PASS_UV)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_RGBA);
+ if(!(passflag & SCE_PASS_RGBA)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_DIFF);
+ if(!(passflag & SCE_PASS_DIFFUSE)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_SPEC);
+ if(!(passflag & SCE_PASS_SPEC)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_SHADOW);
+ if(!(passflag & SCE_PASS_SHADOW)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_AO);
+ if(!(passflag & SCE_PASS_AO)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_REFLECT);
+ if(!(passflag & SCE_PASS_REFLECT)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_REFRACT);
+ if(!(passflag & SCE_PASS_REFRACT)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_INDIRECT);
+ if(!(passflag & SCE_PASS_INDIRECT)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_INDEXOB);
+ if(!(passflag & SCE_PASS_INDEXOB)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_INDEXMA);
+ if(!(passflag & SCE_PASS_INDEXMA)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_MIST);
+ if(!(passflag & SCE_PASS_MIST)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_EMIT);
+ if(!(passflag & SCE_PASS_EMIT)) sock->flag |= SOCK_UNAVAIL;
+ sock= BLI_findlink(&node->outputs, RRES_OUT_ENV);
+ if(!(passflag & SCE_PASS_ENVIRONMENT)) sock->flag |= SOCK_UNAVAIL;
+
+}
+
+/* based on rules, force sockets hidden always */
+void ntreeCompositForceHidden(bNodeTree *ntree, Scene *curscene)
+{
+ bNode *node;
+
+ if(ntree==NULL) return;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if( node->type==CMP_NODE_R_LAYERS) {
+ Scene *sce= node->id?(Scene *)node->id:curscene;
+ SceneRenderLayer *srl= BLI_findlink(&sce->r.layers, node->custom1);
+ if(srl)
+ force_hidden_passes(node, srl->passflag);
+ }
+ else if( node->type==CMP_NODE_IMAGE) {
+ Image *ima= (Image *)node->id;
+ if(ima) {
+ if(ima->rr) {
+ ImageUser *iuser= node->storage;
+ RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer);
+ if(rl)
+ force_hidden_passes(node, rl->passflag);
+ else
+ force_hidden_passes(node, 0);
+ }
+ else if(ima->type!=IMA_TYPE_MULTILAYER) { /* if ->rr not yet read we keep inputs */
+ force_hidden_passes(node, RRES_OUT_Z);
+ }
+ else
+ force_hidden_passes(node, 0);
+ }
+ else
+ force_hidden_passes(node, 0);
+ }
+ }
+
+}
+
+/* called from render pipeline, to tag render input and output */
+/* need to do all scenes, to prevent errors when you re-render 1 scene */
+void ntreeCompositTagRender(Scene *curscene)
+{
+ Scene *sce;
+
+ for(sce= G.main->scene.first; sce; sce= sce->id.next) {
+ if(sce->nodetree) {
+ bNode *node;
+
+ for(node= sce->nodetree->nodes.first; node; node= node->next) {
+ if(node->id==(ID *)curscene || node->type==CMP_NODE_COMPOSITE)
+ NodeTagChanged(sce->nodetree, node);
+ else if(node->type==CMP_NODE_TEXTURE) /* uses scene sizex/sizey */
+ NodeTagChanged(sce->nodetree, node);
+ }
+ }
+ }
+}
+
+static int node_animation_properties(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *sock;
+ const ListBase *lb;
+ Link *link;
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ /* check to see if any of the node's properties have fcurves */
+ RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr);
+ lb = RNA_struct_type_properties(ptr.type);
+
+ for (link=lb->first; link; link=link->next) {
+ int driven, len=1, index;
+ prop = (PropertyRNA *)link;
+
+ if (RNA_property_array_check(prop))
+ len = RNA_property_array_length(&ptr, prop);
+
+ for (index=0; index<len; index++) {
+ if (rna_get_fcurve(&ptr, prop, index, NULL, &driven)) {
+ NodeTagChanged(ntree, node);
+ return 1;
+ }
+ }
+ }
+
+ /* now check node sockets */
+ for (sock = node->inputs.first; sock; sock=sock->next) {
+ int driven, len=1, index;
+
+ RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
+ prop = RNA_struct_find_property(&ptr, "default_value");
+ if (prop) {
+ if (RNA_property_array_check(prop))
+ len = RNA_property_array_length(&ptr, prop);
+
+ for (index=0; index<len; index++) {
+ if (rna_get_fcurve(&ptr, prop, index, NULL, &driven)) {
+ NodeTagChanged(ntree, node);
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* tags nodes that have animation capabilities */
+int ntreeCompositTagAnimated(bNodeTree *ntree)
+{
+ bNode *node;
+ int tagged= 0;
+
+ if(ntree==NULL) return 0;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+
+ tagged = node_animation_properties(ntree, node);
+
+ /* otherwise always tag these node types */
+ if(node->type==CMP_NODE_IMAGE) {
+ Image *ima= (Image *)node->id;
+ if(ima && ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) {
+ NodeTagChanged(ntree, node);
+ tagged= 1;
+ }
+ }
+ else if(node->type==CMP_NODE_TIME) {
+ NodeTagChanged(ntree, node);
+ tagged= 1;
+ }
+ /* here was tag render layer, but this is called after a render, so re-composites fail */
+ else if(node->type==NODE_GROUP) {
+ if( ntreeCompositTagAnimated((bNodeTree *)node->id) ) {
+ NodeTagChanged(ntree, node);
+ }
+ }
+ }
+
+ return tagged;
+}
+
+
+/* called from image window preview */
+void ntreeCompositTagGenerators(bNodeTree *ntree)
+{
+ bNode *node;
+
+ if(ntree==NULL) return;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+ if( ELEM(node->type, CMP_NODE_R_LAYERS, CMP_NODE_IMAGE))
+ NodeTagChanged(ntree, node);
+ }
+}
+
+/* XXX after render animation system gets a refresh, this call allows composite to end clean */
+void ntreeClearTags(bNodeTree *ntree)
+{
+ bNode *node;
+
+ if(ntree==NULL) return;
+
+ for(node= ntree->nodes.first; node; node= node->next) {
+ node->need_exec= 0;
+ if(node->type==NODE_GROUP)
+ ntreeClearTags((bNodeTree *)node->id);
+ }
+}