Blender V2.61 - r43446

bvh.h

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2009 Blender Foundation.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): André Pinto.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include "MEM_guardedalloc.h"
00034 
00035 #include "BLI_math.h"
00036 
00037 #include "raycounter.h"
00038 #include "rayintersection.h"
00039 #include "rayobject.h"
00040 #include "rayobject_hint.h"
00041 #include "rayobject_rtbuild.h"
00042 
00043 #include <assert.h>
00044 
00045 #ifdef __SSE__
00046 #include <xmmintrin.h>
00047 #endif
00048 
00049 #ifndef RE_RAYTRACE_BVH_H
00050 #define RE_RAYTRACE_BVH_H
00051 
00052 #ifdef __SSE__
00053 inline int test_bb_group4(__m128 *bb_group, const Isect *isec)
00054 {
00055     const __m128 tmin0 = _mm_setzero_ps();
00056     const __m128 tmax0 = _mm_set_ps1(isec->dist);
00057 
00058     float start[3], idot_axis[3];
00059     copy_v3_v3(start, isec->start);
00060     copy_v3_v3(idot_axis, isec->idot_axis);
00061 
00062     const __m128 tmin1 = _mm_max_ps(tmin0, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[0]], _mm_set_ps1(start[0]) ), _mm_set_ps1(idot_axis[0])) );
00063     const __m128 tmax1 = _mm_min_ps(tmax0, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[1]], _mm_set_ps1(start[0]) ), _mm_set_ps1(idot_axis[0])) );
00064     const __m128 tmin2 = _mm_max_ps(tmin1, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[2]], _mm_set_ps1(start[1]) ), _mm_set_ps1(idot_axis[1])) );
00065     const __m128 tmax2 = _mm_min_ps(tmax1, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[3]], _mm_set_ps1(start[1]) ), _mm_set_ps1(idot_axis[1])) );
00066     const __m128 tmin3 = _mm_max_ps(tmin2, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[4]], _mm_set_ps1(start[2]) ), _mm_set_ps1(idot_axis[2])) );
00067     const __m128 tmax3 = _mm_min_ps(tmax2, _mm_mul_ps( _mm_sub_ps( bb_group[isec->bv_index[5]], _mm_set_ps1(start[2]) ), _mm_set_ps1(idot_axis[2])) );
00068     
00069     return _mm_movemask_ps(_mm_cmpge_ps(tmax3, tmin3));
00070 }
00071 #endif
00072 
00073 /*
00074  * Determines the distance that the ray must travel to hit the bounding volume of the given node
00075  * Based on Tactical Optimization of Ray/Box Intersection, by Graham Fyffe
00076  *  [http://tog.acm.org/resources/RTNews/html/rtnv21n1.html#art9]
00077  */
00078 static int rayobject_bb_intersect_test(const Isect *isec, const float *_bb)
00079 {
00080     const float *bb = _bb;
00081     
00082     float t1x = (bb[isec->bv_index[0]] - isec->start[0]) * isec->idot_axis[0];
00083     float t2x = (bb[isec->bv_index[1]] - isec->start[0]) * isec->idot_axis[0];
00084     float t1y = (bb[isec->bv_index[2]] - isec->start[1]) * isec->idot_axis[1];
00085     float t2y = (bb[isec->bv_index[3]] - isec->start[1]) * isec->idot_axis[1];
00086     float t1z = (bb[isec->bv_index[4]] - isec->start[2]) * isec->idot_axis[2];
00087     float t2z = (bb[isec->bv_index[5]] - isec->start[2]) * isec->idot_axis[2];
00088 
00089     RE_RC_COUNT(isec->raycounter->bb.test);
00090     
00091     if(t1x > t2y || t2x < t1y || t1x > t2z || t2x < t1z || t1y > t2z || t2y < t1z) return 0;
00092     if(t2x < 0.0 || t2y < 0.0 || t2z < 0.0) return 0;
00093     if(t1x > isec->dist || t1y > isec->dist || t1z > isec->dist) return 0;
00094     RE_RC_COUNT(isec->raycounter->bb.hit);  
00095 
00096     return 1;
00097 }
00098 
00099 /* bvh tree generics */
00100 template<class Tree> static int bvh_intersect(Tree *obj, Isect *isec);
00101 
00102 template<class Tree> static void bvh_add(Tree *obj, RayObject *ob)
00103 {
00104     rtbuild_add( obj->builder, ob );
00105 }
00106 
00107 template<class Node>
00108 inline bool is_leaf(Node *node)
00109 {
00110     return !RE_rayobject_isAligned(node);
00111 }
00112 
00113 template<class Tree> static void bvh_done(Tree *obj);
00114 
00115 template<class Tree>
00116 static void bvh_free(Tree *obj)
00117 {
00118     if(obj->builder)
00119         rtbuild_free(obj->builder);
00120 
00121     if(obj->node_arena)
00122         BLI_memarena_free(obj->node_arena);
00123 
00124     MEM_freeN(obj);
00125 }
00126 
00127 template<class Tree>
00128 static void bvh_bb(Tree *obj, float *min, float *max)
00129 {
00130     if(obj->root)
00131         bvh_node_merge_bb(obj->root, min, max);
00132 }
00133 
00134 
00135 template<class Tree>
00136 static float bvh_cost(Tree *obj)
00137 {
00138     assert(obj->cost >= 0.0);
00139     return obj->cost;
00140 }
00141 
00142 
00143 
00144 /* bvh tree nodes generics */
00145 template<class Node> static inline int bvh_node_hit_test(Node *node, Isect *isec)
00146 {
00147     return rayobject_bb_intersect_test(isec, (const float*)node->bb);
00148 }
00149 
00150 
00151 template<class Node>
00152 static inline void bvh_node_merge_bb(Node *node, float *min, float *max)
00153 {
00154     if(is_leaf(node))
00155     {
00156         RE_rayobject_merge_bb( (RayObject*)node, min, max);
00157     }
00158     else
00159     {
00160         DO_MIN(node->bb  , min);
00161         DO_MAX(node->bb+3, max);
00162     }
00163 }
00164 
00165 
00166 
00167 /*
00168  * recursively transverse a BVH looking for a rayhit using a local stack
00169  */
00170 template<class Node> static inline void bvh_node_push_childs(Node *node, Isect *isec, Node **stack, int &stack_pos);
00171 
00172 template<class Node,int MAX_STACK_SIZE,bool TEST_ROOT,bool SHADOW>
00173 static int bvh_node_stack_raycast(Node *root, Isect *isec)
00174 {
00175     Node *stack[MAX_STACK_SIZE];
00176     int hit = 0, stack_pos = 0;
00177         
00178     if(!TEST_ROOT && !is_leaf(root))
00179         bvh_node_push_childs(root, isec, stack, stack_pos);
00180     else
00181         stack[stack_pos++] = root;
00182 
00183     while(stack_pos)
00184     {
00185         Node *node = stack[--stack_pos];
00186         if(!is_leaf(node))
00187         {
00188             if(bvh_node_hit_test(node,isec))
00189             {
00190                 bvh_node_push_childs(node, isec, stack, stack_pos);
00191                 assert(stack_pos <= MAX_STACK_SIZE);
00192             }
00193         }
00194         else
00195         {
00196             hit |= RE_rayobject_intersect( (RayObject*)node, isec);
00197             if(SHADOW && hit) return hit;
00198         }
00199     }
00200     return hit;
00201 }
00202 
00203 
00204 #ifdef __SSE__
00205 /*
00206  * Generic SIMD bvh recursion
00207  * this was created to be able to use any simd (with the cost of some memmoves)
00208  * it can take advantage of any SIMD width and doens't needs any special tree care
00209  */
00210 template<class Node,int MAX_STACK_SIZE,bool TEST_ROOT>
00211 static int bvh_node_stack_raycast_simd(Node *root, Isect *isec)
00212 {
00213     Node *stack[MAX_STACK_SIZE];
00214 
00215     int hit = 0, stack_pos = 0;
00216         
00217     if(!TEST_ROOT)
00218     {
00219         if(!is_leaf(root))
00220         {
00221             if(!is_leaf(root->child))
00222                 bvh_node_push_childs(root, isec, stack, stack_pos);
00223             else
00224                 return RE_rayobject_intersect( (RayObject*)root->child, isec);
00225         }
00226         else
00227             return RE_rayobject_intersect( (RayObject*)root, isec);
00228     }
00229     else
00230     {
00231         if(!is_leaf(root))
00232             stack[stack_pos++] = root;
00233         else
00234             return RE_rayobject_intersect( (RayObject*)root, isec);
00235     }
00236 
00237     while(true)
00238     {
00239         //Use SIMD 4
00240         if(stack_pos >= 4)
00241         {
00242             __m128 t_bb[6];
00243             Node * t_node[4];
00244             
00245             stack_pos -= 4;
00246 
00247             /* prepare the 4BB for SIMD */
00248             t_node[0] = stack[stack_pos+0]->child;
00249             t_node[1] = stack[stack_pos+1]->child;
00250             t_node[2] = stack[stack_pos+2]->child;
00251             t_node[3] = stack[stack_pos+3]->child;
00252             
00253             const float *bb0 = stack[stack_pos+0]->bb;
00254             const float *bb1 = stack[stack_pos+1]->bb;
00255             const float *bb2 = stack[stack_pos+2]->bb;
00256             const float *bb3 = stack[stack_pos+3]->bb;
00257             
00258             const __m128 x0y0x1y1 = _mm_shuffle_ps( _mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(1,0,1,0) );
00259             const __m128 x2y2x3y3 = _mm_shuffle_ps( _mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(1,0,1,0) );
00260             t_bb[0] = _mm_shuffle_ps( x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(2,0,2,0) );
00261             t_bb[1] = _mm_shuffle_ps( x0y0x1y1, x2y2x3y3, _MM_SHUFFLE(3,1,3,1) );
00262 
00263             const __m128 z0X0z1X1 = _mm_shuffle_ps( _mm_load_ps(bb0), _mm_load_ps(bb1), _MM_SHUFFLE(3,2,3,2) );
00264             const __m128 z2X2z3X3 = _mm_shuffle_ps( _mm_load_ps(bb2), _mm_load_ps(bb3), _MM_SHUFFLE(3,2,3,2) );
00265             t_bb[2] = _mm_shuffle_ps( z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(2,0,2,0) );
00266             t_bb[3] = _mm_shuffle_ps( z0X0z1X1, z2X2z3X3, _MM_SHUFFLE(3,1,3,1) );
00267 
00268             const __m128 Y0Z0Y1Z1 = _mm_shuffle_ps( _mm_load_ps(bb0+4), _mm_load_ps(bb1+4), _MM_SHUFFLE(1,0,1,0) );
00269             const __m128 Y2Z2Y3Z3 = _mm_shuffle_ps( _mm_load_ps(bb2+4), _mm_load_ps(bb3+4), _MM_SHUFFLE(1,0,1,0) );
00270             t_bb[4] = _mm_shuffle_ps( Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(2,0,2,0) );
00271             t_bb[5] = _mm_shuffle_ps( Y0Z0Y1Z1, Y2Z2Y3Z3, _MM_SHUFFLE(3,1,3,1) );
00272 /*          
00273             for(int i=0; i<4; i++)
00274             {
00275                 Node *t = stack[stack_pos+i];
00276                 assert(!is_leaf(t));
00277                 
00278                 float *bb = ((float*)t_bb)+i;
00279                 bb[4*0] = t->bb[0];
00280                 bb[4*1] = t->bb[1];
00281                 bb[4*2] = t->bb[2];
00282                 bb[4*3] = t->bb[3];
00283                 bb[4*4] = t->bb[4];
00284                 bb[4*5] = t->bb[5];
00285                 t_node[i] = t->child;
00286             }
00287 */
00288             RE_RC_COUNT(isec->raycounter->simd_bb.test);
00289             int res = test_bb_group4( t_bb, isec );
00290 
00291             for(int i=0; i<4; i++)
00292             if(res & (1<<i))
00293             {
00294                 RE_RC_COUNT(isec->raycounter->simd_bb.hit);
00295                 if(!is_leaf(t_node[i]))
00296                 {
00297                     for(Node *t=t_node[i]; t; t=t->sibling)
00298                     {
00299                         assert(stack_pos < MAX_STACK_SIZE);
00300                         stack[stack_pos++] = t;
00301                     }
00302                 }
00303                 else
00304                 {
00305                     hit |= RE_rayobject_intersect( (RayObject*)t_node[i], isec);
00306                     if(hit && isec->mode == RE_RAY_SHADOW) return hit;              
00307                 }   
00308             }
00309         }
00310         else if(stack_pos > 0)
00311         {   
00312             Node *node = stack[--stack_pos];
00313             assert(!is_leaf(node));
00314             
00315             if(bvh_node_hit_test(node,isec))
00316             {
00317                 if(!is_leaf(node->child))
00318                 {
00319                     bvh_node_push_childs(node, isec, stack, stack_pos);
00320                     assert(stack_pos <= MAX_STACK_SIZE);
00321                 }
00322                 else
00323                 {
00324                     hit |= RE_rayobject_intersect( (RayObject*)node->child, isec);
00325                     if(hit && isec->mode == RE_RAY_SHADOW) return hit;
00326                 }
00327             }
00328         }
00329         else break;
00330     }
00331     return hit;
00332 }
00333 #endif
00334 
00335 /*
00336  * recursively transverse a BVH looking for a rayhit using system stack
00337  */
00338 /*
00339 template<class Node>
00340 static int bvh_node_raycast(Node *node, Isect *isec)
00341 {
00342     int hit = 0;
00343     if(bvh_test_node(node, isec))
00344     {
00345         if(isec->idot_axis[node->split_axis] > 0.0f)
00346         {
00347             int i;
00348             for(i=0; i<BVH_NCHILDS; i++)
00349                 if(!is_leaf(node->child[i]))
00350                 {
00351                     if(node->child[i] == 0) break;
00352                     
00353                     hit |= bvh_node_raycast(node->child[i], isec);
00354                     if(hit && isec->mode == RE_RAY_SHADOW) return hit;
00355                 }
00356                 else
00357                 {
00358                     hit |= RE_rayobject_intersect( (RayObject*)node->child[i], isec);
00359                     if(hit && isec->mode == RE_RAY_SHADOW) return hit;
00360                 }
00361         }
00362         else
00363         {
00364             int i;
00365             for(i=BVH_NCHILDS-1; i>=0; i--)
00366                 if(!is_leaf(node->child[i]))
00367                 {
00368                     if(node->child[i])
00369                     {
00370                         hit |= dfs_raycast(node->child[i], isec);
00371                         if(hit && isec->mode == RE_RAY_SHADOW) return hit;
00372                     }
00373                 }
00374                 else
00375                 {
00376                     hit |= RE_rayobject_intersect( (RayObject*)node->child[i], isec);
00377                     if(hit && isec->mode == RE_RAY_SHADOW) return hit;
00378                 }
00379         }
00380     }
00381     return hit;
00382 }
00383 */
00384 
00385 template<class Node,class HintObject>
00386 void bvh_dfs_make_hint(Node *node, LCTSHint *hint, int reserve_space, HintObject *hintObject)
00387 {
00388     assert( hint->size + reserve_space + 1 <= RE_RAY_LCTS_MAX_SIZE );
00389     
00390     if(is_leaf(node))
00391     {
00392         hint->stack[hint->size++] = (RayObject*)node;
00393     }
00394     else
00395     {
00396         int childs = count_childs(node);
00397         if(hint->size + reserve_space + childs <= RE_RAY_LCTS_MAX_SIZE)
00398         {
00399             int result = hint_test_bb(hintObject, node->bb, node->bb+3);
00400             if(result == HINT_RECURSE)
00401             {
00402                 /* We are 100% sure the ray will be pass inside this node */
00403                 bvh_dfs_make_hint_push_siblings(node->child, hint, reserve_space, hintObject);
00404             }
00405             else if(result == HINT_ACCEPT)
00406             {
00407                 hint->stack[hint->size++] = (RayObject*)node;
00408             }
00409         }
00410         else
00411         {
00412             hint->stack[hint->size++] = (RayObject*)node;
00413         }
00414     }
00415 }
00416 
00417 
00418 template<class Tree>
00419 static RayObjectAPI* bvh_get_api(int maxstacksize);
00420 
00421 
00422 template<class Tree, int DFS_STACK_SIZE>
00423 static inline RayObject *bvh_create_tree(int size)
00424 {
00425     Tree *obj= (Tree*)MEM_callocN(sizeof(Tree), "BVHTree" );
00426     assert( RE_rayobject_isAligned(obj) ); /* RayObject API assumes real data to be 4-byte aligned */   
00427     
00428     obj->rayobj.api = bvh_get_api<Tree>(DFS_STACK_SIZE);
00429     obj->root = NULL;
00430     
00431     obj->node_arena = NULL;
00432     obj->builder    = rtbuild_create( size );
00433     
00434     return RE_rayobject_unalignRayAPI((RayObject*) obj);
00435 }
00436 
00437 #endif