Blender V2.61 - r43446
|
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