Merged Soc 2009 - raytrace optimization [0]

from branch [1] at rev 23647

[0] - http://wiki.blender.org/index.php/User:Jaguarandi/SummerOfCode2009/
[1] - https://svn.blender.org/svnroot/bf-blender/branches/soc-2009-jaguarandi
This commit is contained in:
Andre Susano Pinto 2009-10-06 02:56:11 +00:00
commit 04b60b4324
38 changed files with 5174 additions and 1024 deletions

@ -179,8 +179,13 @@ class SCENE_PT_performance(RenderButtonsPanel):
sub.itemR(rd, "free_image_textures")
sub = col.column()
sub.active = rd.render_raytracing
sub.itemL(text="Ray Tracing Octree:")
sub.itemR(rd, "octree_resolution", text="")
sub.itemL(text="Acceleration structure:")
sub.itemR(rd, "raytrace_structure", text="")
if rd.raytrace_structure == "OCTREE":
sub.itemR(rd, "octree_resolution", text="Resolution")
else:
sub.itemR(rd, "use_instances", text="Instances")
sub.itemR(rd, "use_local_coords", text="Local Coordinates")
class SCENE_PT_post_processing(RenderButtonsPanel):
__label__ = "Post Processing"

@ -75,6 +75,14 @@
#define INIT_MINMAX2(min, max) { (min)[0]= (min)[1]= 1.0e30f; (max)[0]= (max)[1]= -1.0e30f; }
#define DO_MIN(vec, min) { if( (min)[0]>(vec)[0] ) (min)[0]= (vec)[0]; \
if( (min)[1]>(vec)[1] ) (min)[1]= (vec)[1]; \
if( (min)[2]>(vec)[2] ) (min)[2]= (vec)[2]; } \
#define DO_MAX(vec, max) { if( (max)[0]<(vec)[0] ) (max)[0]= (vec)[0]; \
if( (max)[1]<(vec)[1] ) (max)[1]= (vec)[1]; \
if( (max)[2]<(vec)[2] ) (max)[2]= (vec)[2]; } \
#define DO_MINMAX(vec, min, max) { if( (min)[0]>(vec)[0] ) (min)[0]= (vec)[0]; \
if( (min)[1]>(vec)[1] ) (min)[1]= (vec)[1]; \
if( (min)[2]>(vec)[2] ) (min)[2]= (vec)[2]; \

@ -37,6 +37,10 @@
#ifndef BLI_MEMARENA_H
#define BLI_MEMARENA_H
#ifdef __cplusplus
extern "C" {
#endif
/* A reasonable standard buffer size, big
* enough to not cause much internal fragmentation,
* small enough not to waste resources
@ -53,7 +57,14 @@ void BLI_memarena_free (struct MemArena *ma);
void BLI_memarena_use_malloc (struct MemArena *ma);
void BLI_memarena_use_calloc (struct MemArena *ma);
void BLI_memarena_use_align(struct MemArena *ma, int align);
void* BLI_memarena_alloc (struct MemArena *ma, int size);
#ifdef __cplusplus
}
#endif
#endif

@ -52,6 +52,7 @@ typedef struct BVHNode
{
struct BVHNode **children;
struct BVHNode *parent; // some user defined traversed need that
struct BVHNode *skip[2];
float *bv; // Bounding volume of all nodes, max 13 axis
int index; // face, edge, vertex index
char totnode; // how many nodes are used, used for speedup
@ -101,6 +102,8 @@ typedef struct BVHRayCastData
BVHTreeRay ray;
float ray_dot_axis[13];
float idot_axis[13];
int index[6];
BVHTreeRayHit hit;
} BVHRayCastData;
@ -353,6 +356,23 @@ static int partition_nth_element(BVHNode **a, int _begin, int _end, int n, int a
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
static void build_skip_links(BVHTree *tree, BVHNode *node, BVHNode *left, BVHNode *right)
{
int i;
node->skip[0] = left;
node->skip[1] = right;
for (i = 0; i < node->totnode; i++)
{
if(i+1 < node->totnode)
build_skip_links(tree, node->children[i], left, node->children[i+1] );
else
build_skip_links(tree, node->children[i], left, right );
left = node->children[i];
}
}
/*
* BVHTree bounding volumes functions
@ -939,6 +959,7 @@ void BLI_bvhtree_balance(BVHTree *tree)
for(i = 0; i < tree->totbranch; i++)
tree->nodes[tree->totleaf + i] = branches_array + i;
build_skip_links(tree, tree->nodes[tree->totleaf], NULL, NULL);
//bvhtree_info(tree);
}
@ -1405,6 +1426,7 @@ int BLI_bvhtree_find_nearest(BVHTree *tree, const float *co, BVHTreeNearest *nea
* raycast is done by performing a DFS on the BVHTree and saving the closest hit
*/
//Determines the distance that the ray must travel to hit the bounding volume of the given node
static float ray_nearest_hit(BVHRayCastData *data, float *bv)
{
@ -1443,13 +1465,40 @@ static float ray_nearest_hit(BVHRayCastData *data, float *bv)
return low;
}
//Determines the distance that the ray must travel to hit the bounding volume of the given node
//Based on Tactical Optimization of Ray/Box Intersection, by Graham Fyffe
//[http://tog.acm.org/resources/RTNews/html/rtnv21n1.html#art9]
//
//TODO this doens't has data->ray.radius in consideration
static float fast_ray_nearest_hit(const BVHRayCastData *data, const BVHNode *node)
{
const float *bv = node->bv;
float dist;
float t1x = (bv[data->index[0]] - data->ray.origin[0]) * data->idot_axis[0];
float t2x = (bv[data->index[1]] - data->ray.origin[0]) * data->idot_axis[0];
float t1y = (bv[data->index[2]] - data->ray.origin[1]) * data->idot_axis[1];
float t2y = (bv[data->index[3]] - data->ray.origin[1]) * data->idot_axis[1];
float t1z = (bv[data->index[4]] - data->ray.origin[2]) * data->idot_axis[2];
float t2z = (bv[data->index[5]] - data->ray.origin[2]) * data->idot_axis[2];
if(t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return FLT_MAX;
if(t2x < 0.0 || t2y < 0.0 || t2z < 0.0) return FLT_MAX;
if(t1x > data->hit.dist || t1y > data->hit.dist || t1z > data->hit.dist) return FLT_MAX;
dist = t1x;
if (t1y > dist) dist = t1y;
if (t1z > dist) dist = t1z;
return dist;
}
static void dfs_raycast(BVHRayCastData *data, BVHNode *node)
{
int i;
//ray-bv is really fast.. and simple tests revealed its worth to test it
//before calling the ray-primitive functions
float dist = ray_nearest_hit(data, node->bv);
float dist = fast_ray_nearest_hit(data, node);
if(dist >= data->hit.dist) return;
if(node->totnode == 0)
@ -1483,6 +1532,37 @@ static void dfs_raycast(BVHRayCastData *data, BVHNode *node)
}
}
static void iterative_raycast(BVHRayCastData *data, BVHNode *node)
{
while(node)
{
float dist = fast_ray_nearest_hit(data, node);
if(dist >= data->hit.dist)
{
node = node->skip[1];
continue;
}
if(node->totnode == 0)
{
if(data->callback)
data->callback(data->userdata, node->index, &data->ray, &data->hit);
else
{
data->hit.index = node->index;
data->hit.dist = dist;
VECADDFAC(data->hit.co, data->ray.origin, data->ray.direction, dist);
}
node = node->skip[1];
}
else
{
node = node->children[0];
}
}
}
int BLI_bvhtree_ray_cast(BVHTree *tree, const float *co, const float *dir, float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
{
int i;
@ -1503,9 +1583,16 @@ int BLI_bvhtree_ray_cast(BVHTree *tree, const float *co, const float *dir, float
for(i=0; i<3; i++)
{
data.ray_dot_axis[i] = INPR( data.ray.direction, KDOP_AXES[i]);
data.idot_axis[i] = 1.0f / data.ray_dot_axis[i];
if(fabs(data.ray_dot_axis[i]) < FLT_EPSILON)
{
data.ray_dot_axis[i] = 0.0;
}
data.index[2*i] = data.idot_axis[i] < 0.0 ? 1 : 0;
data.index[2*i+1] = 1 - data.index[2*i];
data.index[2*i] += 2*i;
data.index[2*i+1] += 2*i;
}
@ -1518,7 +1605,10 @@ int BLI_bvhtree_ray_cast(BVHTree *tree, const float *co, const float *dir, float
}
if(root)
{
dfs_raycast(&data, root);
// iterative_raycast(&data, root);
}
if(hit)

@ -45,6 +45,7 @@ struct MemArena {
int bufsize, cursize;
int use_calloc;
int align;
LinkNode *bufs;
};
@ -52,6 +53,7 @@ struct MemArena {
MemArena *BLI_memarena_new(int bufsize) {
MemArena *ma= MEM_callocN(sizeof(*ma), "memarena");
ma->bufsize= bufsize;
ma->align = 8;
return ma;
}
@ -64,6 +66,11 @@ void BLI_memarena_use_malloc(MemArena *ma) {
ma->use_calloc= 0;
}
void BLI_memarena_use_align(struct MemArena *ma, int align) {
/* align should be a power of two */
ma->align = align;
}
void BLI_memarena_free(MemArena *ma) {
BLI_linklist_free(ma->bufs, (void(*)(void*)) MEM_freeN);
MEM_freeN(ma);
@ -77,16 +84,28 @@ void *BLI_memarena_alloc(MemArena *ma, int size) {
/* ensure proper alignment by rounding
* size up to multiple of 8 */
size= PADUP(size, 8);
size= PADUP(size, ma->align);
if (size>ma->cursize) {
ma->cursize= (size>ma->bufsize)?size:ma->bufsize;
unsigned char *tmp;
if(size > ma->bufsize - (ma->align - 1))
{
ma->cursize = PADUP(size+1, ma->align);
}
else ma->cursize = ma->bufsize;
if(ma->use_calloc)
ma->curbuf= MEM_callocN(ma->cursize, "memarena calloc");
else
ma->curbuf= MEM_mallocN(ma->cursize, "memarena malloc");
BLI_linklist_prepend(&ma->bufs, ma->curbuf);
/* align alloc'ed memory (needed if align > 8) */
tmp = (unsigned char*)PADUP( (intptr_t) ma->curbuf, ma->align);
ma->cursize -= (tmp - ma->curbuf);
ma->curbuf = tmp;
}
ptr= ma->curbuf;
@ -95,3 +114,4 @@ void *BLI_memarena_alloc(MemArena *ma, int size) {
return ptr;
}

@ -105,8 +105,9 @@ struct LaplacianSystem {
float *p; /* values from all p vectors */
float *mindist; /* minimum distance to a bone for all vertices */
RayTree *raytree; /* ray tracing acceleration structure */
MFace **vface; /* a face that the vertex belongs to */
RayObject *raytree; /* ray tracing acceleration structure */
RayFace *faces; /* faces to add to the ray tracing struture */
MFace **vface; /* a face that the vertex belongs to */
} heat;
#ifdef RIGID_DEFORM
@ -394,75 +395,41 @@ float laplacian_system_get_solution(int v)
#define DISTANCE_EPSILON 1e-4f
/* Raytracing for vertex to bone visibility */
static LaplacianSystem *HeatSys = NULL;
static void heat_ray_coords_func(RayFace *face, float **v1, float **v2, float **v3, float **v4)
{
MFace *mface= (MFace*)face;
float (*verts)[3]= HeatSys->heat.verts;
*v1= verts[mface->v1];
*v2= verts[mface->v2];
*v3= verts[mface->v3];
*v4= (mface->v4)? verts[mface->v4]: NULL;
}
static int heat_ray_check_func(Isect *is, int ob, RayFace *face)
{
float *v1, *v2, *v3, *v4, nor[3];
/* don't intersect if the ray faces along the face normal */
heat_ray_coords_func(face, &v1, &v2, &v3, &v4);
if(v4) CalcNormFloat4(v1, v2, v3, v4, nor);
else CalcNormFloat(v1, v2, v3, nor);
return (INPR(nor, is->vec) < 0);
}
static void heat_ray_tree_create(LaplacianSystem *sys)
{
Mesh *me = sys->heat.mesh;
RayTree *tree;
MFace *mface;
float min[3], max[3];
int a;
/* create a raytrace tree from the mesh */
INIT_MINMAX(min, max);
sys->heat.raytree = RE_rayobject_vbvh_create(me->totface);
sys->heat.faces = MEM_callocN(sizeof(RayFace)*me->totface, "Heat RayFaces");
sys->heat.vface = MEM_callocN(sizeof(MFace*)*me->totvert, "HeatVFaces");
for(a=0; a<me->totvert; a++)
DO_MINMAX(sys->heat.verts[a], min, max);
tree= RE_ray_tree_create(64, me->totface, min, max,
heat_ray_coords_func, heat_ray_check_func, NULL, NULL);
for(a=0; a<me->totface; a++) {
sys->heat.vface= MEM_callocN(sizeof(MFace*)*me->totvert, "HeatVFaces");
HeatSys= sys;
for(a=0, mface=me->mface; a<me->totface; a++, mface++) {
RE_ray_tree_add_face(tree, 0, mface);
MFace *mface = me->mface+a;
RayFace *rayface = sys->heat.faces+a;
RayObject *obj = RE_rayface_from_coords(
rayface, me, mface,
sys->heat.verts[mface->v1], sys->heat.verts[mface->v2],
sys->heat.verts[mface->v3], mface->v4 ? sys->heat.verts[mface->v4] : 0
);
RE_rayobject_add(sys->heat.raytree, obj);
//Setup inverse pointers to use on isect.orig
sys->heat.vface[mface->v1]= mface;
sys->heat.vface[mface->v2]= mface;
sys->heat.vface[mface->v3]= mface;
if(mface->v4) sys->heat.vface[mface->v4]= mface;
}
HeatSys= NULL;
RE_ray_tree_done(tree);
sys->heat.raytree= tree;
RE_rayobject_done(sys->heat.raytree);
}
static int heat_ray_bone_visible(LaplacianSystem *sys, int vertex, int bone)
{
Isect isec;
MFace *mface;
float dir[3];
float end[3];
int visible;
mface= sys->heat.vface[vertex];
@ -473,22 +440,19 @@ static int heat_ray_bone_visible(LaplacianSystem *sys, int vertex, int bone)
memset(&isec, 0, sizeof(isec));
isec.mode= RE_RAY_SHADOW;
isec.lay= -1;
isec.face_last= NULL;
isec.faceorig= mface;
isec.orig.ob = sys->heat.mesh;
isec.orig.face = mface;
isec.skip = RE_SKIP_CULLFACE;
VECCOPY(isec.start, sys->heat.verts[vertex]);
PclosestVL3Dfl(isec.end, isec.start,
sys->heat.root[bone], sys->heat.tip[bone]);
PclosestVL3Dfl(end, isec.start, sys->heat.root[bone], sys->heat.tip[bone]);
/* add an extra offset to the start position to avoid self intersection */
VECSUB(dir, isec.end, isec.start);
Normalize(dir);
VecMulf(dir, 1e-5);
VecAddf(isec.start, isec.start, dir);
HeatSys= sys;
visible= !RE_ray_tree_intersect(sys->heat.raytree, &isec);
HeatSys= NULL;
VECSUB(isec.vec, end, isec.start);
isec.labda = 1.0f - 1e-5;
VECADDFAC( isec.start, isec.start, isec.vec, 1e-5);
visible= !RE_rayobject_raycast(sys->heat.raytree, &isec);
return visible;
}
@ -752,8 +716,9 @@ void heat_bone_weighting(Object *ob, Mesh *me, float (*verts)[3], int numbones,
/* free */
if(vertsflipped) MEM_freeN(vertsflipped);
RE_ray_tree_free(sys->heat.raytree);
RE_rayobject_free(sys->heat.raytree);
MEM_freeN(sys->heat.vface);
MEM_freeN(sys->heat.faces);
MEM_freeN(sys->heat.mindist);
MEM_freeN(sys->heat.H);
@ -1049,7 +1014,7 @@ typedef struct MeshDeformBind {
int *varidx;
/* raytrace */
RayTree *raytree;
RayObject *raytree;
} MeshDeformBind;
/* ray intersection */
@ -1173,7 +1138,7 @@ static void meshdeform_ray_tree_free(MeshDeformBind *mdb)
static int meshdeform_intersect(MeshDeformBind *mdb, Isect *isec)
{
MFace *mface;
float face[4][3], co[3], uvw[3], len, nor[3];
float face[4][3], co[3], uvw[3], len, nor[3], end[3];
int f, hit, is= 0, totface;
isec->labda= 1e10;
@ -1181,6 +1146,8 @@ static int meshdeform_intersect(MeshDeformBind *mdb, Isect *isec)
mface= mdb->cagedm->getFaceArray(mdb->cagedm);
totface= mdb->cagedm->getNumFaces(mdb->cagedm);
VECADDFAC( end, isec->start, isec->vec, isec->labda );
for(f=0; f<totface; f++, mface++) {
VECCOPY(face[0], mdb->cagecos[mface->v1]);
VECCOPY(face[1], mdb->cagecos[mface->v2]);
@ -1188,26 +1155,26 @@ static int meshdeform_intersect(MeshDeformBind *mdb, Isect *isec)
if(mface->v4) {
VECCOPY(face[3], mdb->cagecos[mface->v4]);
hit= meshdeform_tri_intersect(isec->start, isec->end, face[0], face[1], face[2], co, uvw);
hit = meshdeform_tri_intersect(isec->start, end, face[0], face[1], face[2], co, uvw);
if(hit) {
CalcNormFloat(face[0], face[1], face[2], nor);
}
else {
hit= meshdeform_tri_intersect(isec->start, isec->end, face[0], face[2], face[3], co, uvw);
hit= meshdeform_tri_intersect(isec->start, end, face[0], face[2], face[3], co, uvw);
CalcNormFloat(face[0], face[2], face[3], nor);
}
}
else {
hit= meshdeform_tri_intersect(isec->start, isec->end, face[0], face[1], face[2], co, uvw);
hit= meshdeform_tri_intersect(isec->start, end, face[0], face[1], face[2], co, uvw);
CalcNormFloat(face[0], face[1], face[2], nor);
}
if(hit) {
len= VecLenf(isec->start, co)/VecLenf(isec->start, isec->end);
len= VecLenf(isec->start, co)/VecLenf(isec->start, end);
if(len < isec->labda) {
isec->labda= len;
isec->face= mface;
isec->hit.face = mface;
isec->isect= (INPR(isec->vec, nor) <= 0.0f);
is= 1;
}
@ -1223,20 +1190,18 @@ static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, float
Isect isec;
float (*cagecos)[3];
MFace *mface;
float vert[4][3], len;
float vert[4][3], len, end[3];
static float epsilon[3]= {0, 0, 0}; //1e-4, 1e-4, 1e-4};
/* setup isec */
memset(&isec, 0, sizeof(isec));
isec.mode= RE_RAY_MIRROR; /* we want the closest intersection */
isec.lay= -1;
isec.face_last= NULL;
isec.faceorig= NULL;
isec.labda= 1e10f;
VECADD(isec.start, co1, epsilon);
VECADD(isec.end, co2, epsilon);
VECSUB(isec.vec, isec.end, isec.start);
VECADD(end, co2, epsilon);
VECSUB(isec.vec, end, isec.start);
#if 0
/*if(RE_ray_tree_intersect(mdb->raytree, &isec)) {*/
@ -1244,7 +1209,7 @@ static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, float
if(meshdeform_intersect(mdb, &isec)) {
len= isec.labda;
mface= isec.face;
mface=(MFace*)isec.hit.face;
/* create MDefBoundIsect */
isect= BLI_memarena_alloc(mdb->memarena, sizeof(*isect));

@ -167,6 +167,8 @@ typedef struct SceneRenderLayer {
#define SCE_PASS_RADIO 8192 /* Radio removed, can use for new GI? */
#define SCE_PASS_MIST 16384
#define SCE_PASS_RAYHITS 32768
/* note, srl->passflag is treestore element 'nr' in outliner, short still... */
@ -240,9 +242,23 @@ typedef struct RenderData {
*/
int mode;
/* render engine (deprecated), octree resolution */
short renderer, ocres;
/**
* Flags for raytrace settings. Use bit-masking to access the settings.
*/
int raytrace_options;
/**
* Raytrace acceleration structure
*/
short raytrace_structure;
/* renderer (deprecated) */
short renderer;
/* octree resolution */
short ocres;
short pad4;
/**
* What to do with the sky/background. Picks sky/premul/key
* blending for the background
@ -255,6 +271,7 @@ typedef struct RenderData {
short osa;
short frs_sec, edgeint;
/* safety, border and display rect */
rctf safety, border;
@ -804,6 +821,18 @@ typedef struct Scene {
#define R_INTERN 0
#define R_YAFRAY 1
/* raytrace structure */
#define R_RAYSTRUCTURE_AUTO 0
#define R_RAYSTRUCTURE_OCTREE 1
#define R_RAYSTRUCTURE_BLIBVH 2
#define R_RAYSTRUCTURE_VBVH 3
#define R_RAYSTRUCTURE_SIMD_SVBVH 4 /* needs SIMD */
#define R_RAYSTRUCTURE_SIMD_QBVH 5 /* needs SIMD */
/* raytrace_options */
#define R_RAYTRACE_USE_LOCAL_COORDS 0x0001
#define R_RAYTRACE_USE_INSTANCES 0x0002
/* scemode (int now) */
#define R_DOSEQ 0x0001
#define R_BG_RENDER 0x0002

@ -1185,7 +1185,17 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
{256, "OCTREE_RES_256", 0, "256", ""},
{512, "OCTREE_RES_512", 0, "512", ""},
{0, NULL, 0, NULL, NULL}};
static EnumPropertyItem raytrace_structure_items[] = {
{R_RAYSTRUCTURE_AUTO, "AUTO", 0, "Auto", ""},
{R_RAYSTRUCTURE_OCTREE, "OCTREE", 0, "Octree", "Use old Octree structure."},
{R_RAYSTRUCTURE_BLIBVH, "BLIBVH", 0, "BLI BVH", "Use BLI K-Dop BVH.c"},
{R_RAYSTRUCTURE_VBVH, "VBVH", 0, "vBVH", ""},
{R_RAYSTRUCTURE_SIMD_SVBVH, "SIMD_SVBVH", 0, "SIMD SVBVH", ""},
{R_RAYSTRUCTURE_SIMD_QBVH, "SIMD_QBVH", 0, "SIMD QBVH", ""},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem fixed_oversample_items[] = {
{5, "OVERSAMPLE_5", 0, "5", ""},
{8, "OVERSAMPLE_8", 0, "8", ""},
@ -1613,7 +1623,23 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_enum_items(prop, octree_resolution_items);
RNA_def_property_ui_text(prop, "Octree Resolution", "Resolution of raytrace accelerator. Use higher resolutions for larger scenes.");
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
prop= RNA_def_property(srna, "raytrace_structure", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "raytrace_structure");
RNA_def_property_enum_items(prop, raytrace_structure_items);
RNA_def_property_ui_text(prop, "Raytrace Acceleration Structure", "Type of raytrace accelerator structure.");
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
prop= RNA_def_property(srna, "use_instances", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "raytrace_options", R_RAYTRACE_USE_INSTANCES);
RNA_def_property_ui_text(prop, "Use Instances", "Instance support leads to effective memory reduction when using duplicates.");
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
prop= RNA_def_property(srna, "use_local_coords", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "raytrace_options", R_RAYTRACE_USE_LOCAL_COORDS);
RNA_def_property_ui_text(prop, "Use Local Coords", "Vertex coordinates are stored localy on each primitive. Increases memory usage, but may have impact on speed.");
RNA_def_property_update(prop, NC_SCENE|ND_RENDER_OPTIONS, NULL);
prop= RNA_def_property(srna, "antialiasing", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "mode", R_OSA);
RNA_def_property_ui_text(prop, "Anti-Aliasing", "Render and combine multiple samples per pixel to prevent jagged edges.");

@ -1,8 +1,10 @@
#!/usr/bin/python
Import ('env')
cflags=''
cflags = ['-O2','-msse2','-mfpmath=sse']
cxxflags = ['-O2','-msse2','-mfpmath=sse']
sources = env.Glob('intern/source/*.c')
raysources = env.Glob('intern/raytrace/*.cpp')
incs = 'intern/include #/intern/guardedalloc ../blenlib ../makesdna ../makesrna'
incs += ' extern/include ../blenkernel ../radiosity/extern/include ../imbuf'
@ -18,7 +20,7 @@ if env['WITH_BF_OPENEXR']:
defs.append('WITH_OPENEXR')
if env['OURPLATFORM']=='linux2':
cflags='-pthread'
cflags += ['-pthread']
if env['OURPLATFORM'] == 'linux2':
@ -29,3 +31,4 @@ if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
incs += ' ' + env['BF_PTHREADS_INC']
env.BlenderLib ( libname = 'bf_render', sources = sources, includes = Split(incs), defines=defs, libtype='core', priority=145, compileflags=cflags )
env.BlenderLib ( libname = 'bf_render_raytrace', sources = raysources, includes = Split(incs), defines=defs, libtype='core', priority=145, compileflags=cflags, cxx_compileflags=cxxflags )

@ -22,7 +22,7 @@
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
* RE_raytrace.h: ray tracing api, can be used independently from the renderer.
@ -31,84 +31,182 @@
#ifndef RE_RAYTRACE_H
#define RE_RAYTRACE_H
#ifdef __cplusplus
extern "C" {
#endif
#define RE_RAYCOUNTER /* enable counters per ray, usefull for measuring raytrace structures performance */
#define RE_RAY_LCTS_MAX_SIZE 256
#define RT_USE_LAST_HIT /* last shadow hit is reused before raycasting on whole tree */
//#define RT_USE_HINT /* last hit object is reused before raycasting on whole tree */
#ifdef RE_RAYCOUNTER
typedef struct RayCounter RayCounter;
struct RayCounter
{
struct
{
unsigned long long test, hit;
} faces, bb, simd_bb, raycast, raytrace_hint, rayshadow_last_hit;
};
#endif
/* Internals about raycasting structures can be found on intern/raytree.h */
typedef struct RayObject RayObject;
typedef struct Isect Isect;
typedef struct RayHint RayHint;
typedef struct RayTraceHint RayTraceHint;
struct DerivedMesh;
struct Mesh;
struct VlakRen;
struct ObjectInstanceRen;
int RE_rayobject_raycast(RayObject *r, Isect *i);
void RE_rayobject_add (RayObject *r, RayObject *);
void RE_rayobject_done(RayObject *r);
void RE_rayobject_free(RayObject *r);
/* Extend min/max coords so that the rayobject is inside them */
void RE_rayobject_merge_bb(RayObject *ob, float *min, float *max);
/* initializes an hint for optiming raycast where it is know that a ray will pass by the given BB often the origin point */
void RE_rayobject_hint_bb(RayObject *r, RayHint *hint, float *min, float *max);
/* initializes an hint for optiming raycast where it is know that a ray will be contained inside the given cone*/
/* void RE_rayobject_hint_cone(RayObject *r, RayHint *hint, float *); */
/* RayObject constructors */
RayObject* RE_rayobject_octree_create(int ocres, int size);
RayObject* RE_rayobject_instance_create(RayObject *target, float transform[][4], void *ob, void *target_ob);
RayObject* RE_rayobject_blibvh_create(int size); /* BLI_kdopbvh.c */
RayObject* RE_rayobject_vbvh_create(int size); /* raytrace/rayobject_vbvh.c */
RayObject* RE_rayobject_svbvh_create(int size); /* raytrace/rayobject_svbvh.c */
RayObject* RE_rayobject_qbvh_create(int size); /* raytrace/rayobject_qbvh.c */
/*
* This ray object represents a triangle or a quad face.
* All data needed to realize intersection is "localy" available.
*/
typedef struct RayFace
{
float v1[4], v2[4], v3[4], v4[3];
int quad;
void *ob;
void *face;
} RayFace;
#define RE_rayface_isQuad(a) ((a)->quad)
RayObject* RE_rayface_from_vlak(RayFace *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr);
RayObject* RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *co1, float *co2, float *co3, float *co4);
/*
* This ray object represents faces directly from a given VlakRen structure.
* Thus allowing to save memory, but making code triangle intersection dependant on render structures
*/
typedef struct VlakPrimitive
{
struct ObjectInstanceRen *ob;
struct VlakRen *face;
} VlakPrimitive;
RayObject* RE_vlakprimitive_from_vlak(VlakPrimitive *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr);
/*
* Raytrace hints
*/
typedef struct LCTSHint LCTSHint;
struct LCTSHint
{
int size;
RayObject *stack[RE_RAY_LCTS_MAX_SIZE];
};
struct RayHint
{
union
{
LCTSHint lcts;
} data;
};
/* Ray Intersection */
struct Isect
{
float start[3];
float vec[3];
float labda;
/* length of vec, configured by RE_rayobject_raycast */
int bv_index[6];
float idot_axis[3];
float dist;
/* float end[3]; - not used */
float u, v;
struct
{
void *ob;
void *face;
}
hit, orig;
RayObject *last_hit; /* last hit optimization */
#ifdef RT_USE_HINT
RayTraceHint *hint, *hit_hint;
#endif
short isect; /* which half of quad */
short mode; /* RE_RAY_SHADOW, RE_RAY_MIRROR, RE_RAY_SHADOW_TRA */
int lay; /* -1 default, set for layer lamps */
int skip; /* RE_SKIP_CULLFACE */
float col[4]; /* RGBA for shadow_tra */
void *userdata;
RayHint *hint;
#ifdef RE_RAYCOUNTER
RayCounter *raycounter;
#endif
};
/* 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;
/* skip options */
#define RE_SKIP_CULLFACE (1 << 0)
/* object numbers above this are transformed */
#define RE_RAY_TRANSFORM_OFFS 0x8000000
/* if using this flag then *face should be a pointer to a VlakRen */
#define RE_SKIP_VLR_NEIGHBOUR (1 << 1)
#define RE_SKIP_VLR_RENDER_CHECK (1 << 2)
#define RE_SKIP_VLR_NON_SOLID_MATERIAL (1 << 3)
/* convert from pointer to index in array and back, with offset if the
* instance is transformed */
#define RAY_OBJECT_SET(re, obi) \
((obi == NULL)? 0: \
((obi - (re)->objectinstance) + ((obi->flag & R_TRANSFORMED)? RE_RAY_TRANSFORM_OFFS: 0)))
/* TODO use: FLT_MAX? */
#define RE_RAYTRACE_MAXDIST 1e33
#define RAY_OBJECT_GET(re, i) \
((re)->objectinstance + ((i >= RE_RAY_TRANSFORM_OFFS)? i-RE_RAY_TRANSFORM_OFFS: i))
#ifdef __cplusplus
}
#endif
/* 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 */
int ob;
RayFace *faceorig; /* start face */
int oborig;
RayFace *face_last; /* for shadow optimize, last intersected face */
int ob_last;
short isect; /* which half of quad */
short mode; /* RE_RAY_SHADOW, RE_RAY_MIRROR, RE_RAY_SHADOW_TRA */
int lay; /* -1 default, set for layer lamps */
/* only used externally */
float col[4]; /* RGBA for shadow_tra */
/* octree only */
RayFace *facecontr;
int obcontr;
float ddalabda;
short faceisect; /* flag if facecontr was done or not */
/* custom pointer to be used in the RayCheckFunc */
void *userdata;
} 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, int ob, RayFace *face);
typedef float *(*RayObjectTransformFunc)(void *userdata, int ob);
/* tree building and freeing */
RayTree *RE_ray_tree_create(int ocres, int totface, float *min, float *max,
RayCoordsFunc coordfunc, RayCheckFunc checkfunc,
RayObjectTransformFunc transformfunc, void *userdata);
void RE_ray_tree_add_face(RayTree *tree, int ob, 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_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc check);
int RE_ray_face_intersection(Isect *is, RayObjectTransformFunc transformfunc,
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__*/

@ -30,6 +30,7 @@
#ifndef RE_SHADER_EXT_H
#define RE_SHADER_EXT_H
#include "RE_raytrace.h" /* For RE_RAYCOUNTER */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* this include is for shading and texture exports */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@ -56,6 +57,7 @@ typedef struct ShadeResult
float refr[3];
float nor[3];
float winspeed[4];
float rayhits[4];
} ShadeResult;
/* only here for quick copy */
@ -177,6 +179,10 @@ typedef struct ShadeInput
struct Group *light_override;
struct Material *mat_override;
#ifdef RE_RAYCOUNTER
RayCounter raycounter;
#endif
} ShadeInput;

@ -0,0 +1,55 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef RE_RAYCOUNTER_H
#define RE_RAYCOUNTER_H
#include "RE_raytrace.h"
#ifdef RE_RAYCOUNTER
/* #define RE_RC_INIT(isec, shi) (isec).count = re_rc_counter+(shi).thread */
#define RE_RC_INIT(isec, shi) (isec).raycounter = &((shi).raycounter)
void RE_RC_INFO (RayCounter *rc);
void RE_RC_MERGE(RayCounter *rc, RayCounter *tmp);
#define RE_RC_COUNT(var) (var)++
extern RayCounter re_rc_counter[];
#else
# define RE_RC_INIT(isec,shi)
# define RE_RC_INFO(rc)
# define RE_RC_MERGE(dest,src)
# define RE_RC_COUNT(var)
#endif
#endif

@ -0,0 +1,208 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef RE_RAYOBJECT_H
#define RE_RAYOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "RE_raytrace.h"
#include "render_types.h"
#include <float.h>
/* RayObject
A ray object is everything where we can cast rays like:
* a face/triangle
* an octree
* a bvh tree
* an octree of bvh's
* a bvh of bvh's
All types of RayObjects can be created by implementing the
callbacks of the RayObject.
Due to high computing time evolved with casting on faces
there is a special type of RayObject (named RayFace)
which won't use callbacks like other generic nodes.
In order to allow a mixture of RayFace+RayObjects,
all RayObjects must be 4byte aligned, allowing us to use the
2 least significant bits (with the mask 0x03) to define the
type of RayObject.
This leads to 4 possible types of RayObject:
addr&3 - type of object
0 Self (reserved for each structure)
1 RayFace (tri/quad primitive)
2 RayObject (generic with API callbacks)
3 VlakPrimitive
(vlak primitive - to be used when we have a vlak describing the data
eg.: on render code)
0 means it's reserved and has it own meaning inside each ray acceleration structure
(this way each structure can use the allign offset to determine if a node represents a
RayObject primitive, which can be used to save memory)
You actually don't need to care about this if you are only using the API
described on RE_raytrace.h
*/
/* used to align a given ray object */
#define RE_rayobject_align(o) ((RayObject*)(((intptr_t)o)&(~3)))
/* used to unalign a given ray object */
#define RE_rayobject_unalignRayFace(o) ((RayObject*)(((intptr_t)o)|1))
#define RE_rayobject_unalignRayAPI(o) ((RayObject*)(((intptr_t)o)|2))
#define RE_rayobject_unalignVlakPrimitive(o) ((RayObject*)(((intptr_t)o)|3))
/* used to test the type of ray object */
#define RE_rayobject_isAligned(o) ((((intptr_t)o)&3) == 0)
#define RE_rayobject_isRayFace(o) ((((intptr_t)o)&3) == 1)
#define RE_rayobject_isRayAPI(o) ((((intptr_t)o)&3) == 2)
#define RE_rayobject_isVlakPrimitive(o) ((((intptr_t)o)&3) == 3)
/*
* This class is intended as a place holder for control, configuration of the rayobject like:
* - stop building (TODO maybe when porting build to threads this could be implemented with some thread_cancel function)
* - max number of threads and threads callback to use during build
* ...
*/
typedef int (*RE_rayobjectcontrol_test_break_callback)(void *data);
typedef struct RayObjectControl RayObjectControl;
struct RayObjectControl
{
void *data;
RE_rayobjectcontrol_test_break_callback test_break;
};
/*
* This rayobject represents a generic object. With it's own callbacks for raytrace operations.
* It's suitable to implement things like LOD.
*/
struct RayObject
{
struct RayObjectAPI *api;
struct RayObjectControl control;
};
typedef int (*RE_rayobject_raycast_callback)(RayObject *, Isect *);
typedef void (*RE_rayobject_add_callback)(RayObject *raytree, RayObject *rayobject);
typedef void (*RE_rayobject_done_callback)(RayObject *);
typedef void (*RE_rayobject_free_callback)(RayObject *);
typedef void (*RE_rayobject_merge_bb_callback)(RayObject *, float *min, float *max);
typedef float (*RE_rayobject_cost_callback)(RayObject *);
typedef void (*RE_rayobject_hint_bb_callback)(RayObject *, RayHint *, float *, float *);
typedef struct RayObjectAPI
{
RE_rayobject_raycast_callback raycast;
RE_rayobject_add_callback add;
RE_rayobject_done_callback done;
RE_rayobject_free_callback free;
RE_rayobject_merge_bb_callback bb;
RE_rayobject_cost_callback cost;
RE_rayobject_hint_bb_callback hint_bb;
} RayObjectAPI;
/*
* This function differs from RE_rayobject_raycast
* RE_rayobject_intersect does NOT perform last-hit optimization
* So this is probably a function to call inside raytrace structures
*/
int RE_rayobject_intersect(RayObject *r, Isect *i);
/*
* Returns distance ray must travel to hit the given bounding box
* BB should be in format [2][3]
*/
/* float RE_rayobject_bb_intersect(const Isect *i, const float *bb); */
int RE_rayobject_bb_intersect_test(const Isect *i, const float *bb); /* same as bb_intersect but doens't calculates distance */
/*
* Returns the expected cost of raycast on this node, primitives have a cost of 1
*/
float RE_rayobject_cost(RayObject *r);
/*
* Returns true if for some reason a heavy processing function should stop
* (eg.: user asked to stop during a tree a build)
*/
int RE_rayobjectcontrol_test_break(RayObjectControl *c);
#define ISECT_EPSILON ((float)FLT_EPSILON)
#if !defined(_WIN32)
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#define BENCH(a,name) \
do { \
double _t1, _t2; \
struct timeval _tstart, _tend; \
clock_t _clock_init = clock(); \
gettimeofday ( &_tstart, NULL); \
(a); \
gettimeofday ( &_tend, NULL); \
_t1 = ( double ) _tstart.tv_sec + ( double ) _tstart.tv_usec/ ( 1000*1000 ); \
_t2 = ( double ) _tend.tv_sec + ( double ) _tend.tv_usec/ ( 1000*1000 ); \
printf("BENCH:%s: %fs (real) %fs (cpu)\n", #name, _t2-_t1, (float)(clock()-_clock_init)/CLOCKS_PER_SEC);\
} while(0)
#else
#define BENCH(a) (a)
#endif
#ifdef __cplusplus
}
#endif
#endif

@ -53,6 +53,8 @@ struct VlakTableNode;
struct GHash;
struct RenderBuckets;
struct ObjectInstanceRen;
struct RayObject;
struct RayFace;
#define TABLEINITSIZE 1024
#define LAMPINITSIZE 256
@ -172,7 +174,10 @@ struct Render
ListBase parts;
/* octree tables and variables for raytrace */
void *raytree;
struct RayObject *raytree;
struct RayFace *rayfaces;
struct VlakPrimitive *rayprimitives;
float maxdist; /* needed for keeping an incorrect behaviour of SUN and HEMI lights (avoid breaking old scenes) */
/* occlusion tree */
void *occlusiontree;
@ -285,6 +290,13 @@ typedef struct ObjectRen {
int actmtface, actmcol, bakemtface;
float obmat[4][4]; /* only used in convertblender.c, for instancing */
/* used on makeraytree */
struct RayObject *raytree;
struct RayFace *rayfaces;
struct VlakPrimitive *rayprimitives;
struct ObjectInstanceRen *rayobi;
} ObjectRen;
typedef struct ObjectInstanceRen {
@ -304,6 +316,11 @@ typedef struct ObjectInstanceRen {
float *vectors;
int totvector;
/* used on makeraytree */
struct RayObject *raytree;
int transform_primitives;
} ObjectInstanceRen;
/* ------------------------------------------------------------------------- */
@ -429,7 +446,7 @@ typedef struct MatInside {
typedef struct VolPrecachePart
{
struct VolPrecachePart *next, *prev;
struct RayTree *tree;
struct RayObject *tree;
struct ShadeInput *shi;
struct ObjectInstanceRen *obi;
int num;
@ -540,8 +557,7 @@ typedef struct LampRen {
short YF_glowtype;
/* ray optim */
VlakRen *vlr_last[BLENDER_MAX_THREADS];
ObjectInstanceRen *obi_last[BLENDER_MAX_THREADS];
struct RayObject *last_hit[BLENDER_MAX_THREADS];
struct MTex *mtex[MAX_MTEX];

@ -95,6 +95,7 @@ int get_sample_layers(struct RenderPart *pa, struct RenderLayer *rl, struct Rend
extern void freeraytree(Render *re);
extern void makeraytree(Render *re);
RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi);
extern void ray_shadow(ShadeInput *, LampRen *, float *);
extern void ray_trace(ShadeInput *, ShadeResult *);

@ -0,0 +1,400 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "rayobject.h"
#include "raycounter.h"
#include "MEM_guardedalloc.h"
#include "rayobject_rtbuild.h"
#include "rayobject_hint.h"
#include <assert.h>
#ifdef __SSE__
#include <xmmintrin.h>
#endif
#ifndef RE_RAYTRACE_BVH_H
#define RE_RAYTRACE_BVH_H
#ifdef __SSE__
inline int test_bb_group4(__m128 *bb_group, const Isect *isec)
{
const __m128 tmin0 = _mm_setzero_ps();
const __m128 tmax0 = _mm_load1_ps(&isec->labda);
const __m128 tmin1 = _mm_max_ps(tmin0, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[0]], _mm_load1_ps(&isec->start[0]) ), _mm_load1_ps(&isec->idot_axis[0])) );
const __m128 tmax1 = _mm_min_ps(tmax0, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[1]], _mm_load1_ps(&isec->start[0]) ), _mm_load1_ps(&isec->idot_axis[0])) );
const __m128 tmin2 = _mm_max_ps(tmin1, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[2]], _mm_load1_ps(&isec->start[1]) ), _mm_load1_ps(&isec->idot_axis[1])) );
const __m128 tmax2 = _mm_min_ps(tmax1, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[3]], _mm_load1_ps(&isec->start[1]) ), _mm_load1_ps(&isec->idot_axis[1])) );
const __m128 tmin3 = _mm_max_ps(tmin2, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[4]], _mm_load1_ps(&isec->start[2]) ), _mm_load1_ps(&isec->idot_axis[2])) );
const __m128 tmax3 = _mm_min_ps(tmax2, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[5]], _mm_load1_ps(&isec->start[2]) ), _mm_load1_ps(&isec->idot_axis[2])) );
return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3));
}
#endif
/* bvh tree generics */
template<class Tree> static int bvh_intersect(Tree *obj, Isect *isec);
template<class Tree> static void bvh_add(Tree *obj, RayObject *ob)
{
rtbuild_add( obj->builder, ob );
}
template<class Node>
inline bool is_leaf(Node *node)
{
return !RE_rayobject_isAligned(node);
}
template<class Tree> static void bvh_done(Tree *obj);
template<class Tree>
static void bvh_free(Tree *obj)
{
if(obj->builder)
rtbuild_free(obj->builder);
if(obj->node_arena)
BLI_memarena_free(obj->node_arena);
MEM_freeN(obj);
}
template<class Tree>
static void bvh_bb(Tree *obj, float *min, float *max)
{
bvh_node_merge_bb(obj->root, min, max);
}
template<class Tree>
static float bvh_cost(Tree *obj)
{
assert(obj->cost >= 0.0);
return obj->cost;
}
/* bvh tree nodes generics */
template<class Node> static inline int bvh_node_hit_test(Node *node, Isect *isec)
{
return RE_rayobject_bb_intersect_test(isec, (const float*)node->bb);
}
template<class Node>
static void bvh_node_merge_bb(Node *node, float *min, float *max)
{
if(is_leaf(node))
{
RE_rayobject_merge_bb( (RayObject*)node, min, max);
}
else
{
DO_MIN(node->bb , min);
DO_MAX(node->bb+3, max);
}
}
/*
* recursivly transverse a BVH looking for a rayhit using a local stack
*/
template<class Node> static inline void bvh_node_push_childs(Node *node, Isect *isec, Node **stack, int &stack_pos);
template<class Node,int MAX_STACK_SIZE,bool TEST_ROOT>
static int bvh_node_stack_raycast(Node *root, Isect *isec)
{
Node *stack[MAX_STACK_SIZE];
int hit = 0, stack_pos = 0;
if(!TEST_ROOT && !is_leaf(root))
bvh_node_push_childs(root, isec, stack, stack_pos);
else
stack[stack_pos++] = root;
while(stack_pos)
{
Node *node = stack[--stack_pos];
if(!is_leaf(node))
{
if(bvh_node_hit_test(node,isec))
{
bvh_node_push_childs(node, isec, stack, stack_pos);
assert(stack_pos <= MAX_STACK_SIZE);
}
}
else
{
hit |= RE_rayobject_intersect( (RayObject*)node, isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
}
return hit;
}
#ifdef __SSE__
/*
* Generic SIMD bvh recursion
* this was created to be able to use any simd (with the cost of some memmoves)
* it can take advantage of any SIMD width and doens't needs any special tree care
*/
template<class Node,int MAX_STACK_SIZE,bool TEST_ROOT>
static int bvh_node_stack_raycast_simd(Node *root, Isect *isec)
{
Node *stack[MAX_STACK_SIZE];
int hit = 0, stack_pos = 0;
if(!TEST_ROOT)
{
if(!is_leaf(root))
{
if(!is_leaf(root->child))
bvh_node_push_childs(root, isec, stack, stack_pos);
else
return RE_rayobject_intersect( (RayObject*)root->child, isec);
}
else
return RE_rayobject_intersect( (RayObject*)root, isec);
}
else
{
if(!is_leaf(root))
stack[stack_pos++] = root;
else
return RE_rayobject_intersect( (RayObject*)root, isec);
}
while(true)
{
//Use SIMD 4
if(stack_pos >= 4)
{
__m128 t_bb[6];
Node * t_node[4];
stack_pos -= 4;
/* prepare the 4BB for SIMD */
t_node[0] = stack[stack_pos+0]->child;
t_node[1] = stack[stack_pos+1]->child;
t_node[2] = stack[stack_pos+2]->child;
t_node[3] = stack[stack_pos+3]->child;
const float *bb0 = stack[stack_pos+0]->bb;
const float *bb1 = stack[stack_pos+1]->bb;
const float *bb2 = stack[stack_pos+2]->bb;
const float *bb3 = stack[stack_pos+3]->bb;
const __m128 x0y0x1y1 = _mm_shuffle_ps( _mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(1,0,1,0) );
const __m128 x2y2x3y3 = _mm_shuffle_ps( _mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(1,0,1,0) );
t_bb[0] = _mm_shuffle_ps( x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(2,0,2,0) );
t_bb[1] = _mm_shuffle_ps( x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(3,1,3,1) );
const __m128 z0X0z1X1 = _mm_shuffle_ps( _mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(3,2,3,2) );
const __m128 z2X2z3X3 = _mm_shuffle_ps( _mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(3,2,3,2) );
t_bb[2] = _mm_shuffle_ps( z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(2,0,2,0) );
t_bb[3] = _mm_shuffle_ps( z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(3,1,3,1) );
const __m128 Y0Z0Y1Z1 = _mm_shuffle_ps( _mm_load_ps(bb0+4), _mm_load_ps(bb1+4), _MM_SHUFFLE(1,0,1,0) );
const __m128 Y2Z2Y3Z3 = _mm_shuffle_ps( _mm_load_ps(bb2+4), _mm_load_ps(bb3+4), _MM_SHUFFLE(1,0,1,0) );
t_bb[4] = _mm_shuffle_ps( Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(2,0,2,0) );
t_bb[5] = _mm_shuffle_ps( Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(3,1,3,1) );
/*
for(int i=0; i<4; i++)
{
Node *t = stack[stack_pos+i];
assert(!is_leaf(t));
float *bb = ((float*)t_bb)+i;
bb[4*0] = t->bb[0];
bb[4*1] = t->bb[1];
bb[4*2] = t->bb[2];
bb[4*3] = t->bb[3];
bb[4*4] = t->bb[4];
bb[4*5] = t->bb[5];
t_node[i] = t->child;
}
*/
RE_RC_COUNT(isec->raycounter->simd_bb.test);
int res = test_bb_group4( t_bb, isec );
for(int i=0; i<4; i++)
if(res & (1<<i))
{
RE_RC_COUNT(isec->raycounter->simd_bb.hit);
if(!is_leaf(t_node[i]))
{
for(Node *t=t_node[i]; t; t=t->sibling)
{
assert(stack_pos < MAX_STACK_SIZE);
stack[stack_pos++] = t;
}
}
else
{
hit |= RE_rayobject_intersect( (RayObject*)t_node[i], isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
}
}
else if(stack_pos > 0)
{
Node *node = stack[--stack_pos];
assert(!is_leaf(node));
if(bvh_node_hit_test(node,isec))
{
if(!is_leaf(node->child))
{
bvh_node_push_childs(node, isec, stack, stack_pos);
assert(stack_pos <= MAX_STACK_SIZE);
}
else
{
hit |= RE_rayobject_intersect( (RayObject*)node->child, isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
}
}
else break;
}
return hit;
}
#endif
/*
* recursively transverse a BVH looking for a rayhit using system stack
*/
/*
template<class Node>
static int bvh_node_raycast(Node *node, Isect *isec)
{
int hit = 0;
if(bvh_test_node(node, isec))
{
if(isec->idot_axis[node->split_axis] > 0.0f)
{
int i;
for(i=0; i<BVH_NCHILDS; i++)
if(!is_leaf(node->child[i]))
{
if(node->child[i] == 0) break;
hit |= bvh_node_raycast(node->child[i], isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
else
{
hit |= RE_rayobject_intersect( (RayObject*)node->child[i], isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
}
else
{
int i;
for(i=BVH_NCHILDS-1; i>=0; i--)
if(!is_leaf(node->child[i]))
{
if(node->child[i])
{
hit |= dfs_raycast(node->child[i], isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
}
else
{
hit |= RE_rayobject_intersect( (RayObject*)node->child[i], isec);
if(hit && isec->mode == RE_RAY_SHADOW) return hit;
}
}
}
return hit;
}
*/
template<class Node,class HintObject>
void bvh_dfs_make_hint(Node *node, LCTSHint *hint, int reserve_space, HintObject *hintObject)
{
assert( hint->size + reserve_space + 1 <= RE_RAY_LCTS_MAX_SIZE );
if(is_leaf(node))
{
hint->stack[hint->size++] = (RayObject*)node;
}
else
{
int childs = count_childs(node);
if(hint->size + reserve_space + childs <= RE_RAY_LCTS_MAX_SIZE)
{
int result = hint_test_bb(hintObject, node->bb, node->bb+3);
if(result == HINT_RECURSE)
{
/* We are 100% sure the ray will be pass inside this node */
bvh_dfs_make_hint_push_siblings(node->child, hint, reserve_space, hintObject);
}
else if(result == HINT_ACCEPT)
{
hint->stack[hint->size++] = (RayObject*)node;
}
}
else
{
hint->stack[hint->size++] = (RayObject*)node;
}
}
}
template<class Tree>
static RayObjectAPI* bvh_get_api(int maxstacksize);
template<class Tree, int DFS_STACK_SIZE>
static inline RayObject *bvh_create_tree(int size)
{
Tree *obj= (Tree*)MEM_callocN(sizeof(Tree), "BVHTree" );
assert( RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */
obj->rayobj.api = bvh_get_api<Tree>(DFS_STACK_SIZE);
obj->root = NULL;
obj->node_arena = NULL;
obj->builder = rtbuild_create( size );
return RE_rayobject_unalignRayAPI((RayObject*) obj);
}
#endif

@ -0,0 +1,537 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <assert.h>
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
#include "DNA_material_types.h"
#include "RE_raytrace.h"
#include "render_types.h"
#include "rayobject.h"
#include "raycounter.h"
/*
* Determines the distance that the ray must travel to hit the bounding volume of the given node
* Based on Tactical Optimization of Ray/Box Intersection, by Graham Fyffe
* [http://tog.acm.org/resources/RTNews/html/rtnv21n1.html#art9]
*/
int RE_rayobject_bb_intersect_test(const Isect *isec, const float *_bb)
{
const float *bb = _bb;
float t1x = (bb[isec->bv_index[0]] - isec->start[0]) * isec->idot_axis[0];
float t2x = (bb[isec->bv_index[1]] - isec->start[0]) * isec->idot_axis[0];
float t1y = (bb[isec->bv_index[2]] - isec->start[1]) * isec->idot_axis[1];
float t2y = (bb[isec->bv_index[3]] - isec->start[1]) * isec->idot_axis[1];
float t1z = (bb[isec->bv_index[4]] - isec->start[2]) * isec->idot_axis[2];
float t2z = (bb[isec->bv_index[5]] - isec->start[2]) * isec->idot_axis[2];
RE_RC_COUNT(isec->raycounter->bb.test);
if(t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return 0;
if(t2x < 0.0 || t2y < 0.0 || t2z < 0.0) return 0;
if(t1x > isec->labda || t1y > isec->labda || t1z > isec->labda) return 0;
RE_RC_COUNT(isec->raycounter->bb.hit);
return 1;
}
/* only for self-intersecting test with current render face (where ray left) */
static int intersection2(VlakRen *face, float r0, float r1, float r2, float rx1, float ry1, float rz1)
{
float co1[3], co2[3], co3[3], co4[3];
float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22;
float m0, m1, m2, divdet, det, det1;
float u1, v, u2;
VECCOPY(co1, face->v1->co);
VECCOPY(co2, face->v2->co);
if(face->v4)
{
VECCOPY(co3, face->v4->co);
VECCOPY(co4, face->v3->co);
}
else
{
VECCOPY(co3, face->v3->co);
}
t00= co3[0]-co1[0];
t01= co3[1]-co1[1];
t02= co3[2]-co1[2];
t10= co3[0]-co2[0];
t11= co3[1]-co2[1];
t12= co3[2]-co2[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-co3[0];
m1= ry1-co3[1];
m2= rz1-co3[2];
det1= m0*x0+m1*x1+m2*x2;
if(divdet!=0.0f) {
u1= det1/divdet;
if(u1<ISECT_EPSILON) {
det= t00*(m1*r2-m2*r1);
det+= t01*(m2*r0-m0*r2);
det+= t02*(m0*r1-m1*r0);
v= det/divdet;
if(v<ISECT_EPSILON && (u1 + v) > -(1.0f+ISECT_EPSILON)) {
return 1;
}
}
}
if(face->v4) {
t20= co3[0]-co4[0];
t21= co3[1]-co4[1];
t22= co3[2]-co4[2];
divdet= t20*x0+t21*x1+t22*x2;
if(divdet!=0.0f) {
u2= det1/divdet;
if(u2<ISECT_EPSILON) {
det= t20*(m1*r2-m2*r1);
det+= t21*(m2*r0-m0*r2);
det+= t22*(m0*r1-m1*r0);
v= det/divdet;
if(v<ISECT_EPSILON && (u2 + v) >= -(1.0f+ISECT_EPSILON)) {
return 2;
}
}
}
}
return 0;
}
static inline int vlr_check_intersect(Isect *is, ObjectInstanceRen *obi, VlakRen *vlr)
{
/* for baking selected to active non-traceable materials might still
* be in the raytree */
if(!(vlr->mat->mode & MA_TRACEBLE))
return 0;
/* 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 & obi->lay);
}
static inline int vlr_check_intersect_solid(Isect *is, ObjectInstanceRen* obi, VlakRen *vlr)
{
/* solid material types only */
if (vlr->mat->material_type == MA_TYPE_SURFACE)
return 1;
else
return 0;
}
static inline int rayface_check_cullface(RayFace *face, Isect *is)
{
float nor[3];
/* don't intersect if the ray faces along the face normal */
if(face->quad) CalcNormFloat4(face->v1, face->v2, face->v3, face->v4, nor);
else CalcNormFloat(face->v1, face->v2, face->v3, nor);
return (INPR(nor, is->vec) < 0);
}
/* ray - triangle or quad intersection */
/* this function shall only modify Isect if it detects an hit */
static int intersect_rayface(RayObject *hit_obj, RayFace *face, Isect *is)
{
float co1[3],co2[3],co3[3],co4[3];
float x0,x1,x2,t00,t01,t02,t10,t11,t12,t20,t21,t22,r0,r1,r2;
float m0, m1, m2, divdet, det1;
float labda, u, v;
short ok=0;
if(is->orig.ob == face->ob && is->orig.face == face->face)
return 0;
if(is->skip & RE_SKIP_VLR_RENDER_CHECK)
{
if(vlr_check_intersect(is, (ObjectInstanceRen*)face->ob, (VlakRen*)face->face ) == 0)
return 0;
}
if(is->skip & RE_SKIP_VLR_NON_SOLID_MATERIAL)
{
if(vlr_check_intersect_solid(is, (ObjectInstanceRen*)face->ob, (VlakRen*)face->face) == 0)
return 0;
}
if(is->skip & RE_SKIP_CULLFACE)
{
if(rayface_check_cullface(face, is) == 0)
return 0;
}
RE_RC_COUNT(is->raycounter->faces.test);
//Load coords
VECCOPY(co1, face->v1);
VECCOPY(co2, face->v2);
if(RE_rayface_isQuad(face))
{
VECCOPY(co3, face->v4);
VECCOPY(co4, face->v3);
}
else
{
VECCOPY(co3, face->v3);
}
t00= co3[0]-co1[0];
t01= co3[1]-co1[1];
t02= co3[2]-co1[2];
t10= co3[0]-co2[0];
t11= co3[1]-co2[1];
t12= co3[2]-co2[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]-co3[0];
m1= is->start[1]-co3[1];
m2= is->start[2]-co3[2];
det1= m0*x0+m1*x1+m2*x2;
if(divdet!=0.0f) {
divdet= 1.0f/divdet;
u= det1*divdet;
if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) {
float 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<ISECT_EPSILON && (u + v) > -(1.0f+ISECT_EPSILON)) {
labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12);
if(labda>-ISECT_EPSILON && labda<is->labda) {
ok= 1;
}
}
}
}
if(ok==0 && RE_rayface_isQuad(face)) {
t20= co3[0]-co4[0];
t21= co3[1]-co4[1];
t22= co3[2]-co4[2];
divdet= t20*x0+t21*x1+t22*x2;
if(divdet!=0.0f) {
divdet= 1.0f/divdet;
u = det1*divdet;
if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) {
float 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<ISECT_EPSILON && (u + v) >-(1.0f+ISECT_EPSILON)) {
labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12);
if(labda>-ISECT_EPSILON && labda<is->labda) {
ok= 2;
}
}
}
}
}
if(ok) {
/* 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->skip & RE_SKIP_VLR_NEIGHBOUR)
{
if(labda < 0.1f && is->orig.ob == face->ob)
{
VlakRen * a = (VlakRen*)is->orig.face;
VlakRen * b = (VlakRen*)face->face;
/* 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(a->v1==b->v1 || a->v2==b->v1 || a->v3==b->v1 || a->v4==b->v1
|| a->v1==b->v2 || a->v2==b->v2 || a->v3==b->v2 || a->v4==b->v2
|| a->v1==b->v3 || a->v2==b->v3 || a->v3==b->v3 || a->v4==b->v3
|| (b->v4 && (a->v1==b->v4 || a->v2==b->v4 || a->v3==b->v4 || a->v4==b->v4)))
if(!intersection2((VlakRen*)a, -r0, -r1, -r2, is->start[0], is->start[1], is->start[2]))
{
return 0;
}
}
}
RE_RC_COUNT(is->raycounter->faces.hit);
is->isect= ok; // wich half of the quad
is->labda= labda;
is->u= u; is->v= v;
is->hit.ob = face->ob;
is->hit.face = face->face;
#ifdef RT_USE_LAST_HIT
is->last_hit = hit_obj;
#endif
return 1;
}
return 0;
}
RayObject* RE_rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRen *vlr)
{
return RE_rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : 0 );
}
RayObject* RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *v1, float *v2, float *v3, float *v4)
{
rayface->ob = ob;
rayface->face = face;
VECCOPY(rayface->v1, v1);
VECCOPY(rayface->v2, v2);
VECCOPY(rayface->v3, v3);
if(v4)
{
VECCOPY(rayface->v4, v4);
rayface->quad = 1;
}
else
{
rayface->quad = 0;
}
return RE_rayobject_unalignRayFace(rayface);
}
RayObject* RE_vlakprimitive_from_vlak(VlakPrimitive *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr)
{
face->ob = obi;
face->face = vlr;
return RE_rayobject_unalignVlakPrimitive(face);
}
int RE_rayobject_raycast(RayObject *r, Isect *isec)
{
int i;
RE_RC_COUNT(isec->raycounter->raycast.test);
/* Setup vars used on raycast */
isec->labda *= Normalize(isec->vec);
isec->dist = VecLength(isec->vec);
for(i=0; i<3; i++)
{
isec->idot_axis[i] = 1.0f / isec->vec[i];
isec->bv_index[2*i] = isec->idot_axis[i] < 0.0 ? 1 : 0;
isec->bv_index[2*i+1] = 1 - isec->bv_index[2*i];
isec->bv_index[2*i] = i+3*isec->bv_index[2*i];
isec->bv_index[2*i+1] = i+3*isec->bv_index[2*i+1];
}
#ifdef RT_USE_LAST_HIT
/* Last hit heuristic */
if(isec->mode==RE_RAY_SHADOW && isec->last_hit)
{
RE_RC_COUNT(isec->raycounter->rayshadow_last_hit.test);
if(RE_rayobject_intersect(isec->last_hit, isec))
{
RE_RC_COUNT(isec->raycounter->raycast.hit);
RE_RC_COUNT(isec->raycounter->rayshadow_last_hit.hit);
return 1;
}
}
#endif
#ifdef RT_USE_HINT
isec->hit_hint = 0;
#endif
if(RE_rayobject_intersect(r, isec))
{
RE_RC_COUNT(isec->raycounter->raycast.hit);
#ifdef RT_USE_HINT
isec->hint = isec->hit_hint;
#endif
return 1;
}
return 0;
}
int RE_rayobject_intersect(RayObject *r, Isect *i)
{
if(RE_rayobject_isRayFace(r))
{
return intersect_rayface(r, (RayFace*) RE_rayobject_align(r), i);
}
else if(RE_rayobject_isVlakPrimitive(r))
{
//TODO optimize (useless copy to RayFace to avoid duplicate code)
VlakPrimitive *face = (VlakPrimitive*) RE_rayobject_align(r);
RayFace nface;
RE_rayface_from_vlak(&nface, face->ob, face->face);
if(face->ob->transform_primitives)
{
Mat4MulVecfl(face->ob->mat, nface.v1);
Mat4MulVecfl(face->ob->mat, nface.v2);
Mat4MulVecfl(face->ob->mat, nface.v3);
if(RE_rayface_isQuad(&nface))
Mat4MulVecfl(face->ob->mat, nface.v4);
}
return intersect_rayface(r, &nface, i);
}
else if(RE_rayobject_isRayAPI(r))
{
r = RE_rayobject_align( r );
return r->api->raycast( r, i );
}
else assert(0);
}
void RE_rayobject_add(RayObject *r, RayObject *o)
{
r = RE_rayobject_align( r );
return r->api->add( r, o );
}
void RE_rayobject_done(RayObject *r)
{
r = RE_rayobject_align( r );
r->api->done( r );
}
void RE_rayobject_free(RayObject *r)
{
r = RE_rayobject_align( r );
r->api->free( r );
}
void RE_rayobject_merge_bb(RayObject *r, float *min, float *max)
{
if(RE_rayobject_isRayFace(r))
{
RayFace *face = (RayFace*) RE_rayobject_align(r);
DO_MINMAX( face->v1, min, max );
DO_MINMAX( face->v2, min, max );
DO_MINMAX( face->v3, min, max );
if(RE_rayface_isQuad(face)) DO_MINMAX( face->v4, min, max );
}
else if(RE_rayobject_isVlakPrimitive(r))
{
VlakPrimitive *face = (VlakPrimitive*) RE_rayobject_align(r);
VlakRen *vlr = face->face;
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 );
}
else if(RE_rayobject_isRayAPI(r))
{
r = RE_rayobject_align( r );
r->api->bb( r, min, max );
}
else assert(0);
}
float RE_rayobject_cost(RayObject *r)
{
if(RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r))
{
return 1.0;
}
else if(RE_rayobject_isRayAPI(r))
{
r = RE_rayobject_align( r );
return r->api->cost( r );
}
else assert(0);
}
void RE_rayobject_hint_bb(RayObject *r, RayHint *hint, float *min, float *max)
{
if(RE_rayobject_isRayFace(r) || RE_rayobject_isVlakPrimitive(r))
{
return;
}
else if(RE_rayobject_isRayAPI(r))
{
r = RE_rayobject_align( r );
return r->api->hint_bb( r, hint, min, max );
}
else assert(0);
}
int RE_rayobjectcontrol_test_break(RayObjectControl *control)
{
if(control->test_break)
return control->test_break( control->data );
return 0;
}

@ -0,0 +1,70 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef RE_RAYTRACE_RAYOBJECT_HINT_H
#define RE_RAYTRACE_RAYOBJECT_HINT_H
#define HINT_RECURSE 1
#define HINT_ACCEPT 0
#define HINT_DISCARD -1
struct HintBB
{
float bb[6];
};
inline int hint_test_bb(HintBB *obj, float *Nmin, float *Nmax)
{
if(bb_fits_inside( Nmin, Nmax, obj->bb, obj->bb+3 ) )
return HINT_RECURSE;
else
return HINT_ACCEPT;
}
/*
struct HintFrustum
{
float co[3];
float no[4][3];
};
inline int hint_test_bb(HintFrustum &obj, float *Nmin, float *Nmax)
{
//if frustum inside BB
{
return HINT_RECURSE;
}
//if BB outside frustum
{
return HINT_DISCARD;
}
return HINT_ACCEPT;
}
*/
#endif

@ -0,0 +1,149 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "vbvh.h"
#include "svbvh.h"
#include "reorganize.h"
#ifdef __SSE__
#define DFS_STACK_SIZE 256
struct QBVHTree
{
RayObject rayobj;
SVBVHNode *root;
MemArena *node_arena;
float cost;
RTBuilder *builder;
};
template<>
void bvh_done<QBVHTree>(QBVHTree *obj)
{
rtbuild_done(obj->builder, &obj->rayobj.control);
//TODO find a away to exactly calculate the needed memory
MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_malloc(arena1);
MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_malloc(arena2);
BLI_memarena_use_align(arena2, 16);
//Build and optimize the tree
//TODO do this in 1 pass (half memory usage during building)
VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1, &obj->rayobj.control).transform(obj->builder);
if(RE_rayobjectcontrol_test_break(&obj->rayobj.control))
{
BLI_memarena_free(arena1);
BLI_memarena_free(arena2);
return;
}
pushup_simd<VBVHNode,4>(root);
obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root);
//Cleanup
BLI_memarena_free(arena1);
rtbuild_free( obj->builder );
obj->builder = NULL;
obj->node_arena = arena2;
obj->cost = 1.0;
}
template<int StackSize>
int intersect(QBVHTree *obj, Isect* isec)
{
//TODO renable hint support
if(RE_rayobject_isAligned(obj->root))
return bvh_node_stack_raycast<SVBVHNode,StackSize,false>( obj->root, isec);
else
return RE_rayobject_intersect( (RayObject*) obj->root, isec );
}
template<class Tree>
void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *min, float *max)
{
//TODO renable hint support
{
hint->size = 0;
hint->stack[hint->size++] = (RayObject*)tree->root;
}
}
/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
template<class Tree, int STACK_SIZE>
RayObjectAPI make_api()
{
static RayObjectAPI api =
{
(RE_rayobject_raycast_callback) ((int(*)(Tree*,Isect*)) &intersect<STACK_SIZE>),
(RE_rayobject_add_callback) ((void(*)(Tree*,RayObject*)) &bvh_add<Tree>),
(RE_rayobject_done_callback) ((void(*)(Tree*)) &bvh_done<Tree>),
(RE_rayobject_free_callback) ((void(*)(Tree*)) &bvh_free<Tree>),
(RE_rayobject_merge_bb_callback)((void(*)(Tree*,float*,float*)) &bvh_bb<Tree>),
(RE_rayobject_cost_callback) ((float(*)(Tree*)) &bvh_cost<Tree>),
(RE_rayobject_hint_bb_callback) ((void(*)(Tree*,LCTSHint*,float*,float*)) &bvh_hint_bb<Tree>)
};
return api;
}
template<class Tree>
RayObjectAPI* bvh_get_api(int maxstacksize)
{
static RayObjectAPI bvh_api256 = make_api<Tree,1024>();
if(maxstacksize <= 1024) return &bvh_api256;
assert(maxstacksize <= 256);
return 0;
}
RayObject *RE_rayobject_qbvh_create(int size)
{
return bvh_create_tree<QBVHTree,DFS_STACK_SIZE>(size);
}
#else
RayObject *RE_rayobject_qbvh_create(int size)
{
puts("WARNING: SSE disabled at compile time\n");
return NULL;
}
#endif

@ -0,0 +1,496 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include "rayobject_rtbuild.h"
#include "MEM_guardedalloc.h"
#include "BLI_arithb.h"
#include "BKE_utildefines.h"
static bool selected_node(RTBuilder::Object *node)
{
return node->selected;
}
static void rtbuild_init(RTBuilder *b)
{
b->split_axis = -1;
b->primitives.begin = 0;
b->primitives.end = 0;
b->primitives.maxsize = 0;
for(int i=0; i<RTBUILD_MAX_CHILDS; i++)
b->child_offset[i] = 0;
for(int i=0; i<3; i++)
b->sorted_begin[i] = b->sorted_end[i] = 0;
INIT_MINMAX(b->bb, b->bb+3);
}
RTBuilder* rtbuild_create(int size)
{
RTBuilder *builder = (RTBuilder*) MEM_mallocN( sizeof(RTBuilder), "RTBuilder" );
RTBuilder::Object *memblock= (RTBuilder::Object*)MEM_mallocN( sizeof(RTBuilder::Object)*size,"RTBuilder.objects");
rtbuild_init(builder);
builder->primitives.begin = builder->primitives.end = memblock;
builder->primitives.maxsize = size;
for(int i=0; i<3; i++)
{
builder->sorted_begin[i] = (RTBuilder::Object**)MEM_mallocN( sizeof(RTBuilder::Object*)*size,"RTBuilder.sorted_objects");
builder->sorted_end[i] = builder->sorted_begin[i];
}
return builder;
}
void rtbuild_free(RTBuilder *b)
{
if(b->primitives.begin) MEM_freeN(b->primitives.begin);
for(int i=0; i<3; i++)
if(b->sorted_begin[i])
MEM_freeN(b->sorted_begin[i]);
MEM_freeN(b);
}
void rtbuild_add(RTBuilder *b, RayObject *o)
{
assert( b->primitives.begin + b->primitives.maxsize != b->primitives.end );
b->primitives.end->obj = o;
b->primitives.end->cost = RE_rayobject_cost(o);
INIT_MINMAX(b->primitives.end->bb, b->primitives.end->bb+3);
RE_rayobject_merge_bb(o, b->primitives.end->bb, b->primitives.end->bb+3);
for(int i=0; i<3; i++)
{
*(b->sorted_end[i]) = b->primitives.end;
b->sorted_end[i]++;
}
b->primitives.end++;
}
int rtbuild_size(RTBuilder *b)
{
return b->sorted_end[0] - b->sorted_begin[0];
}
template<class Obj,int Axis>
static bool obj_bb_compare(const Obj &a, const Obj &b)
{
if(a->bb[Axis] != b->bb[Axis])
return a->bb[Axis] < b->bb[Axis];
return a->obj < b->obj;
}
template<class Item>
static void object_sort(Item *begin, Item *end, int axis)
{
if(axis == 0) return std::sort(begin, end, obj_bb_compare<Item,0> );
if(axis == 1) return std::sort(begin, end, obj_bb_compare<Item,1> );
if(axis == 2) return std::sort(begin, end, obj_bb_compare<Item,2> );
assert(false);
}
void rtbuild_done(RTBuilder *b, RayObjectControl* ctrl)
{
for(int i=0; i<3; i++)
if(b->sorted_begin[i])
{
if(RE_rayobjectcontrol_test_break(ctrl)) break;
object_sort( b->sorted_begin[i], b->sorted_end[i], i );
}
}
RayObject* rtbuild_get_primitive(RTBuilder *b, int index)
{
return b->sorted_begin[0][index]->obj;
}
RTBuilder* rtbuild_get_child(RTBuilder *b, int child, RTBuilder *tmp)
{
rtbuild_init( tmp );
for(int i=0; i<3; i++)
if(b->sorted_begin[i])
{
tmp->sorted_begin[i] = b->sorted_begin[i] + b->child_offset[child ];
tmp->sorted_end [i] = b->sorted_begin[i] + b->child_offset[child+1];
}
else
{
tmp->sorted_begin[i] = 0;
tmp->sorted_end [i] = 0;
}
return tmp;
}
void rtbuild_calc_bb(RTBuilder *b)
{
if(b->bb[0] == 1.0e30f)
{
for(RTBuilder::Object **index = b->sorted_begin[0]; index != b->sorted_end[0]; index++)
RE_rayobject_merge_bb( (*index)->obj , b->bb, b->bb+3);
}
}
void rtbuild_merge_bb(RTBuilder *b, float *min, float *max)
{
rtbuild_calc_bb(b);
DO_MIN(b->bb, min);
DO_MAX(b->bb+3, max);
}
/*
int rtbuild_get_largest_axis(RTBuilder *b)
{
rtbuild_calc_bb(b);
return bb_largest_axis(b->bb, b->bb+3);
}
//Left balanced tree
int rtbuild_mean_split(RTBuilder *b, int nchilds, int axis)
{
int i;
int mleafs_per_child, Mleafs_per_child;
int tot_leafs = rtbuild_size(b);
int missing_leafs;
long long s;
assert(nchilds <= RTBUILD_MAX_CHILDS);
//TODO optimize calc of leafs_per_child
for(s=nchilds; s<tot_leafs; s*=nchilds);
Mleafs_per_child = s/nchilds;
mleafs_per_child = Mleafs_per_child/nchilds;
//split min leafs per child
b->child_offset[0] = 0;
for(i=1; i<=nchilds; i++)
b->child_offset[i] = mleafs_per_child;
//split remaining leafs
missing_leafs = tot_leafs - mleafs_per_child*nchilds;
for(i=1; i<=nchilds; i++)
{
if(missing_leafs > Mleafs_per_child - mleafs_per_child)
{
b->child_offset[i] += Mleafs_per_child - mleafs_per_child;
missing_leafs -= Mleafs_per_child - mleafs_per_child;
}
else
{
b->child_offset[i] += missing_leafs;
missing_leafs = 0;
break;
}
}
//adjust for accumulative offsets
for(i=1; i<=nchilds; i++)
b->child_offset[i] += b->child_offset[i-1];
//Count created childs
for(i=nchilds; b->child_offset[i] == b->child_offset[i-1]; i--);
split_leafs(b, b->child_offset, i, axis);
assert( b->child_offset[0] == 0 && b->child_offset[i] == tot_leafs );
return i;
}
int rtbuild_mean_split_largest_axis(RTBuilder *b, int nchilds)
{
int axis = rtbuild_get_largest_axis(b);
return rtbuild_mean_split(b, nchilds, axis);
}
*/
/*
* "separators" is an array of dim NCHILDS-1
* and indicates where to cut the childs
*/
/*
int rtbuild_median_split(RTBuilder *b, float *separators, int nchilds, int axis)
{
int size = rtbuild_size(b);
assert(nchilds <= RTBUILD_MAX_CHILDS);
if(size <= nchilds)
{
return rtbuild_mean_split(b, nchilds, axis);
}
else
{
int i;
b->split_axis = axis;
//Calculate child offsets
b->child_offset[0] = 0;
for(i=0; i<nchilds-1; i++)
b->child_offset[i+1] = split_leafs_by_plane(b, b->child_offset[i], size, separators[i]);
b->child_offset[nchilds] = size;
for(i=0; i<nchilds; i++)
if(b->child_offset[i+1] - b->child_offset[i] == size)
return rtbuild_mean_split(b, nchilds, axis);
return nchilds;
}
}
int rtbuild_median_split_largest_axis(RTBuilder *b, int nchilds)
{
int la, i;
float separators[RTBUILD_MAX_CHILDS];
rtbuild_calc_bb(b);
la = bb_largest_axis(b->bb,b->bb+3);
for(i=1; i<nchilds; i++)
separators[i-1] = (b->bb[la+3]-b->bb[la])*i / nchilds;
return rtbuild_median_split(b, separators, nchilds, la);
}
*/
//Heuristics Object Splitter
struct SweepCost
{
float bb[6];
float cost;
};
/* Object Surface Area Heuristic splitter */
int rtbuild_heuristic_object_split(RTBuilder *b, int nchilds)
{
int size = rtbuild_size(b);
assert(nchilds == 2);
assert(size > 1);
int baxis = -1, boffset = 0;
if(size > nchilds)
{
float bcost = FLT_MAX;
baxis = -1, boffset = size/2;
SweepCost *sweep = (SweepCost*)MEM_mallocN( sizeof(SweepCost)*size, "RTBuilder.HeuristicSweep" );
for(int axis=0; axis<3; axis++)
{
SweepCost sweep_left;
RTBuilder::Object **obj = b->sorted_begin[axis];
// float right_cost = 0;
for(int i=size-1; i>=0; i--)
{
if(i == size-1)
{
VECCOPY(sweep[i].bb, obj[i]->bb);
VECCOPY(sweep[i].bb+3, obj[i]->bb+3);
sweep[i].cost = obj[i]->cost;
}
else
{
sweep[i].bb[0] = MIN2(obj[i]->bb[0], sweep[i+1].bb[0]);
sweep[i].bb[1] = MIN2(obj[i]->bb[1], sweep[i+1].bb[1]);
sweep[i].bb[2] = MIN2(obj[i]->bb[2], sweep[i+1].bb[2]);
sweep[i].bb[3] = MAX2(obj[i]->bb[3], sweep[i+1].bb[3]);
sweep[i].bb[4] = MAX2(obj[i]->bb[4], sweep[i+1].bb[4]);
sweep[i].bb[5] = MAX2(obj[i]->bb[5], sweep[i+1].bb[5]);
sweep[i].cost = obj[i]->cost + sweep[i+1].cost;
}
// right_cost += obj[i]->cost;
}
sweep_left.bb[0] = obj[0]->bb[0];
sweep_left.bb[1] = obj[0]->bb[1];
sweep_left.bb[2] = obj[0]->bb[2];
sweep_left.bb[3] = obj[0]->bb[3];
sweep_left.bb[4] = obj[0]->bb[4];
sweep_left.bb[5] = obj[0]->bb[5];
sweep_left.cost = obj[0]->cost;
// right_cost -= obj[0]->cost; if(right_cost < 0) right_cost = 0;
for(int i=1; i<size; i++)
{
//Worst case heuristic (cost of each child is linear)
float hcost, left_side, right_side;
left_side = bb_area(sweep_left.bb, sweep_left.bb+3)*(sweep_left.cost+logf(i));
right_side= bb_area(sweep[i].bb, sweep[i].bb+3)*(sweep[i].cost+logf(size-i));
hcost = left_side+right_side;
assert(left_side >= 0);
assert(right_side >= 0);
if(left_side > bcost) break; //No way we can find a better heuristic in this axis
assert(hcost >= 0);
if( hcost < bcost
|| (hcost == bcost && axis < baxis)) //this makes sure the tree built is the same whatever is the order of the sorting axis
{
bcost = hcost;
baxis = axis;
boffset = i;
}
DO_MIN( obj[i]->bb, sweep_left.bb );
DO_MAX( obj[i]->bb+3, sweep_left.bb+3 );
sweep_left.cost += obj[i]->cost;
// right_cost -= obj[i]->cost; if(right_cost < 0) right_cost = 0;
}
assert(baxis >= 0 && baxis < 3);
}
MEM_freeN(sweep);
}
else if(size == 2)
{
baxis = 0;
boffset = 1;
}
else if(size == 1)
{
b->child_offset[0] = 0;
b->child_offset[1] = 1;
return 1;
}
b->child_offset[0] = 0;
b->child_offset[1] = boffset;
b->child_offset[2] = size;
/* Adjust sorted arrays for childs */
for(int i=0; i<boffset; i++) b->sorted_begin[baxis][i]->selected = true;
for(int i=boffset; i<size; i++) b->sorted_begin[baxis][i]->selected = false;
for(int i=0; i<3; i++)
std::stable_partition( b->sorted_begin[i], b->sorted_end[i], selected_node );
return nchilds;
}
/*
* Helper code
* PARTITION code / used on mean-split
* basicly this a std::nth_element (like on C++ STL algorithm)
*/
/*
static void split_leafs(RTBuilder *b, int *nth, int partitions, int split_axis)
{
int i;
b->split_axis = split_axis;
for(i=0; i < partitions-1; i++)
{
assert(nth[i] < nth[i+1] && nth[i+1] < nth[partitions]);
if(split_axis == 0) std::nth_element(b, nth[i], nth[i+1], nth[partitions], obj_bb_compare<RTBuilder::Object,0>);
if(split_axis == 1) std::nth_element(b, nth[i], nth[i+1], nth[partitions], obj_bb_compare<RTBuilder::Object,1>);
if(split_axis == 2) std::nth_element(b, nth[i], nth[i+1], nth[partitions], obj_bb_compare<RTBuilder::Object,2>);
}
}
*/
/*
* Bounding Box utils
*/
float bb_volume(float *min, float *max)
{
return (max[0]-min[0])*(max[1]-min[1])*(max[2]-min[2]);
}
float bb_area(float *min, float *max)
{
float sub[3], a;
sub[0] = max[0]-min[0];
sub[1] = max[1]-min[1];
sub[2] = max[2]-min[2];
a = (sub[0]*sub[1] + sub[0]*sub[2] + sub[1]*sub[2])*2;
assert(a >= 0.0);
return a;
}
int bb_largest_axis(float *min, float *max)
{
float sub[3];
sub[0] = max[0]-min[0];
sub[1] = max[1]-min[1];
sub[2] = max[2]-min[2];
if(sub[0] > sub[1])
{
if(sub[0] > sub[2])
return 0;
else
return 2;
}
else
{
if(sub[1] > sub[2])
return 1;
else
return 2;
}
}
int bb_fits_inside(float *outer_min, float *outer_max, float *inner_min, float *inner_max)
{
int i;
for(i=0; i<3; i++)
if(outer_min[i] > inner_min[i]) return 0;
for(i=0; i<3; i++)
if(outer_max[i] < inner_max[i]) return 0;
return 1;
}

@ -0,0 +1,121 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef RE_RAYOBJECT_RTBUILD_H
#define RE_RAYOBJECT_RTBUILD_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rayobject.h"
/*
* Ray Tree Builder
* this structs helps building any type of tree
* it contains several methods to organiza/split nodes
* allowing to create a given tree on the fly.
*
* Idea is that other trees BVH, BIH can use this code to
* generate with simple calls, and then convert to the theirs
* specific structure on the fly.
*/
#define RTBUILD_MAX_CHILDS 32
typedef struct RTBuilder
{
struct Object
{
RayObject *obj;
float cost;
float bb[6];
int selected;
};
/* list to all primitives added in this tree */
struct
{
Object *begin, *end;
int maxsize;
} primitives;
/* sorted list of rayobjects */
struct Object **sorted_begin[3], **sorted_end[3];
/* axis used (if any) on the split method */
int split_axis;
/* child partitions calculated during splitting */
int child_offset[RTBUILD_MAX_CHILDS+1];
// int child_sorted_axis; /* -1 if not sorted */
float bb[6];
} RTBuilder;
/* used during creation */
RTBuilder* rtbuild_create(int size);
void rtbuild_free(RTBuilder *b);
void rtbuild_add(RTBuilder *b, RayObject *o);
void rtbuild_done(RTBuilder *b, RayObjectControl *c);
void rtbuild_merge_bb(RTBuilder *b, float *min, float *max);
int rtbuild_size(RTBuilder *b);
RayObject* rtbuild_get_primitive(RTBuilder *b, int offset);
/* used during tree reorganization */
RTBuilder* rtbuild_get_child(RTBuilder *b, int child, RTBuilder *tmp);
/* Calculates child partitions and returns number of efectively needed partitions */
int rtbuild_get_largest_axis(RTBuilder *b);
//Object partition
int rtbuild_mean_split(RTBuilder *b, int nchilds, int axis);
int rtbuild_mean_split_largest_axis(RTBuilder *b, int nchilds);
int rtbuild_heuristic_object_split(RTBuilder *b, int nchilds);
//Space partition
int rtbuild_median_split(RTBuilder *b, float *separators, int nchilds, int axis);
int rtbuild_median_split_largest_axis(RTBuilder *b, int nchilds);
/* bb utils */
float bb_area(float *min, float *max);
float bb_volume(float *min, float *max);
int bb_largest_axis(float *min, float *max);
int bb_fits_inside(float *outer_min, float *outer_max, float *inner_min, float *inner_max); /* only returns 0 if merging inner and outerbox would create a box larger than outer box */
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,181 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "vbvh.h"
#include "svbvh.h"
#include "reorganize.h"
#ifdef __SSE__
#define DFS_STACK_SIZE 256
struct SVBVHTree
{
RayObject rayobj;
SVBVHNode *root;
MemArena *node_arena;
float cost;
RTBuilder *builder;
};
/*
* Cost to test N childs
*/
struct PackCost
{
float operator()(int n)
{
return (n / 4) + ((n % 4) > 2 ? 1 : n%4);
}
};
template<>
void bvh_done<SVBVHTree>(SVBVHTree *obj)
{
rtbuild_done(obj->builder, &obj->rayobj.control);
//TODO find a away to exactly calculate the needed memory
MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_malloc(arena1);
MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_malloc(arena2);
BLI_memarena_use_align(arena2, 16);
//Build and optimize the tree
if(0)
{
VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1,&obj->rayobj.control).transform(obj->builder);
if(RE_rayobjectcontrol_test_break(&obj->rayobj.control))
{
BLI_memarena_free(arena1);
BLI_memarena_free(arena2);
return;
}
reorganize(root);
remove_useless(root, &root);
bvh_refit(root);
pushup(root);
pushdown(root);
pushup_simd<VBVHNode,4>(root);
obj->root = Reorganize_SVBVH<VBVHNode>(arena2).transform(root);
}
else
{
//Finds the optimal packing of this tree using a given cost model
//TODO this uses quite a lot of memory, find ways to reduce memory usage during building
OVBVHNode *root = BuildBinaryVBVH<OVBVHNode>(arena1,&obj->rayobj.control).transform(obj->builder);
if(RE_rayobjectcontrol_test_break(&obj->rayobj.control))
{
BLI_memarena_free(arena1);
BLI_memarena_free(arena2);
return;
}
VBVH_optimalPackSIMD<OVBVHNode,PackCost>(PackCost()).transform(root);
obj->root = Reorganize_SVBVH<OVBVHNode>(arena2).transform(root);
}
//Free data
BLI_memarena_free(arena1);
obj->node_arena = arena2;
obj->cost = 1.0;
rtbuild_free( obj->builder );
obj->builder = NULL;
}
template<int StackSize>
int intersect(SVBVHTree *obj, Isect* isec)
{
//TODO renable hint support
if(RE_rayobject_isAligned(obj->root))
return bvh_node_stack_raycast<SVBVHNode,StackSize,false>( obj->root, isec);
else
return RE_rayobject_intersect( (RayObject*) obj->root, isec );
}
template<class Tree>
void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *min, float *max)
{
//TODO renable hint support
{
hint->size = 0;
hint->stack[hint->size++] = (RayObject*)tree->root;
}
}
/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
template<class Tree, int STACK_SIZE>
RayObjectAPI make_api()
{
static RayObjectAPI api =
{
(RE_rayobject_raycast_callback) ((int(*)(Tree*,Isect*)) &intersect<STACK_SIZE>),
(RE_rayobject_add_callback) ((void(*)(Tree*,RayObject*)) &bvh_add<Tree>),
(RE_rayobject_done_callback) ((void(*)(Tree*)) &bvh_done<Tree>),
(RE_rayobject_free_callback) ((void(*)(Tree*)) &bvh_free<Tree>),
(RE_rayobject_merge_bb_callback)((void(*)(Tree*,float*,float*)) &bvh_bb<Tree>),
(RE_rayobject_cost_callback) ((float(*)(Tree*)) &bvh_cost<Tree>),
(RE_rayobject_hint_bb_callback) ((void(*)(Tree*,LCTSHint*,float*,float*)) &bvh_hint_bb<Tree>)
};
return api;
}
template<class Tree>
RayObjectAPI* bvh_get_api(int maxstacksize)
{
static RayObjectAPI bvh_api256 = make_api<Tree,1024>();
if(maxstacksize <= 1024) return &bvh_api256;
assert(maxstacksize <= 256);
return 0;
}
RayObject *RE_rayobject_svbvh_create(int size)
{
return bvh_create_tree<SVBVHTree,DFS_STACK_SIZE>(size);
}
#else
RayObject *RE_rayobject_svbvh_create(int size)
{
puts("WARNING: SSE disabled at compile time\n");
return NULL;
}
#endif

@ -0,0 +1,191 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
int tot_pushup = 0;
int tot_pushdown = 0;
int tot_hints = 0;
#include <assert.h>
#include "rayobject.h"
#include "rayobject_rtbuild.h"
#include "RE_raytrace.h"
#include "BLI_memarena.h"
#include "MEM_guardedalloc.h"
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
#include "reorganize.h"
#include "bvh.h"
#include "vbvh.h"
#include "svbvh.h"
#include <queue>
#include <algorithm>
#define DFS_STACK_SIZE 256
struct VBVHTree
{
RayObject rayobj;
VBVHNode *root;
MemArena *node_arena;
float cost;
RTBuilder *builder;
};
/*
* Cost to test N childs
*/
struct PackCost
{
float operator()(int n)
{
return n;
}
};
template<>
void bvh_done<VBVHTree>(VBVHTree *obj)
{
rtbuild_done(obj->builder, &obj->rayobj.control);
//TODO find a away to exactly calculate the needed memory
MemArena *arena1 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_malloc(arena1);
//Build and optimize the tree
if(1)
{
VBVHNode *root = BuildBinaryVBVH<VBVHNode>(arena1,&obj->rayobj.control).transform(obj->builder);
if(RE_rayobjectcontrol_test_break(&obj->rayobj.control))
{
BLI_memarena_free(arena1);
return;
}
reorganize(root);
remove_useless(root, &root);
bvh_refit(root);
pushup(root);
pushdown(root);
obj->root = root;
}
else
{
/*
TODO
MemArena *arena2 = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
BLI_memarena_use_malloc(arena2);
//Finds the optimal packing of this tree using a given cost model
//TODO this uses quite a lot of memory, find ways to reduce memory usage during building
OVBVHNode *root = BuildBinaryVBVH<OVBVHNode>(arena2).transform(obj->builder);
VBVH_optimalPackSIMD<OVBVHNode,PackCost>(PackCost()).transform(root);
obj->root = Reorganize_VBVH<OVBVHNode>(arena1).transform(root);
BLI_memarena_free(arena2);
*/
}
//Cleanup
rtbuild_free( obj->builder );
obj->builder = NULL;
obj->node_arena = arena1;
obj->cost = 1.0;
}
template<int StackSize>
int intersect(VBVHTree *obj, Isect* isec)
{
//TODO renable hint support
if(RE_rayobject_isAligned(obj->root))
return bvh_node_stack_raycast<VBVHNode,StackSize,false>( obj->root, isec);
else
return RE_rayobject_intersect( (RayObject*) obj->root, isec );
}
template<class Tree>
void bvh_hint_bb(Tree *tree, LCTSHint *hint, float *min, float *max)
{
//TODO renable hint support
{
hint->size = 0;
hint->stack[hint->size++] = (RayObject*)tree->root;
}
}
void bfree(VBVHTree *tree)
{
if(tot_pushup + tot_pushdown + tot_hints + tot_moves)
{
printf("tot pushups: %d\n", tot_pushup);
printf("tot pushdowns: %d\n", tot_pushdown);
printf("tot moves: %d\n", tot_moves);
printf("tot hints created: %d\n", tot_hints);
tot_pushup = 0;
tot_pushdown = 0;
tot_hints = 0;
tot_moves = 0;
}
bvh_free(tree);
}
/* the cast to pointer function is needed to workarround gcc bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11407 */
template<class Tree, int STACK_SIZE>
RayObjectAPI make_api()
{
static RayObjectAPI api =
{
(RE_rayobject_raycast_callback) ((int(*)(Tree*,Isect*)) &intersect<STACK_SIZE>),
(RE_rayobject_add_callback) ((void(*)(Tree*,RayObject*)) &bvh_add<Tree>),
(RE_rayobject_done_callback) ((void(*)(Tree*)) &bvh_done<Tree>),
(RE_rayobject_free_callback) ((void(*)(Tree*)) &bvh_free<Tree>),
(RE_rayobject_merge_bb_callback)((void(*)(Tree*,float*,float*)) &bvh_bb<Tree>),
(RE_rayobject_cost_callback) ((float(*)(Tree*)) &bvh_cost<Tree>),
(RE_rayobject_hint_bb_callback) ((void(*)(Tree*,LCTSHint*,float*,float*)) &bvh_hint_bb<Tree>)
};
return api;
}
template<class Tree>
RayObjectAPI* bvh_get_api(int maxstacksize)
{
static RayObjectAPI bvh_api256 = make_api<Tree,1024>();
if(maxstacksize <= 1024) return &bvh_api256;
assert(maxstacksize <= 256);
return 0;
}
RayObject *RE_rayobject_vbvh_create(int size)
{
return bvh_create_tree<VBVHTree,DFS_STACK_SIZE>(size);
}

@ -0,0 +1,516 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include <queue>
extern int tot_pushup;
extern int tot_pushdown;
template<class Node>
bool node_fits_inside(Node *a, Node *b)
{
return bb_fits_inside(b->bb, b->bb+3, a->bb, a->bb+3);
}
template<class Node>
void reorganize_find_fittest_parent(Node *tree, Node *node, std::pair<float,Node*> &cost)
{
std::queue<Node*> q;
q.push(tree);
while(!q.empty())
{
Node *parent = q.front();
q.pop();
if(parent == node) continue;
if(node_fits_inside(node, parent) && RE_rayobject_isAligned(parent->child) )
{
float pcost = bb_area(parent->bb, parent->bb+3);
cost = std::min( cost, std::make_pair(pcost,parent) );
for(Node *child = parent->child; child; child = child->sibling)
q.push(child);
}
}
}
static int tot_moves = 0;
template<class Node>
void reorganize(Node *root)
{
std::queue<Node*> q;
q.push(root);
while(!q.empty())
{
Node * node = q.front();
q.pop();
if( RE_rayobject_isAligned(node->child) )
{
for(Node **prev = &node->child; *prev; )
{
assert( RE_rayobject_isAligned(*prev) );
q.push(*prev);
std::pair<float,Node*> best(FLT_MAX, root);
reorganize_find_fittest_parent( root, *prev, best );
if(best.second == node)
{
//Already inside the fitnest BB
prev = &(*prev)->sibling;
}
else
{
Node *tmp = *prev;
*prev = (*prev)->sibling;
tmp->sibling = best.second->child;
best.second->child = tmp;
tot_moves++;
}
}
}
if(node != root)
{
}
}
}
/*
* Prunes useless nodes from trees:
* erases nodes with total ammount of primitives = 0
* prunes nodes with only one child (except if that child is a primitive)
*/
template<class Node>
void remove_useless(Node *node, Node **new_node)
{
if( RE_rayobject_isAligned(node->child) )
{
for(Node **prev = &node->child; *prev; )
{
Node *next = (*prev)->sibling;
remove_useless(*prev, prev);
if(*prev == 0)
*prev = next;
else
{
(*prev)->sibling = next;
prev = &((*prev)->sibling);
}
}
}
if(node->child)
{
if(RE_rayobject_isAligned(node->child) && node->child->sibling == 0)
*new_node = node->child;
}
else if(node->child == 0)
*new_node = 0;
}
/*
* Minimizes expected number of BBtest by colapsing nodes
* it uses surface area heuristic for determining whether a node should be colapsed
*/
template<class Node>
void pushup(Node *parent)
{
if(is_leaf(parent)) return;
float p_area = bb_area(parent->bb, parent->bb+3);
Node **prev = &parent->child;
for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; )
{
float c_area = bb_area(child->bb, child->bb+3) ;
int nchilds = count_childs(child);
float original_cost = (c_area / p_area)*nchilds + 1;
float flatten_cost = nchilds;
if(flatten_cost < original_cost && nchilds >= 2)
{
append_sibling(child, child->child);
child = child->sibling;
*prev = child;
// *prev = child->child;
// append_sibling( *prev, child->sibling );
// child = *prev;
tot_pushup++;
}
else
{
*prev = child;
prev = &(*prev)->sibling;
child = *prev;
}
}
for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
pushup(child);
}
/*
* try to optimize number of childs to be a multiple of SSize
*/
template<class Node, int SSize>
void pushup_simd(Node *parent)
{
if(is_leaf(parent)) return;
int n = count_childs(parent);
Node **prev = &parent->child;
for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; )
{
int cn = count_childs(child);
if(cn-1 <= (SSize - (n%SSize) ) % SSize && RE_rayobject_isAligned(child->child) )
{
n += (cn - 1);
append_sibling(child, child->child);
child = child->sibling;
*prev = child;
}
else
{
*prev = child;
prev = &(*prev)->sibling;
child = *prev;
}
}
for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling)
pushup_simd<Node,SSize>(child);
}
/*
* Pushdown
* makes sure no child fits inside any of its sibling
*/
template<class Node>
void pushdown(Node *parent)
{
Node **s_child = &parent->child;
Node * child = parent->child;
while(child && RE_rayobject_isAligned(child))
{
Node *next = child->sibling;
Node **next_s_child = &child->sibling;
//assert(bb_fits_inside(parent->bb, parent->bb+3, child->bb, child->bb+3));
for(Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling)
if(child != i && bb_fits_inside(i->bb, i->bb+3, child->bb, child->bb+3) && RE_rayobject_isAligned(i->child))
{
// todo optimize (should the one with the smallest area?)
// float ia = bb_area(i->bb, i->bb+3)
// if(child->i)
*s_child = child->sibling;
child->sibling = i->child;
i->child = child;
next_s_child = s_child;
tot_pushdown++;
break;
}
child = next;
s_child = next_s_child;
}
for(Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling)
pushdown( i );
}
/*
* BVH refit
* reajust nodes BB (useful if nodes childs where modified)
*/
template<class Node>
float bvh_refit(Node *node)
{
if(is_leaf(node)) return 0;
if(is_leaf(node->child)) return 0;
float total = 0;
for(Node *child = node->child; child; child = child->sibling)
total += bvh_refit(child);
float old_area = bb_area(node->bb, node->bb+3);
INIT_MINMAX(node->bb, node->bb+3);
for(Node *child = node->child; child; child = child->sibling)
{
DO_MIN(child->bb, node->bb);
DO_MAX(child->bb+3, node->bb+3);
}
total += old_area - bb_area(node->bb, node->bb+3);
return total;
}
/*
* this finds the best way to packing a tree acording to a given test cost function
* with the purpose to reduce the expected cost (eg.: number of BB tests).
*/
#include <vector>
#include <cmath>
#define MAX_CUT_SIZE 16
#define MAX_OPTIMIZE_CHILDS MAX_CUT_SIZE
struct OVBVHNode
{
float bb[6];
OVBVHNode *child;
OVBVHNode *sibling;
/*
* Returns min cost to represent the subtree starting at the given node,
* allowing it to have a given cutsize
*/
float cut_cost[MAX_CUT_SIZE];
float get_cost(int cutsize)
{
return cut_cost[cutsize-1];
}
/*
* This saves the cut size of this child, when parent is reaching
* its minimum cut with the given cut size
*/
int cut_size[MAX_CUT_SIZE];
int get_cut_size(int parent_cut_size)
{
return cut_size[parent_cut_size-1];
}
/*
* Reorganize the node based on calculated cut costs
*/
int best_cutsize;
void set_cut(int cutsize, OVBVHNode ***cut)
{
if(cutsize == 1)
{
**cut = this;
*cut = &(**cut)->sibling;
}
else
{
if(cutsize > MAX_CUT_SIZE)
{
for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
{
child->set_cut( 1, cut );
cutsize--;
}
assert(cutsize == 0);
}
else
for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
child->set_cut( child->get_cut_size( cutsize ), cut );
}
}
void optimize()
{
if(RE_rayobject_isAligned(this->child))
{
//Calc new childs
{
OVBVHNode **cut = &(this->child);
set_cut( best_cutsize, &cut );
*cut = NULL;
}
//Optimize new childs
for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling)
child->optimize();
}
}
};
/*
* Calculates an optimal SIMD packing
*
*/
template<class Node, class TestCost>
struct VBVH_optimalPackSIMD
{
TestCost testcost;
VBVH_optimalPackSIMD(TestCost testcost)
{
this->testcost = testcost;
}
/*
* calc best cut on a node
*/
struct calc_best
{
Node *child[MAX_OPTIMIZE_CHILDS];
float child_hit_prob[MAX_OPTIMIZE_CHILDS];
calc_best(Node *node)
{
int nchilds = 0;
//Fetch childs and needed data
{
float parent_area = bb_area(node->bb, node->bb+3);
for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling)
{
this->child[nchilds] = child;
this->child_hit_prob[nchilds] = bb_area(child->bb, child->bb+3) / parent_area;
nchilds++;
}
assert(nchilds >= 2 && nchilds <= MAX_OPTIMIZE_CHILDS);
}
//Build DP table to find minimum cost to represent this node with a given cutsize
int bt [MAX_OPTIMIZE_CHILDS+1][MAX_CUT_SIZE+1]; //backtrace table
float cost[MAX_OPTIMIZE_CHILDS+1][MAX_CUT_SIZE+1]; //cost table (can be reduced to float[2][MAX_CUT_COST])
for(int i=0; i<=nchilds; i++)
for(int j=0; j<=MAX_CUT_SIZE; j++)
cost[i][j] = INFINITY;
cost[0][0] = 0;
for(int i=1; i<=nchilds; i++)
for(int size=i-1; size/*+(nchilds-i)*/<=MAX_CUT_SIZE; size++)
for(int cut=1; cut+size/*+(nchilds-i)*/<=MAX_CUT_SIZE; cut++)
{
float new_cost = cost[i-1][size] + child_hit_prob[i-1]*child[i-1]->get_cost(cut);
if(new_cost < cost[i][size+cut])
{
cost[i][size+cut] = new_cost;
bt[i][size+cut] = cut;
}
}
//Save the ways to archieve the minimum cost with a given cutsize
for(int i = nchilds; i <= MAX_CUT_SIZE; i++)
{
node->cut_cost[i-1] = cost[nchilds][i];
if(cost[nchilds][i] < INFINITY)
{
int current_size = i;
for(int j=nchilds; j>0; j--)
{
child[j-1]->cut_size[i-1] = bt[j][current_size];
current_size -= bt[j][current_size];
}
}
}
}
};
void calc_costs(Node *node)
{
if( RE_rayobject_isAligned(node->child) )
{
int nchilds = 0;
for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling)
{
calc_costs(child);
nchilds++;
}
for(int i=0; i<MAX_CUT_SIZE; i++)
node->cut_cost[i] = INFINITY;
//We are not allowed to look on nodes with with so many childs
if(nchilds > MAX_CUT_SIZE)
{
float cost = 0;
float parent_area = bb_area(node->bb, node->bb+3);
for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling)
{
cost += ( bb_area(child->bb, child->bb+3) / parent_area ) * child->get_cost(1);
}
cost += testcost(nchilds);
node->cut_cost[0] = cost;
node->best_cutsize = nchilds;
}
else
{
calc_best calc(node);
//calc expected cost if we optimaly pack this node
for(int cutsize=nchilds; cutsize<=MAX_CUT_SIZE; cutsize++)
{
float m = node->get_cost(cutsize) + testcost(cutsize);
if(m < node->cut_cost[0])
{
node->cut_cost[0] = m;
node->best_cutsize = cutsize;
}
}
}
assert(node->cut_cost[0] != INFINITY);
}
else
{
node->cut_cost[0] = 1.0f;
for(int i=1; i<MAX_CUT_SIZE; i++)
node->cut_cost[i] = INFINITY;
}
}
Node *transform(Node *node)
{
if(RE_rayobject_isAligned(node->child))
{
static int num = 0;
bool first = false;
if(num == 0) { num++; first = true; }
calc_costs(node);
if(first) printf("expected cost = %f (%d)\n", node->cut_cost[0], node->best_cutsize );
node->optimize();
}
return node;
}
};

@ -0,0 +1,249 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifdef __SSE__
#ifndef RE_RAYTRACE_SVBVH_H
#define RE_RAYTRACE_SVBVH_H
#include "bvh.h"
#include "BLI_memarena.h"
#include <stdio.h>
#include <algorithm>
struct SVBVHNode
{
int nchilds;
//Array of bb, array of childs
float *child_bb;
SVBVHNode **child;
};
template<>
inline int bvh_node_hit_test<SVBVHNode>(SVBVHNode *node, Isect *isec)
{
return 1;
}
template<>
inline void bvh_node_push_childs<SVBVHNode>(SVBVHNode *node, Isect *isec, SVBVHNode **stack, int &stack_pos)
{
int i=0;
while(i+4 <= node->nchilds)
{
int res = test_bb_group4( (__m128*) (node->child_bb+6*i), isec );
RE_RC_COUNT(isec->raycounter->simd_bb.test);
if(res & 1) { stack[stack_pos++] = node->child[i+0]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
if(res & 2) { stack[stack_pos++] = node->child[i+1]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
if(res & 4) { stack[stack_pos++] = node->child[i+2]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
if(res & 8) { stack[stack_pos++] = node->child[i+3]; RE_RC_COUNT(isec->raycounter->simd_bb.hit); }
i += 4;
}
while(i < node->nchilds)
{
if(RE_rayobject_bb_intersect_test(isec, (const float*)node->child_bb+6*i))
stack[stack_pos++] = node->child[i];
i++;
}
}
template<>
void bvh_node_merge_bb<SVBVHNode>(SVBVHNode *node, float *min, float *max)
{
if(is_leaf(node))
{
RE_rayobject_merge_bb( (RayObject*)node, min, max);
}
else
{
int i=0;
while(i+4 <= node->nchilds)
{
float *res = node->child_bb + 6*i;
for(int j=0; j<3; j++)
{
min[j] = MIN2(min[j], res[4*j+0]);
min[j] = MIN2(min[j], res[4*j+1]);
min[j] = MIN2(min[j], res[4*j+2]);
min[j] = MIN2(min[j], res[4*j+3]);
}
for(int j=0; j<3; j++)
{
max[j] = MAX2(max[j], res[4*(j+3)+0]);
max[j] = MAX2(max[j], res[4*(j+3)+1]);
max[j] = MAX2(max[j], res[4*(j+3)+2]);
max[j] = MAX2(max[j], res[4*(j+3)+3]);
}
i += 4;
}
for(; i<node->nchilds; i++)
{
DO_MIN(node->child_bb+6*i , min);
DO_MAX(node->child_bb+3+6*i, max);
}
}
}
/*
* Builds a SVBVH tree form a VBVHTree
*/
template<class OldNode>
struct Reorganize_SVBVH
{
MemArena *arena;
float childs_per_node;
int nodes_with_childs[16];
int useless_bb;
int nodes;
Reorganize_SVBVH(MemArena *a)
{
arena = a;
nodes = 0;
childs_per_node = 0;
useless_bb = 0;
for(int i=0; i<16; i++)
nodes_with_childs[i] = 0;
}
~Reorganize_SVBVH()
{
printf("%f childs per node\n", childs_per_node / nodes);
printf("%d childs BB are useless\n", useless_bb);
for(int i=0; i<16; i++)
printf("%i childs per node: %d/%d = %f\n", i, nodes_with_childs[i], nodes, nodes_with_childs[i]/float(nodes));
}
SVBVHNode *create_node(int nchilds)
{
SVBVHNode *node = (SVBVHNode*)BLI_memarena_alloc(arena, sizeof(SVBVHNode));
node->nchilds = nchilds;
node->child_bb = (float*)BLI_memarena_alloc(arena, sizeof(float)*6*nchilds);
node->child= (SVBVHNode**)BLI_memarena_alloc(arena, sizeof(SVBVHNode*)*nchilds);
return node;
}
void copy_bb(float *bb, const float *old_bb)
{
std::copy( old_bb, old_bb+6, bb );
}
void prepare_for_simd(SVBVHNode *node)
{
int i=0;
while(i+4 <= node->nchilds)
{
float vec_tmp[4*6];
float *res = node->child_bb+6*i;
std::copy( res, res+6*4, vec_tmp);
for(int j=0; j<6; j++)
{
res[4*j+0] = vec_tmp[6*0+j];
res[4*j+1] = vec_tmp[6*1+j];
res[4*j+2] = vec_tmp[6*2+j];
res[4*j+3] = vec_tmp[6*3+j];
}
i += 4;
}
}
/* amt must be power of two */
inline int padup(int num, int amt)
{
return ((num+(amt-1))&~(amt-1));
}
SVBVHNode *transform(OldNode *old)
{
if(is_leaf(old))
return (SVBVHNode*)old;
if(is_leaf(old->child))
return (SVBVHNode*)old->child;
int nchilds = count_childs(old);
int alloc_childs = nchilds;
if(nchilds % 4 > 2)
alloc_childs = padup(nchilds, 4);
SVBVHNode *node = create_node(alloc_childs);
childs_per_node += nchilds;
nodes++;
if(nchilds < 16)
nodes_with_childs[nchilds]++;
useless_bb += alloc_childs-nchilds;
while(alloc_childs > nchilds)
{
const static float def_bb[6] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MIN, FLT_MIN, FLT_MIN };
alloc_childs--;
node->child[alloc_childs] = 0;
copy_bb(node->child_bb+alloc_childs*6, def_bb);
}
int i=nchilds;
for(OldNode *o_child = old->child; o_child; o_child = o_child->sibling)
{
i--;
node->child[i] = transform(o_child);
if(is_leaf(o_child))
{
float bb[6];
INIT_MINMAX(bb, bb+3);
RE_rayobject_merge_bb( (RayObject*)o_child, bb, bb+3);
copy_bb(node->child_bb+i*6, bb);
break;
}
else
{
copy_bb(node->child_bb+i*6, o_child->bb);
}
}
assert( i == 0 );
prepare_for_simd(node);
return node;
}
};
#endif
#endif //__SSE__

@ -0,0 +1,237 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <assert.h>
#include <algorithm>
#include "rayobject_rtbuild.h"
#include "BLI_memarena.h"
/*
* VBVHNode represents a BVHNode with support for a variable number of childrens
*/
struct VBVHNode
{
float bb[6];
VBVHNode *child;
VBVHNode *sibling;
};
/*
* Push nodes (used on dfs)
*/
template<class Node>
inline static void bvh_node_push_childs(Node *node, Isect *isec, Node **stack, int &stack_pos)
{
Node *child = node->child;
if(is_leaf(child))
{
stack[stack_pos++] = child;
}
else
{
while(child)
{
//Skips BB tests on primitives
/*
if(is_leaf(child->child))
stack[stack_pos++] = child->child;
else
*/
stack[stack_pos++] = child;
child = child->sibling;
}
}
}
template<class Node>
int count_childs(Node *parent)
{
int n = 0;
for(Node *i = parent->child; i; i = i->sibling)
{
n++;
if(is_leaf(i))
break;
}
return n;
}
template<class Node>
void append_sibling(Node *node, Node *sibling)
{
while(node->sibling)
node = node->sibling;
node->sibling = sibling;
}
/*
* Builds a binary VBVH from a rtbuild
*/
template<class Node>
struct BuildBinaryVBVH
{
MemArena *arena;
RayObjectControl *control;
void test_break()
{
if(RE_rayobjectcontrol_test_break(control))
throw "Stop";
}
BuildBinaryVBVH(MemArena *a, RayObjectControl *c)
{
arena = a;
control = c;
}
Node *create_node()
{
Node *node = (Node*)BLI_memarena_alloc( arena, sizeof(Node) );
assert( RE_rayobject_isAligned(node) );
node->sibling = NULL;
node->child = NULL;
return node;
}
int rtbuild_split(RTBuilder *builder)
{
return ::rtbuild_heuristic_object_split(builder, 2);
}
Node *transform(RTBuilder *builder)
{
try
{
return _transform(builder);
} catch(...)
{
}
return NULL;
}
Node *_transform(RTBuilder *builder)
{
int size = rtbuild_size(builder);
if(size == 1)
{
Node *node = create_node();
INIT_MINMAX(node->bb, node->bb+3);
rtbuild_merge_bb(builder, node->bb, node->bb+3);
node->child = (Node*) rtbuild_get_primitive( builder, 0 );
return node;
}
else
{
test_break();
Node *node = create_node();
INIT_MINMAX(node->bb, node->bb+3);
rtbuild_merge_bb(builder, node->bb, node->bb+3);
Node **child = &node->child;
int nc = rtbuild_split(builder);
assert(nc == 2);
for(int i=0; i<nc; i++)
{
RTBuilder tmp;
rtbuild_get_child(builder, i, &tmp);
*child = _transform(&tmp);
child = &((*child)->sibling);
}
*child = 0;
return node;
}
}
};
/*
template<class Tree,class OldNode>
struct Reorganize_VBVH
{
Tree *tree;
Reorganize_VBVH(Tree *t)
{
tree = t;
}
VBVHNode *create_node()
{
VBVHNode *node = (VBVHNode*)BLI_memarena_alloc(tree->node_arena, sizeof(VBVHNode));
return node;
}
void copy_bb(VBVHNode *node, OldNode *old)
{
std::copy( old->bb, old->bb+6, node->bb );
}
VBVHNode *transform(OldNode *old)
{
if(is_leaf(old))
return (VBVHNode*)old;
VBVHNode *node = create_node();
VBVHNode **child_ptr = &node->child;
node->sibling = 0;
copy_bb(node,old);
for(OldNode *o_child = old->child; o_child; o_child = o_child->sibling)
{
VBVHNode *n_child = transform(o_child);
*child_ptr = n_child;
if(is_leaf(n_child)) return node;
child_ptr = &n_child->sibling;
}
*child_ptr = 0;
return node;
}
};
*/

@ -358,6 +358,13 @@ static char *get_pass_name(int passtype, int channel)
if(channel==-1) return "Mist";
return "Mist.Z";
}
if(passtype == SCE_PASS_RAYHITS)
{
if(channel==-1) return "Rayhits";
if(channel==0) return "Rayhits.R";
if(channel==1) return "Rayhits.G";
return "Rayhits.B";
}
return "Unknown";
}
@ -409,6 +416,8 @@ static int passtype_from_name(char *str)
if(strcmp(str, "Mist")==0)
return SCE_PASS_MIST;
if(strcmp(str, "RAYHITS")==0)
return SCE_PASS_RAYHITS;
return 0;
}
@ -536,7 +545,7 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int
rl->lay= srl->lay;
rl->lay_zmask= srl->lay_zmask;
rl->layflag= srl->layflag;
rl->passflag= srl->passflag;
rl->passflag= srl->passflag | SCE_PASS_RAYHITS;
rl->pass_xor= srl->pass_xor;
rl->light_override= srl->light_override;
rl->mat_override= srl->mat_override;
@ -580,6 +589,8 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int
render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB);
if(srl->passflag & SCE_PASS_MIST)
render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST);
if(rl->passflag & SCE_PASS_RAYHITS)
render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS);
}
/* sss, previewrender and envmap don't do layers, so we make a default one */

@ -0,0 +1,169 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <assert.h>
#include "MEM_guardedalloc.h"
#include "BKE_utildefines.h"
#include "BLI_kdopbvh.h"
#include "BLI_arithb.h"
#include "RE_raytrace.h"
#include "render_types.h"
#include "rayobject.h"
static int RE_rayobject_blibvh_intersect(RayObject *o, Isect *isec);
static void RE_rayobject_blibvh_add(RayObject *o, RayObject *ob);
static void RE_rayobject_blibvh_done(RayObject *o);
static void RE_rayobject_blibvh_free(RayObject *o);
static void RE_rayobject_blibvh_bb(RayObject *o, float *min, float *max);
static float RE_rayobject_blibvh_cost(RayObject *o)
{
//TODO calculate the expected cost to raycast on this structure
return 1.0;
}
static void RE_rayobject_blibvh_hint_bb(RayObject *o, RayHint *hint, float *min, float *max)
{
return;
}
static RayObjectAPI bvh_api =
{
RE_rayobject_blibvh_intersect,
RE_rayobject_blibvh_add,
RE_rayobject_blibvh_done,
RE_rayobject_blibvh_free,
RE_rayobject_blibvh_bb,
RE_rayobject_blibvh_cost,
RE_rayobject_blibvh_hint_bb
};
typedef struct BVHObject
{
RayObject rayobj;
RayObject **leafs, **next_leaf;
BVHTree *bvh;
float bb[2][3];
} BVHObject;
RayObject *RE_rayobject_blibvh_create(int size)
{
BVHObject *obj= (BVHObject*)MEM_callocN(sizeof(BVHObject), "BVHObject");
assert( RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */
obj->rayobj.api = &bvh_api;
obj->bvh = BLI_bvhtree_new(size, 0.0, 4, 6);
obj->next_leaf = obj->leafs = (RayObject**)MEM_callocN(size*sizeof(RayObject*), "BVHObject leafs");
INIT_MINMAX(obj->bb[0], obj->bb[1]);
return RE_rayobject_unalignRayAPI((RayObject*) obj);
}
struct BVHCallbackUserData
{
Isect *isec;
RayObject **leafs;
};
static void bvh_callback(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
{
struct BVHCallbackUserData *data = (struct BVHCallbackUserData*)userdata;
Isect *isec = data->isec;
RayObject *face = data->leafs[index];
if(RE_rayobject_intersect(face,isec))
{
hit->index = index;
if(isec->mode == RE_RAY_SHADOW)
hit->dist = 0;
else
hit->dist = isec->labda*isec->dist;
}
}
static int RE_rayobject_blibvh_intersect(RayObject *o, Isect *isec)
{
BVHObject *obj = (BVHObject*)o;
BVHTreeRayHit hit;
float dir[3];
struct BVHCallbackUserData data;
data.isec = isec;
data.leafs = obj->leafs;
VECCOPY(dir, isec->vec);
Normalize(dir);
hit.index = 0;
hit.dist = isec->labda*isec->dist;
return BLI_bvhtree_ray_cast(obj->bvh, isec->start, dir, 0.0, &hit, bvh_callback, (void*)&data);
}
static void RE_rayobject_blibvh_add(RayObject *o, RayObject *ob)
{
BVHObject *obj = (BVHObject*)o;
float min_max[6];
INIT_MINMAX(min_max, min_max+3);
RE_rayobject_merge_bb(ob, min_max, min_max+3);
DO_MIN(min_max , obj->bb[0]);
DO_MAX(min_max+3, obj->bb[1]);
BLI_bvhtree_insert(obj->bvh, obj->next_leaf - obj->leafs, min_max, 2 );
*(obj->next_leaf++) = ob;
}
static void RE_rayobject_blibvh_done(RayObject *o)
{
BVHObject *obj = (BVHObject*)o;
BLI_bvhtree_balance(obj->bvh);
}
static void RE_rayobject_blibvh_free(RayObject *o)
{
BVHObject *obj = (BVHObject*)o;
if(obj->bvh)
BLI_bvhtree_free(obj->bvh);
if(obj->leafs)
MEM_freeN(obj->leafs);
MEM_freeN(obj);
}
static void RE_rayobject_blibvh_bb(RayObject *o, float *min, float *max)
{
BVHObject *obj = (BVHObject*)o;
DO_MIN( obj->bb[0], min );
DO_MAX( obj->bb[1], max );
}

@ -0,0 +1,196 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <assert.h>
#include "MEM_guardedalloc.h"
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
#include "RE_raytrace.h"
#include "rayobject.h"
#define RE_COST_INSTANCE (1.0f)
static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec);
static void RE_rayobject_instance_free(RayObject *o);
static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max);
static float RE_rayobject_instance_cost(RayObject *o);
static RayObjectAPI instance_api =
{
RE_rayobject_instance_intersect,
NULL, //static void RE_rayobject_instance_add(RayObject *o, RayObject *ob);
NULL, //static void RE_rayobject_instance_done(RayObject *o);
RE_rayobject_instance_free,
RE_rayobject_instance_bb,
RE_rayobject_instance_cost
};
typedef struct InstanceRayObject
{
RayObject rayobj;
RayObject *target;
void *ob; //Object represented by this instance
void *target_ob; //Object represented by the inner RayObject, needed to handle self-intersection
float global2target[4][4];
float target2global[4][4];
} InstanceRayObject;
RayObject *RE_rayobject_instance_create(RayObject *target, float transform[][4], void *ob, void *target_ob)
{
InstanceRayObject *obj= (InstanceRayObject*)MEM_callocN(sizeof(InstanceRayObject), "InstanceRayObject");
assert( RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */
obj->rayobj.api = &instance_api;
obj->target = target;
obj->ob = ob;
obj->target_ob = target_ob;
Mat4CpyMat4(obj->target2global, transform);
Mat4Invert(obj->global2target, obj->target2global);
return RE_rayobject_unalignRayAPI((RayObject*) obj);
}
static int RE_rayobject_instance_intersect(RayObject *o, Isect *isec)
{
//TODO
// *there is probably a faster way to convert between coordinates
InstanceRayObject *obj = (InstanceRayObject*)o;
int res;
float start[3], vec[3], labda, dist;
int changed = 0, i;
//TODO - this is disabling self intersection on instances
if(isec->orig.ob == obj->ob && obj->ob)
{
changed = 1;
isec->orig.ob = obj->target_ob;
}
VECCOPY( start, isec->start );
VECCOPY( vec , isec->vec );
labda = isec->labda;
dist = isec->dist;
//Transform to target coordinates system
VECADD( isec->vec, isec->vec, isec->start );
Mat4MulVecfl(obj->global2target, isec->start);
Mat4MulVecfl(obj->global2target, isec->vec );
isec->dist = VecLenf( isec->start, isec->vec );
VECSUB( isec->vec, isec->vec, isec->start );
isec->labda *= isec->dist / dist;
//Update idot_axis and bv_index
for(i=0; i<3; i++)
{
isec->idot_axis[i] = 1.0f / isec->vec[i];
isec->bv_index[2*i] = isec->idot_axis[i] < 0.0 ? 1 : 0;
isec->bv_index[2*i+1] = 1 - isec->bv_index[2*i];
isec->bv_index[2*i] = i+3*isec->bv_index[2*i];
isec->bv_index[2*i+1] = i+3*isec->bv_index[2*i+1];
}
//Raycast
res = RE_rayobject_intersect(obj->target, isec);
//Restore coordinate space coords
if(res == 0)
{
isec->labda = labda;
}
else
{
isec->labda *= dist / isec->dist;
isec->hit.ob = obj->ob;
}
isec->dist = dist;
VECCOPY( isec->start, start );
VECCOPY( isec->vec, vec );
if(changed)
isec->orig.ob = obj->ob;
//Update idot_axis and bv_index
for(i=0; i<3; i++)
{
isec->idot_axis[i] = 1.0f / isec->vec[i];
isec->bv_index[2*i] = isec->idot_axis[i] < 0.0 ? 1 : 0;
isec->bv_index[2*i+1] = 1 - isec->bv_index[2*i];
isec->bv_index[2*i] = i+3*isec->bv_index[2*i];
isec->bv_index[2*i+1] = i+3*isec->bv_index[2*i+1];
}
return res;
}
static void RE_rayobject_instance_free(RayObject *o)
{
InstanceRayObject *obj = (InstanceRayObject*)o;
MEM_freeN(obj);
}
static float RE_rayobject_instance_cost(RayObject *o)
{
InstanceRayObject *obj = (InstanceRayObject*)o;
return RE_rayobject_cost(obj->target) + RE_COST_INSTANCE;
}
static void RE_rayobject_instance_bb(RayObject *o, float *min, float *max)
{
//TODO:
// *better bb.. calculated without rotations of bb
// *maybe cache that better-fitted-BB at the InstanceRayObject
InstanceRayObject *obj = (InstanceRayObject*)o;
float m[3], M[3], t[3];
int i, j;
INIT_MINMAX(m, M);
RE_rayobject_merge_bb(obj->target, m, M);
//There must be a faster way than rotating all the 8 vertexs of the BB
for(i=0; i<8; i++)
{
for(j=0; j<3; j++) t[j] = i&(1<<j) ? M[j] : m[j];
Mat4MulVecfl(obj->target2global, t);
DO_MINMAX(t, min, max);
}
}

@ -32,6 +32,7 @@
#include <string.h>
#include <stdlib.h>
#include <float.h>
#include <assert.h>
#include "MEM_guardedalloc.h"
@ -41,10 +42,9 @@
#include "BLI_arithb.h"
#include "RE_raytrace.h"
#include "rayobject.h"
/* ********** structs *************** */
#define BRANCH_ARRAY 1024
#define NODE_ARRAY 4096
@ -61,12 +61,13 @@ typedef struct OcVal
typedef struct Node
{
struct RayFace *v[8];
int ob[8];
struct OcVal ov[8];
struct Node *next;
} Node;
typedef struct Octree {
RayObject rayobj;
struct Branch **adrbranch;
struct Node **adrnode;
float ocsize; /* ocsize: mult factor, max size octree */
@ -74,19 +75,44 @@ typedef struct Octree {
float min[3], max[3];
int ocres;
int branchcount, nodecount;
char *ocface; /* during building only */
RayCoordsFunc coordsfunc;
RayCheckFunc checkfunc;
RayObjectTransformFunc transformfunc;
void *userdata;
/* during building only */
char *ocface;
RayFace **ro_nodes;
int ro_nodes_size, ro_nodes_used;
} Octree;
/* ******** globals ***************** */
static int RE_rayobject_octree_intersect(RayObject *o, Isect *isec);
static void RE_rayobject_octree_add(RayObject *o, RayObject *ob);
static void RE_rayobject_octree_done(RayObject *o);
static void RE_rayobject_octree_free(RayObject *o);
static void RE_rayobject_octree_bb(RayObject *o, float *min, float *max);
/* just for statistics */
static int raycount;
static int accepted, rejected, coherent_ray;
/*
* This function is not expected to be called by current code state.
*/
static float RE_rayobject_octree_cost(RayObject *o)
{
return 1.0;
}
static void RE_rayobject_octree_hint_bb(RayObject *o, RayHint *hint, float *min, float *max)
{
return;
}
static RayObjectAPI octree_api =
{
RE_rayobject_octree_intersect,
RE_rayobject_octree_add,
RE_rayobject_octree_done,
RE_rayobject_octree_free,
RE_rayobject_octree_bb,
RE_rayobject_octree_cost,
RE_rayobject_octree_hint_bb
};
/* **************** ocval method ******************* */
/* within one octree node, a set of 3x15 bits defines a 'boundbox' to OR with */
@ -233,7 +259,7 @@ static int face_in_node(RayFace *face, short x, short y, short z, float rtf[][3]
return 0;
}
static void ocwrite(Octree *oc, int ob, RayFace *face, int quad, short x, short y, short z, float rtf[][3])
static void ocwrite(Octree *oc, RayFace *face, int quad, short x, short y, short z, float rtf[][3])
{
Branch *br;
Node *no;
@ -283,8 +309,7 @@ static void ocwrite(Octree *oc, int ob, RayFace *face, int quad, short x, short
while(no->v[a]!=NULL) a++;
}
no->v[a]= face;
no->ob[a]= ob;
no->v[a]= (RayFace*) RE_rayobject_align(face);
if(quad)
calc_ocval_face(rtf[0], rtf[1], rtf[2], rtf[3], x>>2, y>>1, z, &no->ov[a]);
@ -374,13 +399,10 @@ static void d2dda(Octree *oc, short b1, short b2, short c1, short c2, char *ocfa
ocface[oc->ocres*ocx2+ocy2]=1;
}
static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *ocmin)
static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *ocmin, short *ocmax)
{
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++) {
@ -399,7 +421,7 @@ static void filltriangle(Octree *oc, short c1, short c2, char *ocface, short *oc
}
}
void RE_ray_tree_free(RayTree *tree)
static void RE_rayobject_octree_free(RayObject *tree)
{
Octree *oc= (Octree*)tree;
@ -439,65 +461,40 @@ void RE_ray_tree_free(RayTree *tree)
MEM_freeN(oc);
}
RayTree *RE_ray_tree_create(int ocres, int totface, float *min, float *max, RayCoordsFunc coordsfunc, RayCheckFunc checkfunc, RayObjectTransformFunc transformfunc, void *userdata)
RayObject *RE_rayobject_octree_create(int ocres, int size)
{
Octree *oc;
float t00, t01, t02;
int c, ocres2;
Octree *oc= MEM_callocN(sizeof(Octree), "Octree");
assert( RE_rayobject_isAligned(oc) ); /* RayObject API assumes real data to be 4-byte aligned */
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;
oc->transformfunc= transformfunc;
oc->userdata= userdata;
/* only for debug info */
raycount=0;
accepted= 0;
rejected= 0;
coherent_ray= 0;
oc->rayobj.api = &octree_api;
/* fill main octree struct */
oc->ocres= ocres;
ocres2= oc->ocres*oc->ocres;
oc->ocres = ocres;
VECCOPY(oc->min, min);
VECCOPY(oc->max, max);
oc->ro_nodes = (RayFace**)MEM_callocN(sizeof(RayFace*)*size, "octree rayobject nodes");
oc->ro_nodes_size = size;
oc->ro_nodes_used = 0;
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;
return RE_rayobject_unalignRayAPI((RayObject*) oc);
}
void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
static void RE_rayobject_octree_add(RayObject *tree, RayObject *node)
{
Octree *oc = (Octree*)tree;
float *v1, *v2, *v3, *v4, ocfac[3], rtf[4][3];
assert( RE_rayobject_isRayFace(node) );
assert( oc->ro_nodes_used < oc->ro_nodes_size );
oc->ro_nodes[ oc->ro_nodes_used++ ] = (RayFace*)RE_rayobject_align(node);
}
static void octree_fill_rayface(Octree *oc, RayFace *face)
{
float ocfac[3], rtf[4][3];
float co1[3], co2[3], co3[3], co4[3];
short rts[4][3], ocmin[6], *ocmax;
short rts[4][3];
short ocmin[3], ocmax[3];
char *ocface= oc->ocface; // front, top, size view of face, to fill in
int a, b, c, oc1, oc2, oc3, oc4, x, y, z, ocres2;
@ -507,28 +504,12 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
ocres2= oc->ocres*oc->ocres;
ocmax= ocmin+3;
VECCOPY(co1, face->v1);
VECCOPY(co2, face->v2);
VECCOPY(co3, face->v3);
if(face->v4)
VECCOPY(co4, face->v4);
oc->coordsfunc(face, &v1, &v2, &v3, &v4);
VECCOPY(co1, v1);
VECCOPY(co2, v2);
VECCOPY(co3, v3);
if(v4)
VECCOPY(co4, v4);
if(ob >= RE_RAY_TRANSFORM_OFFS) {
float (*mat)[4]= (float(*)[4])oc->transformfunc(oc->userdata, ob);
if(mat) {
Mat4MulVecfl(mat, co1);
Mat4MulVecfl(mat, co2);
Mat4MulVecfl(mat, co3);
if(v4)
Mat4MulVecfl(mat, co4);
}
}
for(c=0;c<3;c++) {
rtf[0][c]= (co1[c]-oc->min[c])*ocfac[c] ;
rts[0][c]= (short)rtf[0][c];
@ -536,7 +517,7 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
rts[1][c]= (short)rtf[1][c];
rtf[2][c]= (co3[c]-oc->min[c])*ocfac[c] ;
rts[2][c]= (short)rtf[2][c];
if(v4) {
if(RE_rayface_isQuad(face)) {
rtf[3][c]= (co4[c]-oc->min[c])*ocfac[c] ;
rts[3][c]= (short)rtf[3][c];
}
@ -546,7 +527,7 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
oc1= rts[0][c];
oc2= rts[1][c];
oc3= rts[2][c];
if(v4==NULL) {
if(!RE_rayface_isQuad(face)) {
ocmin[c]= MIN3(oc1,oc2,oc3);
ocmax[c]= MAX3(oc1,oc2,oc3);
}
@ -560,7 +541,7 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
}
if(ocmin[0]==ocmax[0] && ocmin[1]==ocmax[1] && ocmin[2]==ocmax[2]) {
ocwrite(oc, ob, face, (v4 != NULL), ocmin[0], ocmin[1], ocmin[2], rtf);
ocwrite(oc, face, RE_rayface_isQuad(face), ocmin[0], ocmin[1], ocmin[2], rtf);
}
else {
@ -570,7 +551,7 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
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) {
if(!RE_rayface_isQuad(face)) {
d2dda(oc, 2,0,0,1,ocface+ocres2,rts,rtf);
d2dda(oc, 2,0,0,2,ocface,rts,rtf);
d2dda(oc, 2,0,1,2,ocface+2*ocres2,rts,rtf);
@ -584,9 +565,9 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
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);
filltriangle(oc, 0,1,ocface+ocres2,ocmin,ocmax);
filltriangle(oc, 0,2,ocface,ocmin,ocmax);
filltriangle(oc, 1,2,ocface+2*ocres2,ocmin,ocmax);
/* init static vars here */
face_in_node(face, 0,0,0, rtf);
@ -599,7 +580,7 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
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, ob, face, (v4 != NULL), x,y,z, rtf);
ocwrite(oc, face, RE_rayface_isQuad(face), x,y,z, rtf);
}
}
}
@ -625,433 +606,107 @@ void RE_ray_tree_add_face(RayTree *tree, int ob, RayFace *face)
}
}
void RE_ray_tree_done(RayTree *tree)
static void RE_rayobject_octree_done(RayObject *tree)
{
Octree *oc= (Octree*)tree;
Octree *oc = (Octree*)tree;
int c;
float t00, t01, t02;
int ocres2 = oc->ocres*oc->ocres;
INIT_MINMAX(oc->min, oc->max);
/* Calculate Bounding Box */
for(c=0; c<oc->ro_nodes_used; c++)
RE_rayobject_merge_bb( RE_rayobject_unalignRayFace(oc->ro_nodes[c]), oc->min, oc->max);
/* Alloc memory */
oc->adrbranch= MEM_callocN(sizeof(void *)*BRANCH_ARRAY, "octree branches");
oc->adrnode= MEM_callocN(sizeof(void *)*NODE_ARRAY, "octree nodes");
oc->adrbranch[0]=(Branch *)MEM_callocN(4096*sizeof(Branch), "makeoctree");
/* the lookup table, per face, for which nodes to fill in */
oc->ocface= MEM_callocN( 3*ocres2 + 8, "ocface");
memset(oc->ocface, 0, 3*ocres2);
for(c=0;c<3;c++) { /* octree enlarge, still needed? */
oc->min[c]-= 0.01f;
oc->max[c]+= 0.01f;
}
t00= oc->max[0]-oc->min[0];
t01= oc->max[1]-oc->min[1];
t02= oc->max[2]-oc->min[2];
/* this minus 0.1 is old safety... seems to be needed? */
oc->ocfacx= (oc->ocres-0.1)/t00;
oc->ocfacy= (oc->ocres-0.1)/t01;
oc->ocfacz= (oc->ocres-0.1)/t02;
oc->ocsize= sqrt(t00*t00+t01*t01+t02*t02); /* global, max size octree */
for(c=0; c<oc->ro_nodes_used; c++)
{
octree_fill_rayface(oc, oc->ro_nodes[c]);
}
MEM_freeN(oc->ocface);
oc->ocface= NULL;
oc->ocface = NULL;
MEM_freeN(oc->ro_nodes);
oc->ro_nodes = NULL;
printf("%f %f - %f\n", oc->min[0], oc->max[0], oc->ocfacx );
printf("%f %f - %f\n", oc->min[1], oc->max[1], oc->ocfacy );
printf("%f %f - %f\n", oc->min[2], oc->max[2], oc->ocfacz );
}
/* ************ raytracer **************** */
#define ISECT_EPSILON ((float)FLT_EPSILON)
/* only for self-intersecting test with current render face (where ray left) */
static int intersection2(RayFace *face, int ob, RayObjectTransformFunc transformfunc, RayCoordsFunc coordsfunc, void *userdata, float r0, float r1, float r2, float rx1, float ry1, float rz1)
static void RE_rayobject_octree_bb(RayObject *tree, float *min, float *max)
{
float *v1, *v2, *v3, *v4, co1[3], co2[3], co3[3], co4[3];
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);
}
VECCOPY(co1, v1);
VECCOPY(co2, v2);
VECCOPY(co3, v3);
if(v4)
VECCOPY(co4, v4);
if(ob >= RE_RAY_TRANSFORM_OFFS) {
float (*mat)[4]= (float(*)[4])transformfunc(userdata, ob);
if(mat) {
Mat4MulVecfl(mat, co1);
Mat4MulVecfl(mat, co2);
Mat4MulVecfl(mat, co3);
if(v4)
Mat4MulVecfl(mat, co4);
}
}
t00= co3[0]-co1[0];
t01= co3[1]-co1[1];
t02= co3[2]-co1[2];
t10= co3[0]-co2[0];
t11= co3[1]-co2[1];
t12= co3[2]-co2[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-co3[0];
m1= ry1-co3[1];
m2= rz1-co3[2];
det1= m0*x0+m1*x1+m2*x2;
if(divdet!=0.0f) {
u1= det1/divdet;
if(u1<ISECT_EPSILON) {
det= t00*(m1*r2-m2*r1);
det+= t01*(m2*r0-m0*r2);
det+= t02*(m0*r1-m1*r0);
v= det/divdet;
if(v<ISECT_EPSILON && (u1 + v) > -(1.0f+ISECT_EPSILON)) {
return 1;
}
}
}
if(v4) {
t20= co3[0]-co4[0];
t21= co3[1]-co4[1];
t22= co3[2]-co4[2];
divdet= t20*x0+t21*x1+t22*x2;
if(divdet!=0.0f) {
u2= det1/divdet;
if(u2<ISECT_EPSILON) {
det= t20*(m1*r2-m2*r1);
det+= t21*(m2*r0-m0*r2);
det+= t22*(m0*r1-m1*r0);
v= det/divdet;
if(v<ISECT_EPSILON && (u2 + v) >= -(1.0f+ISECT_EPSILON)) {
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, RayObjectTransformFunc transformfunc, RayCoordsFunc coordsfunc)
{
RayFace *face= is->face;
int ob= is->ob;
float *v1,*v2,*v3,*v4,co1[3],co2[3],co3[3],co4[3];
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);
}
VECCOPY(co1, v1);
VECCOPY(co2, v2);
VECCOPY(co3, v3);
if(v4)
VECCOPY(co4, v4);
if(ob) {
float (*mat)[4]= (float(*)[4])transformfunc(is->userdata, ob);
if(mat) {
Mat4MulVecfl(mat, co1);
Mat4MulVecfl(mat, co2);
Mat4MulVecfl(mat, co3);
if(v4)
Mat4MulVecfl(mat, co4);
}
}
t00= co3[0]-co1[0];
t01= co3[1]-co1[1];
t02= co3[2]-co1[2];
t10= co3[0]-co2[0];
t11= co3[1]-co2[1];
t12= co3[2]-co2[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]-co3[0];
m1= is->start[1]-co3[1];
m2= is->start[2]-co3[2];
det1= m0*x0+m1*x1+m2*x2;
if(divdet!=0.0f) {
float u;
divdet= 1.0f/divdet;
u= det1*divdet;
if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) {
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<ISECT_EPSILON && (u + v) > -(1.0f+ISECT_EPSILON)) {
float labda;
labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12);
if(labda>-ISECT_EPSILON && labda<1.0f+ISECT_EPSILON) {
is->labda= labda;
is->u= u; is->v= v;
ok= 1;
}
}
}
}
if(ok==0 && v4) {
t20= co3[0]-co4[0];
t21= co3[1]-co4[1];
t22= co3[2]-co4[2];
divdet= t20*x0+t21*x1+t22*x2;
if(divdet!=0.0f) {
float u;
divdet= 1.0f/divdet;
u = det1*divdet;
if(u<ISECT_EPSILON && u>-(1.0f+ISECT_EPSILON)) {
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<ISECT_EPSILON && (u + v) >-(1.0f+ISECT_EPSILON)) {
float labda;
labda= divdet*(cros0*t10 + cros1*t11 + cros2*t12);
if(labda>-ISECT_EPSILON && labda<1.0f+ISECT_EPSILON) {
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 && is->faceorig) {
RayFace *face= is->faceorig;
float *origv1, *origv2, *origv3, *origv4;
short de= 0;
coordsfunc(face, &origv1, &origv2, &origv3, &origv4);
if(ob == is->oborig) {
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->obcontr= is->oborig;
is->facecontr= face;
is->faceisect= intersection2(face, is->oborig, transformfunc, coordsfunc, is->userdata, -r0, -r1, -r2, is->start[0], is->start[1], is->start[2]);
}
if(is->faceisect) return 1;
return 0;
}
}
return 1;
}
return 0;
Octree *oc = (Octree*)tree;
DO_MINMAX(oc->min, min, max);
DO_MINMAX(oc->max, min, max);
}
/* check all faces in this node */
static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval, RayCheckFunc checkfunc)
static int testnode(Octree *oc, Isect *is, Node *no, OcVal ocval)
{
RayFace *face;
int ob;
short nr=0;
OcVal *ov;
/* return on any first hit */
if(is->mode==RE_RAY_SHADOW) {
face= no->v[0];
ob= no->ob[0];
while(face) {
if(!(is->faceorig == face && is->oborig == ob)) {
if(checkfunc(is, ob, face)) {
ov= no->ov+nr;
if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) {
//accepted++;
is->ob= ob;
is->face= face;
if(RE_ray_face_intersection(is, oc->transformfunc, oc->coordsfunc)) {
is->ob_last= ob;
is->face_last= face;
return 1;
}
}
//else rejected++;
}
}
for(; no; no = no->next)
for(nr=0; nr<8; nr++)
{
RayFace *face = no->v[nr];
OcVal *ov = no->ov+nr;
nr++;
if(nr==8) {
no= no->next;
if(no==0) return 0;
nr=0;
if(!face) break;
if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) )
{
if( RE_rayobject_intersect( RE_rayobject_unalignRayFace(face),is) )
return 1;
}
face= no->v[nr];
ob= no->ob[nr];
}
}
else { /* else mirror or glass or shadowtra, return closest face */
Isect isect;
else
{ /* else mirror or glass or shadowtra, return closest face */
int found= 0;
is->labda= 1.0f; /* needed? */
isect= *is; /* copy for sorting */
face= no->v[0];
ob= no->ob[0];
while(face) {
if(!(is->faceorig == face && is->oborig == ob)) {
if(checkfunc(is, ob, face)) {
ov= no->ov+nr;
if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) ) {
//accepted++;
isect.ob= ob;
isect.face= face;
if(RE_ray_face_intersection(&isect, oc->transformfunc, oc->coordsfunc)) {
if(isect.labda<is->labda) {
*is= isect;
found= 1;
}
}
}
//else rejected++;
}
}
for(; no; no = no->next)
for(nr=0; nr<8; nr++)
{
RayFace *face = no->v[nr];
OcVal *ov = no->ov+nr;
nr++;
if(nr==8) {
no= no->next;
if(no==NULL) break;
nr=0;
if(!face) break;
if( (ov->ocx & ocval.ocx) && (ov->ocy & ocval.ocy) && (ov->ocz & ocval.ocz) )
{
if( RE_rayobject_intersect( RE_rayobject_unalignRayFace(face),is) )
found= 1;
}
face= no->v[nr];
ob= no->ob[nr];
}
return found;
@ -1175,23 +830,17 @@ static int do_coherence_test(int ocx1, int ocx2, int ocy1, int ocy2, int ocz1, i
*/
int RE_ray_tree_intersect(RayTree *tree, Isect *is)
{
Octree *oc= (Octree*)tree;
return RE_ray_tree_intersect_check(tree, is, oc->checkfunc);
}
/* return 1: found valid intersection */
/* starts with is->faceorig */
int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc)
/* starts with is->orig.face */
static int RE_rayobject_octree_intersect(RayObject *tree, Isect *is)
{
Octree *oc= (Octree*)tree;
Node *no;
OcVal ocval;
float vec1[3], vec2[3];
float vec1[3], vec2[3], start[3], end[3];
float u1,u2,ox1,ox2,oy1,oy2,oz1,oz2;
float labdao,labdax,ldx,labday,ldy,labdaz,ldz, ddalabda;
float olabda = 0;
int dx,dy,dz;
int xo,yo,zo,c1=0;
int ocx1,ocx2,ocy1, ocy2,ocz1,ocz2;
@ -1200,48 +849,40 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc
if(oc->branchcount==0) return 0;
/* do this before intersect calls */
#if 0
is->facecontr= NULL; /* to check shared edge */
is->obcontr= 0;
is->faceisect= is->isect= 0; /* shared edge, quad half flag */
is->userdata= oc->userdata;
#endif
/* 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 && is->ob_last==is->oborig)) {
if(checkfunc(is, is->ob_last, is->face_last)) {
is->ob= is->ob_last;
is->face= is->face_last;
VECSUB(is->vec, is->end, is->start);
if(RE_ray_face_intersection(is, oc->transformfunc, oc->coordsfunc)) return 1;
}
}
}
ldx= is->end[0] - is->start[0];
VECCOPY( start, is->start );
VECADDFAC( end, is->start, is->vec, is->labda );
ldx= is->vec[0]*is->labda;
olabda = is->labda;
u1= 0.0f;
u2= 1.0f;
/* clip with octree cube */
if(cliptest(-ldx, 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)) {
if(cliptest(-ldx, start[0]-oc->min[0], &u1,&u2)) {
if(cliptest(ldx, oc->max[0]-start[0], &u1,&u2)) {
ldy= is->vec[1]*is->labda;
if(cliptest(-ldy, start[1]-oc->min[1], &u1,&u2)) {
if(cliptest(ldy, oc->max[1]-start[1], &u1,&u2)) {
ldz = is->vec[2]*is->labda;
if(cliptest(-ldz, start[2]-oc->min[2], &u1,&u2)) {
if(cliptest(ldz, oc->max[2]-start[2], &u1,&u2)) {
c1=1;
if(u2<1.0f) {
is->end[0]= is->start[0]+u2*ldx;
is->end[1]= is->start[1]+u2*ldy;
is->end[2]= is->start[2]+u2*ldz;
end[0] = start[0]+u2*ldx;
end[1] = start[1]+u2*ldy;
end[2] = start[2]+u2*ldz;
}
if(u1>0.0f) {
is->start[0]+=u1*ldx;
is->start[1]+=u1*ldy;
is->start[2]+=u1*ldz;
start[0] += u1*ldx;
start[1] += u1*ldy;
start[2] += u1*ldz;
}
}
}
@ -1256,12 +897,12 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc
//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;
ox1= (start[0]-oc->min[0])*oc->ocfacx;
oy1= (start[1]-oc->min[1])*oc->ocfacy;
oz1= (start[2]-oc->min[2])*oc->ocfacz;
ox2= (end[0]-oc->min[0])*oc->ocfacx;
oy2= (end[1]-oc->min[1])*oc->ocfacy;
oz2= (end[2]-oc->min[2])*oc->ocfacz;
ocx1= (int)ox1;
ocy1= (int)oy1;
@ -1269,10 +910,7 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc
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) {
@ -1280,11 +918,11 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc
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, checkfunc) ) return 1;
if( testnode(oc, is, no, ocval) ) return 1;
}
}
else {
int found = 0;
//static int coh_ocx1,coh_ocx2,coh_ocy1, coh_ocy2,coh_ocz1,coh_ocz2;
float dox, doy, doz;
int eqval;
@ -1358,11 +996,16 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc
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, checkfunc) ) return 1;
//is->labda = (u1+ddalabda*(u2-u1))*olabda;
if( testnode(oc, is, no, ocval) )
found = 1;
if(is->labda < (u1+ddalabda*(u2-u1))*olabda)
return found;
}
labdao= ddalabda;
/* traversing ocree nodes need careful detection of smallest values, with proper
@ -1430,13 +1073,8 @@ int RE_ray_tree_intersect_check(RayTree *tree, Isect *is, RayCheckFunc checkfunc
}
/* reached end, no intersections found */
is->ob_last= 0;
is->face_last= NULL;
return 0;
}
float RE_ray_tree_max_size(RayTree *tree)
{
return ((Octree*)tree)->ocsize;
}

@ -0,0 +1,87 @@
/**
* $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) 2009 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "rayobject.h"
#include "raycounter.h"
#ifdef RE_RAYCOUNTER
void RE_RC_INFO(RayCounter *info)
{
printf("----------- Raycast counter --------\n");
printf("Rays total: %llu\n", info->raycast.test );
printf("Rays hit: %llu\n", info->raycast.hit );
printf("\n");
printf("BB tests: %llu\n", info->bb.test );
printf("BB hits: %llu\n", info->bb.hit );
printf("\n");
printf("SIMD BB tests: %llu\n", info->simd_bb.test );
printf("SIMD BB hits: %llu\n", info->simd_bb.hit );
printf("\n");
printf("Primitives tests: %llu\n", info->faces.test );
printf("Primitives hits: %llu\n", info->faces.hit );
printf("------------------------------------\n");
printf("Shadow last-hit tests per ray: %f\n", info->rayshadow_last_hit.test / ((float)info->raycast.test) );
printf("Shadow last-hit hits per ray: %f\n", info->rayshadow_last_hit.hit / ((float)info->raycast.test) );
printf("\n");
printf("Hint tests per ray: %f\n", info->raytrace_hint.test / ((float)info->raycast.test) );
printf("Hint hits per ray: %f\n", info->raytrace_hint.hit / ((float)info->raycast.test) );
printf("\n");
printf("BB tests per ray: %f\n", info->bb.test / ((float)info->raycast.test) );
printf("BB hits per ray: %f\n", info->bb.hit / ((float)info->raycast.test) );
printf("\n");
printf("SIMD tests per ray: %f\n", info->simd_bb.test / ((float)info->raycast.test) );
printf("SIMD hits per ray: %f\n", info->simd_bb.hit / ((float)info->raycast.test) );
printf("\n");
printf("Primitives tests per ray: %f\n", info->faces.test / ((float)info->raycast.test) );
printf("Primitives hits per ray: %f\n", info->faces.hit / ((float)info->raycast.test) );
printf("------------------------------------\n");
}
void RE_RC_MERGE(RayCounter *dest, RayCounter *tmp)
{
dest->faces.test += tmp->faces.test;
dest->faces.hit += tmp->faces.hit;
dest->bb.test += tmp->bb.test;
dest->bb.hit += tmp->bb.hit;
dest->simd_bb.test += tmp->simd_bb.test;
dest->simd_bb.hit += tmp->simd_bb.hit;
dest->raycast.test += tmp->raycast.test;
dest->raycast.hit += tmp->raycast.hit;
dest->rayshadow_last_hit.test += tmp->rayshadow_last_hit.test;
dest->rayshadow_last_hit.hit += tmp->rayshadow_last_hit.hit;
dest->raytrace_hint.test += tmp->raytrace_hint.test;
dest->raytrace_hint.hit += tmp->raytrace_hint.hit;
}
#endif

File diff suppressed because it is too large Load Diff

@ -31,6 +31,7 @@
#include <math.h>
#include <float.h>
#include <string.h>
#include <assert.h>
/* External modules: */
#include "MEM_guardedalloc.h"
@ -520,6 +521,12 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset,
}
}
break;
case SCE_PASS_RAYHITS:
/* */
col= &shr->rayhits;
pixsize= 4;
break;
}
if(col) {
fp= rpass->rect + pixsize*offset;
@ -596,6 +603,10 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult
fp= rpass->rect + offset;
*fp= shr->mist;
break;
case SCE_PASS_RAYHITS:
col= shr->rayhits;
pixsize= 4;
break;
}
if(col) {
fp= rpass->rect + pixsize*offset;
@ -2227,6 +2238,7 @@ static void bake_displacement(void *handle, ShadeInput *shi, float dist, int x,
}
}
#if 0
static int bake_check_intersect(Isect *is, int ob, RayFace *face)
{
BakeShade *bs = (BakeShade*)is->userdata;
@ -2236,9 +2248,13 @@ static int bake_check_intersect(Isect *is, int ob, RayFace *face)
return (R.objectinstance[ob & ~RE_RAY_TRANSFORM_OFFS].obr->ob != bs->actob);
}
#endif
static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist)
static int bake_intersect_tree(RayObject* raytree, Isect* isect, float *start, float *dir, float sign, float *hitco, float *dist)
{
//TODO
assert( 0 );
#if 0
float maxdist;
int hit;
@ -2246,15 +2262,17 @@ static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, flo
if(R.r.bake_maxdist > 0.0f)
maxdist= R.r.bake_maxdist;
else
maxdist= RE_ray_tree_max_size(R.raytree) + R.r.bake_biasdist;
maxdist= FLT_MAX + R.r.bake_biasdist;
//TODO normalized direction?
VECADDFAC(isect->start, start, dir, -R.r.bake_biasdist);
isect->dir[0] = dir[0]*sign;
isect->dir[1] = dir[1]*sign;
isect->dir[2] = dir[2]*sign;
isect->labda = maxdist;
isect->end[0] = isect->start[0] + dir[0]*maxdist*sign;
isect->end[1] = isect->start[1] + dir[1]*maxdist*sign;
isect->end[2] = isect->start[2] + dir[2]*maxdist*sign;
hit = RE_ray_tree_intersect_check(R.raytree, isect, bake_check_intersect);
hit = RE_rayobject_raycast(raytree, isect);
//TODO bake_check_intersect
if(hit) {
hitco[0] = isect->start[0] + isect->labda*isect->vec[0];
hitco[1] = isect->start[1] + isect->labda*isect->vec[1];
@ -2264,6 +2282,8 @@ static int bake_intersect_tree(RayTree* raytree, Isect* isect, float *start, flo
}
return hit;
#endif
return 0;
}
static void bake_set_vlr_dxyco(BakeShade *bs, float *uv1, float *uv2, float *uv3)
@ -2380,8 +2400,9 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v)
for(sign=-1; sign<=1; sign+=2) {
memset(&isec, 0, sizeof(isec));
isec.mode= RE_RAY_MIRROR;
isec.faceorig= (RayFace*)vlr;
isec.oborig= RAY_OBJECT_SET(&R, obi);
isec.orig.ob = obi;
isec.orig.face = vlr;
isec.userdata= bs;
if(bake_intersect_tree(R.raytree, &isec, shi->co, shi->vn, sign, co, &dist)) {
@ -2405,8 +2426,8 @@ static void do_bake_shade(void *handle, int x, int y, float u, float v)
/* if hit, we shade from the new point, otherwise from point one starting face */
if(hit) {
vlr= (VlakRen*)minisec.face;
obi= RAY_OBJECT_GET(&R, minisec.ob);
obi= (ObjectInstanceRen*)minisec.hit.ob;
vlr= (VlakRen*)minisec.hit.face;
quad= (minisec.isect == 2);
VECCOPY(shi->co, minco);

@ -75,6 +75,7 @@
#include "BKE_DerivedMesh.h"
#include "RE_render_ext.h" /* externtex */
#include "RE_raytrace.h"
#include "renderpipeline.h"
#include "render_types.h"
@ -872,13 +873,34 @@ void free_renderdata_tables(Render *re)
MEM_freeN(obr->mtface);
if(obr->mcol)
MEM_freeN(obr->mcol);
if(obr->rayfaces)
{
MEM_freeN(obr->rayfaces);
obr->rayfaces = NULL;
}
if(obr->rayprimitives)
{
MEM_freeN(obr->rayprimitives);
obr->rayprimitives = NULL;
}
if(obr->raytree)
{
RE_rayobject_free(obr->raytree);
obr->raytree = NULL;
}
}
if(re->objectinstance) {
for(obi=re->instancetable.first; obi; obi=obi->next)
{
if(obi->vectors)
MEM_freeN(obi->vectors);
if(obi->raytree)
RE_rayobject_free(obi->raytree);
}
MEM_freeN(re->objectinstance);
re->objectinstance= NULL;
re->totinstance= 0;

@ -44,6 +44,7 @@
#include "BKE_node.h"
/* local include */
#include "raycounter.h"
#include "renderpipeline.h"
#include "render_types.h"
#include "renderdatabase.h"
@ -181,6 +182,9 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
float alpha;
/* ------ main shading loop -------- */
#ifdef RE_RAYCOUNTER
memset(&shi->raycounter, 0, sizeof(shi->raycounter));
#endif
if(shi->mat->nodetree && shi->mat->use_nodes) {
ntreeShaderExecTree(shi->mat->nodetree, shi, shr);
@ -230,6 +234,18 @@ void shade_input_do_shade(ShadeInput *shi, ShadeResult *shr)
/* add z */
shr->z= -shi->co[2];
/* RAYHITS */
/*
if(1 || shi->passflag & SCE_PASS_RAYHITS)
{
shr->rayhits[0] = (float)shi->raycounter.faces.test;
shr->rayhits[1] = (float)shi->raycounter.bb.hit;
shr->rayhits[2] = 0.0;
shr->rayhits[3] = 1.0;
}
*/
RE_RC_MERGE(&re_rc_counter[shi->thread], &shi->raycounter);
}
/* **************************************************************************** */

@ -46,6 +46,7 @@
#include "DNA_material_types.h"
#include "render_types.h"
#include "rendercore.h"
#include "renderdatabase.h"
#include "volumetric.h"
#include "volume_precache.h"
@ -66,11 +67,11 @@ extern struct Render R;
/* Recursive test for intersections, from a point inside the mesh, to outside
* Number of intersections (depth) determine if a point is inside or outside the mesh */
int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int limit, int depth)
int intersect_outside_volume(RayObject *tree, Isect *isect, float *offset, int limit, int depth)
{
if (limit == 0) return depth;
if (RE_ray_tree_intersect(tree, isect)) {
if (RE_rayobject_raycast(tree, isect)) {
float hitco[3];
hitco[0] = isect->start[0] + isect->labda*isect->vec[0];
@ -85,9 +86,8 @@ int intersect_outside_volume(RayTree *tree, Isect *isect, float *offset, int lim
}
/* Uses ray tracing to check if a point is inside or outside an ObjectInstanceRen */
int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co)
int point_inside_obi(RayObject *tree, ObjectInstanceRen *obi, float *co)
{
float maxsize = RE_ray_tree_max_size(tree);
Isect isect;
float vec[3] = {0.0f,0.0f,1.0f};
int final_depth=0, depth=0, limit=20;
@ -95,16 +95,21 @@ int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co)
/* set up the isect */
memset(&isect, 0, sizeof(isect));
VECCOPY(isect.start, co);
VECCOPY(isect.vec, vec);
isect.labda = FLT_MAX;
/*
isect.end[0] = co[0] + vec[0] * maxsize;
isect.end[1] = co[1] + vec[1] * maxsize;
isect.end[2] = co[2] + vec[2] * maxsize;
*/
/* and give it a little offset to prevent self-intersections */
VecMulf(vec, 1e-5);
VecAddf(isect.start, isect.start, vec);
isect.mode= RE_RAY_MIRROR;
isect.face_last= NULL;
isect.last_hit= NULL;
isect.lay= -1;
final_depth = intersect_outside_volume(tree, &isect, vec, limit, depth);
@ -115,10 +120,12 @@ int point_inside_obi(RayTree *tree, ObjectInstanceRen *obi, float *co)
else return 1;
}
static int inside_check_func(Isect *is, int ob, RayFace *face)
/*
static int inside_check_func(Isect *is, int ob, RayObject *face)
{
return 1;
}
static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, float **v4)
{
VlakRen *vlr= (VlakRen*)face;
@ -129,16 +136,16 @@ static void vlr_face_coords(RayFace *face, float **v1, float **v2, float **v3, f
*v4 = (vlr->v4)? vlr->v4->co: NULL;
}
RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax)
RayObject *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax)
{
int v;
VlakRen *vlr= NULL;
/* create empty raytree */
/ * create empty raytree * /
RayTree *tree = RE_ray_tree_create(64, obi->obr->totvlak, bbmin, bbmax,
vlr_face_coords, inside_check_func, NULL, NULL);
/* fill it with faces */
/ * fill it with faces * /
for(v=0; v<obi->obr->totvlak; v++) {
if((v & 255)==0)
vlr= obi->obr->vlaknodes[v>>8].vlak;
@ -152,6 +159,7 @@ RayTree *create_raytree_obi(ObjectInstanceRen *obi, float *bbmin, float *bbmax)
return tree;
}
*/
/* *** light cache filtering *** */
@ -458,7 +466,7 @@ static void *vol_precache_part(void *data)
{
VolPrecachePart *pa = (VolPrecachePart *)data;
ObjectInstanceRen *obi = pa->obi;
RayTree *tree = pa->tree;
RayObject *tree = pa->tree;
ShadeInput *shi = pa->shi;
float scatter_col[3] = {0.f, 0.f, 0.f};
float co[3];
@ -513,7 +521,7 @@ static void precache_setup_shadeinput(Render *re, ObjectInstanceRen *obi, Materi
shi->lay = re->scene->lay;
}
static void precache_init_parts(Render *re, RayTree *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts)
static void precache_init_parts(Render *re, RayObject *tree, ShadeInput *shi, ObjectInstanceRen *obi, int totthread, int *parts)
{
VolumePrecache *vp = obi->volume_precache;
int i=0, x, y, z;
@ -628,7 +636,7 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat
{
VolumePrecache *vp;
VolPrecachePart *nextpa, *pa;
RayTree *tree;
RayObject *tree;
ShadeInput shi;
ListBase threads;
float *bbmin=obi->obr->boundbox[0], *bbmax=obi->obr->boundbox[1];
@ -643,8 +651,11 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat
/* create a raytree with just the faces of the instanced ObjectRen,
* used for checking if the cached point is inside or outside. */
tree = create_raytree_obi(obi, bbmin, bbmax);
//tree = create_raytree_obi(obi, bbmin, bbmax);
tree = makeraytree_object(&R, obi);
if (!tree) return;
INIT_MINMAX(bbmin, bbmax);
RE_rayobject_merge_bb( tree, bbmin, bbmax);
vp = MEM_callocN(sizeof(VolumePrecache), "volume light cache");
@ -707,8 +718,9 @@ void vol_precache_objectinstance_threads(Render *re, ObjectInstanceRen *obi, Mat
BLI_freelistN(&re->volume_precache_parts);
if(tree) {
RE_ray_tree_free(tree);
tree= NULL;
//TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
//RE_rayobject_free(tree);
//tree= NULL;
}
lightcache_filter(obi->volume_precache);
@ -765,16 +777,17 @@ void free_volume_precache(Render *re)
int point_inside_volume_objectinstance(ObjectInstanceRen *obi, float *co)
{
RayTree *tree;
RayObject *tree;
int inside=0;
tree = create_raytree_obi(obi, obi->obr->boundbox[0], obi->obr->boundbox[1]);
tree = makeraytree_object(&R, obi); //create_raytree_obi(obi, obi->obr->boundbox[0], obi->obr->boundbox[1]);
if (!tree) return 0;
inside = point_inside_obi(tree, obi, co);
RE_ray_tree_free(tree);
tree= NULL;
//TODO: makeraytree_object creates a tree and saves it on OBI, if we free this tree we should also clear other pointers to it
//RE_rayobject_free(tree);
//tree= NULL;
return inside;
}

@ -73,18 +73,6 @@ inline float luminance(float* col)
}
/* tracing */
static int vlr_check_intersect_solid(Isect *is, int ob, RayFace *face)
{
VlakRen *vlr = (VlakRen*)face;
/* solid material types only */
if (vlr->mat->material_type == MA_TYPE_SURFACE)
return 1;
else
return 0;
}
static float vol_get_shadow(ShadeInput *shi, LampRen *lar, float *co)
{
float visibility = 1.f;
@ -97,42 +85,42 @@ static float vol_get_shadow(ShadeInput *shi, LampRen *lar, float *co)
} else if (lar->mode & LA_SHAD_RAY) {
/* trace shadow manually, no good lamp api atm */
Isect is;
const float maxsize = RE_ray_tree_max_size(R.raytree);
VecCopyf(is.start, co);
if(lar->type==LA_SUN || lar->type==LA_HEMI) {
is.end[0] = co[0] - lar->vec[0] * maxsize;
is.end[1] = co[1] - lar->vec[1] * maxsize;
is.end[2] = co[2] - lar->vec[2] * maxsize;
is.vec[0] = -lar->vec[0];
is.vec[1] = -lar->vec[1];
is.vec[2] = -lar->vec[2];
is.labda = R.maxdist;
} else {
VecCopyf(is.end, lar->co);
VECSUB( is.vec, lar->co, is.start );
is.labda = VecLength( is.vec );
}
is.mode= RE_RAY_MIRROR;
is.mode = RE_RAY_MIRROR;
is.skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK | RE_SKIP_VLR_NON_SOLID_MATERIAL;
if(lar->mode & (LA_LAYER|LA_LAYER_SHADOW))
is.lay= lar->lay;
else
is.lay= -1;
is.face_last= (RayFace*)lar->vlr_last[shi->thread];
is.ob_last= RAY_OBJECT_SET(&R, lar->obi_last[shi->thread]);
is.faceorig= NULL;
is.oborig= RAY_OBJECT_SET(&R, shi->obi);
is.last_hit = NULL;
is.orig.ob = (void*)shi->obi;
is.orig.face = NULL;
is.last_hit = lar->last_hit[shi->thread];
if(RE_ray_tree_intersect_check(R.raytree, &is, vlr_check_intersect_solid)) {
if(RE_rayobject_raycast(R.raytree,&is)) {
visibility = 0.f;
}
lar->vlr_last[shi->thread]= (VlakRen*)is.face_last;
lar->obi_last[shi->thread]= RAY_OBJECT_GET(&R, is.ob_last);
lar->last_hit[shi->thread]= is.last_hit;
}
return visibility;
}
static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco, Isect *isect, int intersect_type)
{
float maxsize = RE_ray_tree_max_size(R.raytree);
/* XXX TODO - get raytrace max distance from object instance's bounding box */
/* need to account for scaling only, but keep coords in camera space...
* below code is WIP and doesn't work!
@ -142,20 +130,23 @@ static int vol_get_bounds(ShadeInput *shi, float *co, float *vec, float *hitco,
*/
VECCOPY(isect->start, co);
VECCOPY(isect->vec, vec );
isect->labda = FLT_MAX;
/*
isect->end[0] = co[0] + vec[0] * maxsize;
isect->end[1] = co[1] + vec[1] * maxsize;
isect->end[2] = co[2] + vec[2] * maxsize;
*/
isect->mode= RE_RAY_MIRROR;
isect->oborig= RAY_OBJECT_SET(&R, shi->obi);
isect->face_last= NULL;
isect->ob_last= 0;
isect->orig.ob = (void*)shi->obi;
isect->last_hit = NULL;
isect->lay= -1;
if (intersect_type == VOL_BOUNDS_DEPTH) isect->faceorig= (RayFace*)shi->vlr;
else if (intersect_type == VOL_BOUNDS_SS) isect->faceorig= NULL;
if (intersect_type == VOL_BOUNDS_DEPTH) isect->orig.face = (void*)shi->vlr;
else if (intersect_type == VOL_BOUNDS_SS) isect->orig.face= NULL;
if(RE_ray_tree_intersect(R.raytree, isect))
if(RE_rayobject_raycast(R.raytree, isect))
{
hitco[0] = isect->start[0] + isect->labda*isect->vec[0];
hitco[1] = isect->start[1] + isect->labda*isect->vec[1];
@ -202,23 +193,20 @@ static void shade_intersection(ShadeInput *shi, float *col, Isect *is)
static void vol_trace_behind(ShadeInput *shi, VlakRen *vlr, float *co, float *col)
{
Isect isect;
float maxsize = RE_ray_tree_max_size(R.raytree);
VECCOPY(isect.start, co);
isect.end[0] = isect.start[0] + shi->view[0] * maxsize;
isect.end[1] = isect.start[1] + shi->view[1] * maxsize;
isect.end[2] = isect.start[2] + shi->view[2] * maxsize;
isect.faceorig= (RayFace *)vlr;
VECCOPY(isect.vec, shi->view);
isect.labda = FLT_MAX;
isect.mode= RE_RAY_MIRROR;
isect.oborig= RAY_OBJECT_SET(&R, shi->obi);
isect.face_last= NULL;
isect.ob_last= 0;
isect.skip = RE_SKIP_VLR_NEIGHBOUR | RE_SKIP_VLR_RENDER_CHECK;
isect.orig.ob = (void*) shi->obi;
isect.orig.face = (void*)vlr;
isect.last_hit = NULL;
isect.lay= -1;
/* check to see if there's anything behind the volume, otherwise shade the sky */
if(RE_ray_tree_intersect(R.raytree, &isect)) {
if(RE_rayobject_raycast(R.raytree, &isect)) {
shade_intersection(shi, col, &isect);
} else {
shadeSkyView(col, co, shi->view, NULL, shi->thread);
@ -519,7 +507,7 @@ void vol_shade_one_lamp(struct ShadeInput *shi, float *co, LampRen *lar, float *
/* find minimum of volume bounds, or lamp coord */
if (vol_get_bounds(shi, co, lv, hitco, &is, VOL_BOUNDS_SS)) {
float dist = VecLenf(co, hitco);
VlakRen *vlr = (VlakRen *)is.face;
VlakRen *vlr = (VlakRen *)is.hit.face;
/* simple internal shadowing */
if (vlr->mat->material_type == MA_TYPE_SURFACE) {
@ -719,7 +707,7 @@ static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int in
/* (ray intersect ignores front faces here) */
else if (vol_get_bounds(shi, shi->co, shi->view, hitco, &is, VOL_BOUNDS_DEPTH))
{
VlakRen *vlr = (VlakRen *)is.face;
VlakRen *vlr = (VlakRen *)is.hit.face;
startco = shi->co;
endco = hitco;
@ -728,7 +716,7 @@ static void volume_trace(struct ShadeInput *shi, struct ShadeResult *shr, int in
/* if it's another face in the same material */
if (vlr->mat == shi->mat) {
/* trace behind the 2nd (raytrace) hit point */
vol_trace_behind(shi, (VlakRen *)is.face, endco, col);
vol_trace_behind(shi, (VlakRen *)is.hit.face, endco, col);
} else {
shade_intersection(shi, col, &is);
}

@ -213,7 +213,10 @@ void WM_exit(bContext *C)
fastshade_free_render(); /* shaded view */
ED_preview_free_dbase(); /* frees a Main dbase, before free_blender! */
wm_free_reports(C); /* before free_blender! - since the ListBases get freed there */
if(C && CTX_wm_manager(C))
wm_free_reports(C); /* before free_blender! - since the ListBases get freed there */
free_blender(); /* blender.c, does entire library and spacetypes */
// free_matcopybuf();
free_anim_copybuf();