Blender  V2.59
reorganize.h
Go to the documentation of this file.
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 };