source: GTP/trunk/Lib/Vis/Preprocessing/src/BvHierarchy.cpp @ 1911

Revision 1911, 72.5 KB checked in by mattausch, 17 years ago (diff)

added support for pvs correction (warning: does not yet compile)

Line 
1#include <stack>
2#include <time.h>
3#include <iomanip>
4
5#include "BvHierarchy.h"
6#include "ViewCell.h"
7#include "Plane3.h"
8#include "Mesh.h"
9#include "common.h"
10#include "Environment.h"
11#include "Polygon3.h"
12#include "Ray.h"
13#include "AxisAlignedBox3.h"
14#include "Exporter.h"
15#include "Plane3.h"
16#include "ViewCellsManager.h"
17#include "Beam.h"
18#include "VspTree.h"
19#include "HierarchyManager.h"
20
21
22namespace GtpVisibilityPreprocessor {
23
24
25#define PROBABILIY_IS_BV_VOLUME 1
26#define USE_FIXEDPOINT_T 0
27#define USE_VOLUMES_FOR_HEURISTICS 1
28
29  //int BvhNode::sMailId = 10000; //2147483647;
30  //int BvhNode::sReservedMailboxes = 1;
31
32BvHierarchy *BvHierarchy::BvhSubdivisionCandidate::sBvHierarchy = NULL;
33
34
35/// sorting operator
36inline static bool ilt(Intersectable *obj1, Intersectable *obj2)
37{
38        return obj1->mId < obj2->mId;
39}
40
41
42/// sorting operator
43inline static bool smallerSize(Intersectable *obj1, Intersectable *obj2)
44{
45        return obj1->GetBox().SurfaceArea() < obj2->GetBox().SurfaceArea();
46}
47
48
49
50/***************************************************************/
51/*              class BvhNode implementation                   */
52/***************************************************************/
53
54BvhNode::BvhNode():
55mParent(NULL),
56mTimeStamp(0),
57mRenderCost(-1)
58
59{
60       
61}
62
63BvhNode::BvhNode(const AxisAlignedBox3 &bbox):
64mParent(NULL),
65mBoundingBox(bbox),
66mTimeStamp(0),
67mRenderCost(-1)
68{
69}
70
71
72BvhNode::BvhNode(const AxisAlignedBox3 &bbox, BvhInterior *parent):
73mBoundingBox(bbox),
74mParent(parent),
75mTimeStamp(0),
76mRenderCost(-1)
77{
78}
79
80
81bool BvhNode::IsRoot() const
82{
83        return mParent == NULL;
84}
85
86
87BvhInterior *BvhNode::GetParent()
88{
89        return mParent;
90}
91
92
93void BvhNode::SetParent(BvhInterior *parent)
94{
95        mParent = parent;
96}
97
98
99int BvhNode::GetRandomEdgePoint(Vector3 &point,
100                                                                Vector3 &normal)
101{
102        // get random edge
103        const int idx = Random(12);
104        Vector3 a, b;
105        mBoundingBox.GetEdge(idx, &a, &b);
106       
107        const float w = RandomValue(0.0f, 1.0f);
108
109        point = a * w + b * (1.0f - w);
110
111        // TODO
112        normal = Vector3(0);
113
114        return idx;
115}
116
117
118
119/******************************************************************/
120/*              class BvhInterior implementation                  */
121/******************************************************************/
122
123
124BvhLeaf::BvhLeaf(const AxisAlignedBox3 &bbox):
125BvhNode(bbox),
126mSubdivisionCandidate(NULL),
127mGlList(0)
128{
129  mActiveNode = this;
130}
131
132
133BvhLeaf::BvhLeaf(const AxisAlignedBox3 &bbox, BvhInterior *parent):
134  BvhNode(bbox, parent),
135  mGlList(0)
136 
137{
138        mActiveNode = this;
139}
140
141
142BvhLeaf::BvhLeaf(const AxisAlignedBox3 &bbox,
143                                 BvhInterior *parent,
144                                 const int numObjects):
145  BvhNode(bbox, parent),
146  mGlList(0)
147
148{
149        mObjects.reserve(numObjects);
150        mActiveNode = this;
151}
152
153
154bool BvhLeaf::IsLeaf() const
155{
156        return true;
157}
158
159
160BvhLeaf::~BvhLeaf()
161{
162}
163
164
165void BvhLeaf::CollectObjects(ObjectContainer &objects)
166{
167        ObjectContainer::const_iterator oit, oit_end = mObjects.end();
168        for (oit = mObjects.begin(); oit != oit_end; ++ oit)
169        {
170                objects.push_back(*oit);
171        }
172}
173
174/******************************************************************/
175/*              class BvhInterior implementation                  */
176/******************************************************************/
177
178
179BvhInterior::BvhInterior(const AxisAlignedBox3 &bbox):
180BvhNode(bbox), mFront(NULL), mBack(NULL)
181{
182}
183
184
185BvhInterior::BvhInterior(const AxisAlignedBox3 &bbox, BvhInterior *parent):
186BvhNode(bbox, parent), mFront(NULL), mBack(NULL)
187{
188}
189
190
191void BvhInterior::ReplaceChildLink(BvhNode *oldChild, BvhNode *newChild)
192{
193        if (mBack == oldChild)
194                mBack = newChild;
195        else
196                mFront = newChild;
197}
198
199
200bool BvhInterior::IsLeaf() const
201{
202        return false;
203}
204
205
206BvhInterior::~BvhInterior()
207{
208        DEL_PTR(mFront);
209        DEL_PTR(mBack);
210}
211
212
213void BvhInterior::SetupChildLinks(BvhNode *front, BvhNode *back)
214{
215    mBack = back;
216    mFront = front;
217}
218
219
220void BvhInterior::CollectObjects(ObjectContainer &objects)
221{
222        mFront->CollectObjects(objects);
223        mBack->CollectObjects(objects);
224}
225
226
227/*******************************************************************/
228/*                  class BvHierarchy implementation               */
229/*******************************************************************/
230
231
232BvHierarchy::BvHierarchy():
233mRoot(NULL),
234mTimeStamp(1),
235mIsInitialSubdivision(false)
236{
237        ReadEnvironment();
238        mSubdivisionCandidates = new SortableEntryContainer;
239//      for (int i = 0; i < 4; ++ i)
240//              mSortedObjects[i] = NULL;
241}
242
243
244BvHierarchy::~BvHierarchy()
245{
246        // delete the local subdivision candidates
247        DEL_PTR(mSubdivisionCandidates);
248
249        // delete the presorted objects
250        /*for (int i = 0; i < 4; ++ i)
251        {
252                DEL_PTR(mSortedObjects[i]);
253        }*/
254       
255        // delete the tree
256        DEL_PTR(mRoot);
257}
258
259
260void BvHierarchy::ReadEnvironment()
261{
262        bool randomize = false;
263        Environment::GetSingleton()->GetBoolValue("BvHierarchy.Construction.randomize", randomize);
264         
265        // initialise random generator for heuristics
266        if (randomize)
267                Randomize();
268
269        //////////////////////////////
270        //-- termination criteria for autopartition
271
272        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.maxDepth", mTermMaxDepth);
273        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.maxLeaves", mTermMaxLeaves);
274        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.minObjects", mTermMinObjects);
275        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.minRays", mTermMinRays);
276        Environment::GetSingleton()->GetFloatValue(
277                "BvHierarchy.Termination.minProbability", mTermMinProbability);
278    Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.missTolerance", mTermMissTolerance);
279
280
281        //////////////////////////////
282        //-- max cost ratio for early tree termination
283
284        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Termination.maxCostRatio", mTermMaxCostRatio);
285        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Termination.minGlobalCostRatio",
286                mTermMinGlobalCostRatio);
287        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.globalCostMissTolerance",
288                mTermGlobalCostMissTolerance);
289
290
291        //////////////////////////////
292        //-- factors for subdivision heuristics
293
294        // if only the driving axis is used for splits
295        Environment::GetSingleton()->GetBoolValue("BvHierarchy.splitUseOnlyDrivingAxis", mOnlyDrivingAxis);
296        Environment::GetSingleton()->GetFloatValue("BvHierarchy.maxStaticMemory", mMaxMemory);
297        Environment::GetSingleton()->GetBoolValue("BvHierarchy.useCostHeuristics", mUseCostHeuristics);
298        Environment::GetSingleton()->GetBoolValue("BvHierarchy.useSah", mUseSah);
299
300    char subdivisionStatsLog[100];
301        Environment::GetSingleton()->GetStringValue("BvHierarchy.subdivisionStats", subdivisionStatsLog);
302        mSubdivisionStats.open(subdivisionStatsLog);
303
304        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Construction.renderCostDecreaseWeight", mRenderCostDecreaseWeight);
305        Environment::GetSingleton()->GetBoolValue("BvHierarchy.Construction.useGlobalSorting", mUseGlobalSorting);
306        Environment::GetSingleton()->GetIntValue("BvHierarchy.minRaysForVisibility", mMinRaysForVisibility);
307        Environment::GetSingleton()->GetIntValue("BvHierarchy.maxTests", mMaxTests);
308        Environment::GetSingleton()->GetBoolValue("BvHierarchy.Construction.useInitialSubdivision", mApplyInitialPartition);
309        Environment::GetSingleton()->GetIntValue("BvHierarchy.Construction.Initial.minObjects", mInitialMinObjects);
310        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Construction.Initial.maxAreaRatio", mInitialMaxAreaRatio);
311        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Construction.Initial.minArea", mInitialMinArea);
312
313        //mMemoryConst = (float)(sizeof(VspLeaf) + sizeof(VspViewCell));
314        //mMemoryConst = (float)sizeof(BvhLeaf);
315        mMemoryConst = 16;//(float)sizeof(ObjectContainer);
316
317    mUseBboxAreaForSah = true;
318
319        /////////////
320        //-- debug output
321
322        Debug << "******* Bvh hierarchy options ******** " << endl;
323    Debug << "max depth: " << mTermMaxDepth << endl;
324        Debug << "min probabiliy: " << mTermMinProbability<< endl;
325        Debug << "min objects: " << mTermMinObjects << endl;
326        Debug << "max cost ratio: " << mTermMaxCostRatio << endl;
327        Debug << "miss tolerance: " << mTermMissTolerance << endl;
328        Debug << "max leaves: " << mTermMaxLeaves << endl;
329        Debug << "randomize: " << randomize << endl;
330        Debug << "min global cost ratio: " << mTermMinGlobalCostRatio << endl;
331        Debug << "global cost miss tolerance: " << mTermGlobalCostMissTolerance << endl;
332        Debug << "only driving axis: " << mOnlyDrivingAxis << endl;
333        Debug << "max memory: " << mMaxMemory << endl;
334        Debug << "use cost heuristics: " << mUseCostHeuristics << endl;
335        Debug << "use surface area heuristics: " << mUseSah << endl;
336        Debug << "subdivision stats log: " << subdivisionStatsLog << endl;
337        Debug << "split borders: " << mSplitBorder << endl;
338        Debug << "render cost decrease weight: " << mRenderCostDecreaseWeight << endl;
339        Debug << "use global sort: " << mUseGlobalSorting << endl;
340        Debug << "minimal rays for visibility: " << mMinRaysForVisibility << endl;
341        Debug << "bvh mem const: " << mMemoryConst << endl;
342        Debug << "apply initial partition: " << mApplyInitialPartition << endl;
343        Debug << "min objects: " << mInitialMinObjects << endl;
344        Debug << "max area ratio: " << mInitialMaxAreaRatio << endl;
345        Debug << "min area: " << mInitialMinArea << endl;
346
347        Debug << endl;
348}
349
350
351void BvHierarchy::AssociateObjectsWithLeaf(BvhLeaf *leaf)
352{
353        ObjectContainer::const_iterator oit, oit_end = leaf->mObjects.end();
354
355        for (oit = leaf->mObjects.begin(); oit != oit_end; ++ oit)
356        {
357                (*oit)->mBvhLeaf = leaf;
358        }
359}
360
361
362static int CountRays(const ObjectContainer &objects)
363{
364        int nRays = 0;
365
366        ObjectContainer::const_iterator oit, oit_end = objects.end();
367
368        for (oit = objects.begin(); oit != oit_end; ++ oit)
369        {
370                nRays += (int)(*oit)->GetOrCreateRays()->size();
371        }
372
373        return nRays;
374}
375                                                                       
376
377BvhInterior *BvHierarchy::SubdivideNode(const BvhSubdivisionCandidate &sc,
378                                                                                BvhTraversalData &frontData,
379                                                                                BvhTraversalData &backData)
380{
381        const BvhTraversalData &tData = sc.mParentData;
382        BvhLeaf *leaf = tData.mNode;
383        AxisAlignedBox3 parentBox = leaf->GetBoundingBox();
384
385        // update stats: we have two new leaves
386        mBvhStats.nodes += 2;
387
388        if (tData.mDepth > mBvhStats.maxDepth)
389        {
390                mBvhStats.maxDepth = tData.mDepth;
391        }
392
393        // add the new nodes to the tree
394        BvhInterior *node = new BvhInterior(parentBox, leaf->GetParent());
395       
396
397        //////////////////
398        //-- create front and back leaf
399
400        AxisAlignedBox3 fbox = EvalBoundingBox(sc.mFrontObjects, &parentBox);
401        AxisAlignedBox3 bbox = EvalBoundingBox(sc.mBackObjects, &parentBox);
402
403        BvhLeaf *back = new BvhLeaf(bbox, node, (int)sc.mBackObjects.size());
404        BvhLeaf *front = new BvhLeaf(fbox, node, (int)sc.mFrontObjects.size());
405
406        BvhInterior *parent = leaf->GetParent();
407
408        // replace a link from node's parent
409        if (parent)
410        {
411                parent->ReplaceChildLink(leaf, node);
412                node->SetParent(parent);
413        }
414        else // no parent => this node is the root
415        {
416                mRoot = node;
417        }
418
419        // and setup child links
420        node->SetupChildLinks(front, back);
421
422        ++ mBvhStats.splits;
423
424
425        ////////////////////////////////////////
426        //-- fill  front and back traversal data with the new values
427
428        frontData.mDepth = backData.mDepth = tData.mDepth + 1;
429
430        frontData.mNode = front;
431        backData.mNode = back;
432
433        back->mObjects = sc.mBackObjects;
434        front->mObjects = sc.mFrontObjects;
435
436        // if the number of rays is too low, no assumptions can be made
437        // (=> switch to surface area heuristics?)
438        frontData.mNumRays = CountRays(sc.mFrontObjects);
439        backData.mNumRays = CountRays(sc.mBackObjects);
440
441        AssociateObjectsWithLeaf(back);
442        AssociateObjectsWithLeaf(front);
443   
444#if PROBABILIY_IS_BV_VOLUME
445        // volume of bvh (= probability that this bvh can be seen)
446        frontData.mProbability = fbox.GetVolume();
447        backData.mProbability = bbox.GetVolume();
448#else
449        // compute probability of this node being visible,
450        // i.e., volume of the view cells that can see this node
451        frontData.mProbability = EvalViewCellsVolume(sc.mFrontObjects);
452        backData.mProbability = EvalViewCellsVolume(sc.mBackObjects);
453#endif
454
455    // how often was max cost ratio missed in this branch?
456        frontData.mMaxCostMisses = sc.GetMaxCostMisses();
457        backData.mMaxCostMisses = sc.GetMaxCostMisses();
458       
459        // set the time stamp so the order of traversal can be reconstructed
460        node->SetTimeStamp(mHierarchyManager->mTimeStamp ++);
461               
462        // assign the objects in sorted order
463        if (mUseGlobalSorting)
464        {
465                AssignSortedObjects(sc, frontData, backData);
466        }
467       
468        // return the new interior node
469        return node;
470}
471
472
473BvhNode *BvHierarchy::Subdivide(SplitQueue &tQueue,
474                                                                SubdivisionCandidate *splitCandidate,
475                                                                const bool globalCriteriaMet)
476{
477        BvhSubdivisionCandidate *sc =
478                dynamic_cast<BvhSubdivisionCandidate *>(splitCandidate);
479        BvhTraversalData &tData = sc->mParentData;
480
481        BvhNode *currentNode = tData.mNode;
482
483        if (!LocalTerminationCriteriaMet(tData) && !globalCriteriaMet)
484        {       
485                //////////////
486                //-- continue subdivision
487
488                BvhTraversalData tFrontData;
489                BvhTraversalData tBackData;
490                       
491                // create new interior node and two leaf node
492                currentNode = SubdivideNode(*sc, tFrontData, tBackData);
493       
494                // decrease the weighted average cost of the subdivisoin
495                mTotalCost -= sc->GetRenderCostDecrease();
496                mPvsEntries += sc->GetPvsEntriesIncr();
497
498                // subdivision statistics
499                if (1) PrintSubdivisionStats(*sc);
500
501
502                ///////////////////////////
503                //-- push the new split candidates on the queue
504               
505                BvhSubdivisionCandidate *frontCandidate =
506                                new BvhSubdivisionCandidate(tFrontData);
507                BvhSubdivisionCandidate *backCandidate =
508                                new BvhSubdivisionCandidate(tBackData);
509               
510                EvalSubdivisionCandidate(*frontCandidate);
511                EvalSubdivisionCandidate(*backCandidate);
512       
513                // cross reference
514                tFrontData.mNode->SetSubdivisionCandidate(frontCandidate);
515                tBackData.mNode->SetSubdivisionCandidate(backCandidate);
516
517                //cout << "f: " << frontCandidate->GetPriority() << " b: " << backCandidate->GetPriority() << endl;
518                tQueue.Push(frontCandidate);
519                tQueue.Push(backCandidate);
520        }
521
522        /////////////////////////////////
523        //-- node is a leaf => terminate traversal
524
525        if (currentNode->IsLeaf())
526        {
527                /////////////////////
528                //-- store additional info
529                EvaluateLeafStats(tData);
530       
531                // this leaf is no candidate for splitting anymore
532                // => detach subdivision candidate
533                tData.mNode->SetSubdivisionCandidate(NULL);
534                // detach node so we don't delete it with the traversal data
535                tData.mNode = NULL;
536        }
537       
538        return currentNode;
539}
540
541
542float BvHierarchy::EvalPriority(const BvhSubdivisionCandidate &splitCandidate,
543                                                                const float renderCostDecr,
544                                                                const float oldRenderCost) const
545{
546        float priority;
547
548        if (mIsInitialSubdivision)
549        {
550                priority = (float)-splitCandidate.mParentData.mDepth;
551                return priority;
552        }
553
554        BvhLeaf *leaf = splitCandidate.mParentData.mNode;
555
556        // surface area heuristics is used when there is
557        // no view space subdivision available.
558        // In order to have some prioritized traversal,
559        // we use this formula instead
560        if (mHierarchyManager->GetViewSpaceSubdivisionType() ==
561                HierarchyManager::NO_VIEWSPACE_SUBDIV)
562        {
563                priority = EvalSahCost(leaf);
564        }
565        else
566        {
567                // take render cost of node into account
568                // otherwise danger of being stuck in a local minimum!
569                const float factor = mRenderCostDecreaseWeight;
570
571                priority = factor * renderCostDecr + (1.0f - factor) * oldRenderCost;
572               
573                if (mHierarchyManager->mConsiderMemory)
574                {
575                        priority /= ((float)splitCandidate.GetPvsEntriesIncr() + mMemoryConst);
576                }
577        }
578
579        // hack: don't allow empty splits to be taken
580        if (splitCandidate.mFrontObjects.empty() || splitCandidate.mBackObjects.empty())
581                priority = 0;
582
583        return priority;
584}
585
586
587static float AvgRayContribution(const int pvs, const int nRays)
588{
589        return (float)pvs / ((float)nRays + Limits::Small);
590}
591
592
593void BvHierarchy::EvalSubdivisionCandidate(BvhSubdivisionCandidate &splitCandidate,
594                                                                                   bool computeSplitPlane)
595{
596        if (computeSplitPlane)
597        {
598                const bool sufficientSamples =
599                        splitCandidate.mParentData.mNumRays > mMinRaysForVisibility;
600
601                const bool useVisibiliyBasedHeuristics =
602                                        !mUseSah &&
603                                        (mHierarchyManager->GetViewSpaceSubdivisionType() ==
604                                        HierarchyManager::KD_BASED_VIEWSPACE_SUBDIV) &&
605                                        sufficientSamples;
606
607                // compute best object partition
608                const float ratio =     SelectObjectPartition(splitCandidate.mParentData,
609                                                                                                  splitCandidate.mFrontObjects,
610                                                                                                  splitCandidate.mBackObjects,
611                                                                                                  useVisibiliyBasedHeuristics);
612       
613                // cost ratio violated?
614                const bool maxCostRatioViolated = mTermMaxCostRatio < ratio;
615                const int previousMisses = splitCandidate.mParentData.mMaxCostMisses;
616
617                splitCandidate.SetMaxCostMisses(maxCostRatioViolated ?
618                                                                                previousMisses + 1 : previousMisses);
619        }
620
621        BvhLeaf *leaf = splitCandidate.mParentData.mNode;
622
623        if (USE_AVGRAYCONTRI)
624        {
625                // this leaf is a pvs entry in all the view cells
626                // that see one of the objects.
627                const int pvs = CountViewCells(leaf->mObjects);
628                //const int pvs = (int)leaf->mObjects.size();
629       
630                // avg contribution of a ray to a pvs
631                const float avgRayContri =
632                        AvgRayContribution(pvs, splitCandidate.mParentData.mNumRays);
633
634                // high avg ray contri, the result is influenced by undersampling
635                splitCandidate.SetAvgRayContri(avgRayContri);
636        }
637
638        const float oldProp = EvalViewCellsVolume(leaf->mObjects);
639        const float oldRenderCost = EvalRenderCost(leaf->mObjects);
640
641        // compute global decrease in render cost
642        const float newRenderCost = EvalRenderCost(splitCandidate.mFrontObjects) +
643                                                                EvalRenderCost(splitCandidate.mBackObjects);
644
645        const float renderCostDecr = oldRenderCost - newRenderCost;
646       
647        splitCandidate.SetRenderCostDecrease(renderCostDecr);
648
649        // increase in pvs entries
650        const int pvsEntriesIncr = EvalPvsEntriesIncr(splitCandidate);
651        splitCandidate.SetPvsEntriesIncr(pvsEntriesIncr);
652       
653#ifdef GTP_DEBUG
654        Debug << "old render cost: " << oldRenderCost << endl;
655        Debug << "new render cost: " << newRenderCost << endl;
656        Debug << "render cost decrease: " << renderCostDecr << endl;
657#endif
658
659        float priority = EvalPriority(splitCandidate,
660                                                                  renderCostDecr,
661                                                                  oldRenderCost);
662
663        // compute global decrease in render cost
664        splitCandidate.SetPriority(priority);
665}
666
667
668int BvHierarchy::EvalPvsEntriesIncr(BvhSubdivisionCandidate &splitCandidate) const
669{
670        const int oldPvsSize = CountViewCells(splitCandidate.mParentData.mNode->mObjects);
671       
672        const int fPvsSize = CountViewCells(splitCandidate.mFrontObjects);
673        const int bPvsSize = CountViewCells(splitCandidate.mBackObjects);
674
675        return fPvsSize + bPvsSize - oldPvsSize;
676}
677
678
679inline bool BvHierarchy::LocalTerminationCriteriaMet(const BvhTraversalData &tData) const
680{
681        const bool terminationCriteriaMet =
682                        (0
683                        || ((int)tData.mNode->mObjects.size() <= 1)//mTermMinObjects)
684                        //|| (data.mProbability <= mTermMinProbability)
685                        //|| (data.mNumRays <= mTermMinRays)
686                 );
687
688#ifdef _DEBUG
689        if (terminationCriteriaMet)
690        {
691                cout << "bvh local termination criteria met:" << endl;
692                cout << "objects: " << (int)tData.mNode->mObjects.size() << " " << mTermMinObjects << endl;
693        }
694#endif
695        return terminationCriteriaMet;
696}
697
698
699inline bool BvHierarchy::GlobalTerminationCriteriaMet(const BvhTraversalData &data) const
700{
701        // note: tracking for global cost termination
702        // does not make much sense for interleaved vsp / osp partition
703        // as it is the responsibility of the hierarchy manager
704
705        const bool terminationCriteriaMet =
706                (0
707                || (mBvhStats.Leaves() >= mTermMaxLeaves)
708                //|| (mBvhStats.mGlobalCostMisses >= mTermGlobalCostMissTolerance)
709                //|| mOutOfMemory
710                );
711
712#ifdef GTP_DEBUG
713        if (terminationCriteriaMet)
714        {
715                Debug << "bvh global termination criteria met:" << endl;
716                Debug << "cost misses: " << mBvhStats.mGlobalCostMisses << " " << mTermGlobalCostMissTolerance << endl;
717                Debug << "leaves: " << mBvhStats.Leaves() << " " << mTermMaxLeaves << endl;
718        }
719#endif
720        return terminationCriteriaMet;
721}
722
723
724void BvHierarchy::EvaluateLeafStats(const BvhTraversalData &data)
725{
726        // the node became a leaf -> evaluate stats for leafs
727        BvhLeaf *leaf = data.mNode;
728       
729        ++ mCreatedLeaves;
730
731       
732        if (data.mProbability <= mTermMinProbability)
733        {
734                ++ mBvhStats.minProbabilityNodes;
735        }
736
737        ////////////////////////////////////////////
738        // depth related stuff
739
740        if (data.mDepth < mBvhStats.minDepth)
741        {
742                mBvhStats.minDepth = data.mDepth;
743        }
744
745        if (data.mDepth >= mTermMaxDepth)
746        {
747        ++ mBvhStats.maxDepthNodes;
748        }
749
750        // accumulate depth to compute average depth
751        mBvhStats.accumDepth += data.mDepth;
752
753
754        ////////////////////////////////////////////
755        // objects related stuff
756
757        // note: the sum should alwaysbe total number of objects for bvh
758        mBvhStats.objectRefs += (int)leaf->mObjects.size();
759
760        if ((int)leaf->mObjects.size() <= mTermMinObjects)
761        {
762             ++ mBvhStats.minObjectsNodes;
763        }
764
765        if (leaf->mObjects.empty())
766        {
767                ++ mBvhStats.emptyNodes;
768        }
769
770        if ((int)leaf->mObjects.size() > mBvhStats.maxObjectRefs)
771        {
772                mBvhStats.maxObjectRefs = (int)leaf->mObjects.size();
773        }
774
775        if ((int)leaf->mObjects.size() < mBvhStats.minObjectRefs)
776        {
777                mBvhStats.minObjectRefs = (int)leaf->mObjects.size();
778        }
779
780        ////////////////////////////////////////////
781        // ray related stuff
782
783        // note: this number should always accumulate to the total number of rays
784        mBvhStats.rayRefs += data.mNumRays;
785       
786        if (data.mNumRays <= mTermMinRays)
787        {
788             ++ mBvhStats.minRaysNodes;
789        }
790
791        if (data.mNumRays > mBvhStats.maxRayRefs)
792        {
793                mBvhStats.maxRayRefs = data.mNumRays;
794        }
795
796        if (data.mNumRays < mBvhStats.minRayRefs)
797        {
798                mBvhStats.minRayRefs = data.mNumRays;
799        }
800
801#ifdef _DEBUG
802        cout << "depth: " << data.mDepth << " objects: " << (int)leaf->mObjects.size()
803                 << " rays: " << data.mNumRays << " rays / objects "
804                 << (float)data.mNumRays / (float)leaf->mObjects.size() << endl;
805#endif
806}
807
808
809#if 0
810
811/// compute object boundaries using spatial mid split
812float BvHierarchy::EvalLocalObjectPartition(const BvhTraversalData &tData,
813                                                                                        const int axis,
814                                                                                        ObjectContainer &objectsFront,
815                                                                                        ObjectContainer &objectsBack)
816{
817        const float maxBox = tData.mBoundingBox.Max(axis);
818        const float minBox = tData.mBoundingBox.Min(axis);
819
820        float midPoint = (maxBox + minBox) * 0.5f;
821
822        ObjectContainer::const_iterator oit, oit_end = tData.mNode->mObjects.end();
823       
824        for (oit = tData.mNode->mObjects.begin(); oit != oit_end; ++ oit)
825        {
826                Intersectable *obj = *oit;
827                const AxisAlignedBox3 box = obj->GetBox();
828
829                const float objMid = (box.Max(axis) + box.Min(axis)) * 0.5f;
830
831                // object mailed => belongs to back objects
832                if (objMid < midPoint)
833                {
834                        objectsBack.push_back(obj);
835                }
836                else
837                {
838                        objectsFront.push_back(obj);
839                }
840        }
841
842        const float oldRenderCost = EvalRenderCost(tData.mNode->mObjects);
843        const float newRenderCost = EvalRenderCost(objectsFront) * EvalRenderCost(objectsBack);
844
845        const float ratio = newRenderCost / oldRenderCost;
846        return ratio;
847}
848
849#else
850
851/// compute object partition by getting balanced objects on the left and right side
852float BvHierarchy::EvalLocalObjectPartition(const BvhTraversalData &tData,
853                                                                                        const int axis,
854                                                                                        ObjectContainer &objectsFront,
855                                                                                        ObjectContainer &objectsBack)
856{
857        PrepareLocalSubdivisionCandidates(tData, axis);
858       
859        SortableEntryContainer::const_iterator cit, cit_end = mSubdivisionCandidates->end();
860
861        int i = 0;
862        const int border = (int)tData.mNode->mObjects.size() / 2;
863
864    for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit, ++ i)
865        {
866                Intersectable *obj = (*cit).mObject;
867
868                // object mailed => belongs to back objects
869                if (i < border)
870                {
871                        objectsBack.push_back(obj);
872                }
873                else
874                {
875                        objectsFront.push_back(obj);
876                }
877        }
878
879#if 1
880        const float cost = (tData.mNode->GetBoundingBox().Size().DrivingAxis() == axis) ? -1.0f : 0.0f;
881#else
882        const float oldRenderCost = EvalRenderCost(tData.mNode->mObjects);
883        const float newRenderCost = EvalRenderCost(objectsFront) * EvalRenderCost(objectsBack);
884
885        const float cost = newRenderCost / oldRenderCost;
886#endif
887
888        return cost;
889}
890#endif
891
892#if 1
893
894float BvHierarchy::EvalSah(const BvhTraversalData &tData,
895                                                   const int axis,
896                                                   ObjectContainer &objectsFront,
897                                                   ObjectContainer &objectsBack)
898{
899        // go through the lists, count the number of objects left and right
900        // and evaluate the following cost funcion:
901        // C = ct_div_ci  + (ol + or) / queries
902        PrepareLocalSubdivisionCandidates(tData, axis);
903
904        const float totalRenderCost = EvalAbsCost(tData.mNode->mObjects);
905        float objectsLeft = 0, objectsRight = totalRenderCost;
906 
907        const AxisAlignedBox3 nodeBbox = tData.mNode->GetBoundingBox();
908        const float boxArea = nodeBbox.SurfaceArea();
909
910        float minSum = 1e20f;
911 
912        float minBorder = nodeBbox.Max(axis);
913        float maxBorder = nodeBbox.Min(axis);
914
915        float areaLeft = 0, areaRight = 0;
916
917        SortableEntryContainer::const_iterator currentPos =
918                mSubdivisionCandidates->begin();
919       
920        vector<float> bordersRight;
921
922        // we keep track of both borders of the bounding boxes =>
923        // store the events in descending order
924
925        bordersRight.resize(mSubdivisionCandidates->size());
926
927        SortableEntryContainer::reverse_iterator rcit =
928                mSubdivisionCandidates->rbegin(), rcit_end =
929                mSubdivisionCandidates->rend();
930
931        vector<float>::reverse_iterator rbit = bordersRight.rbegin();
932
933        for (; rcit != rcit_end; ++ rcit, ++ rbit)
934        {
935                Intersectable *obj = (*rcit).mObject;
936                const AxisAlignedBox3 obox = obj->GetBox();
937
938                if (obox.Min(axis) < minBorder)
939                {
940                        minBorder = obox.Min(axis);
941                }
942
943                (*rbit) = minBorder;
944        }
945
946        // temporary surface areas
947        float al = 0;
948        float ar = boxArea;
949
950        vector<float>::const_iterator bit = bordersRight.begin();
951        SortableEntryContainer::const_iterator cit, cit_end = mSubdivisionCandidates->end();
952
953        for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit, ++ bit)
954        {
955                Intersectable *obj = (*cit).mObject;
956
957                const float renderCost = mViewCellsManager->EvalRenderCost(obj);
958               
959                objectsLeft += renderCost;
960                objectsRight -= renderCost;
961
962                const AxisAlignedBox3 obox = obj->GetBox();
963
964                // the borders of the bounding boxes have changed
965                if (obox.Max(axis) > maxBorder)
966                {
967                        maxBorder = obox.Max(axis);
968                }
969
970                minBorder = (*bit);
971
972                AxisAlignedBox3 lbox = nodeBbox;
973                AxisAlignedBox3 rbox = nodeBbox;
974
975                lbox.SetMax(axis, maxBorder);
976                rbox.SetMin(axis, minBorder);
977
978                al = lbox.SurfaceArea();
979                ar = rbox.SurfaceArea();
980
981                const bool noValidSplit = ((objectsLeft <= Limits::Small) || (objectsRight <= Limits::Small));
982                const float sum = noValidSplit ? 1e25 : objectsLeft * al + objectsRight * ar;
983     
984                /*cout << "pos=" << (*cit).mPos << "\t q=(" << objectsLeft << "," << objectsRight <<")\t r=("
985                         << lbox.SurfaceArea() << "," << rbox.SurfaceArea() << ")" << endl;
986                cout << "minborder: " << minBorder << " maxborder: " << maxBorder << endl;
987            cout << "cost= " << sum << endl;*/
988       
989                if (sum < minSum)
990                {       
991                        minSum = sum;
992                        areaLeft = al;
993                        areaRight = ar;
994
995                        // objects belong to left side now
996                        for (; currentPos != (cit + 1); ++ currentPos);
997                }
998        }
999
1000        ////////////
1001        //-- assign object to front and back volume
1002
1003        // belongs to back bv
1004        for (cit = mSubdivisionCandidates->begin(); cit != currentPos; ++ cit)
1005                objectsBack.push_back((*cit).mObject);
1006       
1007        // belongs to front bv
1008        for (cit = currentPos; cit != cit_end; ++ cit)
1009                objectsFront.push_back((*cit).mObject);
1010
1011        float newCost = minSum / boxArea;
1012        float ratio = newCost / totalRenderCost;
1013 
1014#ifdef GTP_DEBUG
1015        cout << "\n\nobjects=(" << (int)objectsBack.size() << "," << (int)objectsFront.size() << " of "
1016                 << (int)tData.mNode->mObjects.size() << ")\t area=("
1017                 << areaLeft << ", " << areaRight << ", " << boxArea << ")" << endl
1018                 << "cost= " << newCost << " oldCost=" << totalRenderCost / boxArea << endl;
1019#endif
1020
1021        return ratio;
1022}
1023
1024#else
1025
1026float BvHierarchy::EvalSah(const BvhTraversalData &tData,
1027                                                   const int axis,
1028                                                   ObjectContainer &objectsFront,
1029                                                   ObjectContainer &objectsBack)
1030{
1031        // go through the lists, count the number of objects left and right
1032        // and evaluate the following cost funcion:
1033        // C = ct_div_ci  + (ol + or) / queries
1034        PrepareLocalSubdivisionCandidates(tData, axis);
1035
1036        const float totalRenderCost = EvalAbsCost(tData.mNode->mObjects);
1037        float objectsLeft = 0, objectsRight = totalRenderCost;
1038 
1039        const AxisAlignedBox3 nodeBbox = tData.mNode->GetBoundingBox();
1040
1041        const float minBox = nodeBbox.Min(axis);
1042        const float maxBox = nodeBbox.Max(axis);
1043        const float boxArea = nodeBbox.SurfaceArea();
1044
1045        float minSum = 1e20f;
1046 
1047        Vector3 minBorder = nodeBbox.Max();
1048        Vector3 maxBorder = nodeBbox.Min();
1049
1050        float areaLeft = 0, areaRight = 0;
1051
1052        SortableEntryContainer::const_iterator currentPos =
1053                mSubdivisionCandidates->begin();
1054       
1055        vector<Vector3> bordersRight;
1056
1057        // we keep track of both borders of the bounding boxes =>
1058        // store the events in descending order
1059        bordersRight.resize(mSubdivisionCandidates->size());
1060
1061        SortableEntryContainer::reverse_iterator rcit =
1062                mSubdivisionCandidates->rbegin(), rcit_end =
1063                mSubdivisionCandidates->rend();
1064
1065        vector<Vector3>::reverse_iterator rbit = bordersRight.rbegin();
1066
1067        for (; rcit != rcit_end; ++ rcit, ++ rbit)
1068        {
1069                Intersectable *obj = (*rcit).mObject;
1070                const AxisAlignedBox3 obox = obj->GetBox();
1071
1072                for (int i = 0; i < 3; ++ i)
1073                {
1074                        if (obox.Min(i) < minBorder[i])
1075                        {
1076                                minBorder[i] = obox.Min(i);
1077                        }
1078                }
1079
1080                (*rbit) = minBorder;
1081        }
1082
1083        // temporary surface areas
1084        float al = 0;
1085        float ar = boxArea;
1086
1087        vector<Vector3>::const_iterator bit = bordersRight.begin();
1088        SortableEntryContainer::const_iterator cit, cit_end =
1089                mSubdivisionCandidates->end();
1090
1091        for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit, ++ bit)
1092        {
1093                Intersectable *obj = (*cit).mObject;
1094
1095                const float renderCost = mViewCellsManager->EvalRenderCost(obj);
1096               
1097                objectsLeft += renderCost;
1098                objectsRight -= renderCost;
1099
1100                const AxisAlignedBox3 obox = obj->GetBox();
1101
1102                AxisAlignedBox3 lbox = nodeBbox;
1103                AxisAlignedBox3 rbox = nodeBbox;
1104       
1105                // the borders of the left bounding box have changed
1106                for (int i = 0; i < 3; ++ i)
1107                {
1108                        if (obox.Max(i) > maxBorder[i])
1109                        {
1110                                maxBorder[i] = obox.Max(i);
1111                        }
1112                }
1113
1114                minBorder = (*bit);
1115
1116                lbox.SetMax(maxBorder);
1117                rbox.SetMin(minBorder);
1118
1119                al = lbox.SurfaceArea();
1120                ar = rbox.SurfaceArea();
1121       
1122                const bool noValidSplit = ((objectsLeft <= Limits::Small) || (objectsRight <= Limits::Small));
1123                const float sum =  noValidSplit ? 1e25 : objectsLeft * al + objectsRight * ar;
1124     
1125                /*cout << "pos=" << (*cit).mPos << "\t q=(" << objectsLeft << "," << objectsRight <<")\t r=("
1126                         << lbox.SurfaceArea() << "," << rbox.SurfaceArea() << ")" << endl;
1127                cout << "minborder: " << minBorder << " maxborder: " << maxBorder << endl;
1128            cout << "cost= " << sum << endl;*/
1129       
1130                if (sum < minSum)
1131                {       
1132                        minSum = sum;
1133                        areaLeft = al;
1134                        areaRight = ar;
1135
1136                        // objects belong to left side now
1137                        for (; currentPos != (cit + 1); ++ currentPos);
1138                }
1139        }
1140
1141        /////////////
1142        //-- assign object to front and back volume
1143
1144        // belongs to back bv
1145        for (cit = mSubdivisionCandidates->begin(); cit != currentPos; ++ cit)
1146                objectsBack.push_back((*cit).mObject);
1147       
1148        // belongs to front bv
1149        for (cit = currentPos; cit != cit_end; ++ cit)
1150                objectsFront.push_back((*cit).mObject);
1151
1152        float newCost = minSum / boxArea;
1153        float ratio = newCost / totalRenderCost;
1154 
1155#ifdef GTP_DEBUG
1156        cout << "\n\nobjects=(" << (int)objectsBack.size() << "," << (int)objectsFront.size() << " of "
1157                 << (int)tData.mNode->mObjects.size() << ")\t area=("
1158                 << areaLeft << ", " << areaRight << ", " << boxArea << ")" << endl
1159                 << "cost= " << newCost << " oldCost=" << totalRenderCost / boxArea << endl;
1160#endif
1161
1162        return ratio;
1163}
1164
1165#endif
1166
1167static bool PrepareOutput(const int axis,
1168                                                  const int leaves,
1169                                                  ofstream &sumStats,
1170                                                  ofstream &vollStats,
1171                                                  ofstream &volrStats)
1172{
1173        if ((axis == 0) && (leaves > 0) && (leaves < 90))
1174        {
1175                char str[64];   
1176                sprintf(str, "tmp/bvh_heur_sum-%04d.log", leaves);
1177                sumStats.open(str);
1178                sprintf(str, "tmp/bvh_heur_voll-%04d.log", leaves);
1179                vollStats.open(str);
1180                sprintf(str, "tmp/bvh_heur_volr-%04d.log", leaves);
1181                volrStats.open(str);
1182        }
1183
1184        return sumStats.is_open() && vollStats.is_open() && volrStats.is_open();
1185}
1186
1187
1188static void PrintHeuristics(const float objectsRight,
1189                                                        const float sum,
1190                                                        const float volLeft,
1191                                                        const float volRight,
1192                                                        const float viewSpaceVol,
1193                                                        ofstream &sumStats,
1194                                                        ofstream &vollStats,
1195                                                        ofstream &volrStats)
1196{
1197        sumStats
1198                << "#Position\n" << objectsRight << endl
1199                << "#Sum\n" << sum / viewSpaceVol << endl
1200                << "#Vol\n" << (volLeft +  volRight) / viewSpaceVol << endl;
1201
1202        vollStats
1203                << "#Position\n" << objectsRight << endl
1204                << "#Vol\n" << volLeft / viewSpaceVol << endl;
1205
1206        volrStats
1207                << "#Position\n" << objectsRight << endl
1208                << "#Vol\n" << volRight / viewSpaceVol << endl;
1209}
1210
1211
1212float BvHierarchy::EvalLocalCostHeuristics(const BvhTraversalData &tData,
1213                                                                                   const int axis,
1214                                                                                   ObjectContainer &objectsFront,
1215                                                                                   ObjectContainer &objectsBack)
1216{
1217        /////////////////////////////////////////////
1218        //-- go through the lists, count the number of objects
1219        //-- left and right and evaluate the cost funcion
1220
1221        // prepare the heuristics by setting mailboxes and counters
1222        const float totalVol = PrepareHeuristics(tData, axis);
1223       
1224        // local helper variables
1225        float volLeft = 0;
1226        float volRight = totalVol;
1227       
1228        const float nTotalObjects = EvalAbsCost(tData.mNode->mObjects);
1229        float nObjectsLeft = 0;
1230        float nObjectsRight = nTotalObjects;
1231
1232        const float viewSpaceVol =
1233                mViewCellsManager->GetViewSpaceBox().GetVolume();
1234
1235        SortableEntryContainer::const_iterator backObjectsStart =
1236                mSubdivisionCandidates->begin();
1237
1238        /////////////////////////////////
1239        //-- the parameters for the current optimum
1240
1241        float volBack = volLeft;
1242        float volFront = volRight;
1243        float newRenderCost = nTotalObjects * totalVol;
1244
1245#ifdef GTP_DEBUG
1246        ofstream sumStats;
1247        ofstream vollStats;
1248        ofstream volrStats;
1249
1250        const bool printStats = PrepareOutput(axis,
1251                                                                                  mBvhStats.Leaves(),
1252                                                                                  sumStats,
1253                                                                                  vollStats,
1254                                                                                  volrStats);
1255#endif
1256
1257        ///////////////////////
1258        //-- the sweep heuristics
1259        //-- traverse through events and find best split plane
1260
1261        SortableEntryContainer::const_iterator cit,
1262                cit_end = cit_end = mSubdivisionCandidates->end();
1263
1264        for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit)
1265        {
1266                Intersectable *object = (*cit).mObject;
1267       
1268                // evaluate change in l and r volume
1269                // voll = view cells that see only left node (i.e., left pvs)
1270                // volr = view cells that see only right node (i.e., right pvs)
1271                EvalHeuristicsContribution(object, volLeft, volRight);
1272
1273                const float rc = mViewCellsManager->EvalRenderCost(object);
1274
1275                nObjectsLeft += rc;
1276                nObjectsRight -= rc;
1277               
1278                // split is only valid if #objects on left and right is not zero
1279                const bool noValidSplit = ((nObjectsLeft <= Limits::Small) ||
1280                                                                   (nObjectsRight <= Limits::Small));
1281
1282                // the heuristics
1283            const float sum = noValidSplit ?
1284                        1e25 : volLeft * (float)nObjectsLeft + volRight * (float)nObjectsRight;
1285
1286#ifdef GTP_DEBUG
1287                if (printStats)
1288                {
1289                        PrintHeuristics(nObjectsRight, sum, volLeft, volRight, viewSpaceVol,
1290                                                        sumStats, vollStats, volrStats);
1291                }
1292#endif
1293
1294                if (sum < newRenderCost)
1295                {
1296                        newRenderCost = sum;
1297
1298                        volBack = volLeft;
1299                        volFront = volRight;
1300
1301                        // objects belongs to left side now
1302                        for (; backObjectsStart != (cit + 1); ++ backObjectsStart);
1303                }
1304        }
1305
1306        ////////////////////////////////////////
1307        //-- assign object to front and back volume
1308
1309        // belongs to back bv
1310        for (cit = mSubdivisionCandidates->begin(); cit != backObjectsStart; ++ cit)
1311        {
1312                objectsBack.push_back((*cit).mObject);
1313        }
1314        // belongs to front bv
1315        for (cit = backObjectsStart; cit != cit_end; ++ cit)
1316        {
1317                objectsFront.push_back((*cit).mObject);
1318        }
1319
1320        // render cost of the old parent
1321        const float oldRenderCost = (float)nTotalObjects * totalVol + Limits::Small;
1322        // the relative cost ratio
1323        const float ratio = newRenderCost / oldRenderCost;
1324
1325#ifdef GTP_DEBUG
1326        Debug << "\n§§§§ bvh eval const decrease §§§§" << endl
1327                  << "back pvs: " << (int)objectsBack.size() << " front pvs: "
1328                  << (int)objectsFront.size() << " total pvs: " << nTotalObjects << endl
1329                  << "back p: " << volBack / viewSpaceVol << " front p "
1330                  << volFront / viewSpaceVol << " p: " << totalVol / viewSpaceVol << endl
1331                  << "old rc: " << oldRenderCost / viewSpaceVol << " new rc: "
1332                  << newRenderCost / viewSpaceVol << endl
1333                  << "render cost decrease: "
1334                  << oldRenderCost / viewSpaceVol - newRenderCost / viewSpaceVol << endl;
1335#endif
1336
1337        return ratio;
1338}
1339
1340
1341void BvHierarchy::PrepareLocalSubdivisionCandidates(const BvhTraversalData &tData,
1342                                                                                                        const int axis)                                                                                 
1343{
1344        //-- insert object queries
1345        ObjectContainer *objects = mUseGlobalSorting ?
1346                tData.mSortedObjects[axis] : &tData.mNode->mObjects;
1347
1348        CreateLocalSubdivisionCandidates(*objects, &mSubdivisionCandidates, !mUseGlobalSorting, axis);
1349}
1350
1351
1352void BvHierarchy::CreateLocalSubdivisionCandidates(const ObjectContainer &objects,
1353                                                                                                  SortableEntryContainer **subdivisionCandidates,
1354                                                                                                  const bool sort,
1355                                                                                                  const int axis)
1356{
1357        (*subdivisionCandidates)->clear();
1358
1359        // compute requested size and look if subdivision candidate has to be recomputed
1360        const int requestedSize = (int)objects.size() * 2;
1361       
1362        // creates a sorted split candidates array
1363        if ((*subdivisionCandidates)->capacity() > 500000 &&
1364                requestedSize < (int)((*subdivisionCandidates)->capacity() / 10) )
1365        {
1366        delete (*subdivisionCandidates);
1367                (*subdivisionCandidates) = new SortableEntryContainer;
1368        }
1369
1370        (*subdivisionCandidates)->reserve(requestedSize);
1371
1372        ObjectContainer::const_iterator oit, oit_end = objects.end();
1373
1374        for (oit = objects.begin(); oit < oit_end; ++ oit)
1375        {
1376                Intersectable *object = *oit;
1377                const AxisAlignedBox3 &box = object->GetBox();
1378                const float midPt = (box.Min(axis) + box.Max(axis)) * 0.5f;
1379
1380                (*subdivisionCandidates)->push_back(SortableEntry(object, midPt));
1381        }
1382
1383        if (sort)
1384        {       // no presorted candidate list
1385                stable_sort((*subdivisionCandidates)->begin(), (*subdivisionCandidates)->end());
1386        }
1387}
1388
1389
1390const BvhStatistics &BvHierarchy::GetStatistics() const
1391{
1392        return mBvhStats;
1393}
1394
1395
1396float BvHierarchy::PrepareHeuristics(const BvhTraversalData &tData,
1397                                                                         const int axis)
1398{       
1399        BvhLeaf *leaf = tData.mNode;
1400        float vol = 0;
1401
1402    // sort so we can use a sweep from right to left
1403        PrepareLocalSubdivisionCandidates(tData, axis);
1404       
1405        // collect and mark the view cells as belonging to front pvs
1406        ViewCellContainer viewCells;
1407
1408        const int numRays = CollectViewCells(tData.mNode->mObjects, viewCells, true, true);
1409        //cout << "number of rays: " << numRays << endl;
1410
1411        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1412        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1413        {
1414#if USE_VOLUMES_FOR_HEURISTICS
1415                const float volIncr = (*vit)->GetVolume();
1416#else
1417                const float volIncr = 1.0f;
1418#endif
1419                vol += volIncr;
1420        }
1421
1422        // we will mail view cells switching to the back side
1423        ViewCell::NewMail();
1424       
1425        return vol;
1426}
1427
1428///////////////////////////////////////////////////////////
1429
1430
1431void BvHierarchy::EvalHeuristicsContribution(Intersectable *obj,
1432                                                                                         float &volLeft,
1433                                                                                         float &volRight)
1434{
1435        // collect all view cells associated with this objects
1436        // (also multiple times, if they are pierced by several rays)
1437        ViewCellContainer viewCells;
1438        const bool useMailboxing = false;
1439
1440        CollectViewCells(obj, viewCells, useMailboxing, false, true);
1441
1442        // classify view cells and compute volume contri accordingly
1443        // possible view cell classifications:
1444        // view cell mailed => view cell can be seen from left child node
1445        // view cell counter > 0 view cell can be seen from right child node
1446        // combined: view cell volume belongs to both nodes
1447        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1448       
1449        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1450        {
1451                // view cells can also be seen from left child node
1452                ViewCell *viewCell = *vit;
1453#if USE_VOLUMES_FOR_HEURISTICS
1454                const float vol = viewCell->GetVolume();
1455#else
1456                const float vol = 1.0f;
1457#endif
1458                if (!viewCell->Mailed())
1459                {
1460                        viewCell->Mail();
1461                        // we now see view cell from both nodes
1462                        // => add volume to left node
1463                        volLeft += vol;
1464                }
1465
1466                // last reference into the right node
1467                if (-- viewCell->mCounter == 0)
1468                {       
1469                        // view cell was previously seen from both nodes  =>
1470                        // remove volume from right node
1471                        volRight -= vol;
1472                }
1473        }
1474}
1475
1476
1477void BvHierarchy::SetViewCellsManager(ViewCellsManager *vcm)
1478{
1479        mViewCellsManager = vcm;
1480}
1481
1482
1483AxisAlignedBox3 BvHierarchy::GetBoundingBox() const
1484{
1485        return mBoundingBox;
1486}
1487
1488
1489float BvHierarchy::SelectObjectPartition(const BvhTraversalData &tData,
1490                                                                                 ObjectContainer &frontObjects,
1491                                                                                 ObjectContainer &backObjects,
1492                                                                                 bool useVisibilityBasedHeuristics)
1493{
1494        if (mIsInitialSubdivision)
1495        {
1496                ApplyInitialSplit(tData, frontObjects, backObjects);
1497                return 0;
1498        }
1499
1500        ObjectContainer nFrontObjects[3];
1501        ObjectContainer nBackObjects[3];
1502        float nCostRatio[3];
1503
1504        int sAxis = 0;
1505        int bestAxis = -1;
1506
1507        if (mOnlyDrivingAxis)
1508        {
1509                const AxisAlignedBox3 box = tData.mNode->GetBoundingBox();
1510                sAxis = box.Size().DrivingAxis();
1511        }
1512
1513        // only use a subset of the rays for visibility based heuristics
1514        if (mUseCostHeuristics && useVisibilityBasedHeuristics)
1515        {
1516                VssRayContainer rays;
1517                // maximal 2 objects share the same ray
1518                rays.reserve(tData.mNumRays * 2);
1519                CollectRays(tData.mNode->mObjects, rays);
1520
1521                const float prop = (float)mMaxTests / (float)tData.mNumRays;
1522
1523                VssRay::NewMail();
1524
1525                VssRayContainer::const_iterator rit, rit_end = rays.end();
1526
1527                int nRays = 0;
1528
1529                for (rit = rays.begin(); rit != rit_end; ++ rit)
1530                {
1531                        if ((mMaxTests >= (int)rays.size()) || (Random(1.0f) < prop))
1532                        {
1533                                (*rit)->Mail();
1534                                ++ nRays;
1535                        }
1536                }
1537        }
1538
1539        ////////////////////////////////////
1540        //-- evaluate split cost for all three axis
1541       
1542        for (int axis = 0; axis < 3; ++ axis)
1543        {
1544                if (!mOnlyDrivingAxis || (axis == sAxis))
1545                {
1546                        if (mUseCostHeuristics)
1547                        {
1548                                //////////////////////////////////
1549                //-- split objects using heuristics
1550                               
1551                                if (useVisibilityBasedHeuristics)
1552                                {
1553                                        ///////////
1554                                        //-- heuristics using objects weighted by view cells volume
1555                                        nCostRatio[axis] =
1556                                                EvalLocalCostHeuristics(tData,
1557                                                                                                axis,
1558                                                                                                nFrontObjects[axis],
1559                                                                                                nBackObjects[axis]);
1560                                }
1561                                else
1562                                {       
1563                                        //////////////////
1564                                        //-- view cells not constructed yet     => use surface area heuristic                   
1565                                        nCostRatio[axis] = EvalSah(tData,
1566                                                                                           axis,
1567                                                                                           nFrontObjects[axis],
1568                                                                                           nBackObjects[axis]);
1569                                }
1570                        }
1571                        else
1572                        {
1573                                //-- split objects using some simple criteria
1574                                nCostRatio[axis] =
1575                                        EvalLocalObjectPartition(tData, axis, nFrontObjects[axis], nBackObjects[axis]);
1576                        }
1577
1578                        // no good results for degenerate axis split
1579                        if (1 &&
1580                                (tData.mNode->GetBoundingBox().Size(axis) < 0.0001))//Limits::Small))
1581                        {
1582                                nCostRatio[axis] += 9999;
1583                        }
1584
1585                        if ((bestAxis == -1) || (nCostRatio[axis] < nCostRatio[bestAxis]))
1586                        {
1587                                bestAxis = axis;
1588                        }
1589                }
1590        }
1591
1592    ////////////////
1593        //-- assign values
1594
1595        frontObjects = nFrontObjects[bestAxis];
1596        backObjects = nBackObjects[bestAxis];
1597
1598        //cout << "val: " << nCostRatio[bestAxis] << " axis: " << bestAxis << endl;
1599        return nCostRatio[bestAxis];
1600}
1601
1602
1603int BvHierarchy::AssociateObjectsWithRays(const VssRayContainer &rays) const
1604{
1605        int nRays = 0;
1606        VssRayContainer::const_iterator rit, rit_end = rays.end();
1607
1608        VssRay::NewMail();
1609
1610    for (rit = rays.begin(); rit != rays.end(); ++ rit)
1611        {
1612                VssRay *ray = (*rit);
1613
1614                if (ray->mTerminationObject)
1615                {
1616                        ray->mTerminationObject->GetOrCreateRays()->push_back(ray);
1617                        if (!ray->Mailed())
1618                        {
1619                                ray->Mail();
1620                                ++ nRays;
1621                        }
1622                }
1623
1624#if COUNT_ORIGIN_OBJECTS
1625
1626                if (ray->mOriginObject)
1627                {
1628                        ray->mOriginObject->GetOrCreateRays()->push_back(ray);
1629
1630                        if (!ray->Mailed())
1631                        {
1632                                ray->Mail();
1633                                ++ nRays;
1634                        }
1635                }
1636#endif
1637        }
1638
1639        return nRays;
1640}
1641
1642
1643void BvHierarchy::PrintSubdivisionStats(const SubdivisionCandidate &sc)
1644{
1645        const float costDecr = sc.GetRenderCostDecrease();     
1646
1647        mSubdivisionStats
1648                        << "#Leaves\n" << mBvhStats.Leaves() << endl
1649                        << "#RenderCostDecrease\n" << costDecr << endl
1650                        << "#TotalRenderCost\n" << mTotalCost << endl
1651                        << "#EntriesInPvs\n" << mPvsEntries << endl;
1652}
1653
1654
1655void BvHierarchy::CollectRays(const ObjectContainer &objects,
1656                                                          VssRayContainer &rays) const
1657{
1658        VssRay::NewMail();
1659        ObjectContainer::const_iterator oit, oit_end = objects.end();
1660
1661        // evaluate reverse pvs and view cell volume on left and right cell
1662        // note: should I take all leaf objects or rather the objects hit by rays?
1663        for (oit = objects.begin(); oit != oit_end; ++ oit)
1664        {
1665                Intersectable *obj = *oit;
1666                VssRayContainer::const_iterator rit, rit_end = obj->GetOrCreateRays()->end();
1667
1668                for (rit = obj->GetOrCreateRays()->begin(); rit < rit_end; ++ rit)
1669                {
1670                        VssRay *ray = (*rit);
1671
1672                        if (!ray->Mailed())
1673                        {
1674                                ray->Mail();
1675                                rays.push_back(ray);
1676                        }
1677                }
1678        }
1679}
1680
1681
1682float BvHierarchy::EvalAbsCost(const ObjectContainer &objects)// const
1683{
1684#if USE_BETTER_RENDERCOST_EST
1685        ObjectContainer::const_iterator oit, oit_end = objects.end();
1686
1687        for (oit = objects.begin(); oit != oit_end; ++ oit)
1688        {
1689                objRenderCost += ViewCellsManager::GetRendercost(*oit);
1690        }
1691#else
1692        return (float)objects.size();
1693#endif
1694}
1695
1696
1697float BvHierarchy::EvalSahCost(BvhLeaf *leaf) const
1698{
1699        ////////////////
1700        //-- surface area heuristics
1701
1702        if (leaf->mObjects.empty())
1703                return 0.0f;
1704
1705        const AxisAlignedBox3 box = GetBoundingBox(leaf);
1706        const float area = box.SurfaceArea();
1707        const float viewSpaceArea = mViewCellsManager->GetViewSpaceBox().SurfaceArea();
1708
1709        return EvalAbsCost(leaf->mObjects) * area / viewSpaceArea;
1710}
1711
1712
1713float BvHierarchy::EvalRenderCost(const ObjectContainer &objects) const
1714{       
1715        ///////////////
1716        //-- render cost heuristics
1717
1718        const float viewSpaceVol = mViewCellsManager->GetViewSpaceBox().GetVolume();
1719
1720        // probability that view point lies in a view cell which sees this node
1721        const float p = EvalViewCellsVolume(objects) / viewSpaceVol;
1722    const float objRenderCost = EvalAbsCost(objects);
1723       
1724        return objRenderCost * p;
1725}
1726
1727
1728AxisAlignedBox3 BvHierarchy::EvalBoundingBox(const ObjectContainer &objects,
1729                                                                                         const AxisAlignedBox3 *parentBox) const
1730{
1731        // if there are no objects in this box, box size is set to parent box size.
1732        // Question: Invalidate box instead?
1733        if (parentBox && objects.empty())
1734                return *parentBox;
1735
1736        AxisAlignedBox3 box;
1737        box.Initialize();
1738
1739        ObjectContainer::const_iterator oit, oit_end = objects.end();
1740
1741        for (oit = objects.begin(); oit != oit_end; ++ oit)
1742        {
1743                Intersectable *obj = *oit;
1744                // grow bounding box to include all objects
1745                box.Include(obj->GetBox());
1746        }
1747
1748        return box;
1749}
1750
1751
1752void BvHierarchy::CollectLeaves(BvhNode *root, vector<BvhLeaf *> &leaves) const
1753{
1754        stack<BvhNode *> nodeStack;
1755        nodeStack.push(root);
1756
1757        while (!nodeStack.empty())
1758        {
1759                BvhNode *node = nodeStack.top();
1760                nodeStack.pop();
1761
1762                if (node->IsLeaf())
1763                {
1764                        BvhLeaf *leaf = (BvhLeaf *)node;
1765                        leaves.push_back(leaf);
1766                }
1767                else
1768                {
1769                        BvhInterior *interior = (BvhInterior *)node;
1770
1771                        nodeStack.push(interior->GetBack());
1772                        nodeStack.push(interior->GetFront());
1773                }
1774        }
1775}
1776
1777
1778AxisAlignedBox3 BvHierarchy::GetBoundingBox(BvhNode *node) const
1779{
1780        return node->GetBoundingBox();
1781}
1782
1783
1784int BvHierarchy::CollectViewCells(const ObjectContainer &objects,
1785                                                                  ViewCellContainer &viewCells,
1786                                                                  const bool setCounter,
1787                                                                  const bool onlyMailedRays) const
1788{
1789        ViewCell::NewMail();
1790        ObjectContainer::const_iterator oit, oit_end = objects.end();
1791
1792        int numRays = 0;
1793        // loop through all object and collect view cell pvs of this node
1794        for (oit = objects.begin(); oit != oit_end; ++ oit)
1795        {
1796                // always use only mailed objects
1797                numRays += CollectViewCells(*oit, viewCells, true, setCounter, onlyMailedRays);
1798        }
1799
1800        return numRays;
1801}
1802
1803
1804int BvHierarchy::CollectViewCells(Intersectable *obj,
1805                                                                  ViewCellContainer &viewCells,
1806                                                                  const bool useMailBoxing,
1807                                                                  const bool setCounter,
1808                                                                  const bool onlyMailedRays) const
1809{
1810        VssRayContainer::const_iterator rit, rit_end = obj->GetOrCreateRays()->end();
1811
1812        int numRays = 0;
1813
1814        for (rit = obj->GetOrCreateRays()->begin(); rit < rit_end; ++ rit)
1815        {
1816                VssRay *ray = (*rit);
1817
1818                if (onlyMailedRays && !ray->Mailed())
1819                {
1820                        continue;
1821                }
1822
1823                ++ numRays;
1824
1825                ViewCellContainer tmpViewCells;
1826                mHierarchyManager->mVspTree->GetViewCells(*ray, tmpViewCells);
1827
1828                // matt: probably slow to allocate memory for view cells every time
1829                ViewCellContainer::const_iterator vit, vit_end = tmpViewCells.end();
1830
1831                for (vit = tmpViewCells.begin(); vit != vit_end; ++ vit)
1832                {
1833                        ViewCell *vc = *vit;
1834
1835                        // store view cells
1836                        if (!useMailBoxing || !vc->Mailed())
1837                        {
1838                                if (useMailBoxing) // => view cell not mailed
1839                                {
1840                                        vc->Mail();
1841                                        if (setCounter)
1842                                        {
1843                                                vc->mCounter = 0;
1844                                        }
1845                                }
1846
1847                                viewCells.push_back(vc);
1848                        }
1849                       
1850                        if (setCounter)
1851                        {
1852                                ++ vc->mCounter;
1853                        }
1854                }
1855        }
1856
1857        return numRays;
1858}
1859
1860
1861int BvHierarchy::CountViewCells(Intersectable *obj) const
1862{
1863        int result = 0;
1864       
1865        VssRayContainer::const_iterator rit, rit_end = obj->GetOrCreateRays()->end();
1866
1867        for (rit = obj->GetOrCreateRays()->begin(); rit < rit_end; ++ rit)
1868        {
1869                VssRay *ray = (*rit);
1870                ViewCellContainer tmpViewCells;
1871       
1872                mHierarchyManager->mVspTree->GetViewCells(*ray, tmpViewCells);
1873               
1874                ViewCellContainer::const_iterator vit, vit_end = tmpViewCells.end();
1875                for (vit = tmpViewCells.begin(); vit != vit_end; ++ vit)
1876                {
1877                        ViewCell *vc = *vit;
1878
1879                        // store view cells
1880                        if (!vc->Mailed())
1881                        {
1882                                vc->Mail();
1883                                ++ result;
1884                        }
1885                }
1886        }
1887
1888        return result;
1889}
1890
1891
1892int BvHierarchy::CountViewCells(const ObjectContainer &objects) const
1893{
1894        int nViewCells = 0;
1895        ViewCell::NewMail();
1896        ObjectContainer::const_iterator oit, oit_end = objects.end();
1897
1898        // loop through all object and collect view cell pvs of this node
1899        for (oit = objects.begin(); oit != oit_end; ++ oit)
1900        {
1901                nViewCells += CountViewCells(*oit);
1902        }
1903
1904        return nViewCells;
1905}
1906
1907
1908void BvHierarchy::CollectDirtyCandidates(BvhSubdivisionCandidate *sc,
1909                                                                                 vector<SubdivisionCandidate *> &dirtyList,
1910                                                                                 const bool onlyUnmailed)
1911{
1912        BvhTraversalData &tData = sc->mParentData;
1913        BvhLeaf *node = tData.mNode;
1914       
1915        ViewCellContainer viewCells;
1916        //ViewCell::NewMail();
1917        int numRays = CollectViewCells(node->mObjects, viewCells, false, false);
1918
1919        if (0) cout << "collected " << (int)viewCells.size() << " dirty candidates" << endl;
1920       
1921        // split candidates handling
1922        // these view cells  are thrown into dirty list
1923        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1924
1925        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1926        {
1927        VspViewCell *vc = dynamic_cast<VspViewCell *>(*vit);
1928                VspLeaf *leaf = vc->mLeaves[0];
1929       
1930                SubdivisionCandidate *candidate = leaf->GetSubdivisionCandidate();
1931               
1932                // is this leaf still a split candidate?
1933                if (candidate && (!onlyUnmailed || !candidate->Mailed()))
1934                {
1935                        candidate->Mail();
1936                        candidate->SetDirty(true);
1937                        dirtyList.push_back(candidate);
1938                }
1939        }
1940}
1941
1942
1943BvhNode *BvHierarchy::GetRoot() const
1944{
1945        return mRoot;
1946}
1947
1948
1949bool BvHierarchy::IsObjectInLeaf(BvhLeaf *leaf, Intersectable *object) const
1950{
1951        ObjectContainer::const_iterator oit =
1952                lower_bound(leaf->mObjects.begin(), leaf->mObjects.end(), object, ilt);
1953                               
1954        // objects sorted by id
1955        if ((oit != leaf->mObjects.end()) && ((*oit)->GetId() == object->GetId()))
1956        {
1957                return true;
1958        }
1959        else
1960        {
1961                return false;
1962        }
1963}
1964
1965
1966BvhLeaf *BvHierarchy::GetLeaf(Intersectable *object, BvhNode *node) const
1967{
1968        // rather use the simple version
1969        if (!object)
1970                return NULL;
1971        return object->mBvhLeaf;
1972       
1973        ///////////////////////////////////////
1974        // start from root of tree
1975       
1976        if (node == NULL)
1977                node = mRoot;
1978       
1979        vector<BvhLeaf *> leaves;
1980
1981        stack<BvhNode *> nodeStack;
1982        nodeStack.push(node);
1983 
1984        BvhLeaf *leaf = NULL;
1985 
1986        while (!nodeStack.empty()) 
1987        {
1988                BvhNode *node = nodeStack.top();
1989                nodeStack.pop();
1990       
1991                if (node->IsLeaf())
1992                {
1993                        leaf = dynamic_cast<BvhLeaf *>(node);
1994
1995                        if (IsObjectInLeaf(leaf, object))
1996                        {
1997                                return leaf;
1998                        }
1999                }
2000                else   
2001                {       
2002                        // find point
2003                        BvhInterior *interior = dynamic_cast<BvhInterior *>(node);
2004       
2005                        if (interior->GetBack()->GetBoundingBox().Includes(object->GetBox()))
2006                        {
2007                                nodeStack.push(interior->GetBack());
2008                        }
2009                       
2010                        // search both sides as we are using bounding volumes
2011                        if (interior->GetFront()->GetBoundingBox().Includes(object->GetBox()))
2012                        {
2013                                nodeStack.push(interior->GetFront());
2014                        }
2015                }
2016        }
2017 
2018        return leaf;
2019}
2020
2021
2022bool BvHierarchy::Export(OUT_STREAM &stream)
2023{
2024        ExportNode(mRoot, stream);
2025
2026        return true;
2027}
2028
2029
2030void BvHierarchy::ExportObjects(BvhLeaf *leaf, OUT_STREAM &stream)
2031{
2032        ObjectContainer::const_iterator oit, oit_end = leaf->mObjects.end();
2033        for (oit = leaf->mObjects.begin(); oit != oit_end; ++ oit)
2034        {
2035                stream << (*oit)->GetId() << " ";
2036        }
2037}
2038
2039
2040void BvHierarchy::ExportNode(BvhNode *node, OUT_STREAM &stream)
2041{
2042        if (node->IsLeaf())
2043        {
2044                BvhLeaf *leaf = dynamic_cast<BvhLeaf *>(node);
2045                const AxisAlignedBox3 box = leaf->GetBoundingBox();
2046                stream << "<Leaf"
2047                           << " min=\"" << box.Min().x << " " << box.Min().y << " " << box.Min().z << "\""
2048                           << " max=\"" << box.Max().x << " " << box.Max().y << " " << box.Max().z << "\""
2049                           << " objects=\"";
2050               
2051                //-- export objects
2052                // tmp matt
2053                if (0) ExportObjects(leaf, stream);
2054               
2055                stream << "\" />" << endl;
2056        }
2057        else
2058        {       
2059                BvhInterior *interior = dynamic_cast<BvhInterior *>(node);
2060                const AxisAlignedBox3 box = interior->GetBoundingBox();
2061
2062                stream << "<Interior"
2063                           << " min=\"" << box.Min().x << " " << box.Min().y << " " << box.Min().z << "\""
2064                           << " max=\"" << box.Max().x << " " << box.Max().y << " " << box.Max().z
2065                           << "\">" << endl;
2066
2067                ExportNode(interior->GetBack(), stream);
2068                ExportNode(interior->GetFront(), stream);
2069
2070                stream << "</Interior>" << endl;
2071        }
2072}
2073
2074
2075float BvHierarchy::EvalViewCellsVolume(const ObjectContainer &objects) const
2076{
2077        float vol = 0;
2078
2079        ViewCellContainer viewCells;
2080       
2081        // we have to account for all view cells that can
2082        // be seen from the objects
2083        int numRays = CollectViewCells(objects, viewCells, false, false);
2084
2085        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
2086
2087        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
2088        {
2089                vol += (*vit)->GetVolume();
2090        }
2091
2092        return vol;
2093}
2094
2095
2096void BvHierarchy::Initialise(const ObjectContainer &objects)
2097{
2098        AxisAlignedBox3 box = EvalBoundingBox(objects);
2099
2100        ///////
2101        //-- create new root
2102
2103        BvhLeaf *bvhleaf = new BvhLeaf(box, NULL, (int)objects.size());
2104        bvhleaf->mObjects = objects;
2105        mRoot = bvhleaf;
2106
2107        // compute bounding box from objects
2108        mBoundingBox = mRoot->GetBoundingBox();
2109
2110        // associate root with current objects
2111        AssociateObjectsWithLeaf(bvhleaf);
2112}
2113
2114
2115/*
2116Mesh *BvHierarchy::MergeLeafToMesh()
2117{
2118        vector<BvhLeaf *> leaves;
2119        CollectLeaves(leaves);
2120
2121        vector<BvhLeaf *>::const_iterator lit, lit_end = leaves.end();
2122
2123        for (lit = leaves.begin(); lit != lit_end; ++ lit)
2124        {
2125                Mesh *mesh = MergeLeafToMesh(*lit);
2126        }
2127}*/
2128
2129
2130void BvHierarchy::PrepareConstruction(SplitQueue &tQueue,
2131                                                                          const VssRayContainer &sampleRays,
2132                                                                          const ObjectContainer &objects)
2133{
2134        ///////////////////////////////////////
2135        //-- we assume that we have objects sorted by their id =>
2136        //-- we don't have to sort them here and an binary search
2137        //-- for identifying if a object is in a leaf.
2138       
2139        mBvhStats.Reset();
2140        mBvhStats.Start();
2141        mBvhStats.nodes = 1;
2142               
2143        // store pointer to this tree
2144        BvhSubdivisionCandidate::sBvHierarchy = this;
2145       
2146        // root and bounding box was already constructed
2147        BvhLeaf *bvhLeaf = dynamic_cast<BvhLeaf *>(mRoot);
2148
2149        // multiply termination criterium for comparison,
2150        // so it can be set between zero and one and
2151        // no division is necessary during traversal
2152
2153#if PROBABILIY_IS_BV_VOLUME
2154        mTermMinProbability *= mBoundingBox.GetVolume();
2155        // probability that bounding volume is seen
2156        const float prop = GetBoundingBox().GetVolume();
2157#else
2158        mTermMinProbability *= mVspTree->GetBoundingBox().GetVolume();
2159        // probability that volume is "seen" from the view cells
2160        const float prop = EvalViewCellsVolume(objects);
2161#endif
2162
2163        // only rays intersecting objects in node are interesting
2164        const int nRays = AssociateObjectsWithRays(sampleRays);
2165        //cout << "using " << nRays << " of " << (int)sampleRays.size() << " rays" << endl;
2166
2167        // create bvh traversal data
2168        BvhTraversalData oData(bvhLeaf, 0, prop, nRays);
2169
2170        // create sorted object lists for the first data
2171        if (mUseGlobalSorting)
2172        {
2173                AssignInitialSortedObjectList(oData, objects);
2174        }
2175       
2176
2177        ///////////////////
2178        //-- add first candidate for object space partition     
2179
2180        BvhSubdivisionCandidate *oSubdivisionCandidate =
2181                new BvhSubdivisionCandidate(oData);
2182
2183        bvhLeaf->SetSubdivisionCandidate(oSubdivisionCandidate);
2184
2185        mTotalCost = EvalRenderCost(objects);
2186        mPvsEntries = CountViewCells(objects);
2187
2188        if (mApplyInitialPartition)
2189        {
2190                vector<SubdivisionCandidate *> candidateContainer;
2191
2192                mIsInitialSubdivision = true;
2193               
2194                // evaluate priority
2195                EvalSubdivisionCandidate(*oSubdivisionCandidate);
2196                PrintSubdivisionStats(*oSubdivisionCandidate);
2197
2198                ApplyInitialSubdivision(oSubdivisionCandidate, candidateContainer);             
2199
2200                mIsInitialSubdivision = false;
2201
2202                vector<SubdivisionCandidate *>::const_iterator cit, cit_end = candidateContainer.end();
2203
2204                for (cit = candidateContainer.begin(); cit != cit_end; ++ cit)
2205                {
2206                        BvhSubdivisionCandidate *sCandidate = dynamic_cast<BvhSubdivisionCandidate *>(*cit);
2207                       
2208                        // reevaluate priority
2209                        EvalSubdivisionCandidate(*sCandidate);
2210                        tQueue.Push(sCandidate);
2211                }
2212        }
2213        else
2214        {
2215                // evaluate priority
2216                EvalSubdivisionCandidate(*oSubdivisionCandidate);
2217                PrintSubdivisionStats(*oSubdivisionCandidate);
2218
2219                tQueue.Push(oSubdivisionCandidate);
2220        }
2221               
2222        cout << "size of initial bv subdivision: " << GetStatistics().Leaves() << endl;
2223}
2224
2225
2226void BvHierarchy::AssignInitialSortedObjectList(BvhTraversalData &tData,
2227                                                                                                const ObjectContainer &objects)
2228{
2229        // we sort the objects as a preprocess so they don't have
2230        // to be sorted for each split
2231        for (int i = 0; i < 3; ++ i)
2232        {
2233                SortableEntryContainer *sortedObjects = new SortableEntryContainer();
2234
2235                CreateLocalSubdivisionCandidates(objects,
2236                                                                             &sortedObjects,
2237                                                                                 true,
2238                                                                                 i);
2239               
2240                // copy list into traversal data list
2241                tData.mSortedObjects[i] = new ObjectContainer();
2242                tData.mSortedObjects[i]->reserve((int)objects.size());
2243
2244                SortableEntryContainer::const_iterator oit, oit_end = sortedObjects->end();
2245
2246                for (oit = sortedObjects->begin(); oit != oit_end; ++ oit)
2247                {
2248                        tData.mSortedObjects[i]->push_back((*oit).mObject);
2249                }
2250
2251                delete sortedObjects;
2252        }
2253
2254        // last sorted list: by size
2255        tData.mSortedObjects[3] = new ObjectContainer();
2256        tData.mSortedObjects[3]->reserve((int)objects.size());
2257
2258        *(tData.mSortedObjects[3]) = objects;
2259        stable_sort(tData.mSortedObjects[3]->begin(), tData.mSortedObjects[3]->end(), smallerSize);
2260}
2261
2262
2263void BvHierarchy::AssignSortedObjects(const BvhSubdivisionCandidate &sc,
2264                                                                          BvhTraversalData &frontData,
2265                                                                          BvhTraversalData &backData)
2266{
2267        Intersectable::NewMail();
2268
2269        // we sorted the objects as a preprocess so they don't have
2270        // to be sorted for each split
2271        ObjectContainer::const_iterator fit, fit_end = sc.mFrontObjects.end();
2272
2273        for (fit = sc.mFrontObjects.begin(); fit != fit_end; ++ fit)
2274        {
2275                (*fit)->Mail();
2276        }
2277
2278        for (int i = 0; i < 4; ++ i)
2279        {
2280                frontData.mSortedObjects[i] = new ObjectContainer();
2281                backData.mSortedObjects[i] = new ObjectContainer();
2282
2283                frontData.mSortedObjects[i]->reserve((int)sc.mFrontObjects.size());
2284                backData.mSortedObjects[i]->reserve((int)sc.mFrontObjects.size());
2285
2286                ObjectContainer::const_iterator oit, oit_end = sc.mParentData.mSortedObjects[i]->end();
2287
2288                for (oit = sc.mParentData.mSortedObjects[i]->begin(); oit != oit_end; ++ oit)
2289                {
2290                        if ((*oit)->Mailed())
2291                        {
2292                                frontData.mSortedObjects[i]->push_back(*oit);
2293                        }
2294                        else
2295                        {
2296                                backData.mSortedObjects[i]->push_back(*oit);
2297                        }
2298                }
2299        }
2300}
2301
2302
2303void BvHierarchy::Reset(SplitQueue &tQueue,
2304                                                const VssRayContainer &sampleRays,
2305                                                const ObjectContainer &objects)
2306{
2307        // reset stats
2308        mBvhStats.Reset();
2309        mBvhStats.Start();
2310        mBvhStats.nodes = 1;
2311
2312        // reset root
2313        DEL_PTR(mRoot);
2314       
2315        BvhLeaf *bvhleaf = new BvhLeaf(mBoundingBox, NULL, (int)objects.size());
2316        bvhleaf->mObjects = objects;
2317        mRoot = bvhleaf;
2318       
2319#if PROBABILIY_IS_BV_VOLUME
2320        mTermMinProbability *= mBoundingBox.GetVolume();
2321        // probability that bounding volume is seen
2322        const float prop = GetBoundingBox().GetVolume();
2323#else
2324        mTermMinProbability *= mVspTree->GetBoundingBox().GetVolume();
2325        // probability that volume is "seen" from the view cells
2326        const float prop = EvalViewCellsVolume(objects);
2327#endif
2328
2329        const int nRays = CountRays(objects);
2330        BvhLeaf *bvhLeaf = dynamic_cast<BvhLeaf *>(mRoot);
2331
2332        // create bvh traversal data
2333        BvhTraversalData oData(bvhLeaf, 0, prop, nRays);
2334
2335        AssignInitialSortedObjectList(oData, objects);
2336       
2337
2338        ///////////////////
2339        //-- add first candidate for object space partition     
2340
2341        BvhSubdivisionCandidate *oSubdivisionCandidate =
2342                new BvhSubdivisionCandidate(oData);
2343
2344        EvalSubdivisionCandidate(*oSubdivisionCandidate);
2345        bvhLeaf->SetSubdivisionCandidate(oSubdivisionCandidate);
2346
2347        const float viewSpaceVol = mViewCellsManager->GetViewSpaceBox().GetVolume();
2348        mTotalCost = (float)objects.size() * prop / viewSpaceVol;
2349
2350        PrintSubdivisionStats(*oSubdivisionCandidate);
2351
2352        tQueue.Push(oSubdivisionCandidate);
2353}
2354
2355
2356void BvhStatistics::Print(ostream &app) const
2357{
2358        app << "=========== BvHierarchy statistics ===============\n";
2359
2360        app << setprecision(4);
2361
2362        app << "#N_CTIME  ( Construction time [s] )\n" << Time() << " \n";
2363
2364        app << "#N_NODES ( Number of nodes )\n" << nodes << "\n";
2365
2366        app << "#N_INTERIORS ( Number of interior nodes )\n" << Interior() << "\n";
2367
2368        app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n";
2369
2370        app << "#AXIS_ALIGNED_SPLITS (number of axis aligned splits)\n" << splits << endl;
2371
2372        app << "#N_MAXCOSTNODES  ( Percentage of leaves with terminated because of max cost ratio )\n"
2373                << maxCostNodes * 100 / (double)Leaves() << endl;
2374
2375        app << "#N_PMINPROBABILITYLEAVES  ( Percentage of leaves with mininum probability )\n"
2376                << minProbabilityNodes * 100 / (double)Leaves() << endl;
2377
2378
2379        //////////////////////////////////////////////////
2380       
2381        app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maximum depth )\n"
2382                <<      maxDepthNodes * 100 / (double)Leaves() << endl;
2383       
2384        app << "#N_PMAXDEPTH ( Maximal reached depth )\n" << maxDepth << endl;
2385
2386        app << "#N_PMINDEPTH ( Minimal reached depth )\n" << minDepth << endl;
2387
2388        app << "#AVGDEPTH ( average depth )\n" << AvgDepth() << endl;
2389
2390       
2391        ////////////////////////////////////////////////////////
2392       
2393        app << "#N_PMINOBJECTSLEAVES  ( Percentage of leaves with mininum objects )\n"
2394                << minObjectsNodes * 100 / (double)Leaves() << endl;
2395
2396        app << "#N_MAXOBJECTREFS  ( Max number of object refs / leaf )\n" << maxObjectRefs << "\n";
2397
2398        app << "#N_MINOBJECTREFS  ( Min number of object refs / leaf )\n" << minObjectRefs << "\n";
2399
2400        app << "#N_EMPTYLEAFS ( Empty leafs )\n" << emptyNodes << "\n";
2401       
2402        app << "#N_PAVGOBJECTSLEAVES  ( average object refs / leaf)\n" << AvgObjectRefs() << endl;
2403
2404
2405        ////////////////////////////////////////////////////////
2406       
2407        app << "#N_PMINRAYSLEAVES  ( Percentage of leaves with mininum rays )\n"
2408                << minRaysNodes * 100 / (double)Leaves() << endl;
2409
2410        app << "#N_MAXRAYREFS  ( Max number of ray refs / leaf )\n" << maxRayRefs << "\n";
2411
2412        app << "#N_MINRAYREFS  ( Min number of ray refs / leaf )\n" << minRayRefs << "\n";
2413       
2414        app << "#N_PAVGRAYLEAVES  ( average ray refs / leaf )\n" << AvgRayRefs() << endl;
2415       
2416        app << "#N_PAVGRAYCONTRIBLEAVES  ( Average ray contribution)\n" <<
2417                rayRefs / (double)objectRefs << endl;
2418
2419        app << "#N_PMAXRAYCONTRIBLEAVES  ( Percentage of leaves with maximal ray contribution )\n"<<
2420                maxRayContriNodes * 100 / (double)Leaves() << endl;
2421
2422        app << "#N_PGLOBALCOSTMISSES ( Global cost misses )\n" << mGlobalCostMisses << endl;
2423
2424        app << "========== END OF BvHierarchy statistics ==========\n";
2425}
2426
2427
2428// TODO: return memory usage in MB
2429float BvHierarchy::GetMemUsage() const
2430{
2431        return (float)(sizeof(BvHierarchy)
2432                                   + mBvhStats.Leaves() * sizeof(BvhLeaf)
2433                                   + mBvhStats.Interior() * sizeof(BvhInterior)
2434                                   ) / float(1024 * 1024);
2435}
2436
2437
2438void BvHierarchy::SetActive(BvhNode *node) const
2439{
2440        vector<BvhLeaf *> leaves;
2441
2442        // sets the pointers to the currently active view cells
2443        CollectLeaves(node, leaves);
2444        vector<BvhLeaf *>::const_iterator lit, lit_end = leaves.end();
2445
2446        for (lit = leaves.begin(); lit != lit_end; ++ lit)
2447        {
2448                (*lit)->SetActiveNode(node);
2449        }
2450}
2451
2452
2453BvhNode *BvHierarchy::SubdivideAndCopy(SplitQueue &tQueue,
2454                                                                           SubdivisionCandidate *splitCandidate)
2455{
2456        BvhSubdivisionCandidate *sc =
2457                dynamic_cast<BvhSubdivisionCandidate *>(splitCandidate);
2458        BvhTraversalData &tData = sc->mParentData;
2459
2460        BvhNode *currentNode = tData.mNode;
2461        BvhNode *oldNode = (BvhNode *)splitCandidate->mEvaluationHack;
2462
2463        if (!oldNode->IsLeaf())
2464        {       
2465                //////////////
2466                //-- continue subdivision
2467
2468                BvhTraversalData tFrontData;
2469                BvhTraversalData tBackData;
2470                       
2471                BvhInterior *oldInterior = dynamic_cast<BvhInterior *>(oldNode);
2472               
2473                sc->mFrontObjects.clear();
2474                sc->mBackObjects.clear();
2475
2476                oldInterior->GetFront()->CollectObjects(sc->mFrontObjects);
2477                oldInterior->GetBack()->CollectObjects(sc->mBackObjects);
2478               
2479                // evaluate the changes in render cost and pvs entries
2480                EvalSubdivisionCandidate(*sc, false);
2481
2482                // create new interior node and two leaf node
2483                currentNode = SubdivideNode(*sc, tFrontData, tBackData);
2484       
2485                //oldNode->mRenderCostDecr += sc->GetRenderCostDecrease();
2486                //oldNode->mPvsEntriesIncr += sc->GetPvsEntriesIncr();
2487               
2488                //oldNode->mRenderCostDecr = sc->GetRenderCostDecrease();
2489                //oldNode->mPvsEntriesIncr = sc->GetPvsEntriesIncr();
2490               
2491                ///////////////////////////
2492                //-- push the new split candidates on the queue
2493               
2494                BvhSubdivisionCandidate *frontCandidate = new BvhSubdivisionCandidate(tFrontData);
2495                BvhSubdivisionCandidate *backCandidate = new BvhSubdivisionCandidate(tBackData);
2496
2497                frontCandidate->SetPriority((float)-oldInterior->GetFront()->GetTimeStamp());
2498                backCandidate->SetPriority((float)-oldInterior->GetBack()->GetTimeStamp());
2499
2500                frontCandidate->mEvaluationHack = oldInterior->GetFront();
2501                backCandidate->mEvaluationHack = oldInterior->GetBack();
2502
2503                // cross reference
2504                tFrontData.mNode->SetSubdivisionCandidate(frontCandidate);
2505                tBackData.mNode->SetSubdivisionCandidate(backCandidate);
2506
2507                //cout << "f: " << frontCandidate->GetPriority() << " b: " << backCandidate->GetPriority() << endl;
2508                tQueue.Push(frontCandidate);
2509                tQueue.Push(backCandidate);
2510        }
2511
2512        /////////////////////////////////
2513        //-- node is a leaf => terminate traversal
2514
2515        if (currentNode->IsLeaf())
2516        {
2517                // this leaf is no candidate for splitting anymore
2518                // => detach subdivision candidate
2519                tData.mNode->SetSubdivisionCandidate(NULL);
2520                // detach node so we don't delete it with the traversal data
2521                tData.mNode = NULL;
2522        }
2523       
2524        return currentNode;
2525}
2526
2527
2528void BvHierarchy::CollectObjects(const AxisAlignedBox3 &box,
2529                                                                 ObjectContainer &objects)
2530{
2531  stack<BvhNode *> nodeStack;
2532 
2533  nodeStack.push(mRoot);
2534
2535  while (!nodeStack.empty()) {
2536        BvhNode *node = nodeStack.top();
2537       
2538        nodeStack.pop();
2539        if (node->IsLeaf()) {
2540          BvhLeaf *leaf = (BvhLeaf *)node;
2541          if (Overlap(box, leaf->GetBoundingBox())) {
2542                Intersectable *object = leaf;
2543                if (!object->Mailed()) {
2544                  object->Mail();
2545                  objects.push_back(object);
2546                }
2547          }
2548        }
2549        else
2550          {
2551                BvhInterior *interior = (BvhInterior *)node;
2552                if (Overlap(box, interior->GetBoundingBox())) {
2553                  bool pushed = false;
2554                  if (!interior->GetFront()->Mailed()) {
2555                        nodeStack.push(interior->GetFront());
2556                        pushed = true;
2557                  }
2558                  if (!interior->GetBack()->Mailed()) {
2559                        nodeStack.push(interior->GetBack());
2560                        pushed = true;
2561                  }
2562                  // avoid traversal of this node in the next query
2563                  if (!pushed)
2564                        interior->Mail();
2565                }
2566          }
2567  }
2568}
2569
2570
2571void BvHierarchy::CreateUniqueObjectIds()
2572{
2573        stack<BvhNode *> nodeStack;
2574        nodeStack.push(mRoot);
2575
2576        int currentId = 0;
2577        while (!nodeStack.empty())
2578        {
2579                BvhNode *node = nodeStack.top();
2580                nodeStack.pop();
2581
2582                node->SetId(currentId ++);
2583
2584                if (!node->IsLeaf())
2585                {
2586                        BvhInterior *interior = (BvhInterior *)node;
2587
2588                        nodeStack.push(interior->GetFront());
2589                        nodeStack.push(interior->GetBack());
2590                }
2591        }
2592}
2593
2594
2595void BvHierarchy::ApplyInitialSubdivision(SubdivisionCandidate *firstCandidate,
2596                                                                                  vector<SubdivisionCandidate *> &candidateContainer)
2597{
2598        SplitQueue tempQueue;
2599        tempQueue.Push(firstCandidate);
2600
2601        while (!tempQueue.Empty())
2602        {
2603                SubdivisionCandidate *candidate = tempQueue.Top();
2604                tempQueue.Pop();
2605
2606                BvhSubdivisionCandidate *bsc =
2607                        dynamic_cast<BvhSubdivisionCandidate *>(candidate);
2608
2609                if (!InitialTerminationCriteriaMet(bsc->mParentData))
2610                {
2611                        const bool globalCriteriaMet = GlobalTerminationCriteriaMet(bsc->mParentData);
2612               
2613                        BvhNode *node = Subdivide(tempQueue, bsc, globalCriteriaMet);
2614
2615                        // not needed anymore
2616                        delete bsc;
2617                }
2618                else // initial preprocessing  finished for this candidate
2619                {
2620                        // add to candidate container
2621                        candidateContainer.push_back(bsc);
2622                }
2623        }
2624}
2625
2626
2627void BvHierarchy::ApplyInitialSplit(const BvhTraversalData &tData,
2628                                                                        ObjectContainer &frontObjects,
2629                                                                        ObjectContainer &backObjects)
2630{
2631        ObjectContainer *objects = tData.mSortedObjects[3];
2632
2633        ObjectContainer::const_iterator oit, oit_end = objects->end();
2634   
2635        float maxAreaDiff = -1.0f;
2636
2637        ObjectContainer::const_iterator backObjectsStart = objects->begin();
2638
2639        for (oit = objects->begin(); oit != (objects->end() - 1); ++ oit)
2640        {
2641                Intersectable *objS = *oit;
2642                Intersectable *objL = *(oit + 1);
2643               
2644                const float areaDiff =
2645                                objL->GetBox().SurfaceArea() - objS->GetBox().SurfaceArea();
2646
2647                if (areaDiff > maxAreaDiff)
2648                {
2649                        maxAreaDiff = areaDiff;
2650                        backObjectsStart = oit + 1;
2651                }
2652        }
2653
2654        // belongs to back bv
2655        for (oit = objects->begin(); oit != backObjectsStart; ++ oit)
2656        {
2657                frontObjects.push_back(*oit);
2658        }
2659
2660        // belongs to front bv
2661        for (oit = backObjectsStart; oit != oit_end; ++ oit)
2662        {
2663                backObjects.push_back(*oit);
2664        }
2665       
2666        cout << "front: " << (int)frontObjects.size() << " back: " << (int)backObjects.size() << " "
2667                 << backObjects.front()->GetBox().SurfaceArea() - frontObjects.back()->GetBox().SurfaceArea() << endl;
2668}
2669
2670
2671inline static float AreaRatio(Intersectable *smallObj, Intersectable *largeObj)
2672{
2673        const float areaSmall = smallObj->GetBox().SurfaceArea();
2674        const float areaLarge = largeObj->GetBox().SurfaceArea();
2675
2676        return areaSmall / (areaLarge - areaSmall + Limits::Small);
2677}
2678
2679
2680bool BvHierarchy::InitialTerminationCriteriaMet(const BvhTraversalData &tData) const
2681{
2682        const bool terminationCriteriaMet =
2683                        (0
2684                    || ((int)tData.mNode->mObjects.size() < mInitialMinObjects)
2685                        || (tData.mNode->mObjects.back()->GetBox().SurfaceArea() < mInitialMinArea)
2686                        || (AreaRatio(tData.mNode->mObjects.front(), tData.mNode->mObjects.back()) > mInitialMaxAreaRatio)
2687                        );
2688
2689        cout << "criteria met: "<< terminationCriteriaMet << "\n"
2690                 << "size: " << (int)tData.mNode->mObjects.size() << " max: " << mInitialMinObjects << endl
2691                 << "ratio: " << AreaRatio(tData.mNode->mObjects.front(), tData.mNode->mObjects.back()) << " max: " << mInitialMaxAreaRatio << endl
2692                 << "area: " << tData.mNode->mObjects.back()->GetBox().SurfaceArea() << " max: " << mInitialMinArea << endl << endl;
2693
2694        return terminationCriteriaMet;
2695}
2696
2697
2698// HACK
2699float BvHierarchy::GetRenderCostIncrementially(BvhNode *node) const
2700{
2701        if (node->mRenderCost < 0)
2702        {
2703                //cout <<"p";
2704                if (node->IsLeaf())
2705                {
2706                        BvhLeaf *leaf = dynamic_cast<BvhLeaf *>(node);
2707                        node->mRenderCost = EvalAbsCost(leaf->mObjects);
2708                }
2709                else
2710                {
2711                        BvhInterior *interior = dynamic_cast<BvhInterior *>(node);
2712               
2713                        node->mRenderCost = GetRenderCostIncrementially(interior->GetFront()) +
2714                                                                GetRenderCostIncrementially(interior->GetBack());
2715                }
2716        }
2717
2718        return node->mRenderCost;
2719}
2720
2721
2722void BvHierarchy::Compress()
2723{
2724}
2725
2726
2727}
Note: See TracBrowser for help on using the repository browser.