source: trunk/VUT/GtpVisibilityPreprocessor/src/KdTree.cpp @ 170

Revision 170, 13.6 KB checked in by bittner, 19 years ago (diff)

mesh kd tree added

Line 
1#include <stack>
2#include <algorithm>
3#include <queue>
4#include "Environment.h"
5#include "Mesh.h"
6#include "KdTree.h"
7
8
9
10
11KdNode::KdNode(KdInterior *parent):mParent(parent)
12{
13  if (parent)
14    mDepth = parent->mDepth+1;
15  else
16    mDepth = 0;
17}
18
19KdTree::KdTree()
20{
21  mRoot = new KdLeaf(NULL, 0);
22  environment->GetIntValue("KdTree.Termination.maxDepth", mTermMaxDepth);
23  environment->GetIntValue("KdTree.Termination.minCost", mTermMinCost);
24  environment->GetFloatValue("KdTree.Termination.maxCostRatio", mMaxCostRatio);
25  environment->GetFloatValue("KdTree.Termination.ct_div_ci", mCt_div_ci);
26  environment->GetFloatValue("KdTree.splitBorder", mSplitBorder);
27
28  environment->GetBoolValue("KdTree.sahUseFaces", mSahUseFaces);
29
30  char splitType[64];
31  environment->GetStringValue("KdTree.splitMethod", splitType);
32 
33  mSplitMethod = SPLIT_SPATIAL_MEDIAN;
34  if (strcmp(splitType, "spatialMedian") == 0)
35    mSplitMethod = SPLIT_SPATIAL_MEDIAN;
36  else
37    if (strcmp(splitType, "objectMedian") == 0)
38      mSplitMethod = SPLIT_OBJECT_MEDIAN;
39    else
40      if (strcmp(splitType, "SAH") == 0)
41        mSplitMethod = SPLIT_SAH;
42      else {
43        cerr<<"Wrong kd split type "<<splitType<<endl;
44        exit(1);
45      }
46  splitCandidates = NULL;
47}
48
49bool
50KdTree::Construct()
51{
52  if (!splitCandidates)
53    splitCandidates = new vector<SortableEntry>;
54
55  // first construct a leaf that will get subdivide
56  KdLeaf *leaf = (KdLeaf *) mRoot;
57
58  mStat.nodes = 1;
59
60  mBox.Initialize();
61 
62  MeshContainer::const_iterator mi;
63  for ( mi = leaf->mObjects.begin();
64        mi != leaf->mObjects.end();
65        mi++) {
66    mBox.Include((*mi)->GetBox());
67  }
68
69  cout <<"KdTree Root Box:"<< mBox<<endl;
70  mRoot = Subdivide(TraversalData(leaf, mBox, 0));
71
72  // remove the allocated array
73  delete splitCandidates;
74
75  return true;
76}
77
78KdNode *
79KdTree::Subdivide(const TraversalData &tdata)
80{
81
82  KdNode *result = NULL;
83
84  priority_queue<TraversalData> tStack;
85  //  stack<STraversalData> tStack;
86 
87  tStack.push(tdata);
88  AxisAlignedBox3 backBox, frontBox;
89
90 
91  while (!tStack.empty()) {
92
93#if 0
94    if ( GetMemUsage() > maxMemory ) {
95      // count statistics on unprocessed leafs
96      while (!tStack.empty()) {
97        EvaluateLeafStats(tStack.top());
98        tStack.pop();
99      }
100      break;
101    }
102#endif
103
104    TraversalData data = tStack.top();
105    tStack.pop();
106   
107    KdNode *node = SubdivideNode((KdLeaf *) data.mNode,
108                                 data.mBox,
109                                 backBox,
110                                 frontBox
111                                 );
112    if (result == NULL)
113      result = node;
114   
115    if (!node->IsLeaf()) {
116
117      KdInterior *interior = (KdInterior *) node;
118      // push the children on the stack
119      tStack.push(TraversalData(interior->mBack, backBox, data.mDepth+1));
120      tStack.push(TraversalData(interior->mFront, frontBox, data.mDepth+1));
121     
122    } else {
123      EvaluateLeafStats(data);
124    }
125  }
126 
127  return result;
128
129}
130
131
132
133bool
134KdTree::TerminationCriteriaMet(const KdLeaf *leaf)
135{
136  //  cerr<<"\n OBJECTS="<<leaf->mObjects.size()<<endl;
137  return
138    (leaf->mObjects.size() <= mTermMinCost) ||
139    (leaf->mDepth >= mTermMaxDepth);
140 
141}
142
143
144int
145KdTree::SelectPlane(KdLeaf *leaf,
146                    const AxisAlignedBox3 &box,
147                    float &position
148                    )
149{
150  int axis = -1;
151 
152  switch (mSplitMethod)
153    {
154    case SPLIT_SPATIAL_MEDIAN: {
155      axis = box.Size().DrivingAxis();
156      position = (box.Min()[axis] + box.Max()[axis])*0.5f;
157      break;
158    }
159    case SPLIT_SAH: {
160      int objectsBack, objectsFront;
161      float costRatio;
162      bool mOnlyDrivingAxis = false;
163      if (mOnlyDrivingAxis) {
164        axis = box.Size().DrivingAxis();
165        costRatio = BestCostRatio(leaf,
166                                  box,
167                                  axis,
168                                  position,
169                                  objectsBack,
170                                  objectsFront);
171      } else {
172        costRatio = MAX_FLOAT;
173        for (int i=0; i < 3; i++) {
174          float p;
175          float r = BestCostRatio(leaf,
176                                  box,
177                                  i,
178                                  p,
179                                  objectsBack,
180                                  objectsFront);
181          if (r < costRatio) {
182            costRatio = r;
183            axis = i;
184            position = p;
185          }
186        }
187      }
188     
189      if (costRatio > mMaxCostRatio) {
190        //      cout<<"Too big cost ratio "<<costRatio<<endl;
191        axis = -1;
192      }
193      break;
194    }
195   
196    }
197  return axis;
198}
199
200KdNode *
201KdTree::SubdivideNode(
202                      KdLeaf *leaf,
203                      const AxisAlignedBox3 &box,
204                      AxisAlignedBox3 &backBBox,
205                      AxisAlignedBox3 &frontBBox
206                      )
207{
208 
209  if (TerminationCriteriaMet(leaf))
210    return leaf;
211 
212  float position;
213 
214  // select subdivision axis
215  int axis = SelectPlane( leaf, box, position );
216
217  if (axis == -1) {
218    return leaf;
219  }
220 
221  mStat.nodes+=2;
222  mStat.splits[axis]++;
223 
224  // add the new nodes to the tree
225  KdInterior *node = new KdInterior(leaf->mParent);
226
227  node->mAxis = axis;
228  node->mPosition = position;
229  node->mBox = box;
230 
231  backBBox = box;
232  frontBBox = box;
233 
234  // first count ray sides
235  int objectsBack = 0;
236  int objectsFront = 0;
237 
238  backBBox.SetMax(axis, position);
239  frontBBox.SetMin(axis, position);
240
241  MeshContainer::const_iterator mi;
242
243  for ( mi = leaf->mObjects.begin();
244        mi != leaf->mObjects.end();
245        mi++) {
246    // determine the side of this ray with respect to the plane
247    AxisAlignedBox3 box = (*mi)->GetBox();
248    if (box.Max(axis) > position )
249      objectsFront++;
250   
251    if (box.Min(axis) < position )
252      objectsBack++;
253  }
254
255 
256  KdLeaf *back = new KdLeaf(node, objectsBack);
257  KdLeaf *front = new KdLeaf(node, objectsFront);
258
259  // replace a link from node's parent
260  if (  leaf->mParent )
261    leaf->mParent->ReplaceChildLink(leaf, node);
262
263  // and setup child links
264  node->SetupChildLinks(back, front);
265 
266  for (mi = leaf->mObjects.begin();
267       mi != leaf->mObjects.end();
268       mi++) {
269    // determine the side of this ray with respect to the plane
270    AxisAlignedBox3 box = (*mi)->GetBox();
271
272    if (box.Max(axis) >= position )
273      front->mObjects.push_back(*mi);
274   
275    if (box.Min(axis) < position )
276      back->mObjects.push_back(*mi);
277   
278    mStat.objectRefs -= leaf->mObjects.size();
279    mStat.objectRefs += objectsBack + objectsFront;
280  }
281 
282  delete leaf;
283  return node;
284}
285
286
287
288void
289KdTreeStatistics::Print(ostream &app) const
290{
291  app << "===== KdTree statistics ===============\n";
292
293  app << "#N_RAYS Number of rays )\n"
294      << rays <<endl;
295  app << "#N_DOMAINS  ( Number of query domains )\n"
296      << queryDomains <<endl;
297 
298  app << "#N_NODES ( Number of nodes )\n" << nodes << "\n";
299
300  app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n";
301
302  app << "#N_SPLITS ( Number of splits in axes x y z dx dy dz \n";
303  for (int i=0; i<7; i++)
304    app << splits[i] <<" ";
305  app <<endl;
306
307  app << "#N_RAYREFS ( Number of rayRefs )\n" <<
308    rayRefs << "\n";
309
310  app << "#N_RAYRAYREFS  ( Number of rayRefs / ray )\n" <<
311    rayRefs/(double)rays << "\n";
312
313  app << "#N_LEAFRAYREFS  ( Number of rayRefs / leaf )\n" <<
314    rayRefs/(double)Leaves() << "\n";
315
316  app << "#N_MAXOBJECTREFS  ( Max number of rayRefs / leaf )\n" <<
317    maxObjectRefs << "\n";
318
319  app << "#N_NONEMPTYRAYREFS  ( Number of rayRefs in nonEmpty leaves / non empty leaf )\n" <<
320    rayRefsNonZeroQuery/(double)(Leaves() - zeroQueryNodes) << "\n";
321
322  app << "#N_LEAFDOMAINREFS  ( Number of query domain Refs / leaf )\n" <<
323    objectRefs/(double)Leaves() << "\n";
324
325  //  app << setprecision(4);
326
327  app << "#N_PEMPTYLEAVES  ( Percentage of leaves with zero query domains )\n"<<
328    zeroQueryNodes*100/(double)Leaves()<<endl;
329
330  app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maxdepth )\n"<<
331    maxDepthNodes*100/(double)Leaves()<<endl;
332
333  app << "#N_PMINCOSTLEAVES  ( Percentage of leaves with minCost )\n"<<
334    minCostNodes*100/(double)Leaves()<<endl;
335
336  app << "#N_ADDED_RAYREFS  (Number of dynamically added ray references )\n"<<
337    addedRayRefs<<endl;
338
339  app << "#N_REMOVED_RAYREFS  (Number of dynamically removed ray references )\n"<<
340    removedRayRefs<<endl;
341
342  //  app << setprecision(4);
343
344  //  app << "#N_CTIME  ( Construction time [s] )\n"
345  //      << Time() << " \n";
346
347  app << "===== END OF KdTree statistics ==========\n";
348
349}
350
351
352
353void
354KdTree::EvaluateLeafStats(const TraversalData &data)
355{
356
357  // the node became a leaf -> evaluate stats for leafs
358  KdLeaf *leaf = (KdLeaf *)data.mNode;
359
360  if (data.mDepth > mTermMaxDepth)
361    mStat.maxDepthNodes++;
362 
363  if ( (int)(leaf->mObjects.size()) < mTermMinCost)
364    mStat.minCostNodes++;
365 
366 
367  if ( (int)(leaf->mObjects.size()) > mStat.maxObjectRefs)
368    mStat.maxObjectRefs = leaf->mObjects.size();
369 
370}
371
372
373
374void
375KdTree::SortSplitCandidates(
376                            KdLeaf *node,
377                            const int axis
378                            )
379{
380  splitCandidates->clear();
381 
382  int requestedSize = 2*node->mObjects.size();
383  // creates a sorted split candidates array
384  if (splitCandidates->capacity() > 500000 &&
385      requestedSize < (int)(splitCandidates->capacity()/10) ) {
386    delete splitCandidates;
387    splitCandidates = new vector<SortableEntry>;
388  }
389 
390  splitCandidates->reserve(requestedSize);
391 
392  // insert all queries
393  for(MeshContainer::const_iterator mi = node->mObjects.begin();
394      mi < node->mObjects.end();
395      mi++) {
396    AxisAlignedBox3 box = (*mi)->GetBox();
397
398    splitCandidates->push_back(SortableEntry(SortableEntry::MESH_MIN,
399                                             box.Min(axis),
400                                             (void *)*mi)
401                               );
402   
403   
404    splitCandidates->push_back(SortableEntry(SortableEntry::MESH_MAX,
405                                             box.Max(axis),
406                                             (void *)*mi)
407                               );
408  }
409 
410  stable_sort(splitCandidates->begin(), splitCandidates->end());
411}
412
413
414float
415KdTree::BestCostRatio(
416                      KdLeaf *node,
417                      const AxisAlignedBox3 &box,
418                      const int axis,
419                      float &position,
420                      int &objectsBack,
421                      int &objectsFront
422                      )
423{
424
425  SortSplitCandidates(node, axis);
426 
427  // go through the lists, count the number of objects left and right
428  // and evaluate the following cost funcion:
429  // C = ct_div_ci  + (ol + or)/queries
430
431  int totalFaces = 0;
432  vector<SortableEntry>::const_iterator ci;
433
434  for(ci = splitCandidates->begin();
435      ci < splitCandidates->end();
436      ci++)
437    if ((*ci).type == SortableEntry::MESH_MIN) {
438      Mesh *mesh = ((MeshInstance *)(*ci).data)->GetMesh();
439      totalFaces += mesh->mFaces.size();
440    }
441
442  int facesLeft = 0;
443  int facesRight = totalFaces;
444
445  int objectsLeft = 0, objectsRight = node->mObjects.size();
446 
447  float minBox = box.Min(axis);
448  float maxBox = box.Max(axis);
449  float boxArea = box.SurfaceArea();
450 
451  float minBand = minBox + mSplitBorder*(maxBox - minBox);
452  float maxBand = minBox + (1.0f - mSplitBorder)*(maxBox - minBox);
453 
454  float minSum = 1e20;
455 
456  for(ci = splitCandidates->begin();
457      ci < splitCandidates->end();
458      ci++) {
459    Mesh *mesh = ((MeshInstance *)(*ci).data)->GetMesh();
460    switch ((*ci).type) {
461    case SortableEntry::MESH_MIN:
462      objectsLeft++;
463      facesLeft += mesh->mFaces.size();
464      break;
465    case SortableEntry::MESH_MAX:
466      objectsRight--;
467      facesRight -= mesh->mFaces.size();
468      break;
469    }
470
471    if ((*ci).value > minBand && (*ci).value < maxBand) {
472      AxisAlignedBox3 lbox = box;
473      AxisAlignedBox3 rbox = box;
474      lbox.SetMax(axis, (*ci).value);
475      rbox.SetMin(axis, (*ci).value);
476
477      float sum;
478      if (mSahUseFaces)
479        sum = facesLeft*lbox.SurfaceArea() + facesRight*rbox.SurfaceArea();
480      else
481        sum = objectsLeft*lbox.SurfaceArea() + objectsRight*rbox.SurfaceArea();
482     
483      //      cout<<"pos="<<(*ci).value<<"\t q=("<<ql<<","<<qr<<")\t r=("<<rl<<","<<rr<<")"<<endl;
484      //      cout<<"cost= "<<sum<<endl;
485     
486      if (sum < minSum) {
487        minSum = sum;
488        position = (*ci).value;
489       
490        objectsBack = objectsLeft;
491        objectsFront = objectsRight;
492      }
493    }
494  }
495 
496  float oldCost = mSahUseFaces ? totalFaces : node->mObjects.size();
497  float newCost = mCt_div_ci + minSum/boxArea;
498  float ratio = newCost/oldCost;
499 
500#if 0
501  cout<<"===================="<<endl;
502  cout<<"costRatio="<<ratio<<" pos="<<position<<" t="<<(position - minBox)/(maxBox - minBox)
503      <<"\t o=("<<objectsBack<<","<<objectsFront<<")"<<endl;
504#endif
505  return ratio;
506}
507
508int
509KdTree::CastRay(
510                Ray &ray
511                )
512{
513  int hits = 0;
514 
515  stack<RayTraversalData> tStack;
516 
517  float maxt = 1e6;
518  float mint = 0;
519
520 
521  if (!mBox.GetMinMaxT(ray, &mint, &maxt))
522    return 0;
523
524  if (mint < 0)
525    mint = 0;
526 
527  maxt += Limits::Threshold;
528 
529  Vector3 entp = ray.Extrap(mint);
530  Vector3 extp = ray.Extrap(maxt);
531 
532  KdNode *node = mRoot;
533  KdNode *farChild;
534  float position;
535  int axis;
536 
537  while (1) {
538    if (!node->IsLeaf()) {
539      KdInterior *in = (KdInterior *) node;
540      position = in->mPosition;
541      axis = in->mAxis;
542
543      if (entp[axis] <= position) {
544        if (extp[axis] <= position) {
545          node = in->mBack;
546          // cases N1,N2,N3,P5,Z2,Z3
547          continue;
548        } else {
549          // case N4
550          node = in->mBack;
551          farChild = in->mFront;
552        }
553      }
554      else {
555        if (position <= extp[axis]) {
556          node = in->mFront;
557          // cases P1,P2,P3,N5,Z1
558          continue;
559        } else {
560          node = in->mFront;
561          farChild = in->mBack;
562          // case P4
563        }
564      }
565      // $$ modification 3.5.2004 - hints from Kamil Ghais
566      // case N4 or P4
567      float tdist = (position - ray.GetLoc(axis)) / ray.GetDir(axis);
568      tStack.push(RayTraversalData(farChild, extp, maxt));
569      extp = ray.GetLoc() + ray.GetDir()*tdist;
570      maxt = tdist;
571    } else {
572      // compute intersection with all objects in this leaf
573      KdLeaf *leaf = (KdLeaf *) node;
574      ray.leaves.push_back(leaf);
575     
576      MeshContainer::const_iterator mi;
577      for ( mi = leaf->mObjects.begin();
578            mi != leaf->mObjects.end();
579            mi++) {
580        MeshInstance *mesh = *mi;
581        if (!mesh->Mailed() ) {
582          mesh->Mail();
583          //ray.meshes.push_back(mesh);
584          hits += mesh->CastRay(ray);
585        }
586      }
587
588      if (hits && ray.GetType() == Ray::LOCAL_RAY)
589        if (ray.intersections[0].mT <= maxt)
590          break;
591     
592      // get the next node from the stack
593      if (tStack.empty())
594        break;
595     
596      entp = extp;
597      mint = maxt;
598      RayTraversalData &s  = tStack.top();
599      node = s.mNode;
600      extp = s.mExitPoint;
601      maxt = s.mMaxT;
602      tStack.pop();
603    }
604  }
605 
606 
607  return hits;
608}
609
Note: See TracBrowser for help on using the repository browser.