This would previously break because begin/end functions for each tree type still have some checks of the ntree->execdata pointer in them, despite the intended use of execdata instances instead of trees themselves for execution data storage. This is an artifact of the old execution system that required these checks to be made in the functions to avoid multiple execution of top-level trees. Now these functions take an additional argument, so group nodes can prevent them from setting and checking the nodetree->execdata pointers.
829 lines
23 KiB
C
829 lines
23 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** 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
|
|
};
|
|
|
|
|
|
/* XXX Group nodes must set use_tree_data to false, since their trees can be shared by multiple nodes.
|
|
* If use_tree_data is true, the ntree->execdata pointer is checked to avoid multiple execution of top-level trees.
|
|
*/
|
|
struct bNodeTreeExec *ntreeCompositBeginExecTree(bNodeTree *ntree, int use_tree_data)
|
|
{
|
|
bNodeTreeExec *exec;
|
|
bNode *node;
|
|
bNodeSocket *sock;
|
|
|
|
if (use_tree_data) {
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
if (use_tree_data) {
|
|
/* 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;
|
|
}
|
|
|
|
/* XXX Group nodes must set use_tree_data to false, since their trees can be shared by multiple nodes.
|
|
* If use_tree_data is true, the ntree->execdata pointer is checked to avoid multiple execution of top-level trees.
|
|
*/
|
|
void ntreeCompositEndExecTree(bNodeTreeExec *exec, int use_tree_data)
|
|
{
|
|
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);
|
|
|
|
if (use_tree_data) {
|
|
/* 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, 1);
|
|
}
|
|
else
|
|
ntreeTexEndExecTree(tex->nodetree->execdata, 1);
|
|
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) {
|
|
/* XXX this is the top-level tree, so we use the ntree->execdata pointer. */
|
|
exec = ntreeCompositBeginExecTree(ntree, 1);
|
|
}
|
|
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);
|
|
|
|
/* XXX top-level tree uses the ntree->execdata pointer */
|
|
ntreeCompositEndExecTree(exec, 1);
|
|
}
|
|
|
|
/* *********************************************** */
|
|
|
|
/* 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);
|
|
}
|
|
}
|