diff options
author | Joseph Eagar <joeedh@gmail.com> | 2009-11-02 17:45:12 +0300 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2009-11-02 17:45:12 +0300 |
commit | 7225c078b716f44fe0f482d56e88fabd5f635518 (patch) | |
tree | 46c2f6c516bb57039845255c058af1e47fd5c7ec /source/blender | |
parent | e3a410d224380a82099cfd7e9f47074293df001b (diff) |
bmesh compile fixes
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_key.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/key.c | 40 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 1245 | ||||
-rw-r--r-- | source/blender/editors/mesh/bmeshutils.c | 2 | ||||
-rw-r--r-- | source/blender/render/intern/source/rayobject_blibvh.c | 169 | ||||
-rw-r--r-- | source/blender/render/intern/source/rayobject_instance.c | 200 | ||||
-rw-r--r-- | source/blender/render/intern/source/rayobject_octree.c | 1080 | ||||
-rw-r--r-- | source/blender/render/intern/source/rayobject_raycounter.c | 87 |
8 files changed, 2823 insertions, 2 deletions
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index b70801a9edd..22ce89dcb81 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -47,8 +47,10 @@ extern "C" { #endif void free_key(struct Key *sc); +void free_key_nolib(struct Key *key); struct Key *add_key(struct ID *id); struct Key *copy_key(struct Key *key); +struct Key *copy_key_nolib(struct Key *key); void make_local_key(struct Key *key); void sort_keys(struct Key *key); diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index c0e953e7eb4..81ad0ef6658 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -96,6 +96,20 @@ void free_key(Key *key) } +void free_key_nolib(Key *key) +{ + KeyBlock *kb; + + while( (kb= key->block.first) ) { + + if(kb->data) MEM_freeN(kb->data); + + BLI_remlink(&key->block, kb); + MEM_freeN(kb); + } + +} + /* GS reads the memory pointed at in a specific ordering. There are, * however two definitions for it. I have jotted them down here, both, * but I think the first one is actually used. The thing is that @@ -182,6 +196,32 @@ Key *copy_key(Key *key) return keyn; } + +Key *copy_key_nolib(Key *key) +{ + Key *keyn; + KeyBlock *kbn, *kb; + + if(key==0) return 0; + + keyn= MEM_dupallocN(key); + + BLI_duplicatelist(&keyn->block, &key->block); + + kb= key->block.first; + kbn= keyn->block.first; + while(kbn) { + + if(kbn->data) kbn->data= MEM_dupallocN(kbn->data); + if(kb==key->refkey) keyn->refkey= kbn; + + kbn= kbn->next; + kb= kb->next; + } + + return keyn; +} + void make_local_key(Key *key) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index c7898633156..fc9cc1f680b 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -1927,4 +1927,1247 @@ static void composit_begin_exec(bNodeTree *ntree, int is_group) bNode *node; bNodeSocket *sock; - for(node= ntree->
\ No newline at end of file + for(node= ntree->nodes.first; node; node= node->next) { + + /* initialize needed for groups */ + node->exec= 0; + + if(is_group==0) { + for(sock= node->outputs.first; sock; sock= sock->next) { + bNodeStack *ns= ntree->stack + sock->stack_index; + + if(sock->ns.data) { + ns->data= sock->ns.data; + sock->ns.data= NULL; + } + } + } + /* cannot initialize them while using in threads */ + if(ELEM3(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB)) { + curvemapping_initialize(node->storage); + if(node->type==CMP_NODE_CURVE_RGB) + curvemapping_premultiply(node->storage, 0); + } + if(node->type==NODE_GROUP) + composit_begin_exec((bNodeTree *)node->id, 1); + + } +} + +/* copy stack compbufs to sockets */ +static void composit_end_exec(bNodeTree *ntree, int is_group) +{ + extern void print_compbuf(char *str, struct CompBuf *cbuf); + bNode *node; + bNodeStack *ns; + int a; + + for(node= ntree->nodes.first; node; node= node->next) { + if(is_group==0) { + bNodeSocket *sock; + + for(sock= node->outputs.first; sock; sock= sock->next) { + ns= ntree->stack + sock->stack_index; + if(ns->data) { + sock->ns.data= ns->data; + ns->data= NULL; + } + } + } + if(node->type==CMP_NODE_CURVE_RGB) + curvemapping_premultiply(node->storage, 1); + + if(node->type==NODE_GROUP) + composit_end_exec((bNodeTree *)node->id, 1); + + node->need_exec= 0; + } + + if(is_group==0) { + /* internally, group buffers are not stored */ + for(ns= ntree->stack, a=0; a<ntree->stacksize; a++, ns++) { + if(ns->data) { + printf("freed leftover buffer from stack\n"); + free_compbuf(ns->data); + ns->data= NULL; + } + } + } +} + +static void group_tag_used_outputs(bNode *gnode, bNodeStack *stack) +{ + bNodeTree *ntree= (bNodeTree *)gnode->id; + bNode *node; + + stack+= gnode->stack_index; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo->execfunc) { + bNodeSocket *sock; + + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->intern) { + if(sock->link) { + bNodeStack *ns= stack + sock->link->fromsock->stack_index; + ns->hasoutput= 1; + ns->sockettype= sock->link->fromsock->type; + } + else + sock->ns.sockettype= sock->type; + } + } + } + } +} + +/* notes below are ancient! (ton) */ +/* stack indices make sure all nodes only write in allocated data, for making it thread safe */ +/* only root tree gets the stack, to enable instances to have own stack entries */ +/* per tree (and per group) unique indices are created */ +/* the index_ext we need to be able to map from groups to the group-node own stack */ + +typedef struct bNodeThreadStack { + struct bNodeThreadStack *next, *prev; + bNodeStack *stack; + int used; +} bNodeThreadStack; + +static bNodeThreadStack *ntreeGetThreadStack(bNodeTree *ntree, int thread) +{ + ListBase *lb= &ntree->threadstack[thread]; + bNodeThreadStack *nts; + + for(nts=lb->first; nts; nts=nts->next) { + if(!nts->used) { + nts->used= 1; + return nts; + } + } + nts= MEM_callocN(sizeof(bNodeThreadStack), "bNodeThreadStack"); + nts->stack= MEM_dupallocN(ntree->stack); + nts->used= 1; + BLI_addtail(lb, nts); + + return nts; +} + +static void ntreeReleaseThreadStack(bNodeThreadStack *nts) +{ + nts->used= 0; +} + +/* free texture delegates */ +static void tex_end_exec(bNodeTree *ntree) +{ + bNodeThreadStack *nts; + bNodeStack *ns; + int th, a; + + if(ntree->threadstack) + for(th=0; th<BLENDER_MAX_THREADS; th++) + for(nts=ntree->threadstack[th].first; nts; nts=nts->next) + for(ns= nts->stack, a=0; a<ntree->stacksize; a++, ns++) + if(ns->data) + MEM_freeN(ns->data); + +} + +void ntreeBeginExecTree(bNodeTree *ntree) +{ + /* let's make it sure */ + if(ntree->init & NTREE_EXEC_INIT) + return; + + /* allocate the thread stack listbase array */ + if(ntree->type!=NTREE_COMPOSIT) + ntree->threadstack= MEM_callocN(BLENDER_MAX_THREADS*sizeof(ListBase), "thread stack array"); + + /* goes recursive over all groups */ + ntree->stacksize= ntree_begin_exec_tree(ntree); + + if(ntree->stacksize) { + bNode *node; + bNodeStack *ns; + int a; + + /* allocate the base stack */ + ns=ntree->stack= MEM_callocN(ntree->stacksize*sizeof(bNodeStack), "node stack"); + + /* tag inputs, the get_stack() gives own socket stackdata if not in use */ + for(a=0; a<ntree->stacksize; a++, ns++) ns->hasinput= 1; + + /* tag used outputs, so we know when we can skip operations */ + for(node= ntree->nodes.first; node; node= node->next) { + bNodeSocket *sock; + + /* composite has own need_exec tag handling */ + if(ntree->type!=NTREE_COMPOSIT) + node->need_exec= 1; + + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->link) { + ns= ntree->stack + sock->link->fromsock->stack_index; + ns->hasoutput= 1; + ns->sockettype= sock->link->fromsock->type; + } + else + sock->ns.sockettype= sock->type; + + if(sock->link) { + bNodeLink *link= sock->link; + /* this is the test for a cyclic case */ + if(link->fromnode && link->tonode) { + if(link->fromnode->level >= link->tonode->level && link->tonode->level!=0xFFF); + else { + node->need_exec= 0; + } + } + } + } + + if(node->type==NODE_GROUP && node->id) + group_tag_used_outputs(node, ntree->stack); + + } + + if(ntree->type==NTREE_COMPOSIT) + composit_begin_exec(ntree, 0); + } + + ntree->init |= NTREE_EXEC_INIT; +} + +void ntreeEndExecTree(bNodeTree *ntree) +{ + + if(ntree->init & NTREE_EXEC_INIT) { + bNodeThreadStack *nts; + int a; + + /* another callback candidate! */ + if(ntree->type==NTREE_COMPOSIT) + composit_end_exec(ntree, 0); + else if(ntree->type==NTREE_TEXTURE) + tex_end_exec(ntree); + + if(ntree->stack) { + MEM_freeN(ntree->stack); + ntree->stack= NULL; + } + + if(ntree->threadstack) { + for(a=0; a<BLENDER_MAX_THREADS; a++) { + for(nts=ntree->threadstack[a].first; nts; nts=nts->next) + if (nts->stack) MEM_freeN(nts->stack); + BLI_freelistN(&ntree->threadstack[a]); + } + + MEM_freeN(ntree->threadstack); + ntree->threadstack= NULL; + } + + ntree->init &= ~NTREE_EXEC_INIT; + } +} + +static void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out) +{ + bNodeSocket *sock; + + /* build pointer stack */ + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->link) + *(in++)= stack + sock->link->fromsock->stack_index; + else + *(in++)= &sock->ns; + } + + for(sock= node->outputs.first; sock; sock= sock->next) { + *(out++)= stack + sock->stack_index; + } +} + +/* nodes are presorted, so exec is in order of list */ +void ntreeExecTree(bNodeTree *ntree, void *callerdata, int thread) +{ + bNode *node; + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *stack; + bNodeThreadStack *nts = NULL; + + /* only when initialized */ + if((ntree->init & NTREE_EXEC_INIT)==0) + ntreeBeginExecTree(ntree); + + /* composite does 1 node per thread, so no multiple stacks needed */ + if(ntree->type==NTREE_COMPOSIT) { + stack= ntree->stack; + } + else { + nts= ntreeGetThreadStack(ntree, thread); + stack= nts->stack; + } + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->need_exec) { + if(node->typeinfo->execfunc) { + node_get_stack(node, stack, nsin, nsout); + node->typeinfo->execfunc(callerdata, node, nsin, nsout); + } + else if(node->type==NODE_GROUP && node->id) { + node_get_stack(node, stack, nsin, nsout); + node_group_execute(stack, callerdata, node, nsin, nsout); + } + } + } + + if(nts) + ntreeReleaseThreadStack(nts); +} + + +/* ***************************** 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 *node_v) +{ + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ + bNode *node= node_v; + 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->type==NODE_GROUP && node->id) { + node_group_execute(thd->stack, thd->rd, node, nsin, nsout); + } + + node->exec |= NODE_READY; + return 0; +} + +/* return total of executable nodes, for timecursor */ +/* only compositor uses it */ +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) +{ + /* 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= ntree->stack + sock->stack_index; + if(ns->data) { + free_compbuf(ns->data); + ns->data= NULL; + // printf("freed buf node %s \n", node->name); + } + } + } + } +} + +static bNode *getExecutableNode(bNodeTree *ntree) +{ + bNode *node; + bNodeSocket *sock; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->exec==0) { + + /* input sockets should be ready */ + for(sock= node->inputs.first; sock; sock= sock->next) { + if(sock->link) + if((sock->link->fromnode->exec & NODE_READY)==0) + break; + } + if(sock==NULL) + return node; + } + } + return NULL; +} + + +/* optimized tree execute test for compositing */ +void ntreeCompositExecTree(bNodeTree *ntree, RenderData *rd, int do_preview) +{ + bNode *node; + ListBase threads; + ThreadData thdata; + int totnode, rendering= 1; + + if(ntree==NULL) return; + + if(do_preview) + ntreeInitPreview(ntree, 0, 0); + + ntreeBeginExecTree(ntree); + + /* prevent unlucky accidents */ + if(G.background) + rd->scemode &= ~R_COMP_CROP; + + /* setup callerdata for thread callback */ + thdata.rd= rd; + thdata.stack= ntree->stack; + + /* fixed seed, for example noise texture */ + BLI_srandom(rd->cfra); + + /* sets need_exec tags in nodes */ + totnode= setExecutableNodes(ntree, &thdata); + + BLI_init_threads(&threads, exec_composite_node, rd->threads); + + while(rendering) { + + if(BLI_available_threads(&threads)) { + node= getExecutableNode(ntree); + if(node) { + + if(ntree->timecursor) + ntree->timecursor(ntree->tch, totnode); + if(ntree->stats_draw) { + char str[64]; + sprintf(str, "Compositing %d %s", totnode, node->name); + ntree->stats_draw(ntree->sdh, str); + } + totnode--; + + node->threaddata = &thdata; + node->exec= NODE_PROCESSING; + BLI_insert_thread(&threads, node); + } + 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(node= ntree->nodes.first; node; node= node->next) { + if(node->exec & NODE_READY) { + if((node->exec & NODE_FINISHED)==0) { + BLI_remove_thread(&threads, node); /* this waits for running thread to finish btw */ + node->exec |= NODE_FINISHED; + + /* freeing unused buffers */ + if(rd->scemode & R_COMP_FREE) + freeExecutableNode(ntree); + } + } + else rendering= 1; + } + } + + BLI_end_threads(&threads); + + ntreeEndExecTree(ntree); +} + + +/* ********** copy composite tree entirely, to allow threaded exec ******************* */ +/* ***************** do NOT execute this in a thread! ****************** */ + +/* returns localized composite tree for execution in threads */ +/* local tree then owns all compbufs */ +bNodeTree *ntreeLocalize(bNodeTree *ntree) +{ + bNodeTree *ltree= ntreeCopyTree(ntree, 0); + bNode *node; + bNodeSocket *sock; + + /* move over the compbufs */ + /* right after ntreeCopyTree() oldsock pointers are valid */ + for(node= ntree->nodes.first; node; node= node->next) { + + /* store new_node pointer to original */ + node->new_node->new_node= node; + /* ensure new user input gets handled ok */ + node->need_exec= 0; + + if(ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { + if(node->id) { + if(node->flag & NODE_DO_OUTPUT) + node->new_node->id= (ID *)BKE_image_copy((Image *)node->id); + else + node->new_node->id= NULL; + } + } + + for(sock= node->outputs.first; sock; sock= sock->next) { + + sock->new_sock->ns.data= sock->ns.data; + sock->ns.data= NULL; + sock->new_sock->new_sock= sock; + } + } + + return ltree; +} + +static int node_exists(bNodeTree *ntree, bNode *testnode) +{ + bNode *node= ntree->nodes.first; + for(; node; node= node->next) + if(node==testnode) + return 1; + return 0; +} + +static int outsocket_exists(bNode *node, bNodeSocket *testsock) +{ + bNodeSocket *sock= node->outputs.first; + for(; sock; sock= sock->next) + if(sock==testsock) + return 1; + return 0; +} + + +/* sync local composite with real tree */ +/* local composite is supposed to be running, be careful moving previews! */ +void ntreeLocalSync(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(node_exists(ntree, lnode->new_node)) { + + if(lnode->preview && lnode->preview->rect) { + node_free_preview(lnode->new_node); + lnode->new_node->preview= lnode->preview; + lnode->preview= NULL; + } + } + } + } +} + +/* merge local tree results back, and free local tree */ +/* we have to assume the editor already changed completely */ +void ntreeLocalMerge(bNodeTree *localtree, bNodeTree *ntree) +{ + bNode *lnode; + bNodeSocket *lsock; + + /* move over the compbufs and previews */ + for(lnode= localtree->nodes.first; lnode; lnode= lnode->next) { + if(node_exists(ntree, lnode->new_node)) { + + if(lnode->preview && lnode->preview->rect) { + node_free_preview(lnode->new_node); + lnode->new_node->preview= lnode->preview; + lnode->preview= NULL; + } + + 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(outsocket_exists(lnode->new_node, lsock->new_sock)) { + lsock->new_sock->ns.data= lsock->ns.data; + lsock->ns.data= NULL; + lsock->new_sock= NULL; + } + } + } + } + ntreeFreeTree(localtree); + MEM_freeN(localtree); +} + +/* *********************************************** */ + +/* GPU material from shader nodes */ + +static void gpu_from_node_stack(ListBase *sockets, bNodeStack **ns, GPUNodeStack *gs) +{ + bNodeSocket *sock; + int i; + + for (sock=sockets->first, i=0; sock; sock=sock->next, i++) { + memset(&gs[i], 0, sizeof(gs[i])); + + QUATCOPY(gs[i].vec, ns[i]->vec); + gs[i].link= ns[i]->data; + + if (sock->type == SOCK_VALUE) + gs[i].type= GPU_FLOAT; + else if (sock->type == SOCK_VECTOR) + gs[i].type= GPU_VEC3; + else if (sock->type == SOCK_RGBA) + gs[i].type= GPU_VEC4; + else + gs[i].type= GPU_NONE; + + gs[i].name = ""; + gs[i].hasinput= ns[i]->hasinput && ns[i]->data; + gs[i].hasoutput= ns[i]->hasinput && ns[i]->data; + gs[i].sockettype= ns[i]->sockettype; + } + + gs[i].type= GPU_NONE; +} + +static void data_from_gpu_stack(ListBase *sockets, bNodeStack **ns, GPUNodeStack *gs) +{ + bNodeSocket *sock; + int i; + + for (sock=sockets->first, i=0; sock; sock=sock->next, i++) { + ns[i]->data= gs[i].link; + ns[i]->hasinput= gs[i].hasinput && gs[i].link; + ns[i]->hasoutput= gs[i].hasoutput; + ns[i]->sockettype= gs[i].sockettype; + } +} + +static void gpu_node_group_execute(bNodeStack *stack, GPUMaterial *mat, bNode *gnode, bNodeStack **in, bNodeStack **out) +{ + bNode *node; + bNodeTree *ntree= (bNodeTree *)gnode->id; + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ + GPUNodeStack gpuin[MAX_SOCKET+1], gpuout[MAX_SOCKET+1]; + int doit = 0; + + if(ntree==NULL) return; + + stack+= gnode->stack_index; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo->gpufunc) { + group_node_get_stack(node, stack, nsin, nsout, in, out); + + doit = 0; + + /* for groups, only execute outputs for edited group */ + if(node->typeinfo->nclass==NODE_CLASS_OUTPUT) { + if(gnode->flag & NODE_GROUP_EDIT) + if(node->flag & NODE_DO_OUTPUT) + doit = 1; + } + else + doit = 1; + + if(doit) { + gpu_from_node_stack(&node->inputs, nsin, gpuin); + gpu_from_node_stack(&node->outputs, nsout, gpuout); + if(node->typeinfo->gpufunc(mat, node, gpuin, gpuout)) + data_from_gpu_stack(&node->outputs, nsout, gpuout); + } + } + } +} + +void ntreeGPUMaterialNodes(bNodeTree *ntree, GPUMaterial *mat) +{ + bNode *node; + bNodeStack *stack; + bNodeStack *nsin[MAX_SOCKET]; /* arbitrary... watch this */ + bNodeStack *nsout[MAX_SOCKET]; /* arbitrary... watch this */ + GPUNodeStack gpuin[MAX_SOCKET+1], gpuout[MAX_SOCKET+1]; + + if((ntree->init & NTREE_EXEC_INIT)==0) + ntreeBeginExecTree(ntree); + + stack= ntree->stack; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->typeinfo->gpufunc) { + node_get_stack(node, stack, nsin, nsout); + gpu_from_node_stack(&node->inputs, nsin, gpuin); + gpu_from_node_stack(&node->outputs, nsout, gpuout); + if(node->typeinfo->gpufunc(mat, node, gpuin, gpuout)) + data_from_gpu_stack(&node->outputs, nsout, gpuout); + } + else if(node->type==NODE_GROUP && node->id) { + node_get_stack(node, stack, nsin, nsout); + gpu_node_group_execute(stack, mat, node, nsin, nsout); + } + } + + ntreeEndExecTree(ntree); +} + +/* **************** call to switch lamploop for material node ************ */ + +void (*node_shader_lamp_loop)(struct ShadeInput *, struct ShadeResult *); + +void set_node_shader_lamp_loop(void (*lamp_loop_func)(ShadeInput *, ShadeResult *)) +{ + node_shader_lamp_loop= lamp_loop_func; +} + +/* 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_RADIO); + if(!(passflag & SCE_PASS_RADIO)) 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_MIST); + if(!(passflag & SCE_PASS_MIST)) 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); + } + } + } +} + +/* 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) { + 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; + } + else if(node->type==CMP_NODE_R_LAYERS) { + NodeTagChanged(ntree, node); + tagged= 1; + } + 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); + } +} + +int ntreeTexTagAnimated(bNodeTree *ntree) +{ + bNode *node; + + if(ntree==NULL) return 0; + + for(node= ntree->nodes.first; node; node= node->next) { + if(node->type==TEX_NODE_CURVE_TIME) { + NodeTagChanged(ntree, node); + return 1; + } + else if(node->type==NODE_GROUP) { + if( ntreeTexTagAnimated((bNodeTree *)node->id) ) { + return 1; + } + } + } + + return 0; +} + +/* ************* node definition init ********** */ + +static bNodeType *is_nodetype_registered(ListBase *typelist, int type, ID *id) +{ + bNodeType *ntype= typelist->first; + + for(;ntype; ntype= ntype->next ) + if(ntype->type==type && ntype->id==id) + return ntype; + + return NULL; +} + +/* type can be from a static array, we make copy for duplicate types (like group) */ +void nodeRegisterType(ListBase *typelist, const bNodeType *ntype) +{ + bNodeType *found= is_nodetype_registered(typelist, ntype->type, ntype->id); + + if(found==NULL) { + bNodeType *ntypen= MEM_callocN(sizeof(bNodeType), "node type"); + *ntypen= *ntype; + BLI_addtail(typelist, ntypen); + } +} + +static void registerCompositNodes(ListBase *ntypelist) +{ + nodeRegisterType(ntypelist, &node_group_typeinfo); + nodeRegisterType(ntypelist, &cmp_node_rlayers); + nodeRegisterType(ntypelist, &cmp_node_image); + nodeRegisterType(ntypelist, &cmp_node_texture); + nodeRegisterType(ntypelist, &cmp_node_value); + nodeRegisterType(ntypelist, &cmp_node_rgb); + nodeRegisterType(ntypelist, &cmp_node_curve_time); + + nodeRegisterType(ntypelist, &cmp_node_composite); + nodeRegisterType(ntypelist, &cmp_node_viewer); + nodeRegisterType(ntypelist, &cmp_node_splitviewer); + nodeRegisterType(ntypelist, &cmp_node_output_file); + nodeRegisterType(ntypelist, &cmp_node_view_levels); + + nodeRegisterType(ntypelist, &cmp_node_curve_rgb); + nodeRegisterType(ntypelist, &cmp_node_mix_rgb); + nodeRegisterType(ntypelist, &cmp_node_hue_sat); + nodeRegisterType(ntypelist, &cmp_node_brightcontrast); + nodeRegisterType(ntypelist, &cmp_node_gamma); + nodeRegisterType(ntypelist, &cmp_node_invert); + nodeRegisterType(ntypelist, &cmp_node_alphaover); + nodeRegisterType(ntypelist, &cmp_node_zcombine); + + nodeRegisterType(ntypelist, &cmp_node_normal); + nodeRegisterType(ntypelist, &cmp_node_curve_vec); + nodeRegisterType(ntypelist, &cmp_node_map_value); + nodeRegisterType(ntypelist, &cmp_node_normalize); + + nodeRegisterType(ntypelist, &cmp_node_filter); + nodeRegisterType(ntypelist, &cmp_node_blur); + nodeRegisterType(ntypelist, &cmp_node_dblur); + nodeRegisterType(ntypelist, &cmp_node_bilateralblur); + nodeRegisterType(ntypelist, &cmp_node_vecblur); + nodeRegisterType(ntypelist, &cmp_node_dilateerode); + nodeRegisterType(ntypelist, &cmp_node_defocus); + + nodeRegisterType(ntypelist, &cmp_node_valtorgb); + nodeRegisterType(ntypelist, &cmp_node_rgbtobw); + nodeRegisterType(ntypelist, &cmp_node_setalpha); + nodeRegisterType(ntypelist, &cmp_node_idmask); + nodeRegisterType(ntypelist, &cmp_node_math); + nodeRegisterType(ntypelist, &cmp_node_seprgba); + nodeRegisterType(ntypelist, &cmp_node_combrgba); + nodeRegisterType(ntypelist, &cmp_node_sephsva); + nodeRegisterType(ntypelist, &cmp_node_combhsva); + nodeRegisterType(ntypelist, &cmp_node_sepyuva); + nodeRegisterType(ntypelist, &cmp_node_combyuva); + nodeRegisterType(ntypelist, &cmp_node_sepycca); + nodeRegisterType(ntypelist, &cmp_node_combycca); + nodeRegisterType(ntypelist, &cmp_node_premulkey); + + nodeRegisterType(ntypelist, &cmp_node_diff_matte); + nodeRegisterType(ntypelist, &cmp_node_distance_matte); + nodeRegisterType(ntypelist, &cmp_node_chroma_matte); + nodeRegisterType(ntypelist, &cmp_node_color_matte); + nodeRegisterType(ntypelist, &cmp_node_channel_matte); + nodeRegisterType(ntypelist, &cmp_node_color_spill); + nodeRegisterType(ntypelist, &cmp_node_luma_matte); + + nodeRegisterType(ntypelist, &cmp_node_translate); + nodeRegisterType(ntypelist, &cmp_node_rotate); + nodeRegisterType(ntypelist, &cmp_node_scale); + nodeRegisterType(ntypelist, &cmp_node_flip); + nodeRegisterType(ntypelist, &cmp_node_crop); + nodeRegisterType(ntypelist, &cmp_node_displace); + nodeRegisterType(ntypelist, &cmp_node_mapuv); + nodeRegisterType(ntypelist, &cmp_node_glare); + nodeRegisterType(ntypelist, &cmp_node_tonemap); + nodeRegisterType(ntypelist, &cmp_node_lensdist); +} + +static void registerShaderNodes(ListBase *ntypelist) +{ + nodeRegisterType(ntypelist, &node_group_typeinfo); + nodeRegisterType(ntypelist, &sh_node_output); + nodeRegisterType(ntypelist, &sh_node_mix_rgb); + nodeRegisterType(ntypelist, &sh_node_valtorgb); + nodeRegisterType(ntypelist, &sh_node_rgbtobw); + nodeRegisterType(ntypelist, &sh_node_normal); + nodeRegisterType(ntypelist, &sh_node_geom); + nodeRegisterType(ntypelist, &sh_node_mapping); + nodeRegisterType(ntypelist, &sh_node_curve_vec); + nodeRegisterType(ntypelist, &sh_node_curve_rgb); + nodeRegisterType(ntypelist, &sh_node_math); + nodeRegisterType(ntypelist, &sh_node_vect_math); + nodeRegisterType(ntypelist, &sh_node_squeeze); + nodeRegisterType(ntypelist, &sh_node_camera); + nodeRegisterType(ntypelist, &sh_node_material); + nodeRegisterType(ntypelist, &sh_node_material_ext); + nodeRegisterType(ntypelist, &sh_node_value); + nodeRegisterType(ntypelist, &sh_node_rgb); + nodeRegisterType(ntypelist, &sh_node_texture); + nodeRegisterType(ntypelist, &node_dynamic_typeinfo); + nodeRegisterType(ntypelist, &sh_node_invert); + nodeRegisterType(ntypelist, &sh_node_seprgb); + nodeRegisterType(ntypelist, &sh_node_combrgb); + nodeRegisterType(ntypelist, &sh_node_hue_sat); +} + +static void registerTextureNodes(ListBase *ntypelist) +{ + nodeRegisterType(ntypelist, &node_group_typeinfo); + nodeRegisterType(ntypelist, &tex_node_math); + nodeRegisterType(ntypelist, &tex_node_mix_rgb); + nodeRegisterType(ntypelist, &tex_node_valtorgb); + nodeRegisterType(ntypelist, &tex_node_rgbtobw); + nodeRegisterType(ntypelist, &tex_node_valtonor); + nodeRegisterType(ntypelist, &tex_node_curve_rgb); + nodeRegisterType(ntypelist, &tex_node_curve_time); + nodeRegisterType(ntypelist, &tex_node_invert); + nodeRegisterType(ntypelist, &tex_node_hue_sat); + nodeRegisterType(ntypelist, &tex_node_coord); + nodeRegisterType(ntypelist, &tex_node_distance); + nodeRegisterType(ntypelist, &tex_node_compose); + nodeRegisterType(ntypelist, &tex_node_decompose); + + nodeRegisterType(ntypelist, &tex_node_output); + nodeRegisterType(ntypelist, &tex_node_viewer); + + nodeRegisterType(ntypelist, &tex_node_checker); + nodeRegisterType(ntypelist, &tex_node_texture); + nodeRegisterType(ntypelist, &tex_node_bricks); + nodeRegisterType(ntypelist, &tex_node_image); + + nodeRegisterType(ntypelist, &tex_node_rotate); + nodeRegisterType(ntypelist, &tex_node_translate); + nodeRegisterType(ntypelist, &tex_node_scale); + nodeRegisterType(ntypelist, &tex_node_at); + + nodeRegisterType(ntypelist, &tex_node_proc_voronoi); + nodeRegisterType(ntypelist, &tex_node_proc_blend); + nodeRegisterType(ntypelist, &tex_node_proc_magic); + nodeRegisterType(ntypelist, &tex_node_proc_marble); + nodeRegisterType(ntypelist, &tex_node_proc_clouds); + nodeRegisterType(ntypelist, &tex_node_proc_wood); + nodeRegisterType(ntypelist, &tex_node_proc_musgrave); + nodeRegisterType(ntypelist, &tex_node_proc_noise); + nodeRegisterType(ntypelist, &tex_node_proc_stucci); + nodeRegisterType(ntypelist, &tex_node_proc_distnoise); +} + +static void remove_dynamic_typeinfos(ListBase *list) +{ + bNodeType *ntype= list->first; + bNodeType *next= NULL; + while(ntype) { + next= ntype->next; + if(ntype->type==NODE_DYNAMIC && ntype->id!=NULL) { + BLI_remlink(list, ntype); + if(ntype->inputs) { + bNodeSocketType *sock= ntype->inputs; + while(sock->type!=-1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(ntype->inputs); + } + if(ntype->outputs) { + bNodeSocketType *sock= ntype->outputs; + while(sock->type!=-1) { + MEM_freeN(sock->name); + sock++; + } + MEM_freeN(ntype->outputs); + } + if(ntype->name) { + MEM_freeN(ntype->name); + } + MEM_freeN(ntype); + } + ntype= next; + } +} + +void init_nodesystem(void) +{ + registerCompositNodes(&node_all_composit); + registerShaderNodes(&node_all_shaders); + registerTextureNodes(&node_all_textures); +} + +void free_nodesystem(void) +{ + /*remove_dynamic_typeinfos(&node_all_composit);*/ /* unused for now */ + BLI_freelistN(&node_all_composit); + remove_dynamic_typeinfos(&node_all_shaders); + BLI_freelistN(&node_all_shaders); + BLI_freelistN(&node_all_textures); +} + +/* called from unlink_scene, 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; + } + } + } + } + } +}
\ No newline at end of file diff --git a/source/blender/editors/mesh/bmeshutils.c b/source/blender/editors/mesh/bmeshutils.c index beac07b6415..e5cda669438 100644 --- a/source/blender/editors/mesh/bmeshutils.c +++ b/source/blender/editors/mesh/bmeshutils.c @@ -561,7 +561,7 @@ static void *editbtMesh_to_undoMesh(void *emv) undomesh *me = MEM_callocN(sizeof(undomesh), "undo Mesh"); /*make sure shape keys work*/ - me->me.key = em->me->key; + me->me.key = copy_key_nolib(em->me->key); /*we recalc the tesselation here, to avoid seeding calls to BMEdit_RecalcTesselation throughout the code.*/ diff --git a/source/blender/render/intern/source/rayobject_blibvh.c b/source/blender/render/intern/source/rayobject_blibvh.c new file mode 100644 index 00000000000..d67d8721f47 --- /dev/null +++ b/source/blender/render/intern/source/rayobject_blibvh.c @@ -0,0 +1,169 @@ +/** + * $Id: rayobject_blibvh.c 23649 2009-10-06 02:56:11Z jaguarandi $ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): André Pinto. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include <assert.h> + +#include "MEM_guardedalloc.h" +#include "BKE_utildefines.h" +#include "BLI_kdopbvh.h" +#include "BLI_arithb.h" +#include "RE_raytrace.h" +#include "render_types.h" +#include "rayobject.h" + +static int RE_rayobject_blibvh_intersect(RayObject *o, Isect *isec); +static void RE_rayobject_blibvh_add(RayObject *o, RayObject *ob); +static void RE_rayobject_blibvh_done(RayObject *o); +static void RE_rayobject_blibvh_free(RayObject *o); +static void RE_rayobject_blibvh_bb(RayObject *o, float *min, float *max); + +static float RE_rayobject_blibvh_cost(RayObject *o) +{ + //TODO calculate the expected cost to raycast on this structure + return 1.0; +} + +static void RE_rayobject_blibvh_hint_bb(RayObject *o, RayHint *hint, float *min, float *max) +{ + return; +} + +static RayObjectAPI bvh_api = +{ + RE_rayobject_blibvh_intersect, + RE_rayobject_blibvh_add, + RE_rayobject_blibvh_done, + RE_rayobject_blibvh_free, + RE_rayobject_blibvh_bb, + RE_rayobject_blibvh_cost, + RE_rayobject_blibvh_hint_bb +}; + +typedef struct BVHObject +{ + RayObject rayobj; + RayObject **leafs, **next_leaf; + BVHTree *bvh; + float bb[2][3]; + +} BVHObject; + + +RayObject *RE_rayobject_blibvh_create(int size) +{ + BVHObject *obj= (BVHObject*)MEM_callocN(sizeof(BVHObject), "BVHObject"); + assert( RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */ + + obj->rayobj.api = &bvh_api; + obj->bvh = BLI_bvhtree_new(size, 0.0, 4, 6); + obj->next_leaf = obj->leafs = (RayObject**)MEM_callocN(size*sizeof(RayObject*), "BVHObject leafs"); + + INIT_MINMAX(obj->bb[0], obj->bb[1]); + return RE_rayobject_unalignRayAPI((RayObject*) obj); +} + +struct BVHCallbackUserData +{ + Isect *isec; + RayObject **leafs; +}; + +static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) +{ + struct BVHCallbackUserData *data = (struct BVHCallbackUserData*)userdata; + Isect *isec = data->isec; + RayObject *face = data->leafs[index]; + + if(RE_rayobject_intersect(face,isec)) + { + hit->index = index; + + if(isec->mode == RE_RAY_SHADOW) + hit->dist = 0; + else + hit->dist = isec->labda*isec->dist; + } +} + +static int RE_rayobject_blibvh_intersect(RayObject *o, Isect *isec) +{ + BVHObject *obj = (BVHObject*)o; + BVHTreeRayHit hit; + float dir[3]; + struct BVHCallbackUserData data; + data.isec = isec; + data.leafs = obj->leafs; + + VECCOPY(dir, isec->vec); + Normalize(dir); + + hit.index = 0; + hit.dist = isec->labda*isec->dist; + + return BLI_bvhtree_ray_cast(obj->bvh, isec->start, dir, 0.0, &hit, bvh_callback, (void*)&data); +} + +static void RE_rayobject_blibvh_add(RayObject *o, RayObject *ob) +{ + BVHObject *obj = (BVHObject*)o; + float min_max[6]; + INIT_MINMAX(min_max, min_max+3); + RE_rayobject_merge_bb(ob, min_max, min_max+3); + + DO_MIN(min_max , obj->bb[0]); + DO_MAX(min_max+3, obj->bb[1]); + + BLI_bvhtree_insert(obj->bvh, obj->next_leaf - obj->leafs, min_max, 2 ); + *(obj->next_leaf++) = ob; +} + +static void RE_rayobject_blibvh_done(RayObject *o) +{ + BVHObject *obj = (BVHObject*)o; + BLI_bvhtree_balance(obj->bvh); +} + +static void RE_rayobject_blibvh_free(RayObject *o) +{ + BVHObject *obj = (BVHObject*)o; + + if(obj->bvh) + BLI_bvhtree_free(obj->bvh); + + if(obj->leafs) + MEM_freeN(obj->leafs); + + MEM_freeN(obj); +} + +static void RE_rayobject_blibvh_bb(RayObject *o, float *min, float *max) +{ + BVHObject *obj = (BVHObject*)o; + DO_MIN( obj->bb[0], min ); + DO_MAX( obj->bb[1], max ); +} diff --git a/source/blender/render/intern/source/rayobject_instance.c b/source/blender/render/intern/source/rayobject_instance.c new file mode 100644 index 00000000000..a21f1952e7f --- /dev/null +++ b/source/blender/render/intern/source/rayobject_instance.c @@ -0,0 +1,200 @@ +/** + * $Id: rayobject_instance.c 23763 2009-10-10 18:42:20Z jaguarandi $ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): André Pinto. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include <assert.h> + +#include "MEM_guardedalloc.h" +#include "BKE_utildefines.h" +#include "BLI_arithb.h" +#include "RE_raytrace.h" +#include "rayobject.h" + +#define RE_COST_INSTANCE (1.0f) + +static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec); +static void RE_rayobject_instance_free(RayObject *o); +static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max); +static float RE_rayobject_instance_cost(RayObject *o); + +static void RE_rayobject_instance_hint_bb(RayObject *o, RayHint *hint, float *min, float *max) +{} + +static RayObjectAPI instance_api = +{ + RE_rayobject_instance_intersect, + NULL, //static void RE_rayobject_instance_add(RayObject *o, RayObject *ob); + NULL, //static void RE_rayobject_instance_done(RayObject *o); + RE_rayobject_instance_free, + RE_rayobject_instance_bb, + RE_rayobject_instance_cost, + RE_rayobject_instance_hint_bb +}; + +typedef struct InstanceRayObject +{ + RayObject rayobj; + RayObject *target; + + void *ob; //Object represented by this instance + void *target_ob; //Object represented by the inner RayObject, needed to handle self-intersection + + float global2target[4][4]; + float target2global[4][4]; + +} InstanceRayObject; + + +RayObject *RE_rayobject_instance_create(RayObject *target, float transform[][4], void *ob, void *target_ob) +{ + InstanceRayObject *obj= (InstanceRayObject*)MEM_callocN(sizeof(InstanceRayObject), "InstanceRayObject"); + assert( RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */ + + obj->rayobj.api = &instance_api; + obj->target = target; + obj->ob = ob; + obj->target_ob = target_ob; + + Mat4CpyMat4(obj->target2global, transform); + Mat4Invert(obj->global2target, obj->target2global); + + return RE_rayobject_unalignRayAPI((RayObject*) obj); +} + +static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec) +{ + //TODO + // *there is probably a faster way to convert between coordinates + + InstanceRayObject *obj = (InstanceRayObject*)o; + int res; + float start[3], vec[3], labda, dist; + int changed = 0, i; + + //TODO - this is disabling self intersection on instances + if(isec->orig.ob == obj->ob && obj->ob) + { + changed = 1; + isec->orig.ob = obj->target_ob; + } + + + VECCOPY( start, isec->start ); + VECCOPY( vec , isec->vec ); + labda = isec->labda; + dist = isec->dist; + + //Transform to target coordinates system + VECADD( isec->vec, isec->vec, isec->start ); + + Mat4MulVecfl(obj->global2target, isec->start); + Mat4MulVecfl(obj->global2target, isec->vec ); + + isec->dist = VecLenf( isec->start, isec->vec ); + VECSUB( isec->vec, isec->vec, isec->start ); + + isec->labda *= isec->dist / dist; + + //Update idot_axis and bv_index + for(i=0; i<3; i++) + { + isec->idot_axis[i] = 1.0f / isec->vec[i]; + + isec->bv_index[2*i] = isec->idot_axis[i] < 0.0 ? 1 : 0; + isec->bv_index[2*i+1] = 1 - isec->bv_index[2*i]; + + isec->bv_index[2*i] = i+3*isec->bv_index[2*i]; + isec->bv_index[2*i+1] = i+3*isec->bv_index[2*i+1]; + } + + //Raycast + res = RE_rayobject_intersect(obj->target, isec); + + //Restore coordinate space coords + if(res == 0) + { + isec->labda = labda; + } + else + { + isec->labda *= dist / isec->dist; + isec->hit.ob = obj->ob; + } + isec->dist = dist; + VECCOPY( isec->start, start ); + VECCOPY( isec->vec, vec ); + + if(changed) + isec->orig.ob = obj->ob; + + //Update idot_axis and bv_index + for(i=0; i<3; i++) + { + isec->idot_axis[i] = 1.0f / isec->vec[i]; + + isec->bv_index[2*i] = isec->idot_axis[i] < 0.0 ? 1 : 0; + isec->bv_index[2*i+1] = 1 - isec->bv_index[2*i]; + + isec->bv_index[2*i] = i+3*isec->bv_index[2*i]; + isec->bv_index[2*i+1] = i+3*isec->bv_index[2*i+1]; + } + + return res; +} + +static void RE_rayobject_instance_free(RayObject *o) +{ + InstanceRayObject *obj = (InstanceRayObject*)o; + MEM_freeN(obj); +} + +static float RE_rayobject_instance_cost(RayObject *o) +{ + InstanceRayObject *obj = (InstanceRayObject*)o; + return RE_rayobject_cost(obj->target) + RE_COST_INSTANCE; +} + +static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max) +{ + //TODO: + // *better bb.. calculated without rotations of bb + // *maybe cache that better-fitted-BB at the InstanceRayObject + InstanceRayObject *obj = (InstanceRayObject*)o; + + float m[3], M[3], t[3]; + int i, j; + INIT_MINMAX(m, M); + RE_rayobject_merge_bb(obj->target, m, M); + + //There must be a faster way than rotating all the 8 vertexs of the BB + for(i=0; i<8; i++) + { + for(j=0; j<3; j++) t[j] = i&(1<<j) ? M[j] : m[j]; + Mat4MulVecfl(obj->target2global, t); + DO_MINMAX(t, min, max); + } +} diff --git a/source/blender/render/intern/source/rayobject_octree.c b/source/blender/render/intern/source/rayobject_octree.c new file mode 100644 index 00000000000..82ccb255350 --- /dev/null +++ b/source/blender/render/intern/source/rayobject_octree.c @@ -0,0 +1,1080 @@ +/** + * $Id: rayobject_octree.c 23649 2009-10-06 02:56:11Z jaguarandi $ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 1990-1998 NeoGeo BV. + * All rights reserved. + * + * Contributors: 2004/2005 Blender Foundation, full recode + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* IMPORTANT NOTE: this code must be independent of any other render code + to use it outside the renderer! */ + +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <float.h> +#include <assert.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_material_types.h" + +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" + +#include "rayobject.h" + +/* ********** structs *************** */ +#define BRANCH_ARRAY 1024 +#define NODE_ARRAY 4096 + +typedef struct Branch +{ + struct Branch *b[8]; +} Branch; + +typedef struct OcVal +{ + short ocx, ocy, ocz; +} OcVal; + +typedef struct Node +{ + struct RayFace *v[8]; + struct OcVal ov[8]; + struct Node *next; +} Node; + +typedef struct Octree { + RayObject rayobj; + + struct Branch **adrbranch; + struct Node **adrnode; + float ocsize; /* ocsize: mult factor, max size octree */ + float ocfacx,ocfacy,ocfacz; + float min[3], max[3]; + int ocres; + int branchcount, nodecount; + + /* during building only */ + char *ocface; + + RayFace **ro_nodes; + int ro_nodes_size, ro_nodes_used; + +} Octree; + +static int RE_rayobject_octree_intersect(RayObject *o, Isect *isec); +static void RE_rayobject_octree_add(RayObject *o, RayObject *ob); +static void RE_rayobject_octree_done(RayObject *o); +static void RE_rayobject_octree_free(RayObject *o); +static void RE_rayobject_octree_bb(RayObject *o, float *min, float *max); + +/* + * This function is not expected to be called by current code state. + */ +static float RE_rayobject_octree_cost(RayObject *o) +{ + return 1.0; +} + +static void RE_rayobject_octree_hint_bb(RayObject *o, RayHint *hint, float *min, float *max) +{ + return; +} + +static RayObjectAPI octree_api = +{ + RE_rayobject_octree_intersect, + RE_rayobject_octree_add, + RE_rayobject_octree_done, + RE_rayobject_octree_free, + RE_rayobject_octree_bb, + RE_rayobject_octree_cost, + RE_rayobject_octree_hint_bb +}; + +/* **************** ocval method ******************* */ +/* within one octree node, a set of 3x15 bits defines a 'boundbox' to OR with */ + +#define OCVALRES 15 +#define BROW16(min, max) (((max)>=OCVALRES? 0xFFFF: (1<<(max+1))-1) - ((min>0)? ((1<<(min))-1):0) ) + +static void calc_ocval_face(float *v1, float *v2, float *v3, float *v4, short x, short y, short z, OcVal *ov) +{ + float min[3], max[3]; + int ocmin, ocmax; + + VECCOPY(min, v1); + VECCOPY(max, v1); + DO_MINMAX(v2, min, max); + DO_MINMAX(v3, min, max); + if(v4) { + DO_MINMAX(v4, min, max); + } + + ocmin= OCVALRES*(min[0]-x); + ocmax= OCVALRES*(max[0]-x); + ov->ocx= BROW16(ocmin, ocmax); + + ocmin= OCVALRES*(min[1]-y); + ocmax= OCVALRES*(max[1]-y); + ov->ocy= BROW16(ocmin, ocmax); + + ocmin= OCVALRES*(min[2]-z); + ocmax= OCVALRES*(max[2]-z); + ov->ocz= BROW16(ocmin, ocmax); + +} + +static void calc_ocval_ray(OcVal *ov, float xo, float yo, float zo, float *vec1, float *vec2) +{ + int ocmin, ocmax; + + if(vec1[0]<vec2[0]) { + ocmin= OCVALRES*(vec1[0] - xo); + ocmax= OCVALRES*(vec2[0] - xo); + } else { + ocmin= OCVALRES*(vec2[0] - xo); + ocmax= OCVALRES*(vec1[0] - xo); + } + ov->ocx= BROW16(ocmin, ocmax); + + if(vec1[1]<vec2[1]) { + ocmin= OCVALRES*(vec1[1] - yo); + ocmax= OCVALRES*(vec2[1] - yo); + } else { + ocmin= OCVALRES*(vec2[1] - yo); + ocmax= OCVALRES*(vec1[1] - yo); + } + ov->ocy= BROW16(ocmin, ocmax); + + if(vec1[2]<vec2[2]) { + ocmin= OCVALRES*(vec1[2] - zo); + ocmax= OCVALRES*(vec2[2] - zo); + } else { + ocmin= OCVALRES*(vec2[2] - zo); + ocmax= OCVALRES*(vec1[2] - zo); + } + ov->ocz= BROW16(ocmin, ocmax); +} + +/* ************* octree ************** */ + +static Branch *addbranch(Octree *oc, Branch *br, short ocb) +{ + int index; + + if(br->b[ocb]) return br->b[ocb]; + + oc->branchcount++; + index= oc->branchcount>>12; + + if(oc->adrbranch[index]==NULL) + oc->adrbranch[index]= MEM_callocN(4096*sizeof(Branch), "new oc branch"); + + if(oc->branchcount>= BRANCH_ARRAY*4096) { + printf("error; octree branches full\n"); + oc->branchcount=0; + } + + return br->b[ocb]= oc->adrbranch[index]+(oc->branchcount & 4095); +} + +static Node *addnode(Octree *oc) +{ + int index; + + oc->nodecount++; + index= oc->nodecount>>12; + + if(oc->adrnode[index]==NULL) + oc->adrnode[index]= MEM_callocN(4096*sizeof(Node),"addnode"); + + if(oc->nodecount> NODE_ARRAY*NODE_ARRAY) { + printf("error; octree nodes full\n"); + oc->nodecount=0; + } + + return oc->adrnode[index]+(oc->nodecount & 4095); +} + +static int face_in_node(RayFace *face, short x, short y, short z, float rtf[][3]) +{ + static float nor[3], d; + float fx, fy, fz; + + // init static vars + if(face) { + CalcNormFloat(rtf[0], rtf[1], rtf[2], nor); + d= -nor[0]*rtf[0][0] - nor[1]*rtf[0][1] - nor[2]*rtf[0][2]; + return 0; + } + + fx= x; + fy= y; + fz= z; + + if((fx)*nor[0] + (fy)*nor[1] + (fz)*nor[2] + d > 0.0f) { + if((fx+1)*nor[0] + (fy )*nor[1] + (fz )*nor[2] + d < 0.0f) return 1; + if((fx )*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d < 0.0f) return 1; + if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d < 0.0f) return 1; + + if((fx )*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; + if((fx+1)*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; + if((fx )*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; + if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d < 0.0f) return 1; + } + else { + if((fx+1)*nor[0] + (fy )*nor[1] + (fz )*nor[2] + d > 0.0f) return 1; + if((fx )*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d > 0.0f) return 1; + if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz )*nor[2] + d > 0.0f) return 1; + + if((fx )*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; + if((fx+1)*nor[0] + (fy )*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; + if((fx )*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; + if((fx+1)*nor[0] + (fy+1)*nor[1] + (fz+1)*nor[2] + d > 0.0f) return 1; + } + + return 0; +} + +static void ocwrite(Octree *oc, RayFace *face, int quad, short x, short y, short z, float rtf[][3]) +{ + Branch *br; + Node *no; + short a, oc0, oc1, oc2, oc3, oc4, oc5; + + x<<=2; + y<<=1; + + br= oc->adrbranch[0]; + + if(oc->ocres==512) { + oc0= ((x & 1024)+(y & 512)+(z & 256))>>8; + br= addbranch(oc, br, oc0); + } + if(oc->ocres>=256) { + oc0= ((x & 512)+(y & 256)+(z & 128))>>7; + br= addbranch(oc, br, oc0); + } + if(oc->ocres>=128) { + oc0= ((x & 256)+(y & 128)+(z & 64))>>6; + br= addbranch(oc, br, oc0); + } + + oc0= ((x & 128)+(y & 64)+(z & 32))>>5; + oc1= ((x & 64)+(y & 32)+(z & 16))>>4; + oc2= ((x & 32)+(y & 16)+(z & 8))>>3; + oc3= ((x & 16)+(y & 8)+(z & 4))>>2; + oc4= ((x & 8)+(y & 4)+(z & 2))>>1; + oc5= ((x & 4)+(y & 2)+(z & 1)); + + br= addbranch(oc, br,oc0); + br= addbranch(oc, br,oc1); + br= addbranch(oc, br,oc2); + br= addbranch(oc, br,oc3); + br= addbranch(oc, br,oc4); + no= (Node *)br->b[oc5]; + if(no==NULL) br->b[oc5]= (Branch *)(no= addnode(oc)); + + while(no->next) no= no->next; + + a= 0; + if(no->v[7]) { /* node full */ + no->next= addnode(oc); + no= no->next; + } + else { + while(no->v[a]!=NULL) a++; + } + + no->v[a]= (RayFace*) RE_rayobject_align(face); + + if(quad) + calc_ocval_face(rtf[0], rtf[1], rtf[2], rtf[3], x>>2, y>>1, z, &no->ov[a]); + else + calc_ocval_face(rtf[0], rtf[1], rtf[2], NULL, x>>2, y>>1, z, &no->ov[a]); +} + +static void d2dda(Octree *oc, short b1, short b2, short c1, short c2, char *ocface, short rts[][3], float rtf[][3]) +{ + int ocx1,ocx2,ocy1,ocy2; + int x,y,dx=0,dy=0; + float ox1,ox2,oy1,oy2; + float labda,labdao,labdax,labday,ldx,ldy; + + ocx1= rts[b1][c1]; + ocy1= rts[b1][c2]; + ocx2= rts[b2][c1]; + ocy2= rts[b2][c2]; + + if(ocx1==ocx2 && ocy1==ocy2) { + ocface[oc->ocres*ocx1+ocy1]= 1; + return; + } + + ox1= rtf[b1][c1]; + oy1= rtf[b1][c2]; + ox2= rtf[b2][c1]; + oy2= rtf[b2][c2]; + + if(ox1!=ox2) { + if(ox2-ox1>0.0f) { + labdax= (ox1-ocx1-1.0f)/(ox1-ox2); + ldx= -1.0f/(ox1-ox2); + dx= 1; + } else { + labdax= (ox1-ocx1)/(ox1-ox2); + ldx= 1.0f/(ox1-ox2); + dx= -1; + } + } else { + labdax=1.0f; + ldx=0; + } + + if(oy1!=oy2) { + if(oy2-oy1>0.0f) { + labday= (oy1-ocy1-1.0f)/(oy1-oy2); + ldy= -1.0f/(oy1-oy2); + dy= 1; + } else { + labday= (oy1-ocy1)/(oy1-oy2); + ldy= 1.0f/(oy1-oy2); + dy= -1; + } + } else { + labday=1.0f; + ldy=0; + } + + x=ocx1; y=ocy1; + labda= MIN2(labdax, labday); + + while(TRUE) { + + if(x<0 || y<0 || x>=oc->ocres || y>=oc->ocres); + else ocface[oc->ocres*x+y]= 1; + + labdao=labda; + if(labdax==labday) { + labdax+=ldx; + x+=dx; + labday+=ldy; + y+=dy; + } else { + if(labdax<labday) { + labdax+=ldx; + x+=dx; + } else { + labday+=ldy; + y+=dy; + } + } + labda=MIN2(labdax,labday); + if(labda==labdao) break; + if(labda>=1.0f) break; + } + ocface[oc->ocres*ocx2+ocy2]=1; +} + +static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *ocmin, short *ocmax) +{ + int a, x, y, y1, y2; + + for(x=ocmin[c1];x<=ocmax[c1];x++) { + a= oc->ocres*x; + for(y=ocmin[c2];y<=ocmax[c2];y++) { + if(ocface[a+y]) { + y++; + while(ocface[a+y] && y!=ocmax[c2]) y++; + for(y1=ocmax[c2];y1>y;y1--) { + if(ocface[a+y1]) { + for(y2=y;y2<=y1;y2++) ocface[a+y2]=1; + y1=0; + } + } + y=ocmax[c2]; + } + } + } +} + +static void RE_rayobject_octree_free(RayObject *tree) +{ + Octree *oc= (Octree*)tree; + +#if 0 + printf("branches %d nodes %d\n", oc->branchcount, oc->nodecount); + printf("raycount %d \n", raycount); + printf("ray coherent %d \n", coherent_ray); + printf("accepted %d rejected %d\n", accepted, rejected); +#endif + if(oc->ocface) + MEM_freeN(oc->ocface); + + if(oc->adrbranch) { + int a= 0; + while(oc->adrbranch[a]) { + MEM_freeN(oc->adrbranch[a]); + oc->adrbranch[a]= NULL; + a++; + } + MEM_freeN(oc->adrbranch); + oc->adrbranch= NULL; + } + oc->branchcount= 0; + + if(oc->adrnode) { + int a= 0; + while(oc->adrnode[a]) { + MEM_freeN(oc->adrnode[a]); + oc->adrnode[a]= NULL; + a++; + } + MEM_freeN(oc->adrnode); + oc->adrnode= NULL; + } + oc->nodecount= 0; + + MEM_freeN(oc); +} + + +RayObject *RE_rayobject_octree_create(int ocres, int size) +{ + Octree *oc= MEM_callocN(sizeof(Octree), "Octree"); + assert( RE_rayobject_isAligned(oc) ); /* RayObject API assumes real data to be 4-byte aligned */ + + oc->rayobj.api = &octree_api; + + oc->ocres = ocres; + + oc->ro_nodes = (RayFace**)MEM_callocN(sizeof(RayFace*)*size, "octree rayobject nodes"); + oc->ro_nodes_size = size; + oc->ro_nodes_used = 0; + + + return RE_rayobject_unalignRayAPI((RayObject*) oc); +} + + +static void RE_rayobject_octree_add(RayObject *tree, RayObject *node) +{ + Octree *oc = (Octree*)tree; + + assert( RE_rayobject_isRayFace(node) ); + assert( oc->ro_nodes_used < oc->ro_nodes_size ); + oc->ro_nodes[ oc->ro_nodes_used++ ] = (RayFace*)RE_rayobject_align(node); +} + +static void octree_fill_rayface(Octree *oc, RayFace *face) +{ + float ocfac[3], rtf[4][3]; + float co1[3], co2[3], co3[3], co4[3]; + short rts[4][3]; + short ocmin[3], ocmax[3]; + char *ocface= oc->ocface; // front, top, size view of face, to fill in + int a, b, c, oc1, oc2, oc3, oc4, x, y, z, ocres2; + + ocfac[0]= oc->ocfacx; + ocfac[1]= oc->ocfacy; + ocfac[2]= oc->ocfacz; + + ocres2= oc->ocres*oc->ocres; + + VECCOPY(co1, face->v1); + VECCOPY(co2, face->v2); + VECCOPY(co3, face->v3); + if(face->v4) + VECCOPY(co4, face->v4); + + for(c=0;c<3;c++) { + rtf[0][c]= (co1[c]-oc->min[c])*ocfac[c] ; + rts[0][c]= (short)rtf[0][c]; + rtf[1][c]= (co2[c]-oc->min[c])*ocfac[c] ; + rts[1][c]= (short)rtf[1][c]; + rtf[2][c]= (co3[c]-oc->min[c])*ocfac[c] ; + rts[2][c]= (short)rtf[2][c]; + if(RE_rayface_isQuad(face)) { + rtf[3][c]= (co4[c]-oc->min[c])*ocfac[c] ; + rts[3][c]= (short)rtf[3][c]; + } + } + + for(c=0;c<3;c++) { + oc1= rts[0][c]; + oc2= rts[1][c]; + oc3= rts[2][c]; + if(!RE_rayface_isQuad(face)) { + ocmin[c]= MIN3(oc1,oc2,oc3); + ocmax[c]= MAX3(oc1,oc2,oc3); + } + else { + oc4= rts[3][c]; + ocmin[c]= MIN4(oc1,oc2,oc3,oc4); + ocmax[c]= MAX4(oc1,oc2,oc3,oc4); + } + if(ocmax[c]>oc->ocres-1) ocmax[c]=oc->ocres-1; + if(ocmin[c]<0) ocmin[c]=0; + } + + if(ocmin[0]==ocmax[0] && ocmin[1]==ocmax[1] && ocmin[2]==ocmax[2]) { + ocwrite(oc, face, RE_rayface_isQuad(face), ocmin[0], ocmin[1], ocmin[2], rtf); + } + else { + + d2dda(oc, 0,1,0,1,ocface+ocres2,rts,rtf); + d2dda(oc, 0,1,0,2,ocface,rts,rtf); + d2dda(oc, 0,1,1,2,ocface+2*ocres2,rts,rtf); + d2dda(oc, 1,2,0,1,ocface+ocres2,rts,rtf); + d2dda(oc, 1,2,0,2,ocface,rts,rtf); + d2dda(oc, 1,2,1,2,ocface+2*ocres2,rts,rtf); + if(!RE_rayface_isQuad(face)) { + d2dda(oc, 2,0,0,1,ocface+ocres2,rts,rtf); + d2dda(oc, 2,0,0,2,ocface,rts,rtf); + d2dda(oc, 2,0,1,2,ocface+2*ocres2,rts,rtf); + } + else { + d2dda(oc, 2,3,0,1,ocface+ocres2,rts,rtf); + d2dda(oc, 2,3,0,2,ocface,rts,rtf); + d2dda(oc, 2,3,1,2,ocface+2*ocres2,rts,rtf); + d2dda(oc, 3,0,0,1,ocface+ocres2,rts,rtf); + d2dda(oc, 3,0,0,2,ocface,rts,rtf); + d2dda(oc, 3,0,1,2,ocface+2*ocres2,rts,rtf); + } + /* nothing todo with triangle..., just fills :) */ + filltriangle(oc, 0,1,ocface+ocres2,ocmin,ocmax); + filltriangle(oc, 0,2,ocface,ocmin,ocmax); + filltriangle(oc, 1,2,ocface+2*ocres2,ocmin,ocmax); + + /* init static vars here */ + face_in_node(face, 0,0,0, rtf); + + for(x=ocmin[0];x<=ocmax[0];x++) { + a= oc->ocres*x; + for(y=ocmin[1];y<=ocmax[1];y++) { + if(ocface[a+y+ocres2]) { + b= oc->ocres*y+2*ocres2; + for(z=ocmin[2];z<=ocmax[2];z++) { + if(ocface[b+z] && ocface[a+z]) { + if(face_in_node(NULL, x, y, z, rtf)) + ocwrite(oc, face, RE_rayface_isQuad(face), x,y,z, rtf); + } + } + } + } + } + + /* same loops to clear octree, doubt it can be done smarter */ + for(x=ocmin[0];x<=ocmax[0];x++) { + a= oc->ocres*x; + for(y=ocmin[1];y<=ocmax[1];y++) { + /* x-y */ + ocface[a+y+ocres2]= 0; + + b= oc->ocres*y + 2*ocres2; + for(z=ocmin[2];z<=ocmax[2];z++) { + /* y-z */ + ocface[b+z]= 0; + /* x-z */ + ocface[a+z]= 0; + } + } + } + } +} + +static void RE_rayobject_octree_done(RayObject *tree) +{ + Octree *oc = (Octree*)tree; + int c; + float t00, t01, t02; + int ocres2 = oc->ocres*oc->ocres; + + INIT_MINMAX(oc->min, oc->max); + + /* Calculate Bounding Box */ + for(c=0; c<oc->ro_nodes_used; c++) + RE_rayobject_merge_bb( RE_rayobject_unalignRayFace(oc->ro_nodes[c]), oc->min, oc->max); + + /* Alloc memory */ + oc->adrbranch= MEM_callocN(sizeof(void *)*BRANCH_ARRAY, "octree branches"); + oc->adrnode= MEM_callocN(sizeof(void *)*NODE_ARRAY, "octree nodes"); + + oc->adrbranch[0]=(Branch *)MEM_callocN(4096*sizeof(Branch), "makeoctree"); + + /* the lookup table, per face, for which nodes to fill in */ + oc->ocface= MEM_callocN( 3*ocres2 + 8, "ocface"); + memset(oc->ocface, 0, 3*ocres2); + + for(c=0;c<3;c++) { /* octree enlarge, still needed? */ + oc->min[c]-= 0.01f; + oc->max[c]+= 0.01f; + } + + t00= oc->max[0]-oc->min[0]; + t01= oc->max[1]-oc->min[1]; + t02= oc->max[2]-oc->min[2]; + + /* this minus 0.1 is old safety... seems to be needed? */ + oc->ocfacx= (oc->ocres-0.1)/t00; + oc->ocfacy= (oc->ocres-0.1)/t01; + oc->ocfacz= (oc->ocres-0.1)/t02; + + oc->ocsize= sqrt(t00*t00+t01*t01+t02*t02); /* global, max size octree */ + + for(c=0; c<oc->ro_nodes_used; c++) + { + octree_fill_rayface(oc, oc->ro_nodes[c]); + } + + MEM_freeN(oc->ocface); + oc->ocface = NULL; + MEM_freeN(oc->ro_nodes); + oc->ro_nodes = NULL; + + printf("%f %f - %f\n", oc->min[0], oc->max[0], oc->ocfacx ); + printf("%f %f - %f\n", oc->min[1], oc->max[1], oc->ocfacy ); + printf("%f %f - %f\n", oc->min[2], oc->max[2], oc->ocfacz ); +} + +static void RE_rayobject_octree_bb(RayObject *tree, float *min, float *max) +{ + Octree *oc = (Octree*)tree; + DO_MINMAX(oc->min, min, max); + DO_MINMAX(oc->max, min, max); +} + +/* check all faces in this node */ +static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval) +{ + short nr=0; + + /* return on any first hit */ + if(is->mode==RE_RAY_SHADOW) { + + for(; no; no = no->next) + for(nr=0; nr<8; nr++) + { + RayFace *face = no->v[nr]; + OcVal *ov = no->ov+nr; + + if(!face) break; + + if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) + { + if( RE_rayobject_intersect( RE_rayobject_unalignRayFace(face),is) ) + return 1; + } + } + } + else + { /* else mirror or glass or shadowtra, return closest face */ + int found= 0; + + for(; no; no = no->next) + for(nr=0; nr<8; nr++) + { + RayFace *face = no->v[nr]; + OcVal *ov = no->ov+nr; + + if(!face) break; + + if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) + { + if( RE_rayobject_intersect( RE_rayobject_unalignRayFace(face),is) ) + found= 1; + } + } + + return found; + } + + return 0; +} + +/* find the Node for the octree coord x y z */ +static Node *ocread(Octree *oc, int x, int y, int z) +{ + Branch *br; + int oc1; + + x<<=2; + y<<=1; + + br= oc->adrbranch[0]; + + if(oc->ocres==512) { + oc1= ((x & 1024)+(y & 512)+(z & 256))>>8; + br= br->b[oc1]; + if(br==NULL) { + return NULL; + } + } + if(oc->ocres>=256) { + oc1= ((x & 512)+(y & 256)+(z & 128))>>7; + br= br->b[oc1]; + if(br==NULL) { + return NULL; + } + } + if(oc->ocres>=128) { + oc1= ((x & 256)+(y & 128)+(z & 64))>>6; + br= br->b[oc1]; + if(br==NULL) { + return NULL; + } + } + + oc1= ((x & 128)+(y & 64)+(z & 32))>>5; + br= br->b[oc1]; + if(br) { + oc1= ((x & 64)+(y & 32)+(z & 16))>>4; + br= br->b[oc1]; + if(br) { + oc1= ((x & 32)+(y & 16)+(z & 8))>>3; + br= br->b[oc1]; + if(br) { + oc1= ((x & 16)+(y & 8)+(z & 4))>>2; + br= br->b[oc1]; + if(br) { + oc1= ((x & 8)+(y & 4)+(z & 2))>>1; + br= br->b[oc1]; + if(br) { + oc1= ((x & 4)+(y & 2)+(z & 1)); + return (Node *)br->b[oc1]; + } + } + } + } + } + + return NULL; +} + +static int cliptest(float p, float q, float *u1, float *u2) +{ + float r; + + if(p<0.0f) { + if(q<p) return 0; + else if(q<0.0f) { + r= q/p; + if(r>*u2) return 0; + else if(r>*u1) *u1=r; + } + } + else { + if(p>0.0f) { + if(q<0.0f) return 0; + else if(q<p) { + r= q/p; + if(r<*u1) return 0; + else if(r<*u2) *u2=r; + } + } + else if(q<0.0f) return 0; + } + return 1; +} + +/* extensive coherence checks/storage cancels out the benefit of it, and gives errors... we + need better methods, sample code commented out below (ton) */ + +/* + +in top: static int coh_nodes[16*16*16][6]; +in makeoctree: memset(coh_nodes, 0, sizeof(coh_nodes)); + +static void add_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2) +{ + short *sp; + + sp= coh_nodes[ (ocx2 & 15) + 16*(ocy2 & 15) + 256*(ocz2 & 15) ]; + sp[0]= ocx1; sp[1]= ocy1; sp[2]= ocz1; + sp[3]= ocx2; sp[4]= ocy2; sp[5]= ocz2; + +} + +static int do_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, int ocz2) +{ + short *sp; + + sp= coh_nodes[ (ocx2 & 15) + 16*(ocy2 & 15) + 256*(ocz2 & 15) ]; + if(sp[0]==ocx1 && sp[1]==ocy1 && sp[2]==ocz1 && + sp[3]==ocx2 && sp[4]==ocy2 && sp[5]==ocz2) return 1; + return 0; +} + +*/ + +/* return 1: found valid intersection */ +/* starts with is->orig.face */ +static int RE_rayobject_octree_intersect(RayObject *tree, Isect *is) +{ + Octree *oc= (Octree*)tree; + Node *no; + OcVal ocval; + float vec1[3], vec2[3], start[3], end[3]; + float u1,u2,ox1,ox2,oy1,oy2,oz1,oz2; + float labdao,labdax,ldx,labday,ldy,labdaz,ldz, ddalabda; + float olabda = 0; + int dx,dy,dz; + int xo,yo,zo,c1=0; + int ocx1,ocx2,ocy1, ocy2,ocz1,ocz2; + + /* clip with octree */ + if(oc->branchcount==0) return 0; + + /* do this before intersect calls */ +#if 0 + is->facecontr= NULL; /* to check shared edge */ + is->obcontr= 0; + is->faceisect= is->isect= 0; /* shared edge, quad half flag */ + is->userdata= oc->userdata; +#endif + + VECCOPY( start, is->start ); + VECADDFAC( end, is->start, is->vec, is->labda ); + ldx= is->vec[0]*is->labda; + olabda = is->labda; + u1= 0.0f; + u2= 1.0f; + + /* clip with octree cube */ + if(cliptest(-ldx, start[0]-oc->min[0], &u1,&u2)) { + if(cliptest(ldx, oc->max[0]-start[0], &u1,&u2)) { + ldy= is->vec[1]*is->labda; + if(cliptest(-ldy, start[1]-oc->min[1], &u1,&u2)) { + if(cliptest(ldy, oc->max[1]-start[1], &u1,&u2)) { + ldz = is->vec[2]*is->labda; + if(cliptest(-ldz, start[2]-oc->min[2], &u1,&u2)) { + if(cliptest(ldz, oc->max[2]-start[2], &u1,&u2)) { + c1=1; + if(u2<1.0f) { + end[0] = start[0]+u2*ldx; + end[1] = start[1]+u2*ldy; + end[2] = start[2]+u2*ldz; + } + + if(u1>0.0f) { + start[0] += u1*ldx; + start[1] += u1*ldy; + start[2] += u1*ldz; + } + } + } + } + } + } + } + + if(c1==0) return 0; + + /* reset static variables in ocread */ + //ocread(oc, oc->ocres, 0, 0); + + /* setup 3dda to traverse octree */ + ox1= (start[0]-oc->min[0])*oc->ocfacx; + oy1= (start[1]-oc->min[1])*oc->ocfacy; + oz1= (start[2]-oc->min[2])*oc->ocfacz; + ox2= (end[0]-oc->min[0])*oc->ocfacx; + oy2= (end[1]-oc->min[1])*oc->ocfacy; + oz2= (end[2]-oc->min[2])*oc->ocfacz; + + ocx1= (int)ox1; + ocy1= (int)oy1; + ocz1= (int)oz1; + ocx2= (int)ox2; + ocy2= (int)oy2; + ocz2= (int)oz2; + + if(ocx1==ocx2 && ocy1==ocy2 && ocz1==ocz2) { + no= ocread(oc, ocx1, ocy1, ocz1); + if(no) { + /* exact intersection with node */ + vec1[0]= ox1; vec1[1]= oy1; vec1[2]= oz1; + vec2[0]= ox2; vec2[1]= oy2; vec2[2]= oz2; + calc_ocval_ray(&ocval, (float)ocx1, (float)ocy1, (float)ocz1, vec1, vec2); + if( testnode(oc, is, no, ocval) ) return 1; + } + } + else { + int found = 0; + //static int coh_ocx1,coh_ocx2,coh_ocy1, coh_ocy2,coh_ocz1,coh_ocz2; + float dox, doy, doz; + int eqval; + + /* calc labda en ld */ + dox= ox1-ox2; + doy= oy1-oy2; + doz= oz1-oz2; + + if(dox<-FLT_EPSILON) { + ldx= -1.0f/dox; + labdax= (ocx1-ox1+1.0f)*ldx; + dx= 1; + } else if(dox>FLT_EPSILON) { + ldx= 1.0f/dox; + labdax= (ox1-ocx1)*ldx; + dx= -1; + } else { + labdax=1.0f; + ldx=0; + dx= 0; + } + + if(doy<-FLT_EPSILON) { + ldy= -1.0f/doy; + labday= (ocy1-oy1+1.0f)*ldy; + dy= 1; + } else if(doy>FLT_EPSILON) { + ldy= 1.0f/doy; + labday= (oy1-ocy1)*ldy; + dy= -1; + } else { + labday=1.0f; + ldy=0; + dy= 0; + } + + if(doz<-FLT_EPSILON) { + ldz= -1.0f/doz; + labdaz= (ocz1-oz1+1.0f)*ldz; + dz= 1; + } else if(doz>FLT_EPSILON) { + ldz= 1.0f/doz; + labdaz= (oz1-ocz1)*ldz; + dz= -1; + } else { + labdaz=1.0f; + ldz=0; + dz= 0; + } + + xo=ocx1; yo=ocy1; zo=ocz1; + labdao= ddalabda= MIN3(labdax,labday,labdaz); + + vec2[0]= ox1; + vec2[1]= oy1; + vec2[2]= oz1; + + /* this loop has been constructed to make sure the first and last node of ray + are always included, even when ddalabda==1.0f or larger */ + + while(TRUE) { + + no= ocread(oc, xo, yo, zo); + if(no) { + + /* calculate ray intersection with octree node */ + VECCOPY(vec1, vec2); + // dox,y,z is negative + vec2[0]= ox1-ddalabda*dox; + vec2[1]= oy1-ddalabda*doy; + vec2[2]= oz1-ddalabda*doz; + calc_ocval_ray(&ocval, (float)xo, (float)yo, (float)zo, vec1, vec2); + + //is->labda = (u1+ddalabda*(u2-u1))*olabda; + if( testnode(oc, is, no, ocval) ) + found = 1; + + if(is->labda < (u1+ddalabda*(u2-u1))*olabda) + return found; + } + + + labdao= ddalabda; + + /* traversing ocree nodes need careful detection of smallest values, with proper + exceptions for equal labdas */ + eqval= (labdax==labday); + if(labday==labdaz) eqval += 2; + if(labdax==labdaz) eqval += 4; + + if(eqval) { // only 4 cases exist! + if(eqval==7) { // x=y=z + xo+=dx; labdax+=ldx; + yo+=dy; labday+=ldy; + zo+=dz; labdaz+=ldz; + } + else if(eqval==1) { // x=y + if(labday < labdaz) { + xo+=dx; labdax+=ldx; + yo+=dy; labday+=ldy; + } + else { + zo+=dz; labdaz+=ldz; + } + } + else if(eqval==2) { // y=z + if(labdax < labday) { + xo+=dx; labdax+=ldx; + } + else { + yo+=dy; labday+=ldy; + zo+=dz; labdaz+=ldz; + } + } + else { // x=z + if(labday < labdax) { + yo+=dy; labday+=ldy; + } + else { + xo+=dx; labdax+=ldx; + zo+=dz; labdaz+=ldz; + } + } + } + else { // all three different, just three cases exist + eqval= (labdax<labday); + if(labday<labdaz) eqval += 2; + if(labdax<labdaz) eqval += 4; + + if(eqval==7 || eqval==5) { // x smallest + xo+=dx; labdax+=ldx; + } + else if(eqval==2 || eqval==6) { // y smallest + yo+=dy; labday+=ldy; + } + else { // z smallest + zo+=dz; labdaz+=ldz; + } + + } + + ddalabda=MIN3(labdax,labday,labdaz); + if(ddalabda==labdao) break; + /* to make sure the last node is always checked */ + if(labdao>=1.0f) break; + } + } + + /* reached end, no intersections found */ + return 0; +} + + + diff --git a/source/blender/render/intern/source/rayobject_raycounter.c b/source/blender/render/intern/source/rayobject_raycounter.c new file mode 100644 index 00000000000..366d9cc0b22 --- /dev/null +++ b/source/blender/render/intern/source/rayobject_raycounter.c @@ -0,0 +1,87 @@ +/** + * $Id: rayobject_raycounter.c 23649 2009-10-06 02:56:11Z jaguarandi $ + * + * ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2009 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): André Pinto. + * + * ***** END GPL LICENSE BLOCK ***** + */ +#include "rayobject.h" +#include "raycounter.h" + +#ifdef RE_RAYCOUNTER + +void RE_RC_INFO(RayCounter *info) +{ + printf("----------- Raycast counter --------\n"); + printf("Rays total: %llu\n", info->raycast.test ); + printf("Rays hit: %llu\n", info->raycast.hit ); + printf("\n"); + printf("BB tests: %llu\n", info->bb.test ); + printf("BB hits: %llu\n", info->bb.hit ); + printf("\n"); + printf("SIMD BB tests: %llu\n", info->simd_bb.test ); + printf("SIMD BB hits: %llu\n", info->simd_bb.hit ); + printf("\n"); + printf("Primitives tests: %llu\n", info->faces.test ); + printf("Primitives hits: %llu\n", info->faces.hit ); + printf("------------------------------------\n"); + printf("Shadow last-hit tests per ray: %f\n", info->rayshadow_last_hit.test / ((float)info->raycast.test) ); + printf("Shadow last-hit hits per ray: %f\n", info->rayshadow_last_hit.hit / ((float)info->raycast.test) ); + printf("\n"); + printf("Hint tests per ray: %f\n", info->raytrace_hint.test / ((float)info->raycast.test) ); + printf("Hint hits per ray: %f\n", info->raytrace_hint.hit / ((float)info->raycast.test) ); + printf("\n"); + printf("BB tests per ray: %f\n", info->bb.test / ((float)info->raycast.test) ); + printf("BB hits per ray: %f\n", info->bb.hit / ((float)info->raycast.test) ); + printf("\n"); + printf("SIMD tests per ray: %f\n", info->simd_bb.test / ((float)info->raycast.test) ); + printf("SIMD hits per ray: %f\n", info->simd_bb.hit / ((float)info->raycast.test) ); + printf("\n"); + printf("Primitives tests per ray: %f\n", info->faces.test / ((float)info->raycast.test) ); + printf("Primitives hits per ray: %f\n", info->faces.hit / ((float)info->raycast.test) ); + printf("------------------------------------\n"); +} + +void RE_RC_MERGE(RayCounter *dest, RayCounter *tmp) +{ + dest->faces.test += tmp->faces.test; + dest->faces.hit += tmp->faces.hit; + + dest->bb.test += tmp->bb.test; + dest->bb.hit += tmp->bb.hit; + + dest->simd_bb.test += tmp->simd_bb.test; + dest->simd_bb.hit += tmp->simd_bb.hit; + + dest->raycast.test += tmp->raycast.test; + dest->raycast.hit += tmp->raycast.hit; + + dest->rayshadow_last_hit.test += tmp->rayshadow_last_hit.test; + dest->rayshadow_last_hit.hit += tmp->rayshadow_last_hit.hit; + + dest->raytrace_hint.test += tmp->raytrace_hint.test; + dest->raytrace_hint.hit += tmp->raytrace_hint.hit; +} + +#endif |