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

Revision 1654, 55.1 KB checked in by mattausch, 18 years ago (diff)
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 COUNT_ORIGIN_OBJECTS 0
28
29int BvhNode::sMailId = 10000; //2147483647;
30int 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/***************************************************************/
43/*              class BvhNode implementation                   */
44/***************************************************************/
45
46BvhNode::BvhNode(): mParent(NULL), mMailbox(0)
47{
48}
49
50BvhNode::BvhNode(const AxisAlignedBox3 &bbox):
51mParent(NULL), mBoundingBox(bbox), mMailbox(0)
52{
53}
54
55
56BvhNode::BvhNode(const AxisAlignedBox3 &bbox, BvhInterior *parent):
57mBoundingBox(bbox), mParent(parent), mMailbox(0)
58{
59}
60
61
62bool BvhNode::IsRoot() const
63{
64        return mParent == NULL;
65}
66
67
68BvhInterior *BvhNode::GetParent()
69{
70        return mParent;
71}
72
73
74void BvhNode::SetParent(BvhInterior *parent)
75{
76        mParent = parent;
77}
78
79
80
81/******************************************************************/
82/*              class BvhInterior implementation                  */
83/******************************************************************/
84
85
86BvhLeaf::BvhLeaf(const AxisAlignedBox3 &bbox):
87BvhNode(bbox), mSubdivisionCandidate(NULL)
88{
89}
90
91
92BvhLeaf::BvhLeaf(const AxisAlignedBox3 &bbox, BvhInterior *parent):
93BvhNode(bbox, parent)
94{
95}
96
97
98BvhLeaf::BvhLeaf(const AxisAlignedBox3 &bbox,
99                                 BvhInterior *parent,
100                                 const int numObjects):
101BvhNode(bbox, parent)
102{
103        mObjects.reserve(numObjects);
104}
105
106
107bool BvhLeaf::IsLeaf() const
108{
109        return true;
110}
111
112
113BvhLeaf::~BvhLeaf()
114{
115}
116
117void BvhLeaf::CollectObjects(ObjectContainer &objects)
118{
119        ObjectContainer::const_iterator oit, oit_end = objects.end();
120        for (oit = objects.begin(); oit != oit_end; ++ oit)
121        {
122                objects.push_back(*oit);
123        }
124}
125
126/******************************************************************/
127/*              class BvhInterior implementation                  */
128/******************************************************************/
129
130
131BvhInterior::BvhInterior(const AxisAlignedBox3 &bbox):
132BvhNode(bbox), mFront(NULL), mBack(NULL)
133{
134}
135
136
137BvhInterior::BvhInterior(const AxisAlignedBox3 &bbox, BvhInterior *parent):
138BvhNode(bbox, parent), mFront(NULL), mBack(NULL)
139{
140}
141
142
143void BvhInterior::ReplaceChildLink(BvhNode *oldChild, BvhNode *newChild)
144{
145        if (mBack == oldChild)
146                mBack = newChild;
147        else
148                mFront = newChild;
149}
150
151
152bool BvhInterior::IsLeaf() const
153{
154        return false;
155}
156
157
158BvhInterior::~BvhInterior()
159{
160        DEL_PTR(mFront);
161        DEL_PTR(mBack);
162}
163
164
165void BvhInterior::SetupChildLinks(BvhNode *front, BvhNode *back)
166{
167    mBack = back;
168    mFront = front;
169}
170
171
172void BvhInterior::CollectObjects(ObjectContainer &objects)
173{
174        mFront->CollectObjects(objects);
175        mBack->CollectObjects(objects);
176}
177
178
179/*******************************************************************/
180/*                  class BvHierarchy implementation               */
181/*******************************************************************/
182
183
184BvHierarchy::BvHierarchy():
185mRoot(NULL),
186mTimeStamp(1)
187{
188        ReadEnvironment();
189        mSubdivisionCandidates = new SortableEntryContainer;
190        for (int i = 0; i < 3; ++ i)
191                mSortedObjects[i] = NULL;
192}
193
194
195BvHierarchy::~BvHierarchy()
196{
197        // delete kd intersectables
198        BvhIntersectableMap::iterator it, it_end = mBvhIntersectables.end();
199
200        for (it = mBvhIntersectables.begin(); it != mBvhIntersectables.end(); ++ it)
201        {
202                DEL_PTR((*it).second);
203        }
204
205        DEL_PTR(mSubdivisionCandidates);
206
207        for (int i = 0; i < 3; ++ i)
208        {
209                DEL_PTR(mSortedObjects[i]);
210        }
211        mSubdivisionStats.close();
212}
213
214
215void BvHierarchy::ReadEnvironment()
216{
217        bool randomize = false;
218        Environment::GetSingleton()->GetBoolValue("VspTree.Construction.randomize", randomize);
219        if (randomize)
220                Randomize(); // initialise random generator for heuristics
221
222
223        ////////////////////////////////////
224        //-- termination criteria for autopartition
225
226        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.maxDepth", mTermMaxDepth);
227        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.maxLeaves", mTermMaxLeaves);
228        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.minObjects", mTermMinObjects);
229        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.minRays", mTermMinRays);
230        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Termination.minProbability", mTermMinProbability);
231       
232        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.missTolerance", mTermMissTolerance);
233
234
235        //////////////////////////////
236        //-- max cost ratio for early tree termination
237
238        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Termination.maxCostRatio", mTermMaxCostRatio);
239        Environment::GetSingleton()->GetFloatValue("BvHierarchy.Termination.minGlobalCostRatio",
240                mTermMinGlobalCostRatio);
241        Environment::GetSingleton()->GetIntValue("BvHierarchy.Termination.globalCostMissTolerance",
242                mTermGlobalCostMissTolerance);
243
244
245        //////////////////////////////
246        //-- factors for subdivision heuristics
247
248        // if only the driving axis is used for splits
249        Environment::GetSingleton()->GetBoolValue("BvHierarchy.splitUseOnlyDrivingAxis", mOnlyDrivingAxis);
250        Environment::GetSingleton()->GetFloatValue("BvHierarchy.maxStaticMemory", mMaxMemory);
251        Environment::GetSingleton()->GetBoolValue("BvHierarchy.useCostHeuristics", mUseCostHeuristics);
252        Environment::GetSingleton()->GetBoolValue("BvHierarchy.useSah", mUseSah);
253       
254        char subdivisionStatsLog[100];
255        Environment::GetSingleton()->GetStringValue("BvHierarchy.subdivisionStats", subdivisionStatsLog);
256        mSubdivisionStats.open(subdivisionStatsLog);
257
258        Environment::GetSingleton()->GetFloatValue(
259                "BvHierarchy.Construction.renderCostDecreaseWeight", mRenderCostDecreaseWeight);
260       
261        Environment::GetSingleton()->GetBoolValue("BvHierarchy.Construction.useGlobalSorting", mUseGlobalSorting);
262
263
264        /////////////
265        //-- debug output
266
267        Debug << "******* Bvh hierarchy options ******** " << endl;
268    Debug << "max depth: " << mTermMaxDepth << endl;
269        Debug << "min probabiliy: " << mTermMinProbability<< endl;
270        Debug << "min objects: " << mTermMinObjects << endl;
271        Debug << "max cost ratio: " << mTermMaxCostRatio << endl;
272        Debug << "miss tolerance: " << mTermMissTolerance << endl;
273        Debug << "max leaves: " << mTermMaxLeaves << endl;
274        Debug << "randomize: " << randomize << endl;
275        Debug << "min global cost ratio: " << mTermMinGlobalCostRatio << endl;
276        Debug << "global cost miss tolerance: " << mTermGlobalCostMissTolerance << endl;
277        Debug << "only driving axis: " << mOnlyDrivingAxis << endl;
278        Debug << "max memory: " << mMaxMemory << endl;
279        Debug << "use cost heuristics: " << mUseCostHeuristics << endl;
280        Debug << "subdivision stats log: " << subdivisionStatsLog << endl;
281        Debug << "split borders: " << mSplitBorder << endl;
282        Debug << "render cost decrease weight: " << mRenderCostDecreaseWeight << endl;
283        Debug << "use global sort: " << mUseGlobalSorting << endl;
284        Debug << endl;
285}
286
287
288void BvHierarchy::AssociateObjectsWithLeaf(BvhLeaf *leaf)
289{
290        ObjectContainer::const_iterator oit, oit_end = leaf->mObjects.end();
291        for (oit = leaf->mObjects.begin(); oit != oit_end; ++ oit)
292        {
293                (*oit)->mBvhLeaf = leaf;
294        }
295}
296
297
298static int CountRays(const ObjectContainer &objects)
299{
300        int nRays = 0;
301
302        ObjectContainer::const_iterator oit, oit_end = objects.end();
303
304        for (oit = objects.begin(); oit != oit_end; ++ oit)
305        {
306                nRays += (int)(*oit)->mVssRays.size();
307        }
308
309        return nRays;
310}
311
312
313BvhInterior *BvHierarchy::SubdivideNode(const BvhSubdivisionCandidate &sc,
314                                                                                BvhTraversalData &frontData,
315                                                                                BvhTraversalData &backData)
316{
317        const BvhTraversalData &tData = sc.mParentData;
318        BvhLeaf *leaf = tData.mNode;
319        AxisAlignedBox3 parentBox = leaf->GetBoundingBox();
320
321        // update stats: we have two new leaves
322        mBvhStats.nodes += 2;
323
324        if (tData.mDepth > mBvhStats.maxDepth)
325        {
326                mBvhStats.maxDepth = tData.mDepth;
327        }
328
329        // add the new nodes to the tree
330        BvhInterior *node = new BvhInterior(parentBox, leaf->GetParent());
331       
332
333        //////////////////
334        //-- create front and back leaf
335
336        AxisAlignedBox3 fbox = EvalBoundingBox(sc.mFrontObjects, &parentBox);
337        AxisAlignedBox3 bbox = EvalBoundingBox(sc.mBackObjects, &parentBox);
338
339        BvhLeaf *back =
340                new BvhLeaf(bbox, node, (int)sc.mBackObjects.size());
341        BvhLeaf *front =
342                new BvhLeaf(fbox, node, (int)sc.mFrontObjects.size());
343
344        BvhInterior *parent = leaf->GetParent();
345
346        // replace a link from node's parent
347        if (parent)
348        {
349                parent->ReplaceChildLink(leaf, node);
350                node->SetParent(parent);
351        }
352        else // no parent => this node is the root
353        {
354                mRoot = node;
355        }
356
357        // and setup child links
358        node->SetupChildLinks(front, back);
359
360        ++ mBvhStats.splits;
361
362
363        ////////////////////////////////////////
364        //-- fill  front and back traversal data with the new values
365
366        frontData.mDepth = backData.mDepth = tData.mDepth + 1;
367
368        frontData.mNode = front;
369        backData.mNode = back;
370
371        back->mObjects = sc.mBackObjects;
372        front->mObjects = sc.mFrontObjects;
373
374        // if the number of rays is too low, no assumptions can be made
375        // (=> switch to surface area heuristics?)
376        frontData.mNumRays = CountRays(sc.mFrontObjects);
377        backData.mNumRays = CountRays(sc.mBackObjects);
378
379        AssociateObjectsWithLeaf(back);
380        AssociateObjectsWithLeaf(front);
381   
382#if PROBABILIY_IS_BV_VOLUME
383        // volume of bvh (= probability that this bvh can be seen)
384        frontData.mProbability = fbox.GetVolume();
385        backData.mProbability = bbox.GetVolume();
386#else
387        // compute probability of this node being visible,
388        // i.e., volume of the view cells that can see this node
389        frontData.mProbability = EvalViewCellsVolume(sc.mFrontObjects);
390        backData.mProbability = EvalViewCellsVolume(sc.mBackObjects);
391#endif
392
393    // how often was max cost ratio missed in this branch?
394        frontData.mMaxCostMisses = sc.GetMaxCostMisses();
395        backData.mMaxCostMisses = sc.GetMaxCostMisses();
396       
397        // assign the objects in sorted order
398        if (mUseGlobalSorting)
399        {
400                AssignSortedObjects(sc, frontData, backData);
401        }
402       
403        // return the new interior node
404        return node;
405}
406
407
408BvhNode *BvHierarchy::Subdivide(SplitQueue &tQueue,
409                                                                SubdivisionCandidate *splitCandidate,
410                                                                const bool globalCriteriaMet)
411{
412        BvhSubdivisionCandidate *sc =
413                dynamic_cast<BvhSubdivisionCandidate *>(splitCandidate);
414        BvhTraversalData &tData = sc->mParentData;
415
416        BvhNode *currentNode = tData.mNode;
417
418        if (!LocalTerminationCriteriaMet(tData) && !globalCriteriaMet)
419        {       
420                //////////////
421                //-- continue subdivision
422
423                BvhTraversalData tFrontData;
424                BvhTraversalData tBackData;
425                       
426                // create new interior node and two leaf node
427                currentNode = SubdivideNode(
428                        *sc,
429                        tFrontData,
430                        tBackData);
431       
432                // decrease the weighted average cost of the subdivisoin
433                mTotalCost -= sc->GetRenderCostDecrease();
434
435                // subdivision statistics
436                if (1) PrintSubdivisionStats(*sc);
437
438
439                ///////////////////////////
440                //-- push the new split candidates on the queue
441               
442                BvhSubdivisionCandidate *frontCandidate =
443                        new BvhSubdivisionCandidate(tFrontData);
444                BvhSubdivisionCandidate *backCandidate =
445                        new BvhSubdivisionCandidate(tBackData);
446
447                EvalSubdivisionCandidate(*frontCandidate);
448                EvalSubdivisionCandidate(*backCandidate);
449       
450                // cross reference
451                tFrontData.mNode->SetSubdivisionCandidate(frontCandidate);
452                tBackData.mNode->SetSubdivisionCandidate(backCandidate);
453
454                tQueue.Push(frontCandidate);
455                tQueue.Push(backCandidate);
456        }
457
458        /////////////////////////////////
459        //-- node is a leaf => terminate traversal
460
461        if (currentNode->IsLeaf())
462        {
463                //////////////////////////////////////
464                //-- store additional info
465                EvaluateLeafStats(tData);
466       
467                const bool mStoreRays = true;
468                if (mStoreRays)
469                {
470                        BvhLeaf *leaf = dynamic_cast<BvhLeaf *>(currentNode);
471                        CollectRays(leaf->mObjects, leaf->mVssRays);
472                }
473               
474                //////////////////////////////////////
475               
476                // this leaf is no candidate for splitting anymore
477                // => detach subdivision candidate
478                tData.mNode->SetSubdivisionCandidate(NULL);
479                // detach node so we don't delete it with the traversal data
480                tData.mNode = NULL;
481        }
482       
483        return currentNode;
484}
485
486
487void BvHierarchy::EvalSubdivisionCandidate(BvhSubdivisionCandidate &splitCandidate)
488{
489        // compute best object partition
490        const float ratio =     SelectObjectPartition(
491                                                        splitCandidate.mParentData,
492                                                        splitCandidate.mFrontObjects,
493                                                        splitCandidate.mBackObjects);
494       
495        BvhLeaf *leaf = splitCandidate.mParentData.mNode;
496
497        // cost ratio violated?
498        const bool maxCostRatioViolated = mTermMaxCostRatio < ratio;
499
500        splitCandidate.SetMaxCostMisses(maxCostRatioViolated ?
501                splitCandidate.mParentData.mMaxCostMisses + 1 :
502                splitCandidate.mParentData.mMaxCostMisses);
503
504        const float oldProp = EvalViewCellsVolume(leaf->mObjects);
505        const float oldRenderCost = EvalRenderCost(leaf->mObjects);
506               
507        // compute global decrease in render cost
508        const float newRenderCost =
509                EvalRenderCost(splitCandidate.mFrontObjects) +
510                EvalRenderCost(splitCandidate.mBackObjects);
511
512        const float renderCostDecr = oldRenderCost - newRenderCost;
513       
514#ifdef _DEBUG
515        Debug << "old render cost: " << oldRenderCost << endl;
516        Debug << "new render cost: " << newRenderCost << endl;
517        Debug << "render cost decrease: " << renderCostDecr << endl;
518#endif
519
520#if 1
521        float priority;
522       
523        if (mHierarchyManager->GetViewSpaceSubdivisionType() ==
524                HierarchyManager::NO_VIEWSPACE_SUBDIV)
525        {
526                ////////////////
527                //-- surface area heuristics
528
529        //      if (objects.empty())
530        //              return 0.0f;
531
532                const AxisAlignedBox3 box = EvalBoundingBox(leaf->mObjects);
533                const float area = box.SurfaceArea();
534                const float viewSpaceArea = mViewCellsManager->GetViewSpaceBox().SurfaceArea();
535
536                priority = (float)leaf->mObjects.size() * area / viewSpaceArea;
537        }
538        else
539        {
540                // take render cost of node into account
541                // otherwise danger of being stuck in a local minimum!!
542                priority = mRenderCostDecreaseWeight * renderCostDecr +
543                                   (1.0f - mRenderCostDecreaseWeight) * oldRenderCost;
544        }
545
546#else
547        const float priority = (float)-splitCandidate.mParentData.mDepth;
548#endif
549
550        splitCandidate.SetRenderCostDecrease(renderCostDecr);
551
552        // increase in pvs entries
553        const int pvsEntriesIncr = EvalPvsEntriesIncr(splitCandidate);
554        splitCandidate.SetPvsEntriesIncr(pvsEntriesIncr);
555
556        // compute global decrease in render cost
557        splitCandidate.SetPriority(priority);
558}
559
560
561float BvHierarchy::EvalPriority(const BvhSubdivisionCandidate &splitCandidate) const
562{
563        BvhLeaf *leaf = splitCandidate.mParentData.mNode;
564
565        const float oldRenderCost = EvalRenderCost(leaf->mObjects);
566
567        // compute global decrease in render cost
568        const float newRenderCost =
569                EvalRenderCost(splitCandidate.mFrontObjects) +
570                EvalRenderCost(splitCandidate.mBackObjects);
571
572        const float renderCostDecr = oldRenderCost - newRenderCost;
573
574#ifdef _DEBUG
575        Debug << "old render cost: " << oldRenderCost << endl;
576        Debug << "new render cost: " << newRenderCost << endl;
577        Debug << "render cost decrease: " << renderCostDecr << endl;
578#endif
579
580#if 1
581        // take render cost of node into account
582        // otherwise danger of being stuck in a local minimum!!
583        const float factor = mRenderCostDecreaseWeight;
584        const float priority = factor * renderCostDecr + (1.0f - factor) * oldRenderCost;
585#else
586        const float priority = (float)-splitCandidate.mParentData.mDepth;
587#endif
588
589        return priority;
590}
591
592
593int BvHierarchy::EvalPvsEntriesIncr(BvhSubdivisionCandidate &splitCandidate) const
594{
595        const int oldPvsSize = CountViewCells(splitCandidate.mParentData.mNode->mObjects);
596       
597        const int fPvsSize = CountViewCells(splitCandidate.mFrontObjects);
598        const int bPvsSize = CountViewCells(splitCandidate.mBackObjects);
599
600        return fPvsSize + bPvsSize - oldPvsSize;
601}
602
603
604inline bool BvHierarchy::LocalTerminationCriteriaMet(const BvhTraversalData &data) const
605{
606        return ( 0
607                        || ((int)data.mNode->mObjects.size() <= mTermMinObjects)
608                        //|| (data.mProbability <= mTermMinProbability)
609                        //|| (data.mNumRays <= mTermMinRays)
610                 );
611}
612
613
614inline bool BvHierarchy::GlobalTerminationCriteriaMet(const BvhTraversalData &data) const
615{
616        // note: tracking for global cost termination
617        // does not make much sense for interleaved vsp / osp partition
618        // as it is the responsibility of the hierarchy manager
619
620        const bool terminationCriteriaMet =
621                (0
622                || (mBvhStats.Leaves() >= mTermMaxLeaves)
623                //|| (mBvhStats.mGlobalCostMisses >= mTermGlobalCostMissTolerance)
624                //|| mOutOfMemory
625                );
626
627#ifdef _DEBUG
628        if (terminationCriteriaMet)
629        {
630                Debug << "bvh global termination criteria met:" << endl;
631                Debug << "cost misses: " << mBvhStats.mGlobalCostMisses << " " << mTermGlobalCostMissTolerance << endl;
632                Debug << "leaves: " << mBvhStats.Leaves() << " " << mTermMaxLeaves << endl;
633        }
634#endif
635        return terminationCriteriaMet;
636}
637
638
639void BvHierarchy::EvaluateLeafStats(const BvhTraversalData &data)
640{
641        // the node became a leaf -> evaluate stats for leafs
642        BvhLeaf *leaf = data.mNode;
643       
644        ++ mCreatedLeaves;
645
646       
647        if (data.mProbability <= mTermMinProbability)
648        {
649                ++ mBvhStats.minProbabilityNodes;
650        }
651
652        ////////////////////////////////////////////
653        // depth related stuff
654
655        if (data.mDepth < mBvhStats.minDepth)
656        {
657                mBvhStats.minDepth = data.mDepth;
658        }
659
660        if (data.mDepth >= mTermMaxDepth)
661        {
662        ++ mBvhStats.maxDepthNodes;
663        }
664
665        // accumulate depth to compute average depth
666        mBvhStats.accumDepth += data.mDepth;
667
668
669        ////////////////////////////////////////////
670        // objects related stuff
671
672        // note: this number should always accumulate to the total number of objects
673        mBvhStats.objectRefs += (int)leaf->mObjects.size();
674
675        if ((int)leaf->mObjects.size() <= mTermMinObjects)
676        {
677             ++ mBvhStats.minObjectsNodes;
678        }
679
680        if (leaf->mObjects.empty())
681        {
682                ++ mBvhStats.emptyNodes;
683        }
684
685        if ((int)leaf->mObjects.size() > mBvhStats.maxObjectRefs)
686        {
687                mBvhStats.maxObjectRefs = (int)leaf->mObjects.size();
688        }
689
690        if ((int)leaf->mObjects.size() < mBvhStats.minObjectRefs)
691        {
692                mBvhStats.minObjectRefs = (int)leaf->mObjects.size();
693        }
694
695        ////////////////////////////////////////////
696        // ray related stuff
697
698        // note: this number should always accumulate to the total number of rays
699        mBvhStats.rayRefs += data.mNumRays;
700       
701        if (data.mNumRays <= mTermMinRays)
702        {
703             ++ mBvhStats.minRaysNodes;
704        }
705
706        if (data.mNumRays > mBvhStats.maxRayRefs)
707        {
708                mBvhStats.maxRayRefs = data.mNumRays;
709        }
710
711        if (data.mNumRays < mBvhStats.minRayRefs)
712        {
713                mBvhStats.minRayRefs = data.mNumRays;
714        }
715
716#if 0
717        cout << "depth: " << data.mDepth << " objects: " << (int)leaf->mObjects.size()
718                 << " rays: " << data.mNumRays << " rays / objects "
719                 << (float)data.mNumRays / (float)leaf->mObjects.size() << endl;
720#endif
721}
722
723
724#if 0
725
726/// compute object boundaries using spatial mid split
727float BvHierarchy::EvalLocalObjectPartition(const BvhTraversalData &tData,
728                                                                                        const int axis,
729                                                                                        ObjectContainer &objectsFront,
730                                                                                        ObjectContainer &objectsBack)
731{
732        const float maxBox = tData.mBoundingBox.Max(axis);
733        const float minBox = tData.mBoundingBox.Min(axis);
734
735        float midPoint = (maxBox + minBox) * 0.5f;
736
737        ObjectContainer::const_iterator oit, oit_end = tData.mNode->mObjects.end();
738       
739        for (oit = tData.mNode->mObjects.begin(); oit != oit_end; ++ oit)
740        {
741                Intersectable *obj = *oit;
742                const AxisAlignedBox3 box = obj->GetBox();
743
744                const float objMid = (box.Max(axis) + box.Min(axis)) * 0.5f;
745
746                // object mailed => belongs to back objects
747                if (objMid < midPoint)
748                {
749                        objectsBack.push_back(obj);
750                }
751                else
752                {
753                        objectsFront.push_back(obj);
754                }
755        }
756
757        const float oldRenderCost = EvalRenderCost(tData.mNode->mObjects);
758        const float newRenderCost =
759                EvalRenderCost(objectsFront) * EvalRenderCost(objectsBack);
760
761        const float ratio = newRenderCost / oldRenderCost;
762        return ratio;
763}
764
765#else
766
767/// compute object partition by getting balanced objects on the left and right side
768float BvHierarchy::EvalLocalObjectPartition(const BvhTraversalData &tData,
769                                                                                        const int axis,
770                                                                                        ObjectContainer &objectsFront,
771                                                                                        ObjectContainer &objectsBack)
772{
773        PrepareLocalSubdivisionCandidates(tData, axis);
774       
775        SortableEntryContainer::const_iterator cit, cit_end = mSubdivisionCandidates->end();
776
777        int i = 0;
778        const int border = (int)tData.mNode->mObjects.size() / 2;
779
780    for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit, ++ i)
781        {
782                Intersectable *obj = (*cit).mObject;
783
784                // object mailed => belongs to back objects
785                if (i < border)
786                {
787                        objectsBack.push_back(obj);
788                }
789                else
790                {
791                        objectsFront.push_back(obj);
792                }
793        }
794
795        const float oldRenderCost = EvalRenderCost(tData.mNode->mObjects);
796        const float newRenderCost = EvalRenderCost(objectsFront) + EvalRenderCost(objectsBack);
797
798        const float ratio = newRenderCost / oldRenderCost;
799        return ratio;
800}
801#endif
802
803
804float BvHierarchy::EvalSah(const BvhTraversalData &tData,
805                                                   const int axis,
806                                                   ObjectContainer &objectsFront,
807                                                   ObjectContainer &objectsBack)
808{
809        // go through the lists, count the number of objects left and right
810        // and evaluate the following cost funcion:
811        // C = ct_div_ci  + (ol + or)/queries
812        PrepareLocalSubdivisionCandidates(tData, axis);
813
814        int objectsLeft = 0, objectsRight = (int)tData.mNode->mObjects.size();
815 
816        AxisAlignedBox3 box = tData.mNode->GetBoundingBox();
817
818        float minBox = box.Min(axis);
819        float maxBox = box.Max(axis);
820        float boxArea = box.SurfaceArea();
821
822        float minSum = 1e20f;
823 
824        float minBorder = maxBox;
825        float maxBorder = minBox;
826        float areaLeft = 0, areaRight = 0;
827
828        SortableEntryContainer::const_iterator currentPos =
829                mSubdivisionCandidates->begin();
830
831       
832        // we keep track of both borders of the bounding boxes =>
833        // store the events in descending order
834        vector<float> bordersRight;
835        bordersRight.resize(mSubdivisionCandidates->size());
836
837        SortableEntryContainer::reverse_iterator rcit =
838                mSubdivisionCandidates->rbegin(), rcit_end = mSubdivisionCandidates->rend();
839       
840        vector<float>::reverse_iterator rbit = bordersRight.rbegin();
841       
842        for (; rcit != rcit_end; ++ rcit, ++ rbit)
843        {
844                Intersectable *obj = (*rcit).mObject;
845                const AxisAlignedBox3 box = obj->GetBox();
846
847                if (box.Min(axis) < minBorder)
848                {
849                        minBorder = box.Min(axis);
850                }
851
852                (*rbit) = minBorder;
853        }
854
855        vector<float>::const_iterator bit = bordersRight.begin();
856        SortableEntryContainer::const_iterator cit, cit_end = mSubdivisionCandidates->end();
857
858        for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit, ++ bit)
859        {
860                Intersectable *obj = (*cit).mObject;
861
862                ++ objectsLeft;
863                -- objectsRight;
864
865                AxisAlignedBox3 lbox = box;
866                AxisAlignedBox3 rbox = box;
867       
868                const AxisAlignedBox3 obox = obj->GetBox();
869
870                // the borders of the bounding boxes have changed
871                if (obox.Max(axis) > maxBorder)
872                {
873                        maxBorder = obox.Max(axis);
874                }
875
876                minBorder = (*bit);
877       
878        lbox.SetMax(axis, maxBorder);
879                rbox.SetMin(axis, minBorder);
880       
881                const float al = lbox.SurfaceArea();
882                const float ar = rbox.SurfaceArea();
883
884                const float sum = objectsLeft * al + objectsRight * ar;
885     
886                /*cout << "pos=" << (*cit).mPos << "\t q=(" << objectsLeft << "," << objectsRight <<")\t r=("
887                         << lbox.SurfaceArea() << "," << rbox.SurfaceArea() << ")" << endl;
888                cout << "minborder: " << minBorder << " maxborder: " << maxBorder << endl;
889            cout << "cost= " << sum << endl;
890*/
891                if (sum < minSum)
892                {
893                        minSum = sum;
894                        areaLeft = al;
895                        areaRight = ar;
896                        // objects belong to left side now
897                        for (; currentPos != (cit + 1); ++ currentPos);
898                }
899        }
900
901
902        ////////////////////////////////////////////
903        //-- assign object to front and back volume
904
905        // belongs to back bv
906        for (cit = mSubdivisionCandidates->begin(); cit != currentPos; ++ cit)
907                objectsBack.push_back((*cit).mObject);
908       
909        // belongs to front bv
910        for (cit = currentPos; cit != cit_end; ++ cit)
911                objectsFront.push_back((*cit).mObject);
912
913        float oldCost = (float)tData.mNode->mObjects.size();
914        float newCost = minSum / boxArea;
915        float ratio = newCost / oldCost;
916 
917#ifdef _DEBUG
918        cout << "\n\nobjects=(" << (int)objectsBack.size() << "," << (int)objectsFront.size() << " of "
919                 << (int)tData.mNode->mObjects.size() << ")\t area=("
920                 << areaLeft << "," << areaRight << ")" << endl;
921        cout << "cost= " << minSum << endl;
922#endif
923  return ratio;
924}
925
926
927static bool PrepareOutput(const int axis,
928                                                  const int leaves,
929                                                  ofstream &sumStats,
930                                                  ofstream &vollStats,
931                                                  ofstream &volrStats)
932{
933        if ((axis == 0) && (leaves > 0) && (leaves < 90))
934        {
935                char str[64];   
936                sprintf(str, "tmp/bvh_heur_sum-%04d.log", leaves);
937                sumStats.open(str);
938                sprintf(str, "tmp/bvh_heur_voll-%04d.log", leaves);
939                vollStats.open(str);
940                sprintf(str, "tmp/bvh_heur_volr-%04d.log", leaves);
941                volrStats.open(str);
942        }
943
944        return sumStats.is_open() && vollStats.is_open() && volrStats.is_open();
945}
946
947
948static void PrintHeuristics(const int objectsRight,
949                                                        const float sum,
950                                                        const float volLeft,
951                                                        const float volRight,
952                                                        const float viewSpaceVol,
953                                                        ofstream &sumStats,
954                                                        ofstream &vollStats,
955                                                        ofstream &volrStats)
956{
957        sumStats
958                << "#Position\n" << objectsRight << endl
959                << "#Sum\n" << sum / viewSpaceVol << endl
960                << "#Vol\n" << (volLeft +  volRight) / viewSpaceVol << endl;
961
962        vollStats
963                << "#Position\n" << objectsRight << endl
964                << "#Vol\n" << volLeft / viewSpaceVol << endl;
965
966        volrStats
967                << "#Position\n" << objectsRight << endl
968                << "#Vol\n" << volRight / viewSpaceVol << endl;
969}
970
971
972float BvHierarchy::EvalLocalCostHeuristics(const BvhTraversalData &tData,
973                                                                                   const int axis,
974                                                                                   ObjectContainer &objectsFront,
975                                                                                   ObjectContainer &objectsBack)
976{
977        ////////////////////////////////////////////////////////////////
978        // go through the lists, count the number of objects left and right
979        // and evaluate the cost funcion
980
981        // prepare the heuristics by setting mailboxes and counters.
982        const float totalVol = PrepareHeuristics(tData, axis);
983       
984        // local helper variables
985        float volLeft = 0;
986        float volRight = totalVol;
987        int nObjectsLeft = 0;
988        const int nTotalObjects = (int)tData.mNode->mObjects.size();
989        const float viewSpaceVol = mViewCellsManager->GetViewSpaceBox().GetVolume();
990
991        SortableEntryContainer::const_iterator backObjectsStart =
992                mSubdivisionCandidates->begin();
993
994        /////////////////////////////////
995        //-- the parameters for the current optimum
996
997        float volBack = volLeft;
998        float volFront = volRight;
999        float newRenderCost = nTotalObjects * totalVol;
1000
1001#ifdef _DEBUG
1002        ofstream sumStats;
1003        ofstream vollStats;
1004        ofstream volrStats;
1005
1006        const bool printStats =
1007                PrepareOutput(axis, mBvhStats.Leaves(), sumStats, vollStats, volrStats);
1008#endif
1009
1010        ///////////////////////////////////////////////////
1011        //-- the sweep heuristics
1012        //-- traverse through events and find best split plane
1013
1014        SortableEntryContainer::const_iterator cit, cit_end = cit_end = mSubdivisionCandidates->end();
1015
1016        for (cit = mSubdivisionCandidates->begin(); cit != cit_end; ++ cit)
1017        {
1018                Intersectable *object = (*cit).mObject;
1019       
1020                // evaluate change in l and r volume
1021                // voll = view cells that see only left node (i.e., left pvs)
1022                // volr = view cells that see only right node (i.e., right pvs)
1023                EvalHeuristicsContribution(object, volLeft, volRight);
1024
1025                ++ nObjectsLeft;
1026                const int nObjectsRight = nTotalObjects - nObjectsLeft;
1027
1028                // the heuristics
1029            const float sum = volLeft * (float)nObjectsLeft +
1030                                                  volRight * (float)nObjectsRight;
1031
1032#ifdef _DEBUG
1033                if (printStats)
1034                {
1035                        PrintHeuristics(nObjectsRight, sum, volLeft, volRight, viewSpaceVol,
1036                                                        sumStats, vollStats, volrStats);
1037                }
1038#endif
1039
1040                if (sum < newRenderCost)
1041                {
1042                        newRenderCost = sum;
1043
1044                        volBack = volLeft;
1045                        volFront = volRight;
1046
1047                        // objects belongs to left side now
1048                        for (; backObjectsStart != (cit + 1); ++ backObjectsStart);
1049                }
1050        }
1051
1052        ////////////////////////////////////////////
1053        //-- assign object to front and back volume
1054
1055        // belongs to back bv
1056        for (cit = mSubdivisionCandidates->begin(); cit != backObjectsStart; ++ cit)
1057        {
1058                objectsBack.push_back((*cit).mObject);
1059        }
1060        // belongs to front bv
1061        for (cit = backObjectsStart; cit != cit_end; ++ cit)
1062        {
1063                objectsFront.push_back((*cit).mObject);
1064        }
1065
1066        // render cost of the old parent
1067        const float oldRenderCost = (float)nTotalObjects * totalVol + Limits::Small;
1068        // the relative cost ratio
1069        const float ratio = newRenderCost / oldRenderCost;
1070
1071#ifdef _DEBUG
1072        Debug << "\n§§§§ bvh eval const decrease §§§§" << endl
1073                  << "back pvs: " << (int)objectsBack.size() << " front pvs: " << (int)objectsFront.size() << " total pvs: " << nTotalObjects << endl
1074                  << "back p: " << volBack / viewSpaceVol << " front p " << volFront / viewSpaceVol << " p: " << totalVol / viewSpaceVol << endl
1075                  << "old rc: " << oldRenderCost / viewSpaceVol << " new rc: " << newRenderCost / viewSpaceVol << endl
1076                  << "render cost decrease: " << oldRenderCost / viewSpaceVol - newRenderCost / viewSpaceVol << endl;
1077#endif
1078
1079        return ratio;
1080}
1081
1082
1083void BvHierarchy::PrepareLocalSubdivisionCandidates(const BvhTraversalData &tData,
1084                                                                                                        const int axis)                                                                                 
1085{
1086        //-- insert object queries
1087        ObjectContainer *objects =
1088                mUseGlobalSorting ? tData.mSortedObjects[axis] : &tData.mNode->mObjects;
1089
1090        CreateLocalSubdivisionCandidates(*objects, &mSubdivisionCandidates, !mUseGlobalSorting, axis);
1091}
1092
1093
1094void BvHierarchy::CreateLocalSubdivisionCandidates(const ObjectContainer &objects,
1095                                                                                                  SortableEntryContainer **subdivisionCandidates,
1096                                                                                                  const bool sort,
1097                                                                                                  const int axis)
1098{
1099        (*subdivisionCandidates)->clear();
1100
1101        // compute requested size and look if subdivision candidate has to be recomputed
1102        const int requestedSize = (int)objects.size() * 2;
1103       
1104        // creates a sorted split candidates array
1105        if ((*subdivisionCandidates)->capacity() > 500000 &&
1106                requestedSize < (int)((*subdivisionCandidates)->capacity() / 10) )
1107        {
1108        delete (*subdivisionCandidates);
1109                (*subdivisionCandidates) = new SortableEntryContainer;
1110        }
1111
1112        (*subdivisionCandidates)->reserve(requestedSize);
1113
1114        ObjectContainer::const_iterator oit, oit_end = objects.end();
1115
1116        for (oit = objects.begin(); oit < oit_end; ++ oit)
1117        {
1118                Intersectable *object = *oit;
1119                const AxisAlignedBox3 &box = object->GetBox();
1120                const float midPt = (box.Min(axis) + box.Max(axis)) * 0.5f;
1121
1122                (*subdivisionCandidates)->push_back(SortableEntry(object, midPt));
1123        }
1124
1125        if (sort)
1126        {       // no presorted candidate list
1127                stable_sort((*subdivisionCandidates)->begin(), (*subdivisionCandidates)->end());
1128        }
1129}
1130
1131
1132const BvhStatistics &BvHierarchy::GetStatistics() const
1133{
1134        return mBvhStats;
1135}
1136
1137
1138float BvHierarchy::PrepareHeuristics(const BvhTraversalData &tData, const int axis)
1139{       
1140        BvhLeaf *leaf = tData.mNode;
1141        float vol = 0;
1142
1143    // sort so we can use a sweep from right to left
1144        PrepareLocalSubdivisionCandidates(tData, axis);
1145       
1146        // collect and mark the view cells as belonging to front pvs
1147        ViewCellContainer viewCells;
1148        CollectViewCells(tData.mNode->mObjects, viewCells, true);
1149                       
1150        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1151        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1152        {
1153                vol += (*vit)->GetVolume();
1154        }
1155
1156        // we will mail view cells switching to the back side
1157        ViewCell::NewMail();
1158       
1159        return vol;
1160}
1161
1162///////////////////////////////////////////////////////////
1163
1164
1165void BvHierarchy::EvalHeuristicsContribution(Intersectable *obj,
1166                                                                                         float &volLeft,
1167                                                                                         float &volRight)
1168{
1169        // collect all view cells associated with this objects
1170        // (also multiple times, if they are pierced by several rays)
1171        ViewCellContainer viewCells;
1172        const bool useMailboxing = false;
1173
1174        CollectViewCells(obj, viewCells, useMailboxing);
1175
1176        // classify view cells and compute volume contri accordingly
1177        // possible view cell classifications:
1178        // view cell mailed => view cell can be seen from left child node
1179        // view cell counter > 0 view cell can be seen from right child node
1180        // combined: view cell volume belongs to both nodes
1181        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1182       
1183        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1184        {
1185                // view cells can also be seen from left child node
1186                ViewCell *viewCell = *vit;
1187
1188                const float vol = viewCell->GetVolume();
1189
1190                if (!viewCell->Mailed())
1191                {
1192                        viewCell->Mail();
1193                        // we now see view cell from both nodes
1194                        // => add volume to left node
1195                        volLeft += vol;
1196                }
1197
1198                // last reference into the right node
1199                if (-- viewCell->mCounter == 0)
1200                {       
1201                        // view cell was previously seen from both nodes  =>
1202                        // remove volume from right node
1203                        volRight -= vol;
1204                }
1205        }
1206}
1207
1208
1209void BvHierarchy::SetViewCellsManager(ViewCellsManager *vcm)
1210{
1211        mViewCellsManager = vcm;
1212}
1213
1214
1215AxisAlignedBox3 BvHierarchy::GetBoundingBox() const
1216{
1217        return mBoundingBox;
1218}
1219
1220
1221float BvHierarchy::SelectObjectPartition(const BvhTraversalData &tData,
1222                                                                                 ObjectContainer &frontObjects,
1223                                                                                 ObjectContainer &backObjects)
1224{
1225        ObjectContainer nFrontObjects[3];
1226        ObjectContainer nBackObjects[3];
1227        float nCostRatio[3];
1228
1229        int sAxis = 0;
1230        int bestAxis = -1;
1231
1232        if (mOnlyDrivingAxis)
1233        {
1234                const AxisAlignedBox3 box = tData.mNode->GetBoundingBox();
1235                sAxis = box.Size().DrivingAxis();
1236        }
1237       
1238        ////////////////////////////////////
1239        //-- evaluate split cost for all three axis
1240       
1241        for (int axis = 0; axis < 3; ++ axis)
1242        {
1243                if (!mOnlyDrivingAxis || (axis == sAxis))
1244                {
1245                        if (mUseCostHeuristics)
1246                        {
1247                                //////////////////////////////////
1248                //-- split objects using heuristics
1249                               
1250                                if (!mUseSah &&
1251                                        (mHierarchyManager->GetViewSpaceSubdivisionType() ==
1252                                        HierarchyManager::KD_BASED_VIEWSPACE_SUBDIV))
1253                                {
1254                                        ///////////
1255                                        //-- heuristics using objects weighted by view cells volume
1256                                        nCostRatio[axis] =
1257                                                EvalLocalCostHeuristics(
1258                                                                                                tData,
1259                                                                                                axis,
1260                                                                                                nFrontObjects[axis],
1261                                                                                                nBackObjects[axis]);
1262                                }
1263                                else
1264                                {
1265                                        //////////////////
1266                                        //-- view cells not constructed yet     => use surface area heuristic                   
1267                                        nCostRatio[axis] =
1268                                                EvalSah(
1269                                                                tData,
1270                                                                axis,
1271                                                                nFrontObjects[axis],
1272                                                                nBackObjects[axis]);
1273                                }
1274                        }
1275                        else
1276                        {
1277                                //-- split objects using some simple criteria
1278                                nCostRatio[axis] =
1279                                        EvalLocalObjectPartition(
1280                                                                                         tData,
1281                                                                                         axis,
1282                                                                                         nFrontObjects[axis],
1283                                                                                         nBackObjects[axis]);
1284                        }
1285
1286                        if (bestAxis == -1)
1287                        {
1288                                bestAxis = axis;
1289                        }
1290                        else if (nCostRatio[axis] < nCostRatio[bestAxis])
1291                        {
1292                                bestAxis = axis;
1293                        }
1294                }
1295        }
1296
1297    ////////////////
1298        //-- assign values
1299
1300        frontObjects = nFrontObjects[bestAxis];
1301        backObjects = nBackObjects[bestAxis];
1302
1303        //Debug << "val: " << nCostRatio[bestAxis] << " axis: " << bestAxis << endl;
1304        return nCostRatio[bestAxis];
1305}
1306
1307
1308int BvHierarchy::AssociateObjectsWithRays(const VssRayContainer &rays) const
1309{
1310        int nRays = 0;
1311        VssRayContainer::const_iterator rit, rit_end = rays.end();
1312
1313        VssRay::NewMail();
1314
1315    for (rit = rays.begin(); rit != rays.end(); ++ rit)
1316        {
1317                VssRay *ray = (*rit);
1318
1319                if (ray->mTerminationObject)
1320                {
1321                        ray->mTerminationObject->mVssRays.push_back(ray);
1322                        if (!ray->Mailed())
1323                        {
1324                                ray->Mail();
1325                                ++ nRays;
1326                        }
1327                }
1328#if COUNT_ORIGIN_OBJECTS
1329                if (ray->mOriginObject)
1330                {
1331                        ray->mOriginObject->mVssRays.push_back(ray);
1332
1333                        if (!ray->Mailed())
1334                        {
1335                                ray->Mail();
1336                                ++ nRays;
1337                        }
1338                }
1339#endif
1340        }
1341
1342        return nRays;
1343}
1344
1345
1346void BvHierarchy::PrintSubdivisionStats(const SubdivisionCandidate &sc)
1347{
1348        const float costDecr =
1349                sc.GetRenderCostDecrease();// / mHierarchyManager->GetViewSpaceBox().GetVolume();       
1350
1351        mSubdivisionStats
1352                        << "#Leaves\n" << mBvhStats.Leaves() << endl
1353                        << "#RenderCostDecrease\n" << costDecr << endl
1354                        << "#TotalRenderCost\n" << mTotalCost << endl;
1355}
1356
1357
1358void BvHierarchy::CollectRays(const ObjectContainer &objects,
1359                                                          VssRayContainer &rays) const
1360{
1361        VssRay::NewMail();
1362        ObjectContainer::const_iterator oit, oit_end = objects.end();
1363
1364        // evaluate reverse pvs and view cell volume on left and right cell
1365        // note: should I take all leaf objects or rather the objects hit by rays?
1366        for (oit = objects.begin(); oit != oit_end; ++ oit)
1367        {
1368                Intersectable *obj = *oit;
1369                VssRayContainer::const_iterator rit, rit_end = obj->mVssRays.end();
1370
1371                for (rit = obj->mVssRays.begin(); rit < rit_end; ++ rit)
1372                {
1373                        VssRay *ray = (*rit);
1374
1375                        if (!ray->Mailed())
1376                        {
1377                                ray->Mail();
1378                                rays.push_back(ray);
1379                        }
1380                }
1381        }
1382}
1383
1384
1385float BvHierarchy::EvalRenderCost(const ObjectContainer &objects) const
1386{       
1387        if (0 &&
1388                (mHierarchyManager->GetViewSpaceSubdivisionType() ==
1389                HierarchyManager::NO_VIEWSPACE_SUBDIV))
1390        {
1391                ////////////////
1392                //-- surface area heuristics
1393
1394                if (objects.empty())
1395                        return 0.0f;
1396
1397                const AxisAlignedBox3 box = EvalBoundingBox(objects);
1398                const float area = box.SurfaceArea();
1399                const float viewSpaceArea = mViewCellsManager->GetViewSpaceBox().SurfaceArea();
1400
1401                return (float)objects.size() * area / viewSpaceArea;
1402        }
1403        else
1404        {       ///////////////
1405                //-- render cost heuristics
1406
1407                const float viewSpaceVol = mViewCellsManager->GetViewSpaceBox().GetVolume();
1408
1409                // probability that view point lies in a view cell which sees this node
1410                const float p = EvalViewCellsVolume(objects) / viewSpaceVol;   
1411
1412                return (float)objects.size() * p;
1413        }
1414}
1415
1416
1417AxisAlignedBox3 BvHierarchy::EvalBoundingBox(const ObjectContainer &objects,
1418                                                                                         const AxisAlignedBox3 *parentBox) const
1419{
1420        // if there are no objects in this box, box size is set to parent box size.
1421        // Question: Invalidate box instead?
1422        if (parentBox && objects.empty())
1423                return *parentBox;
1424
1425        AxisAlignedBox3 box;
1426        box.Initialize();
1427
1428        ObjectContainer::const_iterator oit, oit_end = objects.end();
1429
1430        for (oit = objects.begin(); oit != oit_end; ++ oit)
1431        {
1432                Intersectable *obj = *oit;
1433                // grow bounding box to include all objects
1434                box.Include(obj->GetBox());
1435        }
1436
1437        return box;
1438}
1439
1440
1441void BvHierarchy::CollectLeaves(vector<BvhLeaf *> &leaves) const
1442{
1443        stack<BvhNode *> nodeStack;
1444        nodeStack.push(mRoot);
1445
1446        while (!nodeStack.empty())
1447        {
1448                BvhNode *node = nodeStack.top();
1449                nodeStack.pop();
1450
1451                if (node->IsLeaf())
1452                {
1453                        BvhLeaf *leaf = (BvhLeaf *)node;
1454                        leaves.push_back(leaf);
1455                }
1456                else
1457                {
1458                        BvhInterior *interior = (BvhInterior *)node;
1459
1460                        nodeStack.push(interior->GetBack());
1461                        nodeStack.push(interior->GetFront());
1462                }
1463        }
1464}
1465
1466
1467AxisAlignedBox3 BvHierarchy::GetBoundingBox(BvhNode *node) const
1468{
1469        return node->GetBoundingBox();
1470}
1471
1472
1473void BvHierarchy::CollectViewCells(const ObjectContainer &objects,
1474                                                                   ViewCellContainer &viewCells,
1475                                                                   const bool setCounter) const
1476{
1477        // no view cells yet
1478        if (0 && mHierarchyManager->GetViewSpaceSubdivisionType() ==
1479                HierarchyManager::NO_VIEWSPACE_SUBDIV)
1480                return;
1481
1482        ViewCell::NewMail();
1483        ObjectContainer::const_iterator oit, oit_end = objects.end();
1484
1485        // loop through all object and collect view cell pvs of this node
1486        for (oit = objects.begin(); oit != oit_end; ++ oit)
1487        {
1488                CollectViewCells(*oit, viewCells, true, setCounter);
1489        }
1490}
1491
1492
1493void BvHierarchy::CollectViewCells(Intersectable *obj,
1494                                                                   ViewCellContainer &viewCells,
1495                                                                   const bool useMailBoxing,
1496                                                                   const bool setCounter) const
1497{
1498        VssRayContainer::const_iterator rit, rit_end = obj->mVssRays.end();
1499
1500        for (rit = obj->mVssRays.begin(); rit < rit_end; ++ rit)
1501        {
1502                VssRay *ray = (*rit);
1503                ViewCellContainer tmpViewCells;
1504       
1505                mHierarchyManager->mVspTree->GetViewCells(*ray, tmpViewCells);
1506
1507                // matt: probably slow to allocate memory for view cells every time
1508                ViewCellContainer::const_iterator vit, vit_end = tmpViewCells.end();
1509
1510                for (vit = tmpViewCells.begin(); vit != vit_end; ++ vit)
1511                {
1512                        ViewCell *vc = *vit;
1513
1514                        // store view cells
1515                        if (!useMailBoxing || !vc->Mailed())
1516                        {
1517                                if (useMailBoxing)
1518                                {
1519                                        vc->Mail();
1520                                        if (setCounter)
1521                                        {
1522                                                vc->mCounter = 0;
1523                                        }
1524                                }
1525                                viewCells.push_back(vc);
1526                        }
1527                       
1528                        if (setCounter)
1529                        {
1530                                ++ vc->mCounter;
1531                        }
1532                }
1533        }
1534}
1535
1536
1537int BvHierarchy::CountViewCells(Intersectable *obj) const
1538{
1539        int result = 0;
1540       
1541        VssRayContainer::const_iterator rit, rit_end = obj->mVssRays.end();
1542
1543        for (rit = obj->mVssRays.begin(); rit < rit_end; ++ rit)
1544        {
1545                VssRay *ray = (*rit);
1546                ViewCellContainer tmpViewCells;
1547       
1548                mHierarchyManager->mVspTree->GetViewCells(*ray, tmpViewCells);
1549               
1550                ViewCellContainer::const_iterator vit, vit_end = tmpViewCells.end();
1551                for (vit = tmpViewCells.begin(); vit != vit_end; ++ vit)
1552                {
1553                        ViewCell *vc = *vit;
1554
1555                        // store view cells
1556                        if (!vc->Mailed())
1557                        {
1558                                vc->Mail();
1559                                ++ result;
1560                        }
1561                }
1562        }
1563
1564        return result;
1565}
1566
1567
1568int BvHierarchy::CountViewCells(const ObjectContainer &objects) const
1569{
1570        // no view cells yet
1571        if (0 && mHierarchyManager->GetViewSpaceSubdivisionType() ==
1572                HierarchyManager::NO_VIEWSPACE_SUBDIV)
1573                return 1;
1574
1575        int nViewCells = 0;
1576
1577        ViewCell::NewMail();
1578
1579        ObjectContainer::const_iterator oit, oit_end = objects.end();
1580
1581        // loop through all object and collect view cell pvs of this node
1582        for (oit = objects.begin(); oit != oit_end; ++ oit)
1583        {
1584                nViewCells += CountViewCells(*oit);
1585        }
1586
1587        return nViewCells;
1588}
1589
1590
1591void BvHierarchy::CollectDirtyCandidates(BvhSubdivisionCandidate *sc,
1592                                                                                 vector<SubdivisionCandidate *> &dirtyList,
1593                                                                                 const bool onlyUnmailed)
1594{
1595        BvhTraversalData &tData = sc->mParentData;
1596        BvhLeaf *node = tData.mNode;
1597       
1598        ViewCellContainer viewCells;
1599        ViewCell::NewMail();
1600        CollectViewCells(node->mObjects, viewCells, true);
1601
1602        if (0) cout << "collected " << (int)viewCells.size() << " dirty candidates" << endl;
1603       
1604        // split candidates handling
1605        // these view cells  are thrown into dirty list
1606        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1607
1608        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1609        {
1610        VspViewCell *vc = dynamic_cast<VspViewCell *>(*vit);
1611                VspLeaf *leaf = vc->mLeaves[0];
1612       
1613                SubdivisionCandidate *candidate = leaf->GetSubdivisionCandidate();
1614               
1615                // is this leaf still a split candidate?
1616                if (candidate && (!onlyUnmailed || !candidate->Mailed()))
1617                {
1618                        candidate->Mail();
1619                        dirtyList.push_back(candidate);
1620                }
1621        }
1622}
1623
1624
1625BvhNode *BvHierarchy::GetRoot() const
1626{
1627        return mRoot;
1628}
1629
1630
1631bool BvHierarchy::IsObjectInLeaf(BvhLeaf *leaf, Intersectable *object) const
1632{
1633        ObjectContainer::const_iterator oit =
1634                lower_bound(leaf->mObjects.begin(), leaf->mObjects.end(), object, ilt);
1635                               
1636        // objects sorted by id
1637        if ((oit != leaf->mObjects.end()) && ((*oit)->GetId() == object->GetId()))
1638        {
1639                return true;
1640        }
1641        else
1642        {
1643                return false;
1644        }
1645}
1646
1647
1648BvhLeaf *BvHierarchy::GetLeaf(Intersectable *object, BvhNode *node) const
1649{
1650        // rather use the simple version
1651        if (!object) return NULL;
1652        return object->mBvhLeaf;
1653       
1654        ///////////////////////////////////////
1655        // start from root of tree
1656       
1657        if (node == NULL)
1658                node = mRoot;
1659       
1660        vector<BvhLeaf *> leaves;
1661
1662        stack<BvhNode *> nodeStack;
1663        nodeStack.push(node);
1664 
1665        BvhLeaf *leaf = NULL;
1666 
1667        while (!nodeStack.empty()) 
1668        {
1669                BvhNode *node = nodeStack.top();
1670                nodeStack.pop();
1671       
1672                if (node->IsLeaf())
1673                {
1674                        leaf = dynamic_cast<BvhLeaf *>(node);
1675
1676                        if (IsObjectInLeaf(leaf, object))
1677                        {
1678                                return leaf;
1679                        }
1680                }
1681                else   
1682                {       
1683                        // find point
1684                        BvhInterior *interior = dynamic_cast<BvhInterior *>(node);
1685       
1686                        if (interior->GetBack()->GetBoundingBox().Includes(object->GetBox()))
1687                        {
1688                                nodeStack.push(interior->GetBack());
1689                        }
1690                       
1691                        // search both sides as we are using bounding volumes
1692                        if (interior->GetFront()->GetBoundingBox().Includes(object->GetBox()))
1693                        {
1694                                nodeStack.push(interior->GetFront());
1695                        }
1696                }
1697        }
1698 
1699        return leaf;
1700}
1701
1702
1703BvhIntersectable *BvHierarchy::GetOrCreateBvhIntersectable(BvhNode *node)
1704{
1705        // search nodes
1706        std::map<BvhNode *, BvhIntersectable *>::const_iterator it
1707                = mBvhIntersectables.find(node);
1708
1709        if (it != mBvhIntersectables.end())
1710        {
1711                return (*it).second;
1712        }
1713
1714        // not in map => create new entry
1715        BvhIntersectable *bvhObj = new BvhIntersectable(node);
1716        mBvhIntersectables[node] = bvhObj;
1717
1718        return bvhObj;
1719}
1720
1721
1722bool BvHierarchy::Export(OUT_STREAM &stream)
1723{
1724        ExportNode(mRoot, stream);
1725
1726        return true;
1727}
1728
1729
1730void BvHierarchy::ExportObjects(BvhLeaf *leaf, OUT_STREAM &stream)
1731{
1732        ObjectContainer::const_iterator oit, oit_end = leaf->mObjects.end();
1733        for (oit = leaf->mObjects.begin(); oit != oit_end; ++ oit)
1734        {
1735                stream << (*oit)->GetId() << " ";
1736        }
1737}
1738
1739
1740void BvHierarchy::ExportNode(BvhNode *node, OUT_STREAM &stream)
1741{
1742        if (node->IsLeaf())
1743        {
1744                BvhLeaf *leaf = dynamic_cast<BvhLeaf *>(node);
1745                const AxisAlignedBox3 box = leaf->GetBoundingBox();
1746                stream << "<Leaf"
1747                           << " min=\"" << box.Min().x << " " << box.Min().y << " " << box.Min().z << "\""
1748                           << " max=\"" << box.Max().x << " " << box.Max().y << " " << box.Max().z << "\""
1749                           << " objects=\"";
1750               
1751                //-- export objects
1752                ExportObjects(leaf, stream);
1753               
1754                stream << "\" />" << endl;
1755        }
1756        else
1757        {       
1758                BvhInterior *interior = dynamic_cast<BvhInterior *>(node);
1759                const AxisAlignedBox3 box = interior->GetBoundingBox();
1760
1761                stream << "<Interior"
1762                           << " min=\"" << box.Min().x << " " << box.Min().y << " " << box.Min().z << "\""
1763                           << " max=\"" << box.Max().x << " " << box.Max().y << " " << box.Max().z
1764                           << "\">" << endl;
1765
1766                ExportNode(interior->GetBack(), stream);
1767                ExportNode(interior->GetFront(), stream);
1768
1769                stream << "</Interior>" << endl;
1770        }
1771}
1772
1773
1774float BvHierarchy::EvalViewCellsVolume(const ObjectContainer &objects) const
1775{
1776        float vol = 0;
1777
1778        ViewCellContainer viewCells;
1779        CollectViewCells(objects, viewCells);
1780
1781        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
1782
1783        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
1784        {
1785                vol += (*vit)->GetVolume();
1786        }
1787
1788        return vol;
1789}
1790
1791
1792void BvHierarchy::Initialise(const ObjectContainer &objects)
1793{
1794        ///////
1795        //-- create new root
1796
1797        AxisAlignedBox3 box = EvalBoundingBox(objects);
1798        BvhLeaf *bvhleaf = new BvhLeaf(box, NULL, (int)objects.size());
1799        bvhleaf->mObjects = objects;
1800        mRoot = bvhleaf;
1801
1802        // compute bounding box from objects
1803        mBoundingBox = mRoot->GetBoundingBox();
1804
1805        // associate root with current objects
1806        AssociateObjectsWithLeaf(bvhleaf);
1807}
1808
1809
1810/*
1811Mesh *BvHierarchy::MergeLeafToMesh()
1812void BvHierarchy::MergeLeavesToMeshes()
1813{
1814        vector<BvhLeaf *> leaves;
1815        CollectLeaves(leaves);
1816
1817        vector<BvhLeaf *>::const_iterator lit, lit_end = leaves.end();
1818
1819        for (lit = leaves.begin(); lit != lit_end; ++ lit)
1820        {
1821                Mesh *mesh = MergeLeafToMesh(*lit);
1822        }
1823}*/
1824
1825
1826SubdivisionCandidate *BvHierarchy::PrepareConstruction(const VssRayContainer &sampleRays,
1827                                                                                                           const ObjectContainer &objects)
1828{
1829        ///////////////////////////////////////
1830        //-- we assume that we have objects sorted by their id =>
1831        //-- we don't have to sort them here and an binary search
1832        //-- for identifying if a object is in a leaf.
1833       
1834        mBvhStats.Reset();
1835        mBvhStats.Start();
1836        mBvhStats.nodes = 1;
1837               
1838        // store pointer to this tree
1839        BvhSubdivisionCandidate::sBvHierarchy = this;
1840       
1841        // root and bounding box was already constructed
1842        BvhLeaf *bvhLeaf = dynamic_cast<BvhLeaf *>(mRoot);
1843
1844        // multiply termination criterium for comparison,
1845        // so it can be set between zero and one and
1846        // no division is necessary during traversal
1847
1848#if PROBABILIY_IS_BV_VOLUME
1849        mTermMinProbability *= mBoundingBox.GetVolume();
1850        // probability that bounding volume is seen
1851        const float prop = GetBoundingBox().GetVolume();
1852#else
1853        mTermMinProbability *= mVspTree->GetBoundingBox().GetVolume();
1854        // probability that volume is "seen" from the view cells
1855        const float prop = EvalViewCellsVolume(objects);
1856#endif
1857
1858        // only rays intersecting objects in node are interesting
1859        const int nRays = AssociateObjectsWithRays(sampleRays);
1860        //Debug << "using " << nRays << " of " << (int)sampleRays.size() << " rays" << endl;
1861
1862        // create bvh traversal data
1863        BvhTraversalData oData(bvhLeaf, 0, prop, nRays);
1864
1865        // create sorted object lists for the first data
1866        if (mUseGlobalSorting)
1867        {
1868                AssignInitialSortedObjectList(oData);
1869        }
1870       
1871
1872        ///////////////////
1873        //-- add first candidate for object space partition     
1874
1875        BvhSubdivisionCandidate *oSubdivisionCandidate =
1876                new BvhSubdivisionCandidate(oData);
1877
1878        EvalSubdivisionCandidate(*oSubdivisionCandidate);
1879        bvhLeaf->SetSubdivisionCandidate(oSubdivisionCandidate);
1880
1881        const float viewSpaceVol = mViewCellsManager->GetViewSpaceBox().GetVolume();
1882        mTotalCost = (float)objects.size() * prop / viewSpaceVol;
1883
1884        PrintSubdivisionStats(*oSubdivisionCandidate);
1885       
1886        return oSubdivisionCandidate;
1887}
1888
1889
1890void BvHierarchy::AssignInitialSortedObjectList(BvhTraversalData &tData)
1891{
1892        // we sort the objects as a preprocess so they don't have
1893        // to be sorted for each split
1894        for (int i = 0; i < 3; ++ i)
1895        {
1896                // create new objects
1897                if (!mSortedObjects[i])
1898                {
1899                        mSortedObjects[i] = new SortableEntryContainer();
1900                        CreateLocalSubdivisionCandidates(tData.mNode->mObjects, &mSortedObjects[i], true, i);
1901                }
1902
1903                // copy list into traversal data list
1904                tData.mSortedObjects[i] = new ObjectContainer();
1905                tData.mSortedObjects[i]->reserve((int)mSortedObjects[i]->size());
1906
1907                SortableEntryContainer::const_iterator oit, oit_end = mSortedObjects[i]->end();
1908
1909                for (oit = mSortedObjects[i]->begin(); oit != oit_end; ++ oit)
1910                {
1911                        tData.mSortedObjects[i]->push_back((*oit).mObject);
1912                }
1913        }
1914}
1915
1916
1917void BvHierarchy::AssignSortedObjects(const BvhSubdivisionCandidate &sc,
1918                                                                          BvhTraversalData &frontData,
1919                                                                          BvhTraversalData &backData)
1920{
1921        Intersectable::NewMail();
1922
1923        // we sorted the objects as a preprocess so they don't have
1924        // to be sorted for each split
1925        ObjectContainer::const_iterator fit, fit_end = sc.mFrontObjects.end();
1926
1927        for (fit = sc.mFrontObjects.begin(); fit != fit_end; ++ fit)
1928        {
1929                (*fit)->Mail();
1930        }
1931
1932        for (int i = 0; i < 3; ++ i)
1933        {
1934                frontData.mSortedObjects[i] = new ObjectContainer();
1935                backData.mSortedObjects[i] = new ObjectContainer();
1936
1937                frontData.mSortedObjects[i]->reserve((int)sc.mFrontObjects.size());
1938                backData.mSortedObjects[i]->reserve((int)sc.mFrontObjects.size());
1939
1940                ObjectContainer::const_iterator oit, oit_end = sc.mParentData.mSortedObjects[i]->end();
1941
1942                for (oit = sc.mParentData.mSortedObjects[i]->begin(); oit != oit_end; ++ oit)
1943                {
1944                        if ((*oit)->Mailed())
1945                        {
1946                                frontData.mSortedObjects[i]->push_back(*oit);
1947                        }
1948                        else
1949                        {
1950                                backData.mSortedObjects[i]->push_back(*oit);
1951                        }
1952                }
1953        }
1954}
1955
1956
1957SubdivisionCandidate *BvHierarchy::Reset(const VssRayContainer &sampleRays,
1958                                                                                 const ObjectContainer &objects)
1959{
1960        // reset stats
1961        mBvhStats.Reset();
1962        mBvhStats.Start();
1963        mBvhStats.nodes = 1;
1964
1965        // reset root
1966        DEL_PTR(mRoot);
1967       
1968        BvhLeaf *bvhleaf = new BvhLeaf(mBoundingBox, NULL, (int)objects.size());
1969        bvhleaf->mObjects = objects;
1970        mRoot = bvhleaf;
1971       
1972#if PROBABILIY_IS_BV_VOLUME
1973        mTermMinProbability *= mBoundingBox.GetVolume();
1974        // probability that bounding volume is seen
1975        const float prop = GetBoundingBox().GetVolume();
1976#else
1977        mTermMinProbability *= mVspTree->GetBoundingBox().GetVolume();
1978        // probability that volume is "seen" from the view cells
1979        const float prop = EvalViewCellsVolume(objects);
1980#endif
1981
1982        const int nRays = CountRays(objects);
1983        BvhLeaf *bvhLeaf = dynamic_cast<BvhLeaf *>(mRoot);
1984
1985        // create bvh traversal data
1986        BvhTraversalData oData(bvhLeaf, 0, prop, nRays);
1987
1988        AssignInitialSortedObjectList(oData);
1989       
1990
1991        ///////////////////
1992        //-- add first candidate for object space partition     
1993
1994        BvhSubdivisionCandidate *oSubdivisionCandidate =
1995                new BvhSubdivisionCandidate(oData);
1996
1997        EvalSubdivisionCandidate(*oSubdivisionCandidate);
1998        bvhLeaf->SetSubdivisionCandidate(oSubdivisionCandidate);
1999
2000        const float viewSpaceVol = mViewCellsManager->GetViewSpaceBox().GetVolume();
2001        mTotalCost = (float)objects.size() * prop / viewSpaceVol;
2002
2003        PrintSubdivisionStats(*oSubdivisionCandidate);
2004
2005        return oSubdivisionCandidate;
2006}
2007
2008
2009void BvhStatistics::Print(ostream &app) const
2010{
2011        app << "=========== BvHierarchy statistics ===============\n";
2012
2013        app << setprecision(4);
2014
2015        app << "#N_CTIME  ( Construction time [s] )\n" << Time() << " \n";
2016
2017        app << "#N_NODES ( Number of nodes )\n" << nodes << "\n";
2018
2019        app << "#N_INTERIORS ( Number of interior nodes )\n" << Interior() << "\n";
2020
2021        app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n";
2022
2023        app << "#AXIS_ALIGNED_SPLITS (number of axis aligned splits)\n" << splits << endl;
2024
2025        app << "#N_MAXCOSTNODES  ( Percentage of leaves with terminated because of max cost ratio )\n"
2026                << maxCostNodes * 100 / (double)Leaves() << endl;
2027
2028        app << "#N_PMINPROBABILITYLEAVES  ( Percentage of leaves with mininum probability )\n"
2029                << minProbabilityNodes * 100 / (double)Leaves() << endl;
2030
2031
2032        //////////////////////////////////////////////////
2033       
2034        app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maximum depth )\n"
2035                <<      maxDepthNodes * 100 / (double)Leaves() << endl;
2036       
2037        app << "#N_PMAXDEPTH ( Maximal reached depth )\n" << maxDepth << endl;
2038
2039        app << "#N_PMINDEPTH ( Minimal reached depth )\n" << minDepth << endl;
2040
2041        app << "#AVGDEPTH ( average depth )\n" << AvgDepth() << endl;
2042
2043       
2044        ////////////////////////////////////////////////////////
2045       
2046        app << "#N_PMINOBJECTSLEAVES  ( Percentage of leaves with mininum objects )\n"
2047                << minObjectsNodes * 100 / (double)Leaves() << endl;
2048
2049        app << "#N_MAXOBJECTREFS  ( Max number of object refs / leaf )\n" << maxObjectRefs << "\n";
2050
2051        app << "#N_MINOBJECTREFS  ( Min number of object refs / leaf )\n" << minObjectRefs << "\n";
2052
2053        app << "#N_EMPTYLEAFS ( Empty leafs )\n" << emptyNodes << "\n";
2054       
2055        app << "#N_PAVGOBJECTSLEAVES  ( average object refs / leaf)\n" << AvgObjectRefs() << endl;
2056
2057
2058        ////////////////////////////////////////////////////////
2059       
2060        app << "#N_PMINRAYSLEAVES  ( Percentage of leaves with mininum rays )\n"
2061                << minRaysNodes * 100 / (double)Leaves() << endl;
2062
2063        app << "#N_MAXRAYREFS  ( Max number of ray refs / leaf )\n" << maxRayRefs << "\n";
2064
2065        app << "#N_MINRAYREFS  ( Min number of ray refs / leaf )\n" << minRayRefs << "\n";
2066       
2067        app << "#N_PAVGRAYLEAVES  ( average ray refs / leaf )\n" << AvgRayRefs() << endl;
2068       
2069        app << "#N_PAVGRAYCONTRIBLEAVES  ( Average ray contribution)\n" <<
2070                rayRefs / (double)objectRefs << endl;
2071
2072        app << "#N_PMAXRAYCONTRIBLEAVES  ( Percentage of leaves with maximal ray contribution )\n"<<
2073                maxRayContriNodes * 100 / (double)Leaves() << endl;
2074
2075        app << "#N_PGLOBALCOSTMISSES ( Global cost misses )\n" << mGlobalCostMisses << endl;
2076
2077        app << "========== END OF BvHierarchy statistics ==========\n";
2078}
2079
2080
2081// TODO: return memory usage in MB
2082float BvHierarchy::GetMemUsage() const
2083{
2084        return (float)
2085                 (sizeof(BvHierarchy)
2086                  + mBvhStats.Leaves() * sizeof(BvhLeaf)
2087                  + mBvhStats.Interior() * sizeof(BvhInterior)
2088                  ) / (1024.0f * 1024.0f);
2089}
2090
2091
2092}
Note: See TracBrowser for help on using the repository browser.