src/rcrt/BIHNode.hpp

Go to the documentation of this file.
00001 #ifndef BIHNODE_HPP_
00002 #define BIHNODE_HPP_
00003 
00004 #include <iostream>
00005 #include <limits>
00006 #include <algorithm>
00007 #include <stdexcept>
00008 #include <vector>
00009 #include "Traceable.h"
00010 #include "primitives/AABB.h"
00011 #include "loaders/conversion.hpp"
00012 
00013 namespace rcrt
00014 {
00015 
00019 class BIHSplit
00020 {
00021 public:
00022         float split;
00023         Axis axis;
00024         
00025         BIHSplit(float s, Axis ax) : split(s), axis(ax)
00026         {}
00027 };
00028 
00029 
00034 template<class T>
00035 class BIHNode
00036 {
00037 private:
00043         BIHNode<T>* children[2];
00044         
00048         std::vector<T*>* traceables;
00049         
00053         int leftIndex;
00054         
00058         int rightIndex;
00059         
00066         float clip[2];
00067         
00071         Axis splitAxis;
00072         
00073         
00080         Intersection leafIntersect(Ray& r, const float& tMin, const float& tMax) const
00081         {
00082                 Intersection hit;
00083                 if(leftIndex > rightIndex)//sanity check for an empty/invalid node
00084                         return hit;
00085                 for(int i = leftIndex; i < rightIndex+1; i++){
00086                         float rmin = r.minDist();
00087                         float rmax = r.maxDist();
00088                         r.setMinDist(std::max(tMin,rmin));
00089                         r.setMaxDist(std::min(tMax,rmax));//set values for intersecting
00090                         Intersection curr = traceables->at(i)->intersect(r);
00091                         r.setMaxDist(rmax);//reset values for next intersection
00092                         r.setMinDist(rmin); 
00093                         if(curr.isValid()){
00094                                 if(curr.getDistance() < hit.getDistance()){
00095                                         hit = curr;//current is nearer
00096                                 }
00097                                 if(curr.getDistance() < r.maxDist())
00098                                         r.setMaxDist(curr.getDistance());//a valid intersection limits the ray
00099                         }
00100                                         
00101                 }
00102                 return hit;
00103                 
00104         }
00105         
00111         void initNodeAsLeaf(std::vector<T*>* trList, int left, int right)
00112         {
00113                 children[0] = children[1] = 0;
00114                 clip[0] = clip[1] = 0;
00115                 traceables = trList;
00116                 leftIndex = left;
00117                 rightIndex = right;
00118                 splitAxis = X_AXIS;
00119         }
00120         
00129         void initNode(const AABB& bbox, std::vector<T*>* trList, int left, int right, int depth, int singleDepth = 0)
00130         {
00131                 int n = right-left+1;
00132                 int MAXDEPTH = 100;//TODO make MAXDEPTH a parameter on construction
00133                 int MAXSINGLED = 12;//TODO make MAXSINGLED a parameter on construction
00134                 if(n <= 0){//this would be an error in the code, cannot occur currently :)
00135                         throw std::runtime_error("[BIH-Build] - error: indices overlap: left=" + toString(left) + " right=" + toString(right));
00136                 }
00137                 else if(n == 1 || depth > MAXDEPTH || singleDepth > MAXSINGLED){
00138                         //we'll create a leaf
00139                         initNodeAsLeaf(trList,left,right);
00140                         return;
00141                 }
00142                 else {
00143                         //get the split plane = middle of major axis of the bbox
00144                         BIHSplit sp = getSplitPlane(bbox);
00145                         
00146                         //partition trList[left...right] based on the BIHSplit
00147                         int i = left;
00148                         int j = right;
00149                         
00150                         //keep track of minimum and maximum of left and right elements
00151                         float minL = std::numeric_limits<float>::infinity();
00152                         float minR = std::numeric_limits<float>::infinity();
00153                         float maxL = -std::numeric_limits<float>::infinity();
00154                         float maxR = -std::numeric_limits<float>::infinity();
00155                         
00156                         while(i <= j){
00157                                 const AABB& box = trList->at(i)->getBoundingBox();
00158                                 
00159                                 //get the centroid of the current element
00160                                 float pos = trList->at(i)->getCentroid()[sp.axis];
00161                                 float minB = box.getMin(sp.axis);
00162                                 float maxB = box.getMax(sp.axis);
00163                                 if(pos <= sp.split){//element is left
00164                                         //update minimum and maximum
00165                                         minL = std::min(minB, minL);
00166                                         maxL = std::max(maxB, maxL);
00167                                         i++;
00168                                 } else {//element must be swapped to right
00169                                         minR = std::min(minB, minR);//update
00170                                         maxR = std::max(maxB, maxR);
00171                                         if(i != j){
00172                                                 T* currL = trList->at(i);
00173                                                 (*trList)[i] = trList->at(j);
00174                                                 (*trList)[j] = currL;
00175                                         }
00176                                         j--;
00177                                 }
00178                         }
00179                         // here i = j+1 and i is the leftmost element of the right interval                     
00180                         
00181                         
00182                         //create bounding boxes for the child nodes
00183                         //this implements the global heuristic as described
00184                         //in the BIH paper because we always split along 
00185                         //the middle of the largest axis of the bbox
00186                         //and do not modify our boxes in any other way
00187                         //shrinking the boxes to the contained objects
00188                         //did not provide better performance, leads to
00189                         //an inefficient clipping of empty space
00190                         Point3D minRP = bbox.getMinP();
00191                         minRP[sp.axis] = sp.split;
00192                         Point3D maxRP = bbox.getMaxP();
00193                         
00194                         Point3D minLP = bbox.getMinP();
00195                         Point3D maxLP = bbox.getMaxP();
00196                         maxLP[sp.axis] = sp.split;
00197                         
00198                         AABB leftB(minLP,maxLP);
00199                         AABB rightB(minRP,maxRP);
00200                         
00201                         //initialize values of this node
00202                         splitAxis = sp.axis;
00203                         leftIndex = left;
00204                         rightIndex = right;
00205                 
00206                         if(i > right){//no right child
00207                                 //create a "single" node with different clipping 
00208                                 clip[0] = minL;
00209                                 clip[1] = maxL;                         
00210                                 children[0] = new BIHNode(leftB,trList,left,right,depth+1, singleDepth + 1);
00211                                 children[1] = 0;
00212                         }
00213                         else if(i <= left){//no left child
00214                                 //create a "single" node with different clipping
00215                                 clip[0] = minR;
00216                                 clip[1] = maxR;
00217                                 children[0] = new BIHNode(rightB,trList,left,right,depth+1, singleDepth +1);
00218                                 children[1] = 0;
00219 
00220                         } else {//generate standard node and recurse
00221                                 clip[0] = maxL;
00222                                 clip[1] = minR;                         
00223                                 children[0] = new BIHNode(leftB,trList,left,i-1,depth+1);
00224                                 children[1] = new BIHNode(rightB,trList,i,right,depth+1);
00225                         }
00226                 }
00227         }
00228         
00229 public:
00230         BIHNode<T>()//empty node
00231         {
00232                 children[0] = children[1] = 0;
00233                 splitAxis = X_AXIS;
00234                 clip[0] = clip[1] = 0;
00235                 leftIndex = -1; rightIndex = -2;
00236         }
00237         
00248         BIHNode<T>(const AABB& bbox, std::vector<T*>* trList, int left, int right, int depth = 0, int singleD = 0)
00249         {
00250                 initNode(bbox, trList, left, right, depth, singleD);
00251         }
00252         
00253         virtual ~BIHNode()
00254         {
00255                 if(children[0] != 0) delete children[0];
00256                 if(children[1] != 0) delete children[1];
00257         }
00258         
00259         
00260         virtual Intersection intersect(Ray& r, const float& tMin, const float& tMax) const
00261         {
00262 #if 0
00263                 r.bihnodes++;//enable this for the BIHTracer, only for debugging purposes
00264 #endif
00265                 
00266                 if(isLeaf()){
00267                         return leafIntersect(r, tMin, tMax);
00268                 }
00269                 //we intersect the ray against both splitting planes in this node
00270                 float dir = r.dir()[splitAxis];
00271                 float org = r.org()[splitAxis];
00272                 float t[2];
00273                 float invdir = r.invDir()[splitAxis];//use precalculated values
00274                 int near = r.dirSign()[splitAxis];//can encode which node is
00275                 int far = 1 - near;//near with the sign of the direction along the axis
00276                 t[0] = (clip[0]-org)*invdir;//calculated the distances
00277                 t[1] = (clip[1]-org)*invdir;
00278                 
00279                 
00280                 if(dir != 0) {//if the direction is 0 invDir will be NaN, just intersect with both in that case
00281                         if(isSingle()){//special single node intersection
00282                                 if(t[near] > tMax || t[far] < tMin)
00283                                         return Intersection();
00284                                 else {//has only one child
00285                                         return children[0]->intersect(
00286                                                         r, std::max(t[near], tMin), std::min(t[far], tMax));
00287                                 }
00288                         }
00289                         if(t[near] < tMin && t[far] > tMax){
00290                                 //ray segment between clip zones
00291                                 return Intersection();
00292                         }
00293                         if(t[near] < tMin){
00294                                 //ray segment only through far node
00295                                 if(t[far] > tMin){//adapt current ray segment
00296                                         Intersection is(children[far]->intersect(r, t[far], tMax));
00297                                         if(is.isValid() && is.getDistance() < r.maxDist())
00298                                                 r.setMaxDist(is.getDistance());//update r.maxDist for valid intersections
00299                                         return is;
00300                                 } else {
00301                                         return children[far]->intersect(r, tMin, tMax);
00302                                 }
00303                         }
00304                         if(t[far] > tMax){
00305                                 //ray segment only through near node
00306                                 if(t[near] < tMax){//adapt current ray segment
00307                                         Intersection is(children[near]->intersect(r, tMin, t[near]));
00308                                         if(is.isValid() && is.getDistance() < r.maxDist())
00309                                                 r.setMaxDist(is.getDistance());
00310                                         return is;
00311                                 } else {
00312                                         return children[near]->intersect(r, tMin, tMax);
00313                                 }
00314                         }
00315                 }
00316                 // ray segment through both nodes       
00317 
00318                 if(isSingle()){//special single case
00319                         return children[0]->intersect(
00320                                         r, std::max(t[near], tMin), std::min(t[far], tMax));
00321                 }
00322                 
00323                 Intersection isN, isF;
00324                 //go through near node
00325                 isN = children[near]->intersect(r, tMin, tMax);
00326 
00327                 if(isN.isValid() && isN.getDistance() < r.maxDist())
00328                         r.setMaxDist(isN.getDistance());
00329         
00330                 // go through far node  
00331                 isF = children[far]->intersect(r, tMin, tMax);
00332                 
00333                 const bool valN = isN.isValid();
00334                 const bool valF = isF.isValid();
00335                 
00336                 //check for the intersection that is the nearest and valid
00337                 if(valN && valF){
00338                         if(isN.getDistance() < isF.getDistance()){
00339                                 return isN;
00340                         }
00341                         return isF;
00342                 }
00343                 if(!(valN || valF)){
00344                         return Intersection();
00345                 }
00346                 if(!valN)
00347                         return isF;
00348                 else
00349                         return isN;
00350         }
00351         
00352         bool isLeaf() const
00353         {
00354                 return children[0] == 0 && children[1] == 0;
00355         }
00356         
00357         bool isSingle() const
00358         {
00359                 return children[0] != 0 && children[1] == 0;
00360         }
00361         
00362         bool isEmpty() const
00363         {
00364                 return traceables == 0 || traceables.empty();
00365         }
00366         
00367         std::vector<T*>* getTraceables()
00368         {
00369                 return traceables;
00370         }
00371         
00372         static BIHSplit getSplitPlane(const AABB& box)
00373         {
00374                 Axis a = box.getMainAxis();
00375                 return BIHSplit(box.getCentroid()[a], a);
00376         }
00377         
00378 };
00379 
00380 }
00381 
00382 #endif /*BIHNODE_HPP_*/
00383 

Generated on Thu Jan 31 19:26:19 2008 for RenderingCompetitionRayTracer by  doxygen 1.5.3