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 <float.h> 00034 #include <math.h> 00035 #include <stdio.h> 00036 00037 #include <algorithm> 00038 #include <queue> 00039 #include <vector> 00040 00041 #include "BKE_global.h" 00042 00043 #ifdef _WIN32 00044 # ifdef INFINITY 00045 # undef INFINITY 00046 # endif 00047 # define INFINITY FLT_MAX // in mingw math.h: (1.0F/0.0F). This generates compile error, though. 00048 #endif 00049 00050 extern int tot_pushup; 00051 extern int tot_pushdown; 00052 00053 #if !defined(INFINITY) && defined(HUGE_VAL) 00054 #define INFINITY HUGE_VAL 00055 #endif 00056 00057 template<class Node> 00058 bool node_fits_inside(Node *a, Node *b) 00059 { 00060 return bb_fits_inside(b->bb, b->bb+3, a->bb, a->bb+3); 00061 } 00062 00063 template<class Node> 00064 void reorganize_find_fittest_parent(Node *tree, Node *node, std::pair<float,Node*> &cost) 00065 { 00066 std::queue<Node*> q; 00067 q.push(tree); 00068 00069 while(!q.empty()) 00070 { 00071 Node *parent = q.front(); 00072 q.pop(); 00073 00074 if(parent == node) continue; 00075 if(node_fits_inside(node, parent) && RE_rayobject_isAligned(parent->child) ) 00076 { 00077 float pcost = bb_area(parent->bb, parent->bb+3); 00078 cost = std::min( cost, std::make_pair(pcost,parent) ); 00079 for(Node *child = parent->child; child; child = child->sibling) 00080 q.push(child); 00081 } 00082 } 00083 } 00084 00085 static int tot_moves = 0; 00086 template<class Node> 00087 void reorganize(Node *root) 00088 { 00089 std::queue<Node*> q; 00090 00091 q.push(root); 00092 while(!q.empty()) 00093 { 00094 Node * node = q.front(); 00095 q.pop(); 00096 00097 if( RE_rayobject_isAligned(node->child) ) 00098 { 00099 for(Node **prev = &node->child; *prev; ) 00100 { 00101 assert( RE_rayobject_isAligned(*prev) ); 00102 q.push(*prev); 00103 00104 std::pair<float,Node*> best(FLT_MAX, root); 00105 reorganize_find_fittest_parent( root, *prev, best ); 00106 00107 if(best.second == node) 00108 { 00109 //Already inside the fitnest BB 00110 prev = &(*prev)->sibling; 00111 } 00112 else 00113 { 00114 Node *tmp = *prev; 00115 *prev = (*prev)->sibling; 00116 00117 tmp->sibling = best.second->child; 00118 best.second->child = tmp; 00119 00120 tot_moves++; 00121 } 00122 00123 00124 } 00125 } 00126 if(node != root) 00127 { 00128 } 00129 } 00130 } 00131 00132 /* 00133 * Prunes useless nodes from trees: 00134 * erases nodes with total amount of primitives = 0 00135 * prunes nodes with only one child (except if that child is a primitive) 00136 */ 00137 template<class Node> 00138 void remove_useless(Node *node, Node **new_node) 00139 { 00140 if( RE_rayobject_isAligned(node->child) ) 00141 { 00142 00143 for(Node **prev = &node->child; *prev; ) 00144 { 00145 Node *next = (*prev)->sibling; 00146 remove_useless(*prev, prev); 00147 if(*prev == 0) 00148 *prev = next; 00149 else 00150 { 00151 (*prev)->sibling = next; 00152 prev = &((*prev)->sibling); 00153 } 00154 } 00155 } 00156 if(node->child) 00157 { 00158 if(RE_rayobject_isAligned(node->child) && node->child->sibling == 0) 00159 *new_node = node->child; 00160 } 00161 else if(node->child == 0) 00162 *new_node = 0; 00163 } 00164 00165 /* 00166 * Minimizes expected number of BBtest by colapsing nodes 00167 * it uses surface area heuristic for determining whether a node should be colapsed 00168 */ 00169 template<class Node> 00170 void pushup(Node *parent) 00171 { 00172 if(is_leaf(parent)) return; 00173 00174 float p_area = bb_area(parent->bb, parent->bb+3); 00175 Node **prev = &parent->child; 00176 for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; ) 00177 { 00178 float c_area = bb_area(child->bb, child->bb+3) ; 00179 int nchilds = count_childs(child); 00180 float original_cost = ((p_area != 0.0f)? (c_area / p_area)*nchilds: 1.0f) + 1; 00181 float flatten_cost = nchilds; 00182 if(flatten_cost < original_cost && nchilds >= 2) 00183 { 00184 append_sibling(child, child->child); 00185 child = child->sibling; 00186 *prev = child; 00187 00188 // *prev = child->child; 00189 // append_sibling( *prev, child->sibling ); 00190 // child = *prev; 00191 tot_pushup++; 00192 } 00193 else 00194 { 00195 *prev = child; 00196 prev = &(*prev)->sibling; 00197 child = *prev; 00198 } 00199 } 00200 00201 for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling) 00202 pushup(child); 00203 } 00204 00205 /* 00206 * try to optimize number of childs to be a multiple of SSize 00207 */ 00208 template<class Node, int SSize> 00209 void pushup_simd(Node *parent) 00210 { 00211 if(is_leaf(parent)) return; 00212 00213 int n = count_childs(parent); 00214 00215 Node **prev = &parent->child; 00216 for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; ) 00217 { 00218 int cn = count_childs(child); 00219 if(cn-1 <= (SSize - (n%SSize) ) % SSize && RE_rayobject_isAligned(child->child) ) 00220 { 00221 n += (cn - 1); 00222 append_sibling(child, child->child); 00223 child = child->sibling; 00224 *prev = child; 00225 } 00226 else 00227 { 00228 *prev = child; 00229 prev = &(*prev)->sibling; 00230 child = *prev; 00231 } 00232 } 00233 00234 for(Node *child = parent->child; RE_rayobject_isAligned(child) && child; child = child->sibling) 00235 pushup_simd<Node,SSize>(child); 00236 } 00237 00238 00239 /* 00240 * Pushdown 00241 * makes sure no child fits inside any of its sibling 00242 */ 00243 template<class Node> 00244 void pushdown(Node *parent) 00245 { 00246 Node **s_child = &parent->child; 00247 Node * child = parent->child; 00248 00249 while(child && RE_rayobject_isAligned(child)) 00250 { 00251 Node *next = child->sibling; 00252 Node **next_s_child = &child->sibling; 00253 00254 //assert(bb_fits_inside(parent->bb, parent->bb+3, child->bb, child->bb+3)); 00255 00256 for(Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling) 00257 if(child != i && bb_fits_inside(i->bb, i->bb+3, child->bb, child->bb+3) && RE_rayobject_isAligned(i->child)) 00258 { 00259 // todo optimize (should the one with the smallest area?) 00260 // float ia = bb_area(i->bb, i->bb+3) 00261 // if(child->i) 00262 *s_child = child->sibling; 00263 child->sibling = i->child; 00264 i->child = child; 00265 next_s_child = s_child; 00266 00267 tot_pushdown++; 00268 break; 00269 } 00270 child = next; 00271 s_child = next_s_child; 00272 } 00273 00274 for(Node *i = parent->child; RE_rayobject_isAligned(i) && i; i = i->sibling) 00275 pushdown( i ); 00276 } 00277 00278 00279 /* 00280 * BVH refit 00281 * readjust nodes BB (useful if nodes childs where modified) 00282 */ 00283 template<class Node> 00284 float bvh_refit(Node *node) 00285 { 00286 if(is_leaf(node)) return 0; 00287 if(is_leaf(node->child)) return 0; 00288 00289 float total = 0; 00290 00291 for(Node *child = node->child; child; child = child->sibling) 00292 total += bvh_refit(child); 00293 00294 float old_area = bb_area(node->bb, node->bb+3); 00295 INIT_MINMAX(node->bb, node->bb+3); 00296 for(Node *child = node->child; child; child = child->sibling) 00297 { 00298 DO_MIN(child->bb, node->bb); 00299 DO_MAX(child->bb+3, node->bb+3); 00300 } 00301 total += old_area - bb_area(node->bb, node->bb+3); 00302 return total; 00303 } 00304 00305 00306 /* 00307 * this finds the best way to packing a tree according to a given test cost function 00308 * with the purpose to reduce the expected cost (eg.: number of BB tests). 00309 */ 00310 #include <vector> 00311 #define MAX_CUT_SIZE 4 /* svbvh assumes max 4 children! */ 00312 #define MAX_OPTIMIZE_CHILDS MAX_CUT_SIZE 00313 00314 struct OVBVHNode 00315 { 00316 float bb[6]; 00317 00318 OVBVHNode *child; 00319 OVBVHNode *sibling; 00320 00321 /* 00322 * Returns min cost to represent the subtree starting at the given node, 00323 * allowing it to have a given cutsize 00324 */ 00325 float cut_cost[MAX_CUT_SIZE]; 00326 float get_cost(int cutsize) 00327 { 00328 return cut_cost[cutsize-1]; 00329 } 00330 00331 /* 00332 * This saves the cut size of this child, when parent is reaching 00333 * its minimum cut with the given cut size 00334 */ 00335 int cut_size[MAX_CUT_SIZE]; 00336 int get_cut_size(int parent_cut_size) 00337 { 00338 return cut_size[parent_cut_size-1]; 00339 } 00340 00341 /* 00342 * Reorganize the node based on calculated cut costs 00343 */ 00344 int best_cutsize; 00345 void set_cut(int cutsize, OVBVHNode ***cut) 00346 { 00347 if(cutsize == 1) 00348 { 00349 **cut = this; 00350 *cut = &(**cut)->sibling; 00351 } 00352 else 00353 { 00354 if(cutsize > MAX_CUT_SIZE) 00355 { 00356 for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) 00357 { 00358 child->set_cut( 1, cut ); 00359 cutsize--; 00360 } 00361 assert(cutsize == 0); 00362 } 00363 else 00364 for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) 00365 child->set_cut( child->get_cut_size( cutsize ), cut ); 00366 } 00367 } 00368 00369 void optimize() 00370 { 00371 if(RE_rayobject_isAligned(this->child)) 00372 { 00373 //Calc new childs 00374 { 00375 OVBVHNode **cut = &(this->child); 00376 set_cut( best_cutsize, &cut ); 00377 *cut = NULL; 00378 } 00379 00380 //Optimize new childs 00381 for(OVBVHNode *child = this->child; child && RE_rayobject_isAligned(child); child = child->sibling) 00382 child->optimize(); 00383 } 00384 } 00385 }; 00386 00387 /* 00388 * Calculates an optimal SIMD packing 00389 * 00390 */ 00391 template<class Node, class TestCost> 00392 struct VBVH_optimalPackSIMD 00393 { 00394 TestCost testcost; 00395 00396 VBVH_optimalPackSIMD(TestCost testcost) 00397 { 00398 this->testcost = testcost; 00399 } 00400 00401 /* 00402 * calc best cut on a node 00403 */ 00404 struct calc_best 00405 { 00406 Node *child[MAX_OPTIMIZE_CHILDS]; 00407 float child_hit_prob[MAX_OPTIMIZE_CHILDS]; 00408 00409 calc_best(Node *node) 00410 { 00411 int nchilds = 0; 00412 //Fetch childs and needed data 00413 { 00414 float parent_area = bb_area(node->bb, node->bb+3); 00415 for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) 00416 { 00417 this->child[nchilds] = child; 00418 this->child_hit_prob[nchilds] = (parent_area != 0.0f)? bb_area(child->bb, child->bb+3) / parent_area: 1.0f; 00419 nchilds++; 00420 } 00421 00422 assert(nchilds >= 2 && nchilds <= MAX_OPTIMIZE_CHILDS); 00423 } 00424 00425 00426 //Build DP table to find minimum cost to represent this node with a given cutsize 00427 int bt [MAX_OPTIMIZE_CHILDS+1][MAX_CUT_SIZE+1]; //backtrace table 00428 float cost[MAX_OPTIMIZE_CHILDS+1][MAX_CUT_SIZE+1]; //cost table (can be reduced to float[2][MAX_CUT_COST]) 00429 00430 for(int i=0; i<=nchilds; i++) 00431 for(int j=0; j<=MAX_CUT_SIZE; j++) 00432 cost[i][j] = INFINITY; 00433 00434 cost[0][0] = 0; 00435 00436 for(int i=1; i<=nchilds; i++) 00437 for(int size=i-1; size/*+(nchilds-i)*/<=MAX_CUT_SIZE; size++) 00438 for(int cut=1; cut+size/*+(nchilds-i)*/<=MAX_CUT_SIZE; cut++) 00439 { 00440 float new_cost = cost[i-1][size] + child_hit_prob[i-1]*child[i-1]->get_cost(cut); 00441 if(new_cost < cost[i][size+cut]) 00442 { 00443 cost[i][size+cut] = new_cost; 00444 bt[i][size+cut] = cut; 00445 } 00446 } 00447 00448 //Save the ways to archieve the minimum cost with a given cutsize 00449 for(int i = nchilds; i <= MAX_CUT_SIZE; i++) 00450 { 00451 node->cut_cost[i-1] = cost[nchilds][i]; 00452 if(cost[nchilds][i] < INFINITY) 00453 { 00454 int current_size = i; 00455 for(int j=nchilds; j>0; j--) 00456 { 00457 child[j-1]->cut_size[i-1] = bt[j][current_size]; 00458 current_size -= bt[j][current_size]; 00459 } 00460 } 00461 } 00462 } 00463 }; 00464 00465 void calc_costs(Node *node) 00466 { 00467 00468 if( RE_rayobject_isAligned(node->child) ) 00469 { 00470 int nchilds = 0; 00471 for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) 00472 { 00473 calc_costs(child); 00474 nchilds++; 00475 } 00476 00477 for(int i=0; i<MAX_CUT_SIZE; i++) 00478 node->cut_cost[i] = INFINITY; 00479 00480 //We are not allowed to look on nodes with with so many childs 00481 if(nchilds > MAX_CUT_SIZE) 00482 { 00483 float cost = 0; 00484 00485 float parent_area = bb_area(node->bb, node->bb+3); 00486 for(Node *child = node->child; child && RE_rayobject_isAligned(child); child = child->sibling) 00487 { 00488 cost += ((parent_area != 0.0f)? ( bb_area(child->bb, child->bb+3) / parent_area ): 1.0f) * child->get_cost(1); 00489 } 00490 00491 cost += testcost(nchilds); 00492 node->cut_cost[0] = cost; 00493 node->best_cutsize = nchilds; 00494 } 00495 else 00496 { 00497 calc_best calc(node); 00498 00499 //calc expected cost if we optimaly pack this node 00500 for(int cutsize=nchilds; cutsize<=MAX_CUT_SIZE; cutsize++) 00501 { 00502 float m = node->get_cost(cutsize) + testcost(cutsize); 00503 if(m < node->cut_cost[0]) 00504 { 00505 node->cut_cost[0] = m; 00506 node->best_cutsize = cutsize; 00507 } 00508 } 00509 } 00510 assert(node->cut_cost[0] != INFINITY); 00511 } 00512 else 00513 { 00514 node->cut_cost[0] = 1.0f; 00515 for(int i=1; i<MAX_CUT_SIZE; i++) 00516 node->cut_cost[i] = INFINITY; 00517 } 00518 } 00519 00520 Node *transform(Node *node) 00521 { 00522 if(RE_rayobject_isAligned(node->child)) 00523 { 00524 static int num = 0; 00525 bool first = false; 00526 if(num == 0) { num++; first = true; } 00527 00528 calc_costs(node); 00529 if((G.f & G_DEBUG) && first) printf("expected cost = %f (%d)\n", node->cut_cost[0], node->best_cutsize ); 00530 node->optimize(); 00531 } 00532 return node; 00533 } 00534 };