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