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

Revision 1676, 55.6 KB checked in by mattausch, 18 years ago (diff)

worked on pvs heuristics

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