diff options
-rw-r--r-- | source/blender/render/extern/include/RE_raytrace.h | 90 | ||||
-rw-r--r-- | source/blender/render/intern/include/render_types.h | 13 | ||||
-rw-r--r-- | source/blender/render/intern/include/rendercore.h | 4 | ||||
-rw-r--r-- | source/blender/render/intern/source/convertblender.c | 12 | ||||
-rw-r--r-- | source/blender/render/intern/source/envmap.c | 5 | ||||
-rw-r--r-- | source/blender/render/intern/source/ray.c | 2469 | ||||
-rw-r--r-- | source/blender/render/intern/source/rayshade.c | 1229 | ||||
-rw-r--r-- | source/blender/render/intern/source/raytrace.c | 1353 |
8 files changed, 2683 insertions, 2492 deletions
diff --git a/source/blender/render/extern/include/RE_raytrace.h b/source/blender/render/extern/include/RE_raytrace.h new file mode 100644 index 00000000000..095ffcf0c18 --- /dev/null +++ b/source/blender/render/extern/include/RE_raytrace.h @@ -0,0 +1,90 @@ +/** + * $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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2007 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + * RE_raytrace.h: ray tracing api, can be used independently from the renderer. + */ + +#ifndef RE_RAYTRACE_H +#define RE_RAYTRACE_H + +/* ray types */ +#define RE_RAY_SHADOW 0 +#define RE_RAY_MIRROR 1 +#define RE_RAY_SHADOW_TRA 2 + +/* spatial tree for raytracing acceleration */ +typedef void RayTree; +/* abstraction of face type */ +typedef void RayFace; + +/* struct for intersection data */ +typedef struct Isect { + float start[3]; /* start+vec = end, in ray_tree_intersect */ + float vec[3]; + float end[3]; + + float labda, u, v; /* distance to hitpoint, uv weights */ + + RayFace *face; /* face is where to intersect with */ + RayFace *faceorig; /* start face */ + RayFace *face_last; /* for shadow optimize, last intersected face */ + + short isect; /* which half of quad */ + short mode; /* RE_RAYSHADOW, RE_RAYMIRROR, RE_RAYSHADOW_TRA */ + int lay; /* -1 default, set for layer lamps */ + + /* only used externally */ + float col[4]; /* RGBA for shadow_tra */ + + /* octree only */ + RayFace *facecontr; + float ddalabda; + short faceisect; /* flag if facecontr was done or not */ +} Isect; + +/* function callbacks for face type abstraction */ +typedef void (*RayCoordsFunc)(RayFace *face, + float **v1, float **v2, float **v3, float **v4); +typedef int (*RayCheckFunc)(Isect *is, RayFace *face); + +/* tree building and freeing */ +RayTree *RE_ray_tree_create(int ocres, int totface, float *min, float *max, + RayCoordsFunc coordfunc, RayCheckFunc checkfunc); +void RE_ray_tree_add_face(RayTree *tree, RayFace *face); +void RE_ray_tree_done(RayTree *tree); +void RE_ray_tree_free(RayTree *tree); + +/* intersection with full tree and single face */ +int RE_ray_tree_intersect(RayTree *tree, Isect *is); +int RE_ray_face_intersection(Isect *is, RayCoordsFunc coordsfunc); + +/* retrieve the diameter of the tree structure, for setting intersection + end distance */ +float RE_ray_tree_max_size(RayTree *tree); + +#endif /*__RE_RAYTRACE_H__*/ + diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 45aff8e8b60..5c2e788ce44 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -46,7 +46,6 @@ struct Object; struct MemArena; struct VertTableNode; struct VlakTableNode; -struct Octree; struct GHash; #define TABLEINITSIZE 1024 @@ -84,16 +83,6 @@ typedef struct RenderPart char *clipflag; /* clipflags for part zbuffering */ } RenderPart; -typedef struct Octree { - 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; -} Octree; - /* controls state of render, everything that's read-only during render stage */ struct Render { @@ -150,7 +139,7 @@ struct Render ListBase parts; /* octree tables and variables for raytrace */ - Octree oc; + void *raytree; /* use this instead of R.r.cfra */ float cfra; diff --git a/source/blender/render/intern/include/rendercore.h b/source/blender/render/intern/include/rendercore.h index 651fd423e5d..3e3c49e87d0 100644 --- a/source/blender/render/intern/include/rendercore.h +++ b/source/blender/render/intern/include/rendercore.h @@ -91,8 +91,8 @@ void zbufshade_sss_tile(struct RenderPart *pa); /* -------- ray.c ------- */ -extern void freeoctree(Render *re); -extern void makeoctree(Render *re); +extern void freeraytree(Render *re); +extern void makeraytree(Render *re); extern void ray_shadow(ShadeInput *, LampRen *, float *); extern void ray_trace(ShadeInput *, ShadeResult *); diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index cdeddca5216..5c57fc3d743 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -2988,7 +2988,7 @@ void RE_Database_Free(Render *re) re->scene->world->aotables= NULL; } - if(re->r.mode & R_RAYTRACE) freeoctree(re); + if(re->r.mode & R_RAYTRACE) freeraytree(re); free_sss(re); @@ -3461,17 +3461,17 @@ void RE_Database_FromScene(Render *re, Scene *scene, int use_camera_view) } } - /* yafray: 'direct' radiosity, environment maps and octree init not needed for yafray render */ + /* yafray: 'direct' radiosity, environment maps and raytree init not needed for yafray render */ /* although radio mode could be useful at some point, later */ if (re->r.renderer==R_INTERN) { /* RADIO (uses no R anymore) */ if(!re->test_break()) if(re->r.mode & R_RADIO) do_radio_render(re); - /* octree */ + /* raytree */ if(!re->test_break()) { if(re->r.mode & R_RAYTRACE) { - makeoctree(re); + makeraytree(re); } } /* ENVIRONMENT MAPS */ @@ -4048,10 +4048,10 @@ void RE_Database_Baking(Render *re, Scene *scene, int type) } if(type!=RE_BAKE_LIGHT) { - /* octree */ + /* raytree */ if(!re->test_break()) { if(re->r.mode & R_RAYTRACE) { - makeoctree(re); + makeraytree(re); } } } diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index ec55bc0a5e2..ebb444833e8 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -157,7 +157,7 @@ static Render *envmap_render_copy(Render *re, EnvMap *env) envre->vlaknodeslen= re->vlaknodeslen; envre->vlaknodes= re->vlaknodes; envre->customdata_names= re->customdata_names; - envre->oc= re->oc; + envre->raytree= re->raytree; return envre; } @@ -177,8 +177,7 @@ static void envmap_free_render_copy(Render *envre) envre->vlaknodeslen= 0; envre->vlaknodes= NULL; envre->customdata_names.first= envre->customdata_names.last= NULL; - envre->oc.adrbranch= NULL; - envre->oc.adrnode= NULL; + envre->raytree= NULL; RE_FreeRender(envre); } diff --git a/source/blender/render/intern/source/ray.c b/source/blender/render/intern/source/ray.c deleted file mode 100644 index 128a4a900ec..00000000000 --- a/source/blender/render/intern/source/ray.c +++ /dev/null @@ -1,2469 +0,0 @@ -/** - * $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., 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 ***** - */ - - -#include <math.h> -#include <string.h> -#include <stdlib.h> -#include <float.h> - -#include "MEM_guardedalloc.h" - -#include "DNA_material_types.h" -#include "DNA_lamp_types.h" - -#include "BKE_global.h" -#include "BKE_node.h" -#include "BKE_utildefines.h" - -#include "BLI_arithb.h" -#include "BLI_rand.h" -#include "BLI_jitter.h" - -#include "PIL_time.h" - -#include "render_types.h" -#include "renderpipeline.h" -#include "rendercore.h" -#include "renderdatabase.h" -#include "pixelblending.h" -#include "pixelshading.h" -#include "shading.h" -#include "texture.h" - -#define DDA_SHADOW 0 -#define DDA_MIRROR 1 -#define DDA_SHADOW_TRA 2 - -#define RAY_TRA 1 -#define RAY_TRAFLIP 2 - -#define DEPTH_SHADOW_TRA 10 - -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ -/* only to be used here in this file, it's for speed */ -extern struct Render R; -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -/* ********** structs *************** */ - -#define BRANCH_ARRAY 1024 -#define NODE_ARRAY 4096 - -typedef struct Isect { - float start[3], vec[3], end[3]; /* start+vec = end, in d3dda */ - float labda, u, v; - struct VlakRen *vlr, *vlrcontr, *vlrorig; /* vlr is where to intersect with */ - short isect, mode; /* isect: which half of quad, mode: DDA_SHADOW, DDA_MIRROR, DDA_SHADOW_TRA */ - float ddalabda; - float col[4]; /* RGBA for shadow_tra */ - int lay; /* -1 default, set for layer lamps */ - short vlrisect; /* flag whether vlrcontr was done or not */ - /* for optimize, last intersected face */ - VlakRen *vlr_last; -} Isect; - -typedef struct Branch -{ - struct Branch *b[8]; -} Branch; - -typedef struct OcVal -{ - short ocx, ocy, ocz; -} OcVal; - -typedef struct Node -{ - struct VlakRen *v[8]; - struct OcVal ov[8]; - struct Node *next; -} Node; - - -/* ******** globals ***************** */ - -/* just for statistics */ -static int raycount; -static int accepted, rejected, coherent_ray; - - -/* **************** 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(VlakRen *vlr, short x, short y, short z, float rtf[][3]) -{ - static float nor[3], d; - float fx, fy, fz; - - // init static vars - if(vlr) { - 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, VlakRen *vlr, 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]= vlr; - - if(vlr->v4) - 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; - - ocmax=ocmin+3; - - 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]; - } - } - } -} - -void freeoctree(Render *re) -{ - Octree *oc= &re->oc; - -#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->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; -} - -void makeoctree(Render *re) -{ - Octree *oc; - VlakRen *vlr=NULL; - VertRen *v1, *v2, *v3, *v4; - float ocfac[3], t00, t01, t02; - float rtf[4][3]; - int v; - int a, b, c, oc1, oc2, oc3, oc4, x, y, z, ocres2; - short rts[4][3], ocmin[6], *ocmax; - char *ocface; // front, top, size view of face, to fill in - double lasttime= PIL_check_seconds_timer(); - - oc= &re->oc; - oc->adrbranch= MEM_callocN(sizeof(void *)*BRANCH_ARRAY, "octree branches"); - oc->adrnode= MEM_callocN(sizeof(void *)*NODE_ARRAY, "octree nodes"); - - ocmax= ocmin+3; - - /* only for debug info */ - raycount=0; - accepted= 0; - rejected= 0; - coherent_ray= 0; - - /* fill main octree struct */ - oc->ocres= re->r.ocres; - ocres2= oc->ocres*oc->ocres; - INIT_MINMAX(oc->min, oc->max); - - /* first min max octree space */ - for(v=0;v<re->totvlak;v++) { - if((v & 255)==0) vlr= re->vlaknodes[v>>8].vlak; - else vlr++; - if(vlr->mat->mode & MA_TRACEBLE) { - if((vlr->mat->mode & MA_WIRE)==0) { - - DO_MINMAX(vlr->v1->co, oc->min, oc->max); - DO_MINMAX(vlr->v2->co, oc->min, oc->max); - DO_MINMAX(vlr->v3->co, oc->min, oc->max); - if(vlr->v4) { - DO_MINMAX(vlr->v4->co, oc->min, oc->max); - } - } - } - } - - if(oc->min[0] > oc->max[0]) return; /* empty octree */ - - oc->adrbranch[0]=(Branch *)MEM_callocN(4096*sizeof(Branch), "makeoctree"); - - /* the lookup table, per face, for which nodes to fill in */ - ocface= MEM_callocN( 3*ocres2 + 8, "ocface"); - memset(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=ocfac[0]= (oc->ocres-0.1)/t00; - oc->ocfacy=ocfac[1]= (oc->ocres-0.1)/t01; - oc->ocfacz=ocfac[2]= (oc->ocres-0.1)/t02; - - oc->ocsize= sqrt(t00*t00+t01*t01+t02*t02); /* global, max size octree */ - - for(v=0; v<re->totvlak; v++) { - if((v & 255)==0) { - double time= PIL_check_seconds_timer(); - - vlr= re->vlaknodes[v>>8].vlak; - if(re->test_break()) - break; - if(time-lasttime>1.0f) { - char str[32]; - sprintf(str, "Filling Octree: %d", v); - re->i.infostr= str; - re->stats_draw(&re->i); - re->i.infostr= NULL; - lasttime= time; - } - } - else vlr++; - - if(vlr->mat->mode & MA_TRACEBLE) { - if((vlr->mat->mode & MA_WIRE)==0) { - - v1= vlr->v1; - v2= vlr->v2; - v3= vlr->v3; - v4= vlr->v4; - - for(c=0;c<3;c++) { - rtf[0][c]= (v1->co[c]-oc->min[c])*ocfac[c] ; - rts[0][c]= (short)rtf[0][c]; - rtf[1][c]= (v2->co[c]-oc->min[c])*ocfac[c] ; - rts[1][c]= (short)rtf[1][c]; - rtf[2][c]= (v3->co[c]-oc->min[c])*ocfac[c] ; - rts[2][c]= (short)rtf[2][c]; - if(v4) { - rtf[3][c]= (v4->co[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(v4==NULL) { - 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, vlr, 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(v4==NULL) { - 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); - filltriangle(oc, 0,2,ocface,ocmin); - filltriangle(oc, 1,2,ocface+2*ocres2,ocmin); - - /* init static vars here */ - face_in_node(vlr, 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, vlr, 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; - } - } - } - } - } - } - } - - MEM_freeN(ocface); - re->i.infostr= NULL; - re->stats_draw(&re->i); -} - -/* ************ raytracer **************** */ - -/* only for self-intersecting test with current render face (where ray left) */ -static int intersection2(VlakRen *vlr, float r0, float r1, float r2, float rx1, float ry1, float rz1) -{ - VertRen *v1,*v2,*v3,*v4=NULL; - float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22; - float m0, m1, m2, divdet, det, det1; - float u1, v, u2; - - /* happens for baking with non existing face */ - if(vlr->v1==NULL) - return 1; - - v1= vlr->v1; - v2= vlr->v2; - if(vlr->v4) { - v3= vlr->v4; - v4= vlr->v3; - } - else v3= vlr->v3; - - t00= v3->co[0]-v1->co[0]; - t01= v3->co[1]-v1->co[1]; - t02= v3->co[2]-v1->co[2]; - t10= v3->co[0]-v2->co[0]; - t11= v3->co[1]-v2->co[1]; - t12= v3->co[2]-v2->co[2]; - - x0= t11*r2-t12*r1; - x1= t12*r0-t10*r2; - x2= t10*r1-t11*r0; - - divdet= t00*x0+t01*x1+t02*x2; - - m0= rx1-v3->co[0]; - m1= ry1-v3->co[1]; - m2= rz1-v3->co[2]; - det1= m0*x0+m1*x1+m2*x2; - - if(divdet!=0.0f) { - u1= det1/divdet; - - if(u1<=0.0f) { - det= t00*(m1*r2-m2*r1); - det+= t01*(m2*r0-m0*r2); - det+= t02*(m0*r1-m1*r0); - v= det/divdet; - - if(v<=0.0f && (u1 + v) >= -1.0f) { - return 1; - } - } - } - - if(v4) { - - t20= v3->co[0]-v4->co[0]; - t21= v3->co[1]-v4->co[1]; - t22= v3->co[2]-v4->co[2]; - - divdet= t20*x0+t21*x1+t22*x2; - if(divdet!=0.0f) { - u2= det1/divdet; - - if(u2<=0.0f) { - det= t20*(m1*r2-m2*r1); - det+= t21*(m2*r0-m0*r2); - det+= t22*(m0*r1-m1*r0); - v= det/divdet; - - if(v<=0.0f && (u2 + v) >= -1.0f) { - return 2; - } - } - } - } - return 0; -} - -#if 0 -/* ray - line intersection */ -/* disabled until i got real & fast cylinder checking, this code doesnt work proper -for faster strands */ - -static int intersection_strand(Isect *is) -{ - float v1[3], v2[3]; /* length of strand */ - float axis[3], rc[3], nor[3], radline, dist, len; - - /* radius strand */ - radline= 0.5f*VecLenf(is->vlr->v1->co, is->vlr->v2->co); - - VecMidf(v1, is->vlr->v1->co, is->vlr->v2->co); - VecMidf(v2, is->vlr->v3->co, is->vlr->v4->co); - - VECSUB(rc, v1, is->start); /* vector from base ray to base cylinder */ - VECSUB(axis, v2, v1); /* cylinder axis */ - - CROSS(nor, is->vec, axis); - len= VecLength(nor); - - if(len<FLT_EPSILON) - return 0; - - dist= INPR(rc, nor)/len; /* distance between ray and axis cylinder */ - - if(dist<radline && dist>-radline) { - float dot1, dot2, dot3, rlen, alen, div; - float labda; - - /* calculating the intersection point of shortest distance */ - dot1 = INPR(rc, is->vec); - dot2 = INPR(is->vec, axis); - dot3 = INPR(rc, axis); - rlen = INPR(is->vec, is->vec); - alen = INPR(axis, axis); - - div = alen * rlen - dot2 * dot2; - if (ABS(div) < FLT_EPSILON) - return 0; - - labda = (dot1*dot2 - dot3*rlen)/div; - - radline/= sqrt(alen); - - /* labda: where on axis do we have closest intersection? */ - if(labda >= -radline && labda <= 1.0f+radline) { - VlakRen *vlr= is->vlrorig; - VertRen *v1= is->vlr->v1, *v2= is->vlr->v2, *v3= is->vlr->v3, *v4= is->vlr->v4; - /* but we dont do shadows from faces sharing edge */ - - if(v1==vlr->v1 || v2==vlr->v1 || v3==vlr->v1 || v4==vlr->v1) return 0; - if(v1==vlr->v2 || v2==vlr->v2 || v3==vlr->v2 || v4==vlr->v2) return 0; - if(v1==vlr->v3 || v2==vlr->v3 || v3==vlr->v3 || v4==vlr->v3) return 0; - if(vlr->v4) { - if(v1==vlr->v4 || v2==vlr->v4 || v3==vlr->v4 || v4==vlr->v4) return 0; - } - return 1; - } - } - return 0; -} -#endif - -/* ray - triangle or quad intersection */ -static int intersection(Isect *is) -{ - VertRen *v1,*v2,*v3,*v4=NULL; - float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22,r0,r1,r2; - float m0, m1, m2, divdet, det1; - short ok=0; - - /* disabled until i got real & fast cylinder checking, this code doesnt work proper - for faster strands */ -// if(is->mode==DDA_SHADOW && is->vlr->flag & R_STRAND) -// return intersection_strand(is); - - v1= is->vlr->v1; - v2= is->vlr->v2; - if(is->vlr->v4) { - v3= is->vlr->v4; - v4= is->vlr->v3; - } - else v3= is->vlr->v3; - - t00= v3->co[0]-v1->co[0]; - t01= v3->co[1]-v1->co[1]; - t02= v3->co[2]-v1->co[2]; - t10= v3->co[0]-v2->co[0]; - t11= v3->co[1]-v2->co[1]; - t12= v3->co[2]-v2->co[2]; - - r0= is->vec[0]; - r1= is->vec[1]; - r2= is->vec[2]; - - x0= t12*r1-t11*r2; - x1= t10*r2-t12*r0; - x2= t11*r0-t10*r1; - - divdet= t00*x0+t01*x1+t02*x2; - - m0= is->start[0]-v3->co[0]; - m1= is->start[1]-v3->co[1]; - m2= is->start[2]-v3->co[2]; - det1= m0*x0+m1*x1+m2*x2; - - if(divdet!=0.0f) { - float u; - - divdet= 1.0f/divdet; - u= det1*divdet; - if(u<0.0f && u>-1.0f) { - float v, cros0, cros1, cros2; - - cros0= m1*t02-m2*t01; - cros1= m2*t00-m0*t02; - cros2= m0*t01-m1*t00; - v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); - - if(v<0.0f && (u + v) > -1.0f) { - float labda; - labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); - - if(labda>0.0f && labda<1.0f) { - is->labda= labda; - is->u= u; is->v= v; - ok= 1; - } - } - } - } - - if(ok==0 && v4) { - - t20= v3->co[0]-v4->co[0]; - t21= v3->co[1]-v4->co[1]; - t22= v3->co[2]-v4->co[2]; - - divdet= t20*x0+t21*x1+t22*x2; - if(divdet!=0.0f) { - float u; - divdet= 1.0f/divdet; - u = det1*divdet; - - if(u<0.0f && u>-1.0f) { - float v, cros0, cros1, cros2; - cros0= m1*t22-m2*t21; - cros1= m2*t20-m0*t22; - cros2= m0*t21-m1*t20; - v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); - - if(v<0.0f && (u + v) > -1.0f) { - float labda; - labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); - - if(labda>0.0f && labda<1.0f) { - ok= 2; - is->labda= labda; - is->u= u; is->v= v; - } - } - } - } - } - - if(ok) { - is->isect= ok; // wich half of the quad - - if(is->mode!=DDA_SHADOW) { - /* for mirror & tra-shadow: large faces can be filled in too often, this prevents - a face being detected too soon... */ - if(is->labda > is->ddalabda) { - return 0; - } - } - - /* when a shadow ray leaves a face, it can be little outside the edges of it, causing - intersection to be detected in its neighbour face */ - - if(is->vlrcontr && is->vlrisect); // optimizing, the tests below are not needed - else if(is->labda< .1) { - VlakRen *vlr= is->vlrorig; - short de= 0; - - if(v1==vlr->v1 || v2==vlr->v1 || v3==vlr->v1 || v4==vlr->v1) de++; - if(v1==vlr->v2 || v2==vlr->v2 || v3==vlr->v2 || v4==vlr->v2) de++; - if(v1==vlr->v3 || v2==vlr->v3 || v3==vlr->v3 || v4==vlr->v3) de++; - if(vlr->v4) { - if(v1==vlr->v4 || v2==vlr->v4 || v3==vlr->v4 || v4==vlr->v4) de++; - } - if(de) { - - /* so there's a shared edge or vertex, let's intersect ray with vlr - itself, if that's true we can safely return 1, otherwise we assume - the intersection is invalid, 0 */ - - if(is->vlrcontr==NULL) { - is->vlrcontr= vlr; - is->vlrisect= intersection2(vlr, -r0, -r1, -r2, is->start[0], is->start[1], is->start[2]); - } - - if(is->vlrisect) return 1; - return 0; - } - } - - return 1; - } - - return 0; -} - -/* check all faces in this node */ -static int testnode(Isect *is, Node *no, OcVal ocval) -{ - VlakRen *vlr; - short nr=0; - OcVal *ov; - - /* return on any first hit */ - if(is->mode==DDA_SHADOW) { - - vlr= no->v[0]; - while(vlr) { - - if(is->vlrorig != vlr) { - - if(is->lay & vlr->lay) { - - ov= no->ov+nr; - if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { - //accepted++; - is->vlr= vlr; - - if(intersection(is)) { - is->vlr_last= vlr; - return 1; - } - } - //else rejected++; - } - } - - nr++; - if(nr==8) { - no= no->next; - if(no==0) return 0; - nr=0; - } - vlr= no->v[nr]; - } - } - else { /* else mirror or glass or shadowtra, return closest face */ - Isect isect; - int found= 0; - - is->labda= 1.0f; /* needed? */ - isect= *is; /* copy for sorting */ - - vlr= no->v[0]; - while(vlr) { - - if(is->vlrorig != vlr) { - /* I now... cpu cycle waste, might do smarter once */ - if(is->mode==DDA_MIRROR && (vlr->mat->mode & MA_ONLYCAST)); - else if(is->mode==DDA_SHADOW_TRA && !(is->lay & vlr->lay)); - else { - ov= no->ov+nr; - if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { - //accepted++; - - isect.vlr= vlr; - if(intersection(&isect)) { - if(isect.labda<is->labda) *is= isect; - found= 1; - } - } - //else rejected++; - } - } - - nr++; - if(nr==8) { - no= no->next; - if(no==NULL) break; - nr=0; - } - vlr= no->v[nr]; - } - - return found; - } - - return 0; -} - -/* find the Node for the octree coord x y z */ -static Node *ocread(int x, int y, int z) -{ - Branch *br; - int oc1; - - x<<=2; - y<<=1; - - br= R.oc.adrbranch[0]; - - if(R.oc.ocres==512) { - oc1= ((x & 1024)+(y & 512)+(z & 256))>>8; - br= br->b[oc1]; - if(br==NULL) { - return NULL; - } - } - if(R.oc.ocres>=256) { - oc1= ((x & 512)+(y & 256)+(z & 128))>>7; - br= br->b[oc1]; - if(br==NULL) { - return NULL; - } - } - if(R.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->vlrorig */ -static int d3dda(Isect *is) -{ - Node *no; - OcVal ocval; - float vec1[3], vec2[3]; - float u1,u2,ox1,ox2,oy1,oy2,oz1,oz2; - float labdao,labdax,ldx,labday,ldy,labdaz,ldz, ddalabda; - int dx,dy,dz; - int xo,yo,zo,c1=0; - int ocx1,ocx2,ocy1, ocy2,ocz1,ocz2; - - /* clip with octree */ - if(R.oc.branchcount==0) return 0; - - /* do this before intersect calls */ - is->vlrcontr= NULL; /* to check shared edge */ - is->vlrisect= is->isect= 0; /* shared edge, quad half flag */ - - /* only for shadow! */ - if(is->mode==DDA_SHADOW) { - - /* check with last intersected shadow face */ - if(is->vlr_last!=NULL && is->vlr_last!=is->vlrorig) { - if(is->lay & is->vlr_last->lay) { - is->vlr= is->vlr_last; - VECSUB(is->vec, is->end, is->start); - if(intersection(is)) return 1; - } - } - } - - ldx= is->end[0] - is->start[0]; - u1= 0.0f; - u2= 1.0f; - - /* clip with octree cube */ - if(cliptest(-ldx, is->start[0]-R.oc.min[0], &u1,&u2)) { - if(cliptest(ldx, R.oc.max[0]-is->start[0], &u1,&u2)) { - ldy= is->end[1] - is->start[1]; - if(cliptest(-ldy, is->start[1]-R.oc.min[1], &u1,&u2)) { - if(cliptest(ldy, R.oc.max[1]-is->start[1], &u1,&u2)) { - ldz= is->end[2] - is->start[2]; - if(cliptest(-ldz, is->start[2]-R.oc.min[2], &u1,&u2)) { - if(cliptest(ldz, R.oc.max[2]-is->start[2], &u1,&u2)) { - c1=1; - if(u2<1.0f) { - is->end[0]= is->start[0]+u2*ldx; - is->end[1]= is->start[1]+u2*ldy; - is->end[2]= is->start[2]+u2*ldz; - } - if(u1>0.0f) { - is->start[0]+=u1*ldx; - is->start[1]+=u1*ldy; - is->start[2]+=u1*ldz; - } - } - } - } - } - } - } - - if(c1==0) return 0; - - /* reset static variables in ocread */ - //ocread(R.oc.ocres, 0, 0); - - /* setup 3dda to traverse octree */ - ox1= (is->start[0]-R.oc.min[0])*R.oc.ocfacx; - oy1= (is->start[1]-R.oc.min[1])*R.oc.ocfacy; - oz1= (is->start[2]-R.oc.min[2])*R.oc.ocfacz; - ox2= (is->end[0]-R.oc.min[0])*R.oc.ocfacx; - oy2= (is->end[1]-R.oc.min[1])*R.oc.ocfacy; - oz2= (is->end[2]-R.oc.min[2])*R.oc.ocfacz; - - ocx1= (int)ox1; - ocy1= (int)oy1; - ocz1= (int)oz1; - ocx2= (int)ox2; - ocy2= (int)oy2; - ocz2= (int)oz2; - - /* for intersection */ - VECSUB(is->vec, is->end, is->start); - - if(ocx1==ocx2 && ocy1==ocy2 && ocz1==ocz2) { - no= ocread(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); - is->ddalabda= 1.0f; - if( testnode(is, no, ocval) ) return 1; - } - } - else { - //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(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->ddalabda= ddalabda; - if( testnode(is, no, ocval) ) return 1; - } - - 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 */ - is->vlr_last= NULL; - return 0; -} - - -static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) -{ - VlakRen *vlr= is->vlr; - int osatex= 0, norflip; - - /* set up view vector */ - VECCOPY(shi->view, is->vec); - - /* render co */ - shi->co[0]= is->start[0]+is->labda*(shi->view[0]); - shi->co[1]= is->start[1]+is->labda*(shi->view[1]); - shi->co[2]= is->start[2]+is->labda*(shi->view[2]); - - Normalize(shi->view); - - shi->vlr= vlr; - shi->mat= vlr->mat; - memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h - shi->har= shi->mat->har; - - // Osa structs we leave unchanged now - SWAP(int, osatex, shi->osatex); - - shi->dxco[0]= shi->dxco[1]= shi->dxco[2]= 0.0f; - shi->dyco[0]= shi->dyco[1]= shi->dyco[2]= 0.0f; - - // but, set Osa stuff to zero where it can confuse texture code - if(shi->mat->texco & (TEXCO_NORM|TEXCO_REFL) ) { - shi->dxno[0]= shi->dxno[1]= shi->dxno[2]= 0.0f; - shi->dyno[0]= shi->dyno[1]= shi->dyno[2]= 0.0f; - } - - /* face normal, check for flip, need to set puno here */ - norflip= (vlr->n[0]*shi->view[0]+vlr->n[1]*shi->view[1]+vlr->n[2]*shi->view[2]) < 0.0f; - - if(norflip) - shi->puno= vlr->puno ^ 15;// only flip lower 4 bits - else - shi->puno= vlr->puno; - - if(vlr->v4) { - if(is->isect==2) - shade_input_set_triangle_i(shi, vlr, 2, 1, 3); - else - shade_input_set_triangle_i(shi, vlr, 0, 1, 3); - } - else { - shade_input_set_triangle_i(shi, vlr, 0, 1, 2); - } - - /* shade_input_set_triangle_i() sets facenor, now we flip */ - if(norflip) { - shi->facenor[0]= -vlr->n[0]; - shi->facenor[1]= -vlr->n[1]; - shi->facenor[2]= -vlr->n[2]; - } - - shi->u= is->u; - shi->v= is->v; - shi->dx_u= shi->dx_v= shi->dy_u= shi->dy_v= 0.0f; - shade_input_set_normals(shi); - shade_input_set_shade_texco(shi); - - if(is->mode==DDA_SHADOW_TRA) - shade_color(shi, shr); - else { - if(shi->mat->nodetree && shi->mat->use_nodes) { - ntreeShaderExecTree(shi->mat->nodetree, shi, shr); - shi->mat= vlr->mat; /* shi->mat is being set in nodetree */ - } - else - shade_material_loop(shi, shr); - - /* raytrace likes to separate the spec color */ - VECSUB(shr->diff, shr->combined, shr->spec); - } - - SWAP(int, osatex, shi->osatex); // XXXXX!!!! - -} - -static int refraction(float *refract, float *n, float *view, float index) -{ - float dot, fac; - - VECCOPY(refract, view); - - dot= view[0]*n[0] + view[1]*n[1] + view[2]*n[2]; - - if(dot>0.0f) { - index = 1.0f/index; - fac= 1.0f - (1.0f - dot*dot)*index*index; - if(fac<= 0.0f) return 0; - fac= -dot*index + sqrt(fac); - } - else { - fac= 1.0f - (1.0f - dot*dot)*index*index; - if(fac<= 0.0f) return 0; - fac= -dot*index - sqrt(fac); - } - - refract[0]= index*view[0] + fac*n[0]; - refract[1]= index*view[1] + fac*n[1]; - refract[2]= index*view[2] + fac*n[2]; - - return 1; -} - -/* orn = original face normal */ -static void reflection(float *ref, float *n, float *view, float *orn) -{ - float f1; - - f1= -2.0f*(n[0]*view[0]+ n[1]*view[1]+ n[2]*view[2]); - - ref[0]= (view[0]+f1*n[0]); - ref[1]= (view[1]+f1*n[1]); - ref[2]= (view[2]+f1*n[2]); - - if(orn) { - /* test phong normals, then we should prevent vector going to the back */ - f1= ref[0]*orn[0]+ ref[1]*orn[1]+ ref[2]*orn[2]; - if(f1>0.0f) { - f1+= .01f; - ref[0]-= f1*orn[0]; - ref[1]-= f1*orn[1]; - ref[2]-= f1*orn[2]; - } - } -} - -#if 0 -static void color_combine(float *result, float fac1, float fac2, float *col1, float *col2) -{ - float col1t[3], col2t[3]; - - col1t[0]= sqrt(col1[0]); - col1t[1]= sqrt(col1[1]); - col1t[2]= sqrt(col1[2]); - col2t[0]= sqrt(col2[0]); - col2t[1]= sqrt(col2[1]); - col2t[2]= sqrt(col2[2]); - - result[0]= (fac1*col1t[0] + fac2*col2t[0]); - result[0]*= result[0]; - result[1]= (fac1*col1t[1] + fac2*col2t[1]); - result[1]*= result[1]; - result[2]= (fac1*col1t[2] + fac2*col2t[2]); - result[2]*= result[2]; -} -#endif - -static float shade_by_transmission(Isect *is, ShadeInput *shi, ShadeResult *shr) -{ - float dx, dy, dz, d, p; - - if (0 == (shi->mat->mode & (MA_RAYTRANSP|MA_ZTRA))) - return -1; - - if (shi->mat->tx_limit <= 0.0f) { - d= 1.0f; - } - else { - /* shi.co[] calculated by shade_ray() */ - dx= shi->co[0] - is->start[0]; - dy= shi->co[1] - is->start[1]; - dz= shi->co[2] - is->start[2]; - d= sqrt(dx*dx+dy*dy+dz*dz); - if (d > shi->mat->tx_limit) - d= shi->mat->tx_limit; - - p = shi->mat->tx_falloff; - if(p < 0.0f) p= 0.0f; - else if (p > 10.0f) p= 10.0f; - - shr->alpha *= pow(d, p); - if (shr->alpha > 1.0f) - shr->alpha= 1.0f; - } - - return d; -} - -/* the main recursive tracer itself */ -static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, float *col, VlakRen *vlr, int traflag) -{ - ShadeInput shi; - ShadeResult shr; - Isect isec; - float f, f1, fr, fg, fb; - float ref[3]; - - VECCOPY(isec.start, start); - isec.end[0]= start[0]+R.oc.ocsize*vec[0]; - isec.end[1]= start[1]+R.oc.ocsize*vec[1]; - isec.end[2]= start[2]+R.oc.ocsize*vec[2]; - isec.mode= DDA_MIRROR; - isec.vlrorig= vlr; - - if( d3dda(&isec) ) { - float d= 1.0f; - - shi.mask= origshi->mask; - shi.osatex= origshi->osatex; - shi.depth= 1; /* only used to indicate tracing */ - shi.thread= origshi->thread; - shi.sample= 0; - shi.xs= origshi->xs; - shi.ys= origshi->ys; - shi.lay= origshi->lay; - shi.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */ - shi.combinedflag= 0xFFFFFF; /* ray trace does all options */ - shi.do_preview= 0; - shi.light_override= origshi->light_override; - shi.mat_override= origshi->mat_override; - - memset(&shr, 0, sizeof(ShadeResult)); - - shade_ray(&isec, &shi, &shr); - if (traflag & RAY_TRA) - d= shade_by_transmission(&isec, &shi, &shr); - - if(depth>0) { - - if(shi.mat->mode_l & (MA_RAYTRANSP|MA_ZTRA) && shr.alpha < 1.0f) { - float nf, f, f1, refract[3], tracol[4]; - - tracol[0]= shi.r; - tracol[1]= shi.g; - tracol[2]= shi.b; - tracol[3]= col[3]; // we pass on and accumulate alpha - - if(shi.mat->mode & MA_RAYTRANSP) { - /* odd depths: use normal facing viewer, otherwise flip */ - if(traflag & RAY_TRAFLIP) { - float norm[3]; - norm[0]= - shi.vn[0]; - norm[1]= - shi.vn[1]; - norm[2]= - shi.vn[2]; - if (!refraction(refract, norm, shi.view, shi.ang)) - reflection(refract, norm, shi.view, shi.vn); - } - else { - if (!refraction(refract, shi.vn, shi.view, shi.ang)) - reflection(refract, shi.vn, shi.view, shi.vn); - } - traflag |= RAY_TRA; - traceray(origshi, depth-1, shi.co, refract, tracol, shi.vlr, traflag ^ RAY_TRAFLIP); - } - else - traceray(origshi, depth-1, shi.co, shi.view, tracol, shi.vlr, 0); - - f= shr.alpha; f1= 1.0f-f; - nf= d * shi.mat->filter; - fr= 1.0f+ nf*(shi.r-1.0f); - fg= 1.0f+ nf*(shi.g-1.0f); - fb= 1.0f+ nf*(shi.b-1.0f); - shr.diff[0]= f*shr.diff[0] + f1*fr*tracol[0]; - shr.diff[1]= f*shr.diff[1] + f1*fg*tracol[1]; - shr.diff[2]= f*shr.diff[2] + f1*fb*tracol[2]; - - shr.spec[0] *=f; - shr.spec[1] *=f; - shr.spec[2] *=f; - - col[3]= f1*tracol[3] + f; - } - else - col[3]= 1.0f; - - if(shi.mat->mode_l & MA_RAYMIRROR) { - f= shi.ray_mirror; - if(f!=0.0f) f*= fresnel_fac(shi.view, shi.vn, shi.mat->fresnel_mir_i, shi.mat->fresnel_mir); - } - else f= 0.0f; - - if(f!=0.0f) { - float mircol[4]; - - reflection(ref, shi.vn, shi.view, NULL); - traceray(origshi, depth-1, shi.co, ref, mircol, shi.vlr, 0); - - f1= 1.0f-f; - - /* combine */ - //color_combine(col, f*fr*(1.0f-shr.spec[0]), f1, col, shr.diff); - //col[0]+= shr.spec[0]; - //col[1]+= shr.spec[1]; - //col[2]+= shr.spec[2]; - - fr= shi.mirr; - fg= shi.mirg; - fb= shi.mirb; - - col[0]= f*fr*(1.0f-shr.spec[0])*mircol[0] + f1*shr.diff[0] + shr.spec[0]; - col[1]= f*fg*(1.0f-shr.spec[1])*mircol[1] + f1*shr.diff[1] + shr.spec[1]; - col[2]= f*fb*(1.0f-shr.spec[2])*mircol[2] + f1*shr.diff[2] + shr.spec[2]; - } - else { - col[0]= shr.diff[0] + shr.spec[0]; - col[1]= shr.diff[1] + shr.spec[1]; - col[2]= shr.diff[2] + shr.spec[2]; - } - } - else { - col[0]= shr.diff[0] + shr.spec[0]; - col[1]= shr.diff[1] + shr.spec[1]; - col[2]= shr.diff[2] + shr.spec[2]; - } - - } - else { /* sky */ - VECCOPY(shi.view, vec); - Normalize(shi.view); - - shadeSkyView(col, isec.start, shi.view, NULL); - } -} - -/* **************** jitter blocks ********** */ - -/* calc distributed planar energy */ - -static void DP_energy(float *table, float *vec, int tot, float xsize, float ysize) -{ - int x, y, a; - float *fp, force[3], result[3]; - float dx, dy, dist, min; - - min= MIN2(xsize, ysize); - min*= min; - result[0]= result[1]= 0.0f; - - for(y= -1; y<2; y++) { - dy= ysize*y; - for(x= -1; x<2; x++) { - dx= xsize*x; - fp= table; - for(a=0; a<tot; a++, fp+= 2) { - force[0]= vec[0] - fp[0]-dx; - force[1]= vec[1] - fp[1]-dy; - dist= force[0]*force[0] + force[1]*force[1]; - if(dist < min && dist>0.0f) { - result[0]+= force[0]/dist; - result[1]+= force[1]/dist; - } - } - } - } - vec[0] += 0.1*min*result[0]/(float)tot; - vec[1] += 0.1*min*result[1]/(float)tot; - // cyclic clamping - vec[0]= vec[0] - xsize*floor(vec[0]/xsize + 0.5); - vec[1]= vec[1] - ysize*floor(vec[1]/ysize + 0.5); -} - -// random offset of 1 in 2 -static void jitter_plane_offset(float *jitter1, float *jitter2, int tot, float sizex, float sizey, float ofsx, float ofsy) -{ - float dsizex= sizex*ofsx; - float dsizey= sizey*ofsy; - float hsizex= 0.5*sizex, hsizey= 0.5*sizey; - int x; - - for(x=tot; x>0; x--, jitter1+=2, jitter2+=2) { - jitter2[0]= jitter1[0] + dsizex; - jitter2[1]= jitter1[1] + dsizey; - if(jitter2[0] > hsizex) jitter2[0]-= sizex; - if(jitter2[1] > hsizey) jitter2[1]-= sizey; - } -} - -/* called from convertBlenderScene.c */ -/* we do this in advance to get consistant random, not alter the render seed, and be threadsafe */ -void init_jitter_plane(LampRen *lar) -{ - float *fp; - int x, iter=12, tot= lar->ray_totsamp; - - /* at least 4, or max threads+1 tables */ - if(BLENDER_MAX_THREADS < 4) x= 4; - else x= BLENDER_MAX_THREADS+1; - fp= lar->jitter= MEM_mallocN(x*tot*2*sizeof(float), "lamp jitter tab"); - - /* set per-lamp fixed seed */ - BLI_srandom(tot); - - /* fill table with random locations, area_size large */ - for(x=0; x<tot; x++, fp+=2) { - fp[0]= (BLI_frand()-0.5)*lar->area_size; - fp[1]= (BLI_frand()-0.5)*lar->area_sizey; - } - - while(iter--) { - fp= lar->jitter; - for(x=tot; x>0; x--, fp+=2) { - DP_energy(lar->jitter, fp, tot, lar->area_size, lar->area_sizey); - } - } - - /* create the dithered tables (could just check lamp type!) */ - jitter_plane_offset(lar->jitter, lar->jitter+2*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.0f); - jitter_plane_offset(lar->jitter, lar->jitter+4*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.5f); - jitter_plane_offset(lar->jitter, lar->jitter+6*tot, tot, lar->area_size, lar->area_sizey, 0.0f, 0.5f); -} - -/* table around origin, -0.5*size to 0.5*size */ -static float *give_jitter_plane(LampRen *lar, int thread, int xs, int ys) -{ - int tot; - - tot= lar->ray_totsamp; - - if(lar->ray_samp_type & LA_SAMP_JITTER) { - /* made it threadsafe */ - - if(lar->xold[thread]!=xs || lar->yold[thread]!=ys) { - jitter_plane_offset(lar->jitter, lar->jitter+2*(thread+1)*tot, tot, lar->area_size, lar->area_sizey, BLI_thread_frand(thread), BLI_thread_frand(thread)); - lar->xold[thread]= xs; - lar->yold[thread]= ys; - } - return lar->jitter+2*(thread+1)*tot; - } - if(lar->ray_samp_type & LA_SAMP_DITHER) { - return lar->jitter + 2*tot*((xs & 1)+2*(ys & 1)); - } - - return lar->jitter; -} - - -/* ***************** main calls ************** */ - - -/* extern call from render loop */ -void ray_trace(ShadeInput *shi, ShadeResult *shr) -{ - VlakRen *vlr; - float i, f, f1, fr, fg, fb, vec[3], mircol[4], tracol[4]; - float diff[3]; - int do_tra, do_mir; - - do_tra= ((shi->mat->mode & (MA_RAYTRANSP)) && shr->alpha!=1.0f); - do_mir= ((shi->mat->mode & MA_RAYMIRROR) && shi->ray_mirror!=0.0f); - vlr= shi->vlr; - - /* raytrace mirror amd refract like to separate the spec color */ - if(shi->combinedflag & SCE_PASS_SPEC) - VECSUB(diff, shr->combined, shr->spec) /* no ; */ - else - VECCOPY(diff, shr->combined); - - if(do_tra) { - float refract[3]; - float olddiff[3]; - - tracol[3]= shr->alpha; - - refraction(refract, shi->vn, shi->view, shi->ang); - traceray(shi, shi->mat->ray_depth_tra, shi->co, refract, tracol, shi->vlr, RAY_TRA|RAY_TRAFLIP); - - f= shr->alpha; f1= 1.0f-f; - fr= 1.0f+ shi->mat->filter*(shi->r-1.0f); - fg= 1.0f+ shi->mat->filter*(shi->g-1.0f); - fb= 1.0f+ shi->mat->filter*(shi->b-1.0f); - - /* for refract pass */ - VECCOPY(olddiff, diff); - - diff[0]= f*diff[0] + f1*fr*tracol[0]; - diff[1]= f*diff[1] + f1*fg*tracol[1]; - diff[2]= f*diff[2] + f1*fb*tracol[2]; - - if(shi->passflag & SCE_PASS_REFRACT) - VECSUB(shr->refr, diff, olddiff); - - if(shi->combinedflag & SCE_PASS_REFRACT) - VECCOPY(olddiff, diff); - - shr->alpha= tracol[3]; - } - - if(do_mir) { - - i= shi->ray_mirror*fresnel_fac(shi->view, shi->vn, shi->mat->fresnel_mir_i, shi->mat->fresnel_mir); - if(i!=0.0f) { - - fr= i*shi->mirr; - fg= i*shi->mirg; - fb= i*shi->mirb; - - if(vlr->flag & R_SMOOTH) - reflection(vec, shi->vn, shi->view, shi->facenor); - else - reflection(vec, shi->vn, shi->view, NULL); - - traceray(shi, shi->mat->ray_depth, shi->co, vec, mircol, shi->vlr, 0); - - if(shi->passflag & SCE_PASS_REFLECT) { - /* mirror pass is not blocked out with spec */ - shr->refl[0]= fr*mircol[0] - fr*diff[0]; - shr->refl[1]= fg*mircol[1] - fg*diff[1]; - shr->refl[2]= fb*mircol[2] - fb*diff[2]; - } - - if(shi->combinedflag & SCE_PASS_REFLECT) { - - f= fr*(1.0f-shr->spec[0]); f1= 1.0f-i; - diff[0]= f*mircol[0] + f1*diff[0]; - - f= fg*(1.0f-shr->spec[1]); f1= 1.0f-i; - diff[1]= f*mircol[1] + f1*diff[1]; - - f= fb*(1.0f-shr->spec[2]); f1= 1.0f-i; - diff[2]= f*mircol[2] + f1*diff[2]; - } - } - } - /* put back together */ - if(shi->combinedflag & SCE_PASS_SPEC) - VECADD(shr->combined, diff, shr->spec) /* no ; */ - else - VECCOPY(shr->combined, diff); -} - -/* color 'shadfac' passes through 'col' with alpha and filter */ -/* filter is only applied on alpha defined transparent part */ -static void addAlphaLight(float *shadfac, float *col, float alpha, float filter) -{ - float fr, fg, fb; - - fr= 1.0f+ filter*(col[0]-1.0f); - fg= 1.0f+ filter*(col[1]-1.0f); - fb= 1.0f+ filter*(col[2]-1.0f); - - shadfac[0]= alpha*col[0] + fr*(1.0f-alpha)*shadfac[0]; - shadfac[1]= alpha*col[1] + fg*(1.0f-alpha)*shadfac[1]; - shadfac[2]= alpha*col[2] + fb*(1.0f-alpha)*shadfac[2]; - - shadfac[3]= (1.0f-alpha)*shadfac[3]; -} - -static void ray_trace_shadow_tra(Isect *is, int depth, int traflag) -{ - /* ray to lamp, find first face that intersects, check alpha properties, - if it has col[3]>0.0f continue. so exit when alpha is full */ - ShadeInput shi; - ShadeResult shr; - - if( d3dda(is)) { - float d= 1.0f; - /* we got a face */ - - shi.depth= 1; /* only used to indicate tracing */ - shi.mask= 1; - shi.osatex= 0; - shi.thread= shi.sample= 0; - shi.lay= 0; - shi.passflag= 0; - shi.combinedflag= 0; - shi.do_preview= 0; - shi.light_override= NULL; - shi.mat_override= NULL; - - shade_ray(is, &shi, &shr); - if (traflag & RAY_TRA) - d= shade_by_transmission(is, &shi, &shr); - - /* mix colors based on shadfac (rgb + amount of light factor) */ - addAlphaLight(is->col, shr.diff, shr.alpha, d*shi.mat->filter); - - if(depth>0 && is->col[3]>0.0f) { - - /* adapt isect struct */ - VECCOPY(is->start, shi.co); - is->vlrorig= shi.vlr; - - ray_trace_shadow_tra(is, depth-1, traflag | RAY_TRA); - } - } -} - -/* not used, test function for ambient occlusion (yaf: pathlight) */ -/* main problem; has to be called within shading loop, giving unwanted recursion */ -int ray_trace_shadow_rad(ShadeInput *ship, ShadeResult *shr) -{ - static int counter=0, only_one= 0; - extern float hashvectf[]; - Isect isec; - ShadeInput shi; - ShadeResult shr_t; - float vec[3], accum[3], div= 0.0f; - int a; - - if(only_one) { - return 0; - } - only_one= 1; - - accum[0]= accum[1]= accum[2]= 0.0f; - isec.mode= DDA_MIRROR; - isec.vlrorig= ship->vlr; - - for(a=0; a<8*8; a++) { - - counter+=3; - counter %= 768; - VECCOPY(vec, hashvectf+counter); - if(ship->vn[0]*vec[0]+ship->vn[1]*vec[1]+ship->vn[2]*vec[2]>0.0f) { - vec[0]-= vec[0]; - vec[1]-= vec[1]; - vec[2]-= vec[2]; - } - VECCOPY(isec.start, ship->co); - isec.end[0]= isec.start[0] + R.oc.ocsize*vec[0]; - isec.end[1]= isec.start[1] + R.oc.ocsize*vec[1]; - isec.end[2]= isec.start[2] + R.oc.ocsize*vec[2]; - - if( d3dda(&isec)) { - float fac; - shade_ray(&isec, &shi, &shr_t); - fac= isec.labda*isec.labda; - fac= 1.0f; - accum[0]+= fac*(shr_t.diff[0]+shr_t.spec[0]); - accum[1]+= fac*(shr_t.diff[1]+shr_t.spec[1]); - accum[2]+= fac*(shr_t.diff[2]+shr_t.spec[2]); - div+= fac; - } - else div+= 1.0f; - } - - if(div!=0.0f) { - shr->diff[0]+= accum[0]/div; - shr->diff[1]+= accum[1]/div; - shr->diff[2]+= accum[2]/div; - } - shr->alpha= 1.0f; - - only_one= 0; - return 1; -} - -/* aolight: function to create random unit sphere vectors for total random sampling */ -static void RandomSpherical(float *v) -{ - float r; - v[2] = 2.f*BLI_frand()-1.f; - if ((r = 1.f - v[2]*v[2])>0.f) { - float a = 6.283185307f*BLI_frand(); - r = sqrt(r); - v[0] = r * cos(a); - v[1] = r * sin(a); - } - else v[2] = 1.f; -} - -/* calc distributed spherical energy */ -static void DS_energy(float *sphere, int tot, float *vec) -{ - float *fp, fac, force[3], res[3]; - int a; - - res[0]= res[1]= res[2]= 0.0f; - - for(a=0, fp=sphere; a<tot; a++, fp+=3) { - VecSubf(force, vec, fp); - fac= force[0]*force[0] + force[1]*force[1] + force[2]*force[2]; - if(fac!=0.0f) { - fac= 1.0f/fac; - res[0]+= fac*force[0]; - res[1]+= fac*force[1]; - res[2]+= fac*force[2]; - } - } - - VecMulf(res, 0.5); - VecAddf(vec, vec, res); - Normalize(vec); - -} - -/* called from convertBlenderScene.c */ -/* creates an equally distributed spherical sample pattern */ -/* and allocates threadsafe memory */ -void init_ao_sphere(World *wrld) -{ - float *fp; - int a, tot, iter= 16; - - /* we make twice the amount of samples, because only a hemisphere is used */ - tot= 2*wrld->aosamp*wrld->aosamp; - - wrld->aosphere= MEM_mallocN(3*tot*sizeof(float), "AO sphere"); - - /* fixed random */ - BLI_srandom(tot); - - /* init */ - fp= wrld->aosphere; - for(a=0; a<tot; a++, fp+= 3) { - RandomSpherical(fp); - } - - while(iter--) { - for(a=0, fp= wrld->aosphere; a<tot; a++, fp+= 3) { - DS_energy(wrld->aosphere, tot, fp); - } - } - - /* tables */ - wrld->aotables= MEM_mallocN(BLENDER_MAX_THREADS*3*tot*sizeof(float), "AO tables"); -} - -/* give per thread a table, we have to compare xs ys because of way OSA works... */ -static float *threadsafe_table_sphere(int test, int thread, int xs, int ys, int tot) -{ - static int xso[BLENDER_MAX_THREADS], yso[BLENDER_MAX_THREADS]; - static int firsttime= 1; - - if(firsttime) { - memset(xso, 255, sizeof(xso)); - memset(yso, 255, sizeof(yso)); - firsttime= 0; - } - - if(xs==xso[thread] && ys==yso[thread]) return R.wrld.aotables+ thread*tot*3; - if(test) return NULL; - xso[thread]= xs; yso[thread]= ys; - return R.wrld.aotables+ thread*tot*3; -} - -static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) -{ - int tot; - float *vec; - - if(resol>16) resol= 16; - - tot= 2*resol*resol; - - if (type & WO_AORNDSMP) { - static float sphere[2*3*256]; - int a; - - /* total random sampling. NOT THREADSAFE! (should be removed, is not useful) */ - vec= sphere; - for (a=0; a<tot; a++, vec+=3) { - RandomSpherical(vec); - } - - return sphere; - } - else { - float *sphere; - float cosfi, sinfi, cost, sint; - float ang, *vec1; - int a; - - sphere= threadsafe_table_sphere(1, thread, xs, ys, tot); // returns table if xs and ys were equal to last call - if(sphere==NULL) { - sphere= threadsafe_table_sphere(0, thread, xs, ys, tot); - - // random rotation - ang= BLI_thread_frand(thread); - sinfi= sin(ang); cosfi= cos(ang); - ang= BLI_thread_frand(thread); - sint= sin(ang); cost= cos(ang); - - vec= R.wrld.aosphere; - vec1= sphere; - for (a=0; a<tot; a++, vec+=3, vec1+=3) { - vec1[0]= cost*cosfi*vec[0] - sinfi*vec[1] + sint*cosfi*vec[2]; - vec1[1]= cost*sinfi*vec[0] + cosfi*vec[1] + sint*sinfi*vec[2]; - vec1[2]= -sint*vec[0] + cost*vec[2]; - } - } - return sphere; - } -} - - -/* extern call from shade_lamp_loop, ambient occlusion calculus */ -void ray_ao(ShadeInput *shi, float *shadfac) -{ - Isect isec; - float *vec, *nrm, div, bias, sh=0.0f; - float maxdist = R.wrld.aodist; - float dxyview[3]; - int j= -1, tot, actual=0, skyadded=0, aocolor; - - isec.vlrorig= shi->vlr; - isec.vlr_last= NULL; - isec.mode= (R.wrld.aomode & WO_AODIST)?DDA_SHADOW_TRA:DDA_SHADOW; - isec.lay= -1; - - shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; - - /* bias prevents smoothed faces to appear flat */ - if(shi->vlr->flag & R_SMOOTH) { - bias= G.scene->world->aobias; - nrm= shi->vn; - } - else { - bias= 0.0f; - nrm= shi->facenor; - } - - /* prevent sky colors to be added for only shadow (shadow becomes alpha) */ - aocolor= R.wrld.aocolor; - if(shi->mat->mode & MA_ONLYSHADOW) - aocolor= WO_AOPLAIN; - - vec= sphere_sampler(R.wrld.aomode, R.wrld.aosamp, shi->thread, shi->xs, shi->ys); - - // warning: since we use full sphere now, and dotproduct is below, we do twice as much - tot= 2*R.wrld.aosamp*R.wrld.aosamp; - - if(aocolor == WO_AOSKYTEX) { - dxyview[0]= 1.0f/(float)R.wrld.aosamp; - dxyview[1]= 1.0f/(float)R.wrld.aosamp; - dxyview[2]= 0.0f; - } - - while(tot--) { - - if ((vec[0]*nrm[0] + vec[1]*nrm[1] + vec[2]*nrm[2]) > bias) { - /* only ao samples for mask */ - if(R.r.mode & R_OSA) { - j++; - if(j==R.osa) j= 0; - if(!(shi->mask & (1<<j))) { - vec+=3; - continue; - } - } - - actual++; - - /* always set start/end, 3dda clips it */ - VECCOPY(isec.start, shi->co); - isec.end[0] = shi->co[0] - maxdist*vec[0]; - isec.end[1] = shi->co[1] - maxdist*vec[1]; - isec.end[2] = shi->co[2] - maxdist*vec[2]; - - /* do the trace */ - if (d3dda(&isec)) { - if (R.wrld.aomode & WO_AODIST) sh+= exp(-isec.labda*R.wrld.aodistfac); - else sh+= 1.0f; - } - else if(aocolor!=WO_AOPLAIN) { - float skycol[4]; - float fac, view[3]; - - view[0]= -vec[0]; - view[1]= -vec[1]; - view[2]= -vec[2]; - Normalize(view); - - if(aocolor==WO_AOSKYCOL) { - fac= 0.5*(1.0f+view[0]*R.grvec[0]+ view[1]*R.grvec[1]+ view[2]*R.grvec[2]); - shadfac[0]+= (1.0f-fac)*R.wrld.horr + fac*R.wrld.zenr; - shadfac[1]+= (1.0f-fac)*R.wrld.horg + fac*R.wrld.zeng; - shadfac[2]+= (1.0f-fac)*R.wrld.horb + fac*R.wrld.zenb; - } - else { /* WO_AOSKYTEX */ - shadeSkyView(skycol, isec.start, view, dxyview); - shadfac[0]+= skycol[0]; - shadfac[1]+= skycol[1]; - shadfac[2]+= skycol[2]; - } - skyadded++; - } - } - // samples - vec+= 3; - } - - if(actual==0) sh= 1.0f; - else sh = 1.0f - sh/((float)actual); - - if(aocolor!=WO_AOPLAIN && skyadded) { - div= sh/((float)skyadded); - - shadfac[0]*= div; // average color times distances/hits formula - shadfac[1]*= div; // average color times distances/hits formula - shadfac[2]*= div; // average color times distances/hits formula - } - else { - shadfac[0]= shadfac[1]= shadfac[2]= sh; - } -} - - - -/* extern call from shade_lamp_loop */ -void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) -{ - Isect isec; - float lampco[3]; - - /* setup isec */ - if(shi->mat->mode & MA_SHADOW_TRA) isec.mode= DDA_SHADOW_TRA; - else isec.mode= DDA_SHADOW; - - if(lar->mode & LA_LAYER) isec.lay= lar->lay; else isec.lay= -1; - - /* only when not mir tracing, first hit optimm */ - if(shi->depth==0) - isec.vlr_last= lar->vlr_last[shi->thread]; - else - isec.vlr_last= NULL; - - - if(lar->type==LA_SUN || lar->type==LA_HEMI) { - lampco[0]= shi->co[0] - R.oc.ocsize*lar->vec[0]; - lampco[1]= shi->co[1] - R.oc.ocsize*lar->vec[1]; - lampco[2]= shi->co[2] - R.oc.ocsize*lar->vec[2]; - } - else { - VECCOPY(lampco, lar->co); - } - - if(lar->ray_totsamp<2) { - - isec.vlrorig= shi->vlr; - shadfac[3]= 1.0f; // 1.0=full light - - /* set up isec vec */ - VECCOPY(isec.start, shi->co); - VECCOPY(isec.end, lampco); - - if(isec.mode==DDA_SHADOW_TRA) { - /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ - isec.col[0]= isec.col[1]= isec.col[2]= 1.0f; - isec.col[3]= 1.0f; - - ray_trace_shadow_tra(&isec, DEPTH_SHADOW_TRA, 0); - QUATCOPY(shadfac, isec.col); - //printf("shadfac %f %f %f %f\n", shadfac[0], shadfac[1], shadfac[2], shadfac[3]); - } - else if( d3dda(&isec)) shadfac[3]= 0.0f; - } - else { - /* area soft shadow */ - float *jitlamp; - float fac=0.0f, div=0.0f, vec[3]; - int a, j= -1, mask; - - if(isec.mode==DDA_SHADOW_TRA) { - shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; - } - else shadfac[3]= 1.0f; // 1.0=full light - - fac= 0.0f; - jitlamp= give_jitter_plane(lar, shi->thread, shi->xs, shi->ys); - - a= lar->ray_totsamp; - - /* this correction to make sure we always take at least 1 sample */ - mask= shi->mask; - if(a==4) mask |= (mask>>4)|(mask>>8); - else if(a==9) mask |= (mask>>9); - - while(a--) { - - if(R.r.mode & R_OSA) { - j++; - if(j>=R.osa) j= 0; - if(!(mask & (1<<j))) { - jitlamp+= 2; - continue; - } - } - - isec.vlrorig= shi->vlr; // ray_trace_shadow_tra changes it - - vec[0]= jitlamp[0]; - vec[1]= jitlamp[1]; - vec[2]= 0.0f; - Mat3MulVecfl(lar->mat, vec); - - /* set start and end, d3dda clips it */ - VECCOPY(isec.start, shi->co); - isec.end[0]= lampco[0]+vec[0]; - isec.end[1]= lampco[1]+vec[1]; - isec.end[2]= lampco[2]+vec[2]; - - if(isec.mode==DDA_SHADOW_TRA) { - /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ - isec.col[0]= isec.col[1]= isec.col[2]= 1.0f; - isec.col[3]= 1.0f; - - ray_trace_shadow_tra(&isec, DEPTH_SHADOW_TRA, 0); - shadfac[0] += isec.col[0]; - shadfac[1] += isec.col[1]; - shadfac[2] += isec.col[2]; - shadfac[3] += isec.col[3]; - } - else if( d3dda(&isec) ) fac+= 1.0f; - - div+= 1.0f; - jitlamp+= 2; - } - - if(isec.mode==DDA_SHADOW_TRA) { - shadfac[0] /= div; - shadfac[1] /= div; - shadfac[2] /= div; - shadfac[3] /= div; - } - else { - // sqrt makes nice umbra effect - if(lar->ray_samp_type & LA_SAMP_UMBRA) - shadfac[3]= sqrt(1.0f-fac/div); - else - shadfac[3]= 1.0f-fac/div; - } - } - - /* for first hit optim, set last interesected shadow face */ - if(shi->depth==0) - lar->vlr_last[shi->thread]= isec.vlr_last; - -} - -/* only when face points away from lamp, in direction of lamp, trace ray and find first exit point */ -void ray_translucent(ShadeInput *shi, LampRen *lar, float *distfac, float *co) -{ - Isect isec; - float lampco[3]; - - /* setup isec */ - isec.mode= DDA_SHADOW_TRA; - - if(lar->mode & LA_LAYER) isec.lay= lar->lay; else isec.lay= -1; - - if(lar->type==LA_SUN || lar->type==LA_HEMI) { - lampco[0]= shi->co[0] - R.oc.ocsize*lar->vec[0]; - lampco[1]= shi->co[1] - R.oc.ocsize*lar->vec[1]; - lampco[2]= shi->co[2] - R.oc.ocsize*lar->vec[2]; - } - else { - VECCOPY(lampco, lar->co); - } - - isec.vlrorig= shi->vlr; - - /* set up isec vec */ - VECCOPY(isec.start, shi->co); - VECCOPY(isec.end, lampco); - - if( d3dda(&isec)) { - /* we got a face */ - - /* render co */ - co[0]= isec.start[0]+isec.labda*(isec.vec[0]); - co[1]= isec.start[1]+isec.labda*(isec.vec[1]); - co[2]= isec.start[2]+isec.labda*(isec.vec[2]); - - *distfac= VecLength(isec.vec); - } - else - *distfac= 0.0f; -} - - diff --git a/source/blender/render/intern/source/rayshade.c b/source/blender/render/intern/source/rayshade.c new file mode 100644 index 00000000000..1e77b0c8f58 --- /dev/null +++ b/source/blender/render/intern/source/rayshade.c @@ -0,0 +1,1229 @@ +/** + * $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., 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 ***** + */ + +#include <math.h> +#include <string.h> +#include <stdlib.h> +#include <float.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_material_types.h" +#include "DNA_lamp_types.h" + +#include "BKE_global.h" +#include "BKE_node.h" +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" +#include "BLI_rand.h" +#include "BLI_jitter.h" + +#include "PIL_time.h" + +#include "render_types.h" +#include "renderpipeline.h" +#include "rendercore.h" +#include "renderdatabase.h" +#include "pixelblending.h" +#include "pixelshading.h" +#include "shading.h" +#include "texture.h" + +#include "RE_raytrace.h" + +#define RAY_TRA 1 +#define RAY_TRAFLIP 2 + +#define DEPTH_SHADOW_TRA 10 + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */ +/* only to be used here in this file, it's for speed */ +extern struct Render R; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4) +{ + VlakRen *vlr= (VlakRen*)face; + + *v1 = (vlr->v1)? vlr->v1->co: NULL; + *v2 = (vlr->v2)? vlr->v2->co: NULL; + *v3 = (vlr->v3)? vlr->v3->co: NULL; + *v4 = (vlr->v4)? vlr->v4->co: NULL; +} + +static int vlr_check_intersect(Isect *is, RayFace *face) +{ + VlakRen *vlr = (VlakRen*)face; + + /* I know... cpu cycle waste, might do smarter once */ + if(is->mode==RE_RAY_MIRROR) + return !(vlr->mat->mode & MA_ONLYCAST); + else + return (is->lay & vlr->lay); +} + +void freeraytree(Render *re) +{ + if(re->raytree) { + RE_ray_tree_free(re->raytree); + re->raytree= NULL; + } +} + +void makeraytree(Render *re) +{ + VlakRen *vlr= NULL; + float min[3], max[3]; + double lasttime= PIL_check_seconds_timer(); + int v, totface = 0; + + INIT_MINMAX(min, max); + + /* first min max raytree space */ + for(v=0;v<re->totvlak;v++) { + if((v & 255)==0) vlr= re->vlaknodes[v>>8].vlak; + else vlr++; + if(vlr->mat->mode & MA_TRACEBLE) { + if((vlr->mat->mode & MA_WIRE)==0) { + + DO_MINMAX(vlr->v1->co, min, max); + DO_MINMAX(vlr->v2->co, min, max); + DO_MINMAX(vlr->v3->co, min, max); + if(vlr->v4) { + DO_MINMAX(vlr->v4->co, min, max); + } + + totface++; + } + } + } + + if(min[0] > max[0]) return; /* empty raytree */ + + re->raytree= RE_ray_tree_create(re->r.ocres, totface, min, max, vlr_face_coords, vlr_check_intersect); + + for(v=0; v<re->totvlak; v++) { + if((v & 255)==0) { + double time= PIL_check_seconds_timer(); + + vlr= re->vlaknodes[v>>8].vlak; + if(re->test_break()) + break; + if(time-lasttime>1.0f) { + char str[32]; + sprintf(str, "Filling Octree: %d", v); + re->i.infostr= str; + re->stats_draw(&re->i); + re->i.infostr= NULL; + lasttime= time; + } + } + else vlr++; + + if(vlr->mat->mode & MA_TRACEBLE) { + if((vlr->mat->mode & MA_WIRE)==0) { + RE_ray_tree_add_face(re->raytree, (RayFace*)vlr); + } + } + } + + RE_ray_tree_done(re->raytree); + + re->i.infostr= NULL; + re->stats_draw(&re->i); +} + +static void shade_ray(Isect *is, ShadeInput *shi, ShadeResult *shr) +{ + VlakRen *vlr= (VlakRen*)is->face; + int osatex= 0, norflip; + + /* set up view vector */ + VECCOPY(shi->view, is->vec); + + /* render co */ + shi->co[0]= is->start[0]+is->labda*(shi->view[0]); + shi->co[1]= is->start[1]+is->labda*(shi->view[1]); + shi->co[2]= is->start[2]+is->labda*(shi->view[2]); + + Normalize(shi->view); + + shi->vlr= vlr; + shi->mat= vlr->mat; + memcpy(&shi->r, &shi->mat->r, 23*sizeof(float)); // note, keep this synced with render_types.h + shi->har= shi->mat->har; + + // Osa structs we leave unchanged now + SWAP(int, osatex, shi->osatex); + + shi->dxco[0]= shi->dxco[1]= shi->dxco[2]= 0.0f; + shi->dyco[0]= shi->dyco[1]= shi->dyco[2]= 0.0f; + + // but, set Osa stuff to zero where it can confuse texture code + if(shi->mat->texco & (TEXCO_NORM|TEXCO_REFL) ) { + shi->dxno[0]= shi->dxno[1]= shi->dxno[2]= 0.0f; + shi->dyno[0]= shi->dyno[1]= shi->dyno[2]= 0.0f; + } + + /* face normal, check for flip, need to set puno here */ + norflip= (vlr->n[0]*shi->view[0]+vlr->n[1]*shi->view[1]+vlr->n[2]*shi->view[2]) < 0.0f; + + if(norflip) + shi->puno= vlr->puno ^ 15;// only flip lower 4 bits + else + shi->puno= vlr->puno; + + if(vlr->v4) { + if(is->isect==2) + shade_input_set_triangle_i(shi, vlr, 2, 1, 3); + else + shade_input_set_triangle_i(shi, vlr, 0, 1, 3); + } + else { + shade_input_set_triangle_i(shi, vlr, 0, 1, 2); + } + + /* shade_input_set_triangle_i() sets facenor, now we flip */ + if(norflip) { + shi->facenor[0]= -vlr->n[0]; + shi->facenor[1]= -vlr->n[1]; + shi->facenor[2]= -vlr->n[2]; + } + + shi->u= is->u; + shi->v= is->v; + shi->dx_u= shi->dx_v= shi->dy_u= shi->dy_v= 0.0f; + shade_input_set_normals(shi); + shade_input_set_shade_texco(shi); + + if(is->mode==RE_RAY_SHADOW_TRA) + shade_color(shi, shr); + else { + if(shi->mat->nodetree && shi->mat->use_nodes) { + ntreeShaderExecTree(shi->mat->nodetree, shi, shr); + shi->mat= vlr->mat; /* shi->mat is being set in nodetree */ + } + else + shade_material_loop(shi, shr); + + /* raytrace likes to separate the spec color */ + VECSUB(shr->diff, shr->combined, shr->spec); + } + + SWAP(int, osatex, shi->osatex); // XXXXX!!!! + +} + +static int refraction(float *refract, float *n, float *view, float index) +{ + float dot, fac; + + VECCOPY(refract, view); + + dot= view[0]*n[0] + view[1]*n[1] + view[2]*n[2]; + + if(dot>0.0f) { + index = 1.0f/index; + fac= 1.0f - (1.0f - dot*dot)*index*index; + if(fac<= 0.0f) return 0; + fac= -dot*index + sqrt(fac); + } + else { + fac= 1.0f - (1.0f - dot*dot)*index*index; + if(fac<= 0.0f) return 0; + fac= -dot*index - sqrt(fac); + } + + refract[0]= index*view[0] + fac*n[0]; + refract[1]= index*view[1] + fac*n[1]; + refract[2]= index*view[2] + fac*n[2]; + + return 1; +} + +/* orn = original face normal */ +static void reflection(float *ref, float *n, float *view, float *orn) +{ + float f1; + + f1= -2.0f*(n[0]*view[0]+ n[1]*view[1]+ n[2]*view[2]); + + ref[0]= (view[0]+f1*n[0]); + ref[1]= (view[1]+f1*n[1]); + ref[2]= (view[2]+f1*n[2]); + + if(orn) { + /* test phong normals, then we should prevent vector going to the back */ + f1= ref[0]*orn[0]+ ref[1]*orn[1]+ ref[2]*orn[2]; + if(f1>0.0f) { + f1+= .01f; + ref[0]-= f1*orn[0]; + ref[1]-= f1*orn[1]; + ref[2]-= f1*orn[2]; + } + } +} + +#if 0 +static void color_combine(float *result, float fac1, float fac2, float *col1, float *col2) +{ + float col1t[3], col2t[3]; + + col1t[0]= sqrt(col1[0]); + col1t[1]= sqrt(col1[1]); + col1t[2]= sqrt(col1[2]); + col2t[0]= sqrt(col2[0]); + col2t[1]= sqrt(col2[1]); + col2t[2]= sqrt(col2[2]); + + result[0]= (fac1*col1t[0] + fac2*col2t[0]); + result[0]*= result[0]; + result[1]= (fac1*col1t[1] + fac2*col2t[1]); + result[1]*= result[1]; + result[2]= (fac1*col1t[2] + fac2*col2t[2]); + result[2]*= result[2]; +} +#endif + +static float shade_by_transmission(Isect *is, ShadeInput *shi, ShadeResult *shr) +{ + float dx, dy, dz, d, p; + + if (0 == (shi->mat->mode & (MA_RAYTRANSP|MA_ZTRA))) + return -1; + + if (shi->mat->tx_limit <= 0.0f) { + d= 1.0f; + } + else { + /* shi.co[] calculated by shade_ray() */ + dx= shi->co[0] - is->start[0]; + dy= shi->co[1] - is->start[1]; + dz= shi->co[2] - is->start[2]; + d= sqrt(dx*dx+dy*dy+dz*dz); + if (d > shi->mat->tx_limit) + d= shi->mat->tx_limit; + + p = shi->mat->tx_falloff; + if(p < 0.0f) p= 0.0f; + else if (p > 10.0f) p= 10.0f; + + shr->alpha *= pow(d, p); + if (shr->alpha > 1.0f) + shr->alpha= 1.0f; + } + + return d; +} + +/* the main recursive tracer itself */ +static void traceray(ShadeInput *origshi, short depth, float *start, float *vec, float *col, VlakRen *vlr, int traflag) +{ + ShadeInput shi; + ShadeResult shr; + Isect isec; + float f, f1, fr, fg, fb; + float ref[3], maxsize= RE_ray_tree_max_size(R.raytree); + + VECCOPY(isec.start, start); + isec.end[0]= start[0]+maxsize*vec[0]; + isec.end[1]= start[1]+maxsize*vec[1]; + isec.end[2]= start[2]+maxsize*vec[2]; + isec.mode= RE_RAY_MIRROR; + isec.faceorig= (RayFace*)vlr; + + if(RE_ray_tree_intersect(R.raytree, &isec)) { + float d= 1.0f; + + shi.mask= origshi->mask; + shi.osatex= origshi->osatex; + shi.depth= 1; /* only used to indicate tracing */ + shi.thread= origshi->thread; + shi.sample= 0; + shi.xs= origshi->xs; + shi.ys= origshi->ys; + shi.lay= origshi->lay; + shi.passflag= SCE_PASS_COMBINED; /* result of tracing needs no pass info */ + shi.combinedflag= 0xFFFFFF; /* ray trace does all options */ + shi.do_preview= 0; + shi.light_override= origshi->light_override; + shi.mat_override= origshi->mat_override; + + memset(&shr, 0, sizeof(ShadeResult)); + + shade_ray(&isec, &shi, &shr); + if (traflag & RAY_TRA) + d= shade_by_transmission(&isec, &shi, &shr); + + if(depth>0) { + + if(shi.mat->mode_l & (MA_RAYTRANSP|MA_ZTRA) && shr.alpha < 1.0f) { + float nf, f, f1, refract[3], tracol[4]; + + tracol[0]= shi.r; + tracol[1]= shi.g; + tracol[2]= shi.b; + tracol[3]= col[3]; // we pass on and accumulate alpha + + if(shi.mat->mode & MA_RAYTRANSP) { + /* odd depths: use normal facing viewer, otherwise flip */ + if(traflag & RAY_TRAFLIP) { + float norm[3]; + norm[0]= - shi.vn[0]; + norm[1]= - shi.vn[1]; + norm[2]= - shi.vn[2]; + if (!refraction(refract, norm, shi.view, shi.ang)) + reflection(refract, norm, shi.view, shi.vn); + } + else { + if (!refraction(refract, shi.vn, shi.view, shi.ang)) + reflection(refract, shi.vn, shi.view, shi.vn); + } + traflag |= RAY_TRA; + traceray(origshi, depth-1, shi.co, refract, tracol, shi.vlr, traflag ^ RAY_TRAFLIP); + } + else + traceray(origshi, depth-1, shi.co, shi.view, tracol, shi.vlr, 0); + + f= shr.alpha; f1= 1.0f-f; + nf= d * shi.mat->filter; + fr= 1.0f+ nf*(shi.r-1.0f); + fg= 1.0f+ nf*(shi.g-1.0f); + fb= 1.0f+ nf*(shi.b-1.0f); + shr.diff[0]= f*shr.diff[0] + f1*fr*tracol[0]; + shr.diff[1]= f*shr.diff[1] + f1*fg*tracol[1]; + shr.diff[2]= f*shr.diff[2] + f1*fb*tracol[2]; + + shr.spec[0] *=f; + shr.spec[1] *=f; + shr.spec[2] *=f; + + col[3]= f1*tracol[3] + f; + } + else + col[3]= 1.0f; + + if(shi.mat->mode_l & MA_RAYMIRROR) { + f= shi.ray_mirror; + if(f!=0.0f) f*= fresnel_fac(shi.view, shi.vn, shi.mat->fresnel_mir_i, shi.mat->fresnel_mir); + } + else f= 0.0f; + + if(f!=0.0f) { + float mircol[4]; + + reflection(ref, shi.vn, shi.view, NULL); + traceray(origshi, depth-1, shi.co, ref, mircol, shi.vlr, 0); + + f1= 1.0f-f; + + /* combine */ + //color_combine(col, f*fr*(1.0f-shr.spec[0]), f1, col, shr.diff); + //col[0]+= shr.spec[0]; + //col[1]+= shr.spec[1]; + //col[2]+= shr.spec[2]; + + fr= shi.mirr; + fg= shi.mirg; + fb= shi.mirb; + + col[0]= f*fr*(1.0f-shr.spec[0])*mircol[0] + f1*shr.diff[0] + shr.spec[0]; + col[1]= f*fg*(1.0f-shr.spec[1])*mircol[1] + f1*shr.diff[1] + shr.spec[1]; + col[2]= f*fb*(1.0f-shr.spec[2])*mircol[2] + f1*shr.diff[2] + shr.spec[2]; + } + else { + col[0]= shr.diff[0] + shr.spec[0]; + col[1]= shr.diff[1] + shr.spec[1]; + col[2]= shr.diff[2] + shr.spec[2]; + } + } + else { + col[0]= shr.diff[0] + shr.spec[0]; + col[1]= shr.diff[1] + shr.spec[1]; + col[2]= shr.diff[2] + shr.spec[2]; + } + + } + else { /* sky */ + VECCOPY(shi.view, vec); + Normalize(shi.view); + + shadeSkyView(col, isec.start, shi.view, NULL); + } +} + +/* **************** jitter blocks ********** */ + +/* calc distributed planar energy */ + +static void DP_energy(float *table, float *vec, int tot, float xsize, float ysize) +{ + int x, y, a; + float *fp, force[3], result[3]; + float dx, dy, dist, min; + + min= MIN2(xsize, ysize); + min*= min; + result[0]= result[1]= 0.0f; + + for(y= -1; y<2; y++) { + dy= ysize*y; + for(x= -1; x<2; x++) { + dx= xsize*x; + fp= table; + for(a=0; a<tot; a++, fp+= 2) { + force[0]= vec[0] - fp[0]-dx; + force[1]= vec[1] - fp[1]-dy; + dist= force[0]*force[0] + force[1]*force[1]; + if(dist < min && dist>0.0f) { + result[0]+= force[0]/dist; + result[1]+= force[1]/dist; + } + } + } + } + vec[0] += 0.1*min*result[0]/(float)tot; + vec[1] += 0.1*min*result[1]/(float)tot; + // cyclic clamping + vec[0]= vec[0] - xsize*floor(vec[0]/xsize + 0.5); + vec[1]= vec[1] - ysize*floor(vec[1]/ysize + 0.5); +} + +// random offset of 1 in 2 +static void jitter_plane_offset(float *jitter1, float *jitter2, int tot, float sizex, float sizey, float ofsx, float ofsy) +{ + float dsizex= sizex*ofsx; + float dsizey= sizey*ofsy; + float hsizex= 0.5*sizex, hsizey= 0.5*sizey; + int x; + + for(x=tot; x>0; x--, jitter1+=2, jitter2+=2) { + jitter2[0]= jitter1[0] + dsizex; + jitter2[1]= jitter1[1] + dsizey; + if(jitter2[0] > hsizex) jitter2[0]-= sizex; + if(jitter2[1] > hsizey) jitter2[1]-= sizey; + } +} + +/* called from convertBlenderScene.c */ +/* we do this in advance to get consistant random, not alter the render seed, and be threadsafe */ +void init_jitter_plane(LampRen *lar) +{ + float *fp; + int x, iter=12, tot= lar->ray_totsamp; + + /* at least 4, or max threads+1 tables */ + if(BLENDER_MAX_THREADS < 4) x= 4; + else x= BLENDER_MAX_THREADS+1; + fp= lar->jitter= MEM_mallocN(x*tot*2*sizeof(float), "lamp jitter tab"); + + /* set per-lamp fixed seed */ + BLI_srandom(tot); + + /* fill table with random locations, area_size large */ + for(x=0; x<tot; x++, fp+=2) { + fp[0]= (BLI_frand()-0.5)*lar->area_size; + fp[1]= (BLI_frand()-0.5)*lar->area_sizey; + } + + while(iter--) { + fp= lar->jitter; + for(x=tot; x>0; x--, fp+=2) { + DP_energy(lar->jitter, fp, tot, lar->area_size, lar->area_sizey); + } + } + + /* create the dithered tables (could just check lamp type!) */ + jitter_plane_offset(lar->jitter, lar->jitter+2*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.0f); + jitter_plane_offset(lar->jitter, lar->jitter+4*tot, tot, lar->area_size, lar->area_sizey, 0.5f, 0.5f); + jitter_plane_offset(lar->jitter, lar->jitter+6*tot, tot, lar->area_size, lar->area_sizey, 0.0f, 0.5f); +} + +/* table around origin, -0.5*size to 0.5*size */ +static float *give_jitter_plane(LampRen *lar, int thread, int xs, int ys) +{ + int tot; + + tot= lar->ray_totsamp; + + if(lar->ray_samp_type & LA_SAMP_JITTER) { + /* made it threadsafe */ + + if(lar->xold[thread]!=xs || lar->yold[thread]!=ys) { + jitter_plane_offset(lar->jitter, lar->jitter+2*(thread+1)*tot, tot, lar->area_size, lar->area_sizey, BLI_thread_frand(thread), BLI_thread_frand(thread)); + lar->xold[thread]= xs; + lar->yold[thread]= ys; + } + return lar->jitter+2*(thread+1)*tot; + } + if(lar->ray_samp_type & LA_SAMP_DITHER) { + return lar->jitter + 2*tot*((xs & 1)+2*(ys & 1)); + } + + return lar->jitter; +} + + +/* ***************** main calls ************** */ + + +/* extern call from render loop */ +void ray_trace(ShadeInput *shi, ShadeResult *shr) +{ + VlakRen *vlr; + float i, f, f1, fr, fg, fb, vec[3], mircol[4], tracol[4]; + float diff[3]; + int do_tra, do_mir; + + do_tra= ((shi->mat->mode & (MA_RAYTRANSP)) && shr->alpha!=1.0f); + do_mir= ((shi->mat->mode & MA_RAYMIRROR) && shi->ray_mirror!=0.0f); + vlr= shi->vlr; + + /* raytrace mirror amd refract like to separate the spec color */ + if(shi->combinedflag & SCE_PASS_SPEC) + VECSUB(diff, shr->combined, shr->spec) /* no ; */ + else + VECCOPY(diff, shr->combined); + + if(do_tra) { + float refract[3]; + float olddiff[3]; + + tracol[3]= shr->alpha; + + refraction(refract, shi->vn, shi->view, shi->ang); + traceray(shi, shi->mat->ray_depth_tra, shi->co, refract, tracol, shi->vlr, RAY_TRA|RAY_TRAFLIP); + + f= shr->alpha; f1= 1.0f-f; + fr= 1.0f+ shi->mat->filter*(shi->r-1.0f); + fg= 1.0f+ shi->mat->filter*(shi->g-1.0f); + fb= 1.0f+ shi->mat->filter*(shi->b-1.0f); + + /* for refract pass */ + VECCOPY(olddiff, diff); + + diff[0]= f*diff[0] + f1*fr*tracol[0]; + diff[1]= f*diff[1] + f1*fg*tracol[1]; + diff[2]= f*diff[2] + f1*fb*tracol[2]; + + if(shi->passflag & SCE_PASS_REFRACT) + VECSUB(shr->refr, diff, olddiff); + + if(shi->combinedflag & SCE_PASS_REFRACT) + VECCOPY(olddiff, diff); + + shr->alpha= tracol[3]; + } + + if(do_mir) { + + i= shi->ray_mirror*fresnel_fac(shi->view, shi->vn, shi->mat->fresnel_mir_i, shi->mat->fresnel_mir); + if(i!=0.0f) { + + fr= i*shi->mirr; + fg= i*shi->mirg; + fb= i*shi->mirb; + + if(vlr->flag & R_SMOOTH) + reflection(vec, shi->vn, shi->view, shi->facenor); + else + reflection(vec, shi->vn, shi->view, NULL); + + traceray(shi, shi->mat->ray_depth, shi->co, vec, mircol, shi->vlr, 0); + + if(shi->passflag & SCE_PASS_REFLECT) { + /* mirror pass is not blocked out with spec */ + shr->refl[0]= fr*mircol[0] - fr*diff[0]; + shr->refl[1]= fg*mircol[1] - fg*diff[1]; + shr->refl[2]= fb*mircol[2] - fb*diff[2]; + } + + if(shi->combinedflag & SCE_PASS_REFLECT) { + + f= fr*(1.0f-shr->spec[0]); f1= 1.0f-i; + diff[0]= f*mircol[0] + f1*diff[0]; + + f= fg*(1.0f-shr->spec[1]); f1= 1.0f-i; + diff[1]= f*mircol[1] + f1*diff[1]; + + f= fb*(1.0f-shr->spec[2]); f1= 1.0f-i; + diff[2]= f*mircol[2] + f1*diff[2]; + } + } + } + /* put back together */ + if(shi->combinedflag & SCE_PASS_SPEC) + VECADD(shr->combined, diff, shr->spec) /* no ; */ + else + VECCOPY(shr->combined, diff); +} + +/* color 'shadfac' passes through 'col' with alpha and filter */ +/* filter is only applied on alpha defined transparent part */ +static void addAlphaLight(float *shadfac, float *col, float alpha, float filter) +{ + float fr, fg, fb; + + fr= 1.0f+ filter*(col[0]-1.0f); + fg= 1.0f+ filter*(col[1]-1.0f); + fb= 1.0f+ filter*(col[2]-1.0f); + + shadfac[0]= alpha*col[0] + fr*(1.0f-alpha)*shadfac[0]; + shadfac[1]= alpha*col[1] + fg*(1.0f-alpha)*shadfac[1]; + shadfac[2]= alpha*col[2] + fb*(1.0f-alpha)*shadfac[2]; + + shadfac[3]= (1.0f-alpha)*shadfac[3]; +} + +static void ray_trace_shadow_tra(Isect *is, int depth, int traflag) +{ + /* ray to lamp, find first face that intersects, check alpha properties, + if it has col[3]>0.0f continue. so exit when alpha is full */ + ShadeInput shi; + ShadeResult shr; + + if(RE_ray_tree_intersect(R.raytree, is)) { + float d= 1.0f; + /* we got a face */ + + shi.depth= 1; /* only used to indicate tracing */ + shi.mask= 1; + shi.osatex= 0; + shi.thread= shi.sample= 0; + shi.lay= 0; + shi.passflag= 0; + shi.combinedflag= 0; + shi.do_preview= 0; + shi.light_override= NULL; + shi.mat_override= NULL; + + shade_ray(is, &shi, &shr); + if (traflag & RAY_TRA) + d= shade_by_transmission(is, &shi, &shr); + + /* mix colors based on shadfac (rgb + amount of light factor) */ + addAlphaLight(is->col, shr.diff, shr.alpha, d*shi.mat->filter); + + if(depth>0 && is->col[3]>0.0f) { + + /* adapt isect struct */ + VECCOPY(is->start, shi.co); + is->faceorig= (RayFace*)shi.vlr; + + ray_trace_shadow_tra(is, depth-1, traflag | RAY_TRA); + } + } +} + +/* not used, test function for ambient occlusion (yaf: pathlight) */ +/* main problem; has to be called within shading loop, giving unwanted recursion */ +int ray_trace_shadow_rad(ShadeInput *ship, ShadeResult *shr) +{ + static int counter=0, only_one= 0; + extern float hashvectf[]; + Isect isec; + ShadeInput shi; + ShadeResult shr_t; + float vec[3], accum[3], div= 0.0f, maxsize= RE_ray_tree_max_size(R.raytree); + int a; + + if(only_one) { + return 0; + } + only_one= 1; + + accum[0]= accum[1]= accum[2]= 0.0f; + isec.mode= RE_RAY_MIRROR; + isec.faceorig= (RayFace*)ship->vlr; + + for(a=0; a<8*8; a++) { + + counter+=3; + counter %= 768; + VECCOPY(vec, hashvectf+counter); + if(ship->vn[0]*vec[0]+ship->vn[1]*vec[1]+ship->vn[2]*vec[2]>0.0f) { + vec[0]-= vec[0]; + vec[1]-= vec[1]; + vec[2]-= vec[2]; + } + VECCOPY(isec.start, ship->co); + isec.end[0]= isec.start[0] + maxsize*vec[0]; + isec.end[1]= isec.start[1] + maxsize*vec[1]; + isec.end[2]= isec.start[2] + maxsize*vec[2]; + + if(RE_ray_tree_intersect(R.raytree, &isec)) { + float fac; + shade_ray(&isec, &shi, &shr_t); + fac= isec.labda*isec.labda; + fac= 1.0f; + accum[0]+= fac*(shr_t.diff[0]+shr_t.spec[0]); + accum[1]+= fac*(shr_t.diff[1]+shr_t.spec[1]); + accum[2]+= fac*(shr_t.diff[2]+shr_t.spec[2]); + div+= fac; + } + else div+= 1.0f; + } + + if(div!=0.0f) { + shr->diff[0]+= accum[0]/div; + shr->diff[1]+= accum[1]/div; + shr->diff[2]+= accum[2]/div; + } + shr->alpha= 1.0f; + + only_one= 0; + return 1; +} + +/* aolight: function to create random unit sphere vectors for total random sampling */ +static void RandomSpherical(float *v) +{ + float r; + v[2] = 2.f*BLI_frand()-1.f; + if ((r = 1.f - v[2]*v[2])>0.f) { + float a = 6.283185307f*BLI_frand(); + r = sqrt(r); + v[0] = r * cos(a); + v[1] = r * sin(a); + } + else v[2] = 1.f; +} + +/* calc distributed spherical energy */ +static void DS_energy(float *sphere, int tot, float *vec) +{ + float *fp, fac, force[3], res[3]; + int a; + + res[0]= res[1]= res[2]= 0.0f; + + for(a=0, fp=sphere; a<tot; a++, fp+=3) { + VecSubf(force, vec, fp); + fac= force[0]*force[0] + force[1]*force[1] + force[2]*force[2]; + if(fac!=0.0f) { + fac= 1.0f/fac; + res[0]+= fac*force[0]; + res[1]+= fac*force[1]; + res[2]+= fac*force[2]; + } + } + + VecMulf(res, 0.5); + VecAddf(vec, vec, res); + Normalize(vec); + +} + +/* called from convertBlenderScene.c */ +/* creates an equally distributed spherical sample pattern */ +/* and allocates threadsafe memory */ +void init_ao_sphere(World *wrld) +{ + float *fp; + int a, tot, iter= 16; + + /* we make twice the amount of samples, because only a hemisphere is used */ + tot= 2*wrld->aosamp*wrld->aosamp; + + wrld->aosphere= MEM_mallocN(3*tot*sizeof(float), "AO sphere"); + + /* fixed random */ + BLI_srandom(tot); + + /* init */ + fp= wrld->aosphere; + for(a=0; a<tot; a++, fp+= 3) { + RandomSpherical(fp); + } + + while(iter--) { + for(a=0, fp= wrld->aosphere; a<tot; a++, fp+= 3) { + DS_energy(wrld->aosphere, tot, fp); + } + } + + /* tables */ + wrld->aotables= MEM_mallocN(BLENDER_MAX_THREADS*3*tot*sizeof(float), "AO tables"); +} + +/* give per thread a table, we have to compare xs ys because of way OSA works... */ +static float *threadsafe_table_sphere(int test, int thread, int xs, int ys, int tot) +{ + static int xso[BLENDER_MAX_THREADS], yso[BLENDER_MAX_THREADS]; + static int firsttime= 1; + + if(firsttime) { + memset(xso, 255, sizeof(xso)); + memset(yso, 255, sizeof(yso)); + firsttime= 0; + } + + if(xs==xso[thread] && ys==yso[thread]) return R.wrld.aotables+ thread*tot*3; + if(test) return NULL; + xso[thread]= xs; yso[thread]= ys; + return R.wrld.aotables+ thread*tot*3; +} + +static float *sphere_sampler(int type, int resol, int thread, int xs, int ys) +{ + int tot; + float *vec; + + if(resol>16) resol= 16; + + tot= 2*resol*resol; + + if (type & WO_AORNDSMP) { + static float sphere[2*3*256]; + int a; + + /* total random sampling. NOT THREADSAFE! (should be removed, is not useful) */ + vec= sphere; + for (a=0; a<tot; a++, vec+=3) { + RandomSpherical(vec); + } + + return sphere; + } + else { + float *sphere; + float cosfi, sinfi, cost, sint; + float ang, *vec1; + int a; + + sphere= threadsafe_table_sphere(1, thread, xs, ys, tot); // returns table if xs and ys were equal to last call + if(sphere==NULL) { + sphere= threadsafe_table_sphere(0, thread, xs, ys, tot); + + // random rotation + ang= BLI_thread_frand(thread); + sinfi= sin(ang); cosfi= cos(ang); + ang= BLI_thread_frand(thread); + sint= sin(ang); cost= cos(ang); + + vec= R.wrld.aosphere; + vec1= sphere; + for (a=0; a<tot; a++, vec+=3, vec1+=3) { + vec1[0]= cost*cosfi*vec[0] - sinfi*vec[1] + sint*cosfi*vec[2]; + vec1[1]= cost*sinfi*vec[0] + cosfi*vec[1] + sint*sinfi*vec[2]; + vec1[2]= -sint*vec[0] + cost*vec[2]; + } + } + return sphere; + } +} + + +/* extern call from shade_lamp_loop, ambient occlusion calculus */ +void ray_ao(ShadeInput *shi, float *shadfac) +{ + Isect isec; + float *vec, *nrm, div, bias, sh=0.0f; + float maxdist = R.wrld.aodist; + float dxyview[3]; + int j= -1, tot, actual=0, skyadded=0, aocolor; + + isec.faceorig= (RayFace*)shi->vlr; + isec.face_last= NULL; + isec.mode= (R.wrld.aomode & WO_AODIST)?RE_RAY_SHADOW_TRA:RE_RAY_SHADOW; + isec.lay= -1; + + shadfac[0]= shadfac[1]= shadfac[2]= 0.0f; + + /* bias prevents smoothed faces to appear flat */ + if(shi->vlr->flag & R_SMOOTH) { + bias= G.scene->world->aobias; + nrm= shi->vn; + } + else { + bias= 0.0f; + nrm= shi->facenor; + } + + /* prevent sky colors to be added for only shadow (shadow becomes alpha) */ + aocolor= R.wrld.aocolor; + if(shi->mat->mode & MA_ONLYSHADOW) + aocolor= WO_AOPLAIN; + + vec= sphere_sampler(R.wrld.aomode, R.wrld.aosamp, shi->thread, shi->xs, shi->ys); + + // warning: since we use full sphere now, and dotproduct is below, we do twice as much + tot= 2*R.wrld.aosamp*R.wrld.aosamp; + + if(aocolor == WO_AOSKYTEX) { + dxyview[0]= 1.0f/(float)R.wrld.aosamp; + dxyview[1]= 1.0f/(float)R.wrld.aosamp; + dxyview[2]= 0.0f; + } + + while(tot--) { + + if ((vec[0]*nrm[0] + vec[1]*nrm[1] + vec[2]*nrm[2]) > bias) { + /* only ao samples for mask */ + if(R.r.mode & R_OSA) { + j++; + if(j==R.osa) j= 0; + if(!(shi->mask & (1<<j))) { + vec+=3; + continue; + } + } + + actual++; + + /* always set start/end, 3dda clips it */ + VECCOPY(isec.start, shi->co); + isec.end[0] = shi->co[0] - maxdist*vec[0]; + isec.end[1] = shi->co[1] - maxdist*vec[1]; + isec.end[2] = shi->co[2] - maxdist*vec[2]; + + /* do the trace */ + if(RE_ray_tree_intersect(R.raytree, &isec)) { + if (R.wrld.aomode & WO_AODIST) sh+= exp(-isec.labda*R.wrld.aodistfac); + else sh+= 1.0f; + } + else if(aocolor!=WO_AOPLAIN) { + float skycol[4]; + float fac, view[3]; + + view[0]= -vec[0]; + view[1]= -vec[1]; + view[2]= -vec[2]; + Normalize(view); + + if(aocolor==WO_AOSKYCOL) { + fac= 0.5*(1.0f+view[0]*R.grvec[0]+ view[1]*R.grvec[1]+ view[2]*R.grvec[2]); + shadfac[0]+= (1.0f-fac)*R.wrld.horr + fac*R.wrld.zenr; + shadfac[1]+= (1.0f-fac)*R.wrld.horg + fac*R.wrld.zeng; + shadfac[2]+= (1.0f-fac)*R.wrld.horb + fac*R.wrld.zenb; + } + else { /* WO_AOSKYTEX */ + shadeSkyView(skycol, isec.start, view, dxyview); + shadfac[0]+= skycol[0]; + shadfac[1]+= skycol[1]; + shadfac[2]+= skycol[2]; + } + skyadded++; + } + } + // samples + vec+= 3; + } + + if(actual==0) sh= 1.0f; + else sh = 1.0f - sh/((float)actual); + + if(aocolor!=WO_AOPLAIN && skyadded) { + div= sh/((float)skyadded); + + shadfac[0]*= div; // average color times distances/hits formula + shadfac[1]*= div; // average color times distances/hits formula + shadfac[2]*= div; // average color times distances/hits formula + } + else { + shadfac[0]= shadfac[1]= shadfac[2]= sh; + } +} + + + +/* extern call from shade_lamp_loop */ +void ray_shadow(ShadeInput *shi, LampRen *lar, float *shadfac) +{ + Isect isec; + float lampco[3], maxsize; + + /* setup isec */ + if(shi->mat->mode & MA_SHADOW_TRA) isec.mode= RE_RAY_SHADOW_TRA; + else isec.mode= RE_RAY_SHADOW; + + if(lar->mode & LA_LAYER) isec.lay= lar->lay; else isec.lay= -1; + + /* only when not mir tracing, first hit optimm */ + if(shi->depth==0) + isec.face_last= (RayFace*)lar->vlr_last[shi->thread]; + else + isec.face_last= NULL; + + + if(lar->type==LA_SUN || lar->type==LA_HEMI) { + maxsize= RE_ray_tree_max_size(R.raytree); + lampco[0]= shi->co[0] - maxsize*lar->vec[0]; + lampco[1]= shi->co[1] - maxsize*lar->vec[1]; + lampco[2]= shi->co[2] - maxsize*lar->vec[2]; + } + else { + VECCOPY(lampco, lar->co); + } + + if(lar->ray_totsamp<2) { + + isec.faceorig= (RayFace*)shi->vlr; + shadfac[3]= 1.0f; // 1.0=full light + + /* set up isec vec */ + VECCOPY(isec.start, shi->co); + VECCOPY(isec.end, lampco); + + if(isec.mode==RE_RAY_SHADOW_TRA) { + /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ + isec.col[0]= isec.col[1]= isec.col[2]= 1.0f; + isec.col[3]= 1.0f; + + ray_trace_shadow_tra(&isec, DEPTH_SHADOW_TRA, 0); + QUATCOPY(shadfac, isec.col); + //printf("shadfac %f %f %f %f\n", shadfac[0], shadfac[1], shadfac[2], shadfac[3]); + } + else if( RE_ray_tree_intersect(R.raytree, &isec)) shadfac[3]= 0.0f; + } + else { + /* area soft shadow */ + float *jitlamp; + float fac=0.0f, div=0.0f, vec[3]; + int a, j= -1, mask; + + if(isec.mode==RE_RAY_SHADOW_TRA) { + shadfac[0]= shadfac[1]= shadfac[2]= shadfac[3]= 0.0f; + } + else shadfac[3]= 1.0f; // 1.0=full light + + fac= 0.0f; + jitlamp= give_jitter_plane(lar, shi->thread, shi->xs, shi->ys); + + a= lar->ray_totsamp; + + /* this correction to make sure we always take at least 1 sample */ + mask= shi->mask; + if(a==4) mask |= (mask>>4)|(mask>>8); + else if(a==9) mask |= (mask>>9); + + while(a--) { + + if(R.r.mode & R_OSA) { + j++; + if(j>=R.osa) j= 0; + if(!(mask & (1<<j))) { + jitlamp+= 2; + continue; + } + } + + isec.faceorig= (RayFace*)shi->vlr; // ray_trace_shadow_tra changes it + + vec[0]= jitlamp[0]; + vec[1]= jitlamp[1]; + vec[2]= 0.0f; + Mat3MulVecfl(lar->mat, vec); + + /* set start and end, RE_ray_tree_intersect clips it */ + VECCOPY(isec.start, shi->co); + isec.end[0]= lampco[0]+vec[0]; + isec.end[1]= lampco[1]+vec[1]; + isec.end[2]= lampco[2]+vec[2]; + + if(isec.mode==RE_RAY_SHADOW_TRA) { + /* isec.col is like shadfac, so defines amount of light (0.0 is full shadow) */ + isec.col[0]= isec.col[1]= isec.col[2]= 1.0f; + isec.col[3]= 1.0f; + + ray_trace_shadow_tra(&isec, DEPTH_SHADOW_TRA, 0); + shadfac[0] += isec.col[0]; + shadfac[1] += isec.col[1]; + shadfac[2] += isec.col[2]; + shadfac[3] += isec.col[3]; + } + else if( RE_ray_tree_intersect(R.raytree, &isec) ) fac+= 1.0f; + + div+= 1.0f; + jitlamp+= 2; + } + + if(isec.mode==RE_RAY_SHADOW_TRA) { + shadfac[0] /= div; + shadfac[1] /= div; + shadfac[2] /= div; + shadfac[3] /= div; + } + else { + // sqrt makes nice umbra effect + if(lar->ray_samp_type & LA_SAMP_UMBRA) + shadfac[3]= sqrt(1.0f-fac/div); + else + shadfac[3]= 1.0f-fac/div; + } + } + + /* for first hit optim, set last interesected shadow face */ + if(shi->depth==0) + lar->vlr_last[shi->thread]= (VlakRen*)isec.face_last; + +} + +/* only when face points away from lamp, in direction of lamp, trace ray and find first exit point */ +void ray_translucent(ShadeInput *shi, LampRen *lar, float *distfac, float *co) +{ + Isect isec; + float lampco[3], maxsize; + + /* setup isec */ + isec.mode= RE_RAY_SHADOW_TRA; + + if(lar->mode & LA_LAYER) isec.lay= lar->lay; else isec.lay= -1; + + if(lar->type==LA_SUN || lar->type==LA_HEMI) { + maxsize= RE_ray_tree_max_size(R.raytree); + lampco[0]= shi->co[0] - maxsize*lar->vec[0]; + lampco[1]= shi->co[1] - maxsize*lar->vec[1]; + lampco[2]= shi->co[2] - maxsize*lar->vec[2]; + } + else { + VECCOPY(lampco, lar->co); + } + + isec.faceorig= (RayFace*)shi->vlr; + + /* set up isec vec */ + VECCOPY(isec.start, shi->co); + VECCOPY(isec.end, lampco); + + if(RE_ray_tree_intersect(R.raytree, &isec)) { + /* we got a face */ + + /* render co */ + co[0]= isec.start[0]+isec.labda*(isec.vec[0]); + co[1]= isec.start[1]+isec.labda*(isec.vec[1]); + co[2]= isec.start[2]+isec.labda*(isec.vec[2]); + + *distfac= VecLength(isec.vec); + } + else + *distfac= 0.0f; +} + + diff --git a/source/blender/render/intern/source/raytrace.c b/source/blender/render/intern/source/raytrace.c new file mode 100644 index 00000000000..5eb574227bd --- /dev/null +++ b/source/blender/render/intern/source/raytrace.c @@ -0,0 +1,1353 @@ +/** + * $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., 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 "MEM_guardedalloc.h" + +#include "DNA_material_types.h" + +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" + +#include "RE_raytrace.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 { + 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; + char *ocface; /* during building only */ + RayCoordsFunc coordsfunc; + RayCheckFunc checkfunc; +} Octree; + +/* ******** globals ***************** */ + +/* just for statistics */ +static int raycount; +static int accepted, rejected, coherent_ray; + + +/* **************** 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]= 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; + + ocmax=ocmin+3; + + 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]; + } + } + } +} + +void RE_ray_tree_free(RayTree *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); +} + +RayTree *RE_ray_tree_create(int ocres, int totface, float *min, float *max, RayCoordsFunc coordsfunc, RayCheckFunc checkfunc) +{ + Octree *oc; + float t00, t01, t02; + int c, ocres2; + + oc= MEM_callocN(sizeof(Octree), "Octree"); + oc->adrbranch= MEM_callocN(sizeof(void *)*BRANCH_ARRAY, "octree branches"); + oc->adrnode= MEM_callocN(sizeof(void *)*NODE_ARRAY, "octree nodes"); + + oc->coordsfunc= coordsfunc; + oc->checkfunc= checkfunc; + + /* only for debug info */ + raycount=0; + accepted= 0; + rejected= 0; + coherent_ray= 0; + + /* fill main octree struct */ + oc->ocres= ocres; + ocres2= oc->ocres*oc->ocres; + + VECCOPY(oc->min, min); + VECCOPY(oc->max, max); + + 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 */ + + return (RayTree*)oc; +} + +void RE_ray_tree_add_face(RayTree *tree, RayFace *face) +{ + Octree *oc = (Octree*)tree;; + float *v1, *v2, *v3, *v4, ocfac[3], rtf[4][3]; + short rts[4][3], ocmin[6], *ocmax; + 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; + + ocmax= ocmin+3; + + oc->coordsfunc(face, &v1, &v2, &v3, &v4); + + for(c=0;c<3;c++) { + rtf[0][c]= (v1[c]-oc->min[c])*ocfac[c] ; + rts[0][c]= (short)rtf[0][c]; + rtf[1][c]= (v2[c]-oc->min[c])*ocfac[c] ; + rts[1][c]= (short)rtf[1][c]; + rtf[2][c]= (v3[c]-oc->min[c])*ocfac[c] ; + rts[2][c]= (short)rtf[2][c]; + if(v4) { + rtf[3][c]= (v4[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(v4==NULL) { + 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, (v4 != NULL), 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(v4==NULL) { + 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); + filltriangle(oc, 0,2,ocface,ocmin); + filltriangle(oc, 1,2,ocface+2*ocres2,ocmin); + + /* 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, (v4 != NULL), 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; + } + } + } + } +} + +void RE_ray_tree_done(RayTree *tree) +{ + Octree *oc= (Octree*)tree; + + MEM_freeN(oc->ocface); + oc->ocface= NULL; +} + +/* ************ raytracer **************** */ + +/* only for self-intersecting test with current render face (where ray left) */ +static int intersection2(RayFace *face, RayCoordsFunc coordsfunc, float r0, float r1, float r2, float rx1, float ry1, float rz1) +{ + float *v1, *v2, *v3, *v4; + float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22; + float m0, m1, m2, divdet, det, det1; + float u1, v, u2; + + coordsfunc(face, &v1, &v2, &v3, &v4); + + /* happens for baking with non existing face */ + if(v1==NULL) + return 1; + + if (v4) { + SWAP(float*, v3, v4); + } + + t00= v3[0]-v1[0]; + t01= v3[1]-v1[1]; + t02= v3[2]-v1[2]; + t10= v3[0]-v2[0]; + t11= v3[1]-v2[1]; + t12= v3[2]-v2[2]; + + x0= t11*r2-t12*r1; + x1= t12*r0-t10*r2; + x2= t10*r1-t11*r0; + + divdet= t00*x0+t01*x1+t02*x2; + + m0= rx1-v3[0]; + m1= ry1-v3[1]; + m2= rz1-v3[2]; + det1= m0*x0+m1*x1+m2*x2; + + if(divdet!=0.0f) { + u1= det1/divdet; + + if(u1<=0.0f) { + det= t00*(m1*r2-m2*r1); + det+= t01*(m2*r0-m0*r2); + det+= t02*(m0*r1-m1*r0); + v= det/divdet; + + if(v<=0.0f && (u1 + v) >= -1.0f) { + return 1; + } + } + } + + if(v4) { + + t20= v3[0]-v4[0]; + t21= v3[1]-v4[1]; + t22= v3[2]-v4[2]; + + divdet= t20*x0+t21*x1+t22*x2; + if(divdet!=0.0f) { + u2= det1/divdet; + + if(u2<=0.0f) { + det= t20*(m1*r2-m2*r1); + det+= t21*(m2*r0-m0*r2); + det+= t22*(m0*r1-m1*r0); + v= det/divdet; + + if(v<=0.0f && (u2 + v) >= -1.0f) { + return 2; + } + } + } + } + return 0; +} + +#if 0 +/* ray - line intersection */ +/* disabled until i got real & fast cylinder checking, this code doesnt work proper +for faster strands */ + +static int intersection_strand(Isect *is) +{ + float v1[3], v2[3]; /* length of strand */ + float axis[3], rc[3], nor[3], radline, dist, len; + + /* radius strand */ + radline= 0.5f*VecLenf(is->vlr->v1->co, is->vlr->v2->co); + + VecMidf(v1, is->vlr->v1->co, is->vlr->v2->co); + VecMidf(v2, is->vlr->v3->co, is->vlr->v4->co); + + VECSUB(rc, v1, is->start); /* vector from base ray to base cylinder */ + VECSUB(axis, v2, v1); /* cylinder axis */ + + CROSS(nor, is->vec, axis); + len= VecLength(nor); + + if(len<FLT_EPSILON) + return 0; + + dist= INPR(rc, nor)/len; /* distance between ray and axis cylinder */ + + if(dist<radline && dist>-radline) { + float dot1, dot2, dot3, rlen, alen, div; + float labda; + + /* calculating the intersection point of shortest distance */ + dot1 = INPR(rc, is->vec); + dot2 = INPR(is->vec, axis); + dot3 = INPR(rc, axis); + rlen = INPR(is->vec, is->vec); + alen = INPR(axis, axis); + + div = alen * rlen - dot2 * dot2; + if (ABS(div) < FLT_EPSILON) + return 0; + + labda = (dot1*dot2 - dot3*rlen)/div; + + radline/= sqrt(alen); + + /* labda: where on axis do we have closest intersection? */ + if(labda >= -radline && labda <= 1.0f+radline) { + VlakRen *vlr= is->faceorig; + VertRen *v1= is->vlr->v1, *v2= is->vlr->v2, *v3= is->vlr->v3, *v4= is->vlr->v4; + /* but we dont do shadows from faces sharing edge */ + + if(v1==vlr->v1 || v2==vlr->v1 || v3==vlr->v1 || v4==vlr->v1) return 0; + if(v1==vlr->v2 || v2==vlr->v2 || v3==vlr->v2 || v4==vlr->v2) return 0; + if(v1==vlr->v3 || v2==vlr->v3 || v3==vlr->v3 || v4==vlr->v3) return 0; + if(vlr->v4) { + if(v1==vlr->v4 || v2==vlr->v4 || v3==vlr->v4 || v4==vlr->v4) return 0; + } + return 1; + } + } + return 0; +} +#endif + +/* ray - triangle or quad intersection */ +int RE_ray_face_intersection(Isect *is, RayCoordsFunc coordsfunc) +{ + RayFace *face= is->face; + float *v1,*v2,*v3,*v4; + float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22,r0,r1,r2; + float m0, m1, m2, divdet, det1; + short ok=0; + + /* disabled until i got real & fast cylinder checking, this code doesnt work proper + for faster strands */ +// if(is->mode==RE_RAY_SHADOW && is->vlr->flag & R_STRAND) +// return intersection_strand(is); + + coordsfunc(face, &v1, &v2, &v3, &v4); + + if (v4) { + SWAP(float*, v3, v4); + } + + t00= v3[0]-v1[0]; + t01= v3[1]-v1[1]; + t02= v3[2]-v1[2]; + t10= v3[0]-v2[0]; + t11= v3[1]-v2[1]; + t12= v3[2]-v2[2]; + + r0= is->vec[0]; + r1= is->vec[1]; + r2= is->vec[2]; + + x0= t12*r1-t11*r2; + x1= t10*r2-t12*r0; + x2= t11*r0-t10*r1; + + divdet= t00*x0+t01*x1+t02*x2; + + m0= is->start[0]-v3[0]; + m1= is->start[1]-v3[1]; + m2= is->start[2]-v3[2]; + det1= m0*x0+m1*x1+m2*x2; + + if(divdet!=0.0f) { + float u; + + divdet= 1.0f/divdet; + u= det1*divdet; + if(u<0.0f && u>-1.0f) { + float v, cros0, cros1, cros2; + + cros0= m1*t02-m2*t01; + cros1= m2*t00-m0*t02; + cros2= m0*t01-m1*t00; + v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); + + if(v<0.0f && (u + v) > -1.0f) { + float labda; + labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); + + if(labda>0.0f && labda<1.0f) { + is->labda= labda; + is->u= u; is->v= v; + ok= 1; + } + } + } + } + + if(ok==0 && v4) { + + t20= v3[0]-v4[0]; + t21= v3[1]-v4[1]; + t22= v3[2]-v4[2]; + + divdet= t20*x0+t21*x1+t22*x2; + if(divdet!=0.0f) { + float u; + divdet= 1.0f/divdet; + u = det1*divdet; + + if(u<0.0f && u>-1.0f) { + float v, cros0, cros1, cros2; + cros0= m1*t22-m2*t21; + cros1= m2*t20-m0*t22; + cros2= m0*t21-m1*t20; + v= divdet*(cros0*r0 + cros1*r1 + cros2*r2); + + if(v<0.0f && (u + v) > -1.0f) { + float labda; + labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12); + + if(labda>0.0f && labda<1.0f) { + ok= 2; + is->labda= labda; + is->u= u; is->v= v; + } + } + } + } + } + + if(ok) { + is->isect= ok; // wich half of the quad + + if(is->mode!=RE_RAY_SHADOW) { + /* for mirror & tra-shadow: large faces can be filled in too often, this prevents + a face being detected too soon... */ + if(is->labda > is->ddalabda) { + return 0; + } + } + + /* when a shadow ray leaves a face, it can be little outside the edges of it, causing + intersection to be detected in its neighbour face */ + + if(is->facecontr && is->faceisect); // optimizing, the tests below are not needed + else if(is->labda< .1) { + RayFace *face= is->faceorig; + float *origv1, *origv2, *origv3, *origv4; + short de= 0; + + coordsfunc(face, &origv1, &origv2, &origv3, &origv4); + + if(v1==origv1 || v2==origv1 || v3==origv1 || v4==origv1) de++; + if(v1==origv2 || v2==origv2 || v3==origv2 || v4==origv2) de++; + if(v1==origv3 || v2==origv3 || v3==origv3 || v4==origv3) de++; + if(origv4) { + if(v1==origv4 || v2==origv4 || v3==origv4 || v4==origv4) de++; + } + if(de) { + /* so there's a shared edge or vertex, let's intersect ray with face + itself, if that's true we can safely return 1, otherwise we assume + the intersection is invalid, 0 */ + + if(is->facecontr==NULL) { + is->facecontr= face; + is->faceisect= intersection2(face, coordsfunc, -r0, -r1, -r2, is->start[0], is->start[1], is->start[2]); + } + + if(is->faceisect) return 1; + return 0; + } + } + + return 1; + } + + return 0; +} + +/* check all faces in this node */ +static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval) +{ + RayFace *face; + short nr=0; + OcVal *ov; + + /* return on any first hit */ + if(is->mode==RE_RAY_SHADOW) { + + face= no->v[0]; + while(face) { + + if(is->faceorig != face) { + + if(oc->checkfunc(is, face)) { + + ov= no->ov+nr; + if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { + //accepted++; + is->face= face; + + if(RE_ray_face_intersection(is, oc->coordsfunc)) { + is->face_last= face; + return 1; + } + } + //else rejected++; + } + } + + nr++; + if(nr==8) { + no= no->next; + if(no==0) return 0; + nr=0; + } + face= no->v[nr]; + } + } + else { /* else mirror or glass or shadowtra, return closest face */ + Isect isect; + int found= 0; + + is->labda= 1.0f; /* needed? */ + isect= *is; /* copy for sorting */ + + face= no->v[0]; + while(face) { + + if(is->faceorig != face) { + if(oc->checkfunc(is, face)) { + ov= no->ov+nr; + if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) { + //accepted++; + + isect.face= face; + if(RE_ray_face_intersection(&isect, oc->coordsfunc)) { + if(isect.labda<is->labda) *is= isect; + found= 1; + } + } + //else rejected++; + } + } + + nr++; + if(nr==8) { + no= no->next; + if(no==NULL) break; + nr=0; + } + face= no->v[nr]; + } + + 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->faceorig */ +int RE_ray_tree_intersect(RayTree *tree, Isect *is) +{ + Octree *oc= (Octree*)tree; + Node *no; + OcVal ocval; + float vec1[3], vec2[3]; + float u1,u2,ox1,ox2,oy1,oy2,oz1,oz2; + float labdao,labdax,ldx,labday,ldy,labdaz,ldz, ddalabda; + 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 */ + is->facecontr= NULL; /* to check shared edge */ + is->faceisect= is->isect= 0; /* shared edge, quad half flag */ + + /* only for shadow! */ + if(is->mode==RE_RAY_SHADOW) { + + /* check with last intersected shadow face */ + if(is->face_last!=NULL && is->face_last!=is->faceorig) { + if(oc->checkfunc(is, is->face_last)) { + is->face= is->face_last; + VECSUB(is->vec, is->end, is->start); + if(RE_ray_face_intersection(is, oc->coordsfunc)) return 1; + } + } + } + + ldx= is->end[0] - is->start[0]; + u1= 0.0f; + u2= 1.0f; + + /* clip with octree cube */ + if(cliptest(-ldx, is->start[0]-oc->min[0], &u1,&u2)) { + if(cliptest(ldx, oc->max[0]-is->start[0], &u1,&u2)) { + ldy= is->end[1] - is->start[1]; + if(cliptest(-ldy, is->start[1]-oc->min[1], &u1,&u2)) { + if(cliptest(ldy, oc->max[1]-is->start[1], &u1,&u2)) { + ldz= is->end[2] - is->start[2]; + if(cliptest(-ldz, is->start[2]-oc->min[2], &u1,&u2)) { + if(cliptest(ldz, oc->max[2]-is->start[2], &u1,&u2)) { + c1=1; + if(u2<1.0f) { + is->end[0]= is->start[0]+u2*ldx; + is->end[1]= is->start[1]+u2*ldy; + is->end[2]= is->start[2]+u2*ldz; + } + if(u1>0.0f) { + is->start[0]+=u1*ldx; + is->start[1]+=u1*ldy; + is->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= (is->start[0]-oc->min[0])*oc->ocfacx; + oy1= (is->start[1]-oc->min[1])*oc->ocfacy; + oz1= (is->start[2]-oc->min[2])*oc->ocfacz; + ox2= (is->end[0]-oc->min[0])*oc->ocfacx; + oy2= (is->end[1]-oc->min[1])*oc->ocfacy; + oz2= (is->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; + + /* for intersection */ + VECSUB(is->vec, is->end, is->start); + + 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); + is->ddalabda= 1.0f; + if( testnode(oc, is, no, ocval) ) return 1; + } + } + else { + //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->ddalabda= ddalabda; + if( testnode(oc, is, no, ocval) ) return 1; + } + + 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 */ + is->face_last= NULL; + return 0; +} + +float RE_ray_tree_max_size(RayTree *tree) +{ + return ((Octree*)tree)->ocsize; +} + |