src/rcrt/gi/PKDNode.cpp

Go to the documentation of this file.
00001 #include "PKDNode.h"
00002 
00003 using namespace std;
00004 
00005 namespace rcrt{
00006 
00007 PKDNode::PKDNode(std::list<PKDSplitPlane*>* splits, const float& maxRadius, const AABB& bbox )
00008 {
00009         if(splits->size() == 3){
00010                 //generate leaf, there is only one photon left with 3 planes
00011                 //cout << "generating leaf" << endl;
00012                 children[0] = 0;
00013                 children[1] = 0;
00014                 photon = splits->back()->p;
00015                 splitAxis = splits->back()->axis;
00016                 //finally delete the no longer needed splitplanes
00017                 for(list<PKDSplitPlane* >::iterator iter = splits->begin(); iter != splits->end(); ++iter){
00018                         delete (*iter);
00019                 }               
00020                 delete splits;
00021                 return;
00022         }
00023         //getting the best split photon requires evaluation of the cost
00024         //function C(p) = P(V_l)*N_l + P(V_r)*N_r, where P(V_i) = Vol(V_i)/Vol(V)
00025         //As Vol(V) is irrelevant for comparison of C(p1) < C(p2) we can
00026         //safely ignore it, thus P(V_i) = Vol(V_i) => C(p) = V_l * N_l
00027         //We seperate into C_l(p) = V_l*N_l and C_r(p) = V_r*N_r for
00028         //calculation.
00029         const int N = splits->size() / 3;//the list is always a multiple of 3
00030         int N_l[3] = {0,0,0};//number of photons to the left, separate for each axis
00031         int N_r[3] = {N-1,N-1,N-1};//          ''            right(minus the current)
00032         const Point3D& bMin(bbox.getMinP());
00033         const Point3D& bMax(bbox.getMaxP());
00034         PKDSplitPlane* bestSplit = 0;//track the best split
00035         float Cmin = numeric_limits<float>::infinity();
00036         
00037         for(list<PKDSplitPlane* >::const_iterator iter = splits->begin(); iter != splits->end(); ++iter){
00038                 PKDSplitPlane* split = *iter;
00039                 const Axis splitAxis = split->axis;
00040                 const float& pos = split->p->getPos()[splitAxis];
00041                 
00042                 float cost = 0;
00043                 
00044                 if(N_l[splitAxis] != 0){
00045                         //approximating the Volume
00046                         float V_l = 1;
00047                         for(int k = 0; k < 3; k++){
00048                                 if(k == splitAxis)
00049                                         //the volume extends along splitAxis to pos in the max
00050                                         V_l *= (pos-bMin[splitAxis] + 2 * maxRadius);
00051                                 else
00052                                         V_l *= (bMax[k]-bMin[k] + 2 * maxRadius);
00053                         }
00054                         cost += V_l * N_l[splitAxis];
00055                 }
00056                 
00057                 if(N_r[splitAxis] != 0){
00058                         //approximating the Volume
00059                         float V_r = 1;
00060                         for(int k = 0; k < 3; k++){
00061                                 if(k == splitAxis)
00062                                         //the volume extends along splitAxis to pos in the min
00063                                         V_r *= (bMax[splitAxis] - pos + 2 * maxRadius);
00064                                 else
00065                                         V_r *= (bMax[k]-bMin[k] + 2 * maxRadius);
00066                         }
00067                         cost += V_r * N_r[splitAxis];                   
00068                 }
00069                 //update best split and cost if this one is better
00070                 if(cost < Cmin){
00071                         Cmin = cost;
00072                         bestSplit = split;
00073                 }
00074                 
00075                 //update N_l and N_r
00076                 N_l[splitAxis]++;
00077                 N_r[splitAxis]--;
00078         }
00079         
00080         
00081         std::list<PKDSplitPlane* >* leftList = new std::list<PKDSplitPlane* >();
00082         std::list<PKDSplitPlane* >* rightList = new std::list<PKDSplitPlane* >();
00083         
00084         // now that we got us our split plane initialize this node
00085         photon = bestSplit->p;
00086         splitAxis = bestSplit->axis;
00087         
00088         // generate new bounding boxes that don't include this photon
00089         AABB leftBox;
00090         AABB rightBox;
00091         
00092         // and iterate once more over the list to seperate it into two
00093         const float splitValue = photon->getPos()[splitAxis];   
00094         for(list<PKDSplitPlane* >::const_iterator iter = splits->begin(); iter != splits->end(); ++iter){
00095                 PKDSplitPlane* split = *iter;
00096                 //do not include split planes of the photon in this node
00097                 if(split->p == photon)
00098                         continue;
00099                 
00100                 //compare with split on split axis and put in correct list
00101                 if(split->p->getPos()[splitAxis] < splitValue){
00102                         leftList->push_back(split);
00103                         //TODO can do the aabb more efficiently..3/2*N see PKDTree
00104                         leftBox.extend(split->p->getPos());
00105                 }else{
00106                         rightList->push_back(split);
00107                         rightBox.extend(split->p->getPos());
00108                 }
00109         }
00110         //delete unneeded list of splits for this node
00111         delete splits;
00112         
00113         // construct children
00114         if(leftList->size() != 0)
00115                 children[0] = new PKDNode(leftList, maxRadius, leftBox);
00116         else
00117                 children[0] = 0;
00118         
00119         if(rightList->size() != 0)
00120                 children[1] = new PKDNode(rightList, maxRadius, rightBox);
00121         else
00122                 children[1] = 0;
00123 }
00124 
00125 PKDNode::~PKDNode()
00126 {
00127         if(children[0]) delete children[0];
00128         if(children[1]) delete children[1];
00129 }
00130 
00131 bool PKDNode::isLeaf() const
00132 {
00133         return children[0] == 0 && children[1] == 0;
00134 }
00135 
00136 void PKDNode::getKNearest(std::vector<PKDSearchP>* result, const unsigned int& k,
00137                                 const Point3D& pos, const Vec3D& normal, const float& maxRadiusSqr,
00138                                 const float& flat) const
00139 {
00140         const Point3D& pPos(photon->getPos());
00141         const float dx(pPos[0]-pos[0]);
00142         const float dy(pPos[1]-pos[1]);
00143         const float dz(pPos[2]-pos[2]);
00144         float distSqr(dx*dx+dy*dy+dz*dz);
00145         if(flat != 0){
00146                 const float dotP(normal * (pPos - pos));
00147                 const float ellipse(dotP * distSqr * 16 * flat);
00148                 distSqr += ellipse;
00149         }
00150         
00151         float newMaxRadiusSqr = maxRadiusSqr;
00152         if(distSqr < maxRadiusSqr && normal * photon->getDir() < 0){
00153                 // we found a photon that we want to insert
00154                 //TODO only take it into account if it is inside an ellipsoid?
00155                 if(result->size() == k-1){
00156                         //need to make it into a heap now
00157                         result->push_back(PKDSearchP(photon, distSqr));
00158                         make_heap(result->begin(), result->end());
00159                         //size is now k, retaining heap property from now on
00160                         //updating maxRadius, if k Photons found
00161                         newMaxRadiusSqr = result->front().distSqr;
00162                 } 
00163                 else if(result->size() >= k){
00164                         //need to retain heap property here
00165                         //check distance to photon at the front of the queue
00166                         if(distSqr < result->front().distSqr) {
00167                                 //we need to put this photon in, pop the other
00168                                 pop_heap(result->begin(), result->end());
00169                                 result->pop_back();
00170                                 result->push_back(PKDSearchP(photon, distSqr));
00171                                 push_heap(result->begin(), result->end());
00172                         }
00173                         newMaxRadiusSqr = result->front().distSqr;
00174                 } else {
00175                         // well, lets get it in there
00176                         result->push_back(PKDSearchP(photon, distSqr));
00177                         //push_heap(result->begin(),result->end());//for lazy, uncomment
00178                 }
00179                 
00180         }
00181         
00182         if(!isLeaf()){
00183                 const float dist1D = pPos[splitAxis]-pos[splitAxis];
00184                 int near = (dist1D > 0) ? 0 : 1; //which child is nearer?
00185                 //search near subtree first
00186                 if(children[near] != 0)
00187                         children[near]->getKNearest(result, k, pos, normal, newMaxRadiusSqr, flat);
00188                 //search far subtree only if inside our search radius
00189                 if(dist1D*dist1D <= newMaxRadiusSqr)
00190                         if(children[1-near] != 0)
00191                                 children[1-near]->getKNearest(result, k, pos, normal, newMaxRadiusSqr, flat);
00192         }
00193 }
00194 
00195 }

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