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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>2007-07-26 17:38:24 +0400
committerBrecht Van Lommel <brechtvanlommel@pandora.be>2007-07-26 17:38:24 +0400
commitd63da45ca8483d8a977a68533eafe51ea0b7dbf3 (patch)
tree7076cbde8ca18c8dabe38d0a59a6f2fb837017da
parent534ba14d2bb6e7f413399982587f6496db3082be (diff)
Refactor the raytracing code to split the tracing and shading parts into
two separate files, raytrace.c and rayshade.c. The tracing code can now be used separately from the renderer (will be used in a later commit), and the raytracing acceleration structure can now also be easily replaced, if someone wants to experiment with that.
-rw-r--r--source/blender/render/extern/include/RE_raytrace.h90
-rw-r--r--source/blender/render/intern/include/render_types.h13
-rw-r--r--source/blender/render/intern/include/rendercore.h4
-rw-r--r--source/blender/render/intern/source/convertblender.c12
-rw-r--r--source/blender/render/intern/source/envmap.c5
-rw-r--r--source/blender/render/intern/source/ray.c2469
-rw-r--r--source/blender/render/intern/source/rayshade.c1229
-rw-r--r--source/blender/render/intern/source/raytrace.c1353
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;
+}
+