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