source: GTP/trunk/Lib/Vis/Preprocessing/src/VspBspTree.cpp @ 697

Revision 697, 89.1 KB checked in by mattausch, 18 years ago (diff)
Line 
1#include <stack>
2#include <time.h>
3#include <iomanip>
4
5#include "Plane3.h"
6#include "VspBspTree.h"
7#include "Mesh.h"
8#include "common.h"
9#include "ViewCell.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 "ViewCellBsp.h"
17#include "ViewCellsManager.h"
18#include "Beam.h"
19
20#define USE_FIXEDPOINT_T 0
21
22
23//-- static members
24
25
26int VspBspTree::sFrontId = 0;
27int VspBspTree::sBackId = 0;
28int VspBspTree::sFrontAndBackId = 0;
29
30typedef pair<BspNode *, BspNodeGeometry *> bspNodePair;
31
32
33// pvs penalty can be different from pvs size
34inline float EvalPvsPenalty(const int pvs,
35                                                        const int lower,
36                                                        const int upper)
37{
38        // clamp to minmax values
39        if (pvs < lower)
40                return (float)lower;
41        if (pvs > upper)
42                return (float)upper;
43
44        return (float)pvs;
45}
46
47
48
49
50/******************************************************************************/
51/*                       class VspBspTree implementation                      */
52/******************************************************************************/
53
54
55VspBspTree::VspBspTree():
56mRoot(NULL),
57mUseAreaForPvs(false),
58mCostNormalizer(Limits::Small),
59mViewCellsManager(NULL),
60mOutOfBoundsCell(NULL),
61mStoreRays(false),
62mRenderCostWeight(0.5),
63mUseRandomAxis(false),
64mTimeStamp(1)
65{
66        bool randomize = false;
67        environment->GetBoolValue("VspBspTree.Construction.randomize", randomize);
68        if (randomize)
69                Randomize(); // initialise random generator for heuristics
70
71        //-- termination criteria for autopartition
72        environment->GetIntValue("VspBspTree.Termination.maxDepth", mTermMaxDepth);
73        environment->GetIntValue("VspBspTree.Termination.minPvs", mTermMinPvs);
74        environment->GetIntValue("VspBspTree.Termination.minRays", mTermMinRays);
75        environment->GetFloatValue("VspBspTree.Termination.minProbability", mTermMinProbability);
76        environment->GetFloatValue("VspBspTree.Termination.maxRayContribution", mTermMaxRayContribution);
77        environment->GetFloatValue("VspBspTree.Termination.minAccRayLenght", mTermMinAccRayLength);
78        environment->GetFloatValue("VspBspTree.Termination.maxCostRatio", mTermMaxCostRatio);
79        environment->GetIntValue("VspBspTree.Termination.missTolerance", mTermMissTolerance);
80        environment->GetIntValue("VspBspTree.Termination.maxViewCells", mMaxViewCells);
81
82        //-- max cost ratio for early tree termination
83        environment->GetFloatValue("VspBspTree.Termination.maxCostRatio", mTermMaxCostRatio);
84
85        environment->GetFloatValue("VspBspTree.Termination.minGlobalCostRatio", mTermMinGlobalCostRatio);
86        environment->GetIntValue("VspBspTree.Termination.globalCostMissTolerance", mTermGlobalCostMissTolerance);
87
88        // HACK//mTermMinPolygons = 25;
89
90        //-- factors for bsp tree split plane heuristics
91        environment->GetFloatValue("VspBspTree.Factor.pvs", mPvsFactor);
92        environment->GetFloatValue("VspBspTree.Termination.ct_div_ci", mCtDivCi);
93
94
95        //-- partition criteria
96        environment->GetIntValue("VspBspTree.maxPolyCandidates", mMaxPolyCandidates);
97        environment->GetIntValue("VspBspTree.maxRayCandidates", mMaxRayCandidates);
98        environment->GetIntValue("VspBspTree.splitPlaneStrategy", mSplitPlaneStrategy);
99
100        environment->GetFloatValue("VspBspTree.Construction.epsilon", mEpsilon);
101        environment->GetIntValue("VspBspTree.maxTests", mMaxTests);
102
103        // if only the driving axis is used for axis aligned split
104        environment->GetBoolValue("VspBspTree.splitUseOnlyDrivingAxis", mOnlyDrivingAxis);
105       
106        //-- termination criteria for axis aligned split
107        environment->GetFloatValue("VspBspTree.Termination.AxisAligned.maxRayContribution",
108                                                                mTermMaxRayContriForAxisAligned);
109        environment->GetIntValue("VspBspTree.Termination.AxisAligned.minRays",
110                                                         mTermMinRaysForAxisAligned);
111
112        //environment->GetFloatValue("VspBspTree.maxTotalMemory", mMaxTotalMemory);
113        environment->GetFloatValue("VspBspTree.maxStaticMemory", mMaxMemory);
114
115        environment->GetFloatValue("VspBspTree.Construction.renderCostWeight", mRenderCostWeight);
116        environment->GetBoolValue("VspBspTree.usePolygonSplitIfAvailable", mUsePolygonSplitIfAvailable);
117
118        environment->GetBoolValue("VspBspTree.useCostHeuristics", mUseCostHeuristics);
119        environment->GetBoolValue("VspBspTree.useSplitCostQueue", mUseSplitCostQueue);
120        environment->GetBoolValue("VspBspTree.simulateOctree", mSimulateOctree);
121        environment->GetBoolValue("VspBspTree.useRandomAxis", mUseRandomAxis);
122        environment->GetBoolValue("VspBspTree.useBreathFirstSplits", mBreathFirstSplits);
123
124        environment->GetBoolValue("ViewCells.PostProcess.emptyViewCellsMerge", mEmptyViewCellsMergeAllowed);
125       
126        char subdivisionStatsLog[100];
127        environment->GetStringValue("VspBspTree.subdivisionStats", subdivisionStatsLog);
128        mSubdivisionStats.open(subdivisionStatsLog);
129
130        //-- debug output
131
132        Debug << "******* VSP BSP options ******** " << endl;
133    Debug << "max depth: " << mTermMaxDepth << endl;
134        Debug << "min PVS: " << mTermMinPvs << endl;
135        Debug << "min probabiliy: " << mTermMinProbability << endl;
136        Debug << "min rays: " << mTermMinRays << endl;
137        Debug << "max ray contri: " << mTermMaxRayContribution << endl;
138        Debug << "max cost ratio: " << mTermMaxCostRatio << endl;
139        Debug << "miss tolerance: " << mTermMissTolerance << endl;
140        Debug << "max view cells: " << mMaxViewCells << endl;
141        Debug << "max polygon candidates: " << mMaxPolyCandidates << endl;
142        //Debug << "max plane candidates: " << mMaxRayCandidates << endl;
143        Debug << "randomize: " << randomize << endl;
144
145        Debug << "using area for pvs: " << mUseAreaForPvs << endl;
146        Debug << "render cost weight: " << mRenderCostWeight << endl;
147        Debug << "min global cost ratio: " << mTermMinGlobalCostRatio << endl;
148        Debug << "global cost miss tolerance: " << mTermGlobalCostMissTolerance << endl;
149        Debug << "only driving axis: " << mOnlyDrivingAxis << endl;
150        Debug << "max memory: " << mMaxMemory << endl;
151        Debug << "use poly split if available: " << mUsePolygonSplitIfAvailable << endl;
152        Debug << "use cost heuristics: " << mUseCostHeuristics << endl;
153        Debug << "use split cost queue: " << mUseSplitCostQueue << endl;
154        Debug << "subdivision stats log: " << subdivisionStatsLog << endl;
155        Debug << "use random axis: " << mUseRandomAxis << endl;
156        Debug << "breath first splits: " << mBreathFirstSplits << endl;
157        Debug << "empty view cells merge: " << mEmptyViewCellsMergeAllowed << endl;
158
159        Debug << "octree: " << mSimulateOctree << endl;
160
161        Debug << "Split plane strategy: ";
162
163        if (mSplitPlaneStrategy & RANDOM_POLYGON)
164        {
165                Debug << "random polygon ";
166        }
167        if (mSplitPlaneStrategy & AXIS_ALIGNED)
168        {
169                Debug << "axis aligned ";
170        }
171        if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
172        {
173                mCostNormalizer += mLeastRaySplitsFactor;
174                Debug << "least ray splits ";
175        }
176        if (mSplitPlaneStrategy & BALANCED_RAYS)
177        {
178                mCostNormalizer += mBalancedRaysFactor;
179                Debug << "balanced rays ";
180        }
181        if (mSplitPlaneStrategy & PVS)
182        {
183                mCostNormalizer += mPvsFactor;
184                Debug << "pvs";
185        }
186
187
188        mSplitCandidates = new vector<SortableEntry>;
189
190        Debug << endl;
191}
192
193
194BspViewCell *VspBspTree::GetOutOfBoundsCell()
195{
196        return mOutOfBoundsCell;
197}
198
199
200BspViewCell *VspBspTree::GetOrCreateOutOfBoundsCell()
201{
202        if (!mOutOfBoundsCell)
203        {
204                mOutOfBoundsCell = new BspViewCell();
205                mOutOfBoundsCell->SetId(-1);
206                mOutOfBoundsCell->SetValid(false);
207        }
208
209        return mOutOfBoundsCell;
210}
211
212
213const BspTreeStatistics &VspBspTree::GetStatistics() const
214{
215        return mBspStats;
216}
217
218
219VspBspTree::~VspBspTree()
220{
221        DEL_PTR(mRoot);
222        DEL_PTR(mSplitCandidates);
223}
224
225
226int VspBspTree::AddMeshToPolygons(Mesh *mesh,
227                                                                  PolygonContainer &polys,
228                                                                  MeshInstance *parent)
229{
230        FaceContainer::const_iterator fi;
231
232        // copy the face data to polygons
233        for (fi = mesh->mFaces.begin(); fi != mesh->mFaces.end(); ++ fi)
234        {
235                Polygon3 *poly = new Polygon3((*fi), mesh);
236
237                if (poly->Valid(mEpsilon))
238                {
239                        poly->mParent = parent; // set parent intersectable
240                        polys.push_back(poly);
241                }
242                else
243                        DEL_PTR(poly);
244        }
245        return (int)mesh->mFaces.size();
246}
247
248
249int VspBspTree::AddToPolygonSoup(const ViewCellContainer &viewCells,
250                                                          PolygonContainer &polys,
251                                                          int maxObjects)
252{
253        int limit = (maxObjects > 0) ?
254                Min((int)viewCells.size(), maxObjects) : (int)viewCells.size();
255
256        int polysSize = 0;
257
258        for (int i = 0; i < limit; ++ i)
259        {
260                if (viewCells[i]->GetMesh()) // copy the mesh data to polygons
261                {
262                        mBox.Include(viewCells[i]->GetBox()); // add to BSP tree aabb
263                        polysSize +=
264                                AddMeshToPolygons(viewCells[i]->GetMesh(),
265                                                                  polys,
266                                                                  viewCells[i]);
267                }
268        }
269
270        return polysSize;
271}
272
273
274int VspBspTree::AddToPolygonSoup(const ObjectContainer &objects,
275                                                                 PolygonContainer &polys,
276                                                                 int maxObjects)
277{
278        int limit = (maxObjects > 0) ?
279                Min((int)objects.size(), maxObjects) : (int)objects.size();
280
281        for (int i = 0; i < limit; ++i)
282        {
283                Intersectable *object = objects[i];//*it;
284                Mesh *mesh = NULL;
285
286                switch (object->Type()) // extract the meshes
287                {
288                case Intersectable::MESH_INSTANCE:
289                        mesh = dynamic_cast<MeshInstance *>(object)->GetMesh();
290                        break;
291                case Intersectable::VIEW_CELL:
292                        mesh = dynamic_cast<ViewCell *>(object)->GetMesh();
293                        break;
294                        // TODO: handle transformed mesh instances
295                default:
296                        Debug << "intersectable type not supported" << endl;
297                        break;
298                }
299
300        if (mesh) // copy the mesh data to polygons
301                {
302                        mBox.Include(object->GetBox()); // add to BSP tree aabb
303                        AddMeshToPolygons(mesh, polys, NULL);
304                }
305        }
306
307        return (int)polys.size();
308}
309
310
311void VspBspTree::Construct(const VssRayContainer &sampleRays,
312                                                   AxisAlignedBox3 *forcedBoundingBox)
313{
314        mBspStats.nodes = 1;
315        mBox.Initialize();      // initialise BSP tree bounding box
316
317        if (forcedBoundingBox)
318                mBox = *forcedBoundingBox;
319       
320        PolygonContainer polys;
321        RayInfoContainer *rays = new RayInfoContainer();
322
323        VssRayContainer::const_iterator rit, rit_end = sampleRays.end();
324
325        long startTime = GetTime();
326
327        cout << "Extracting polygons from rays ... ";
328
329        Intersectable::NewMail();
330
331        int numObj = 0;
332
333        //-- extract polygons intersected by the rays
334        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
335        {
336                VssRay *ray = *rit;
337
338                if ((mBox.IsInside(ray->mTermination) || !forcedBoundingBox) &&
339                        ray->mTerminationObject &&
340                        !ray->mTerminationObject->Mailed())
341                {
342                        ray->mTerminationObject->Mail();
343                        MeshInstance *obj = dynamic_cast<MeshInstance *>(ray->mTerminationObject);
344                        AddMeshToPolygons(obj->GetMesh(), polys, obj);
345                        ++ numObj;
346
347                        //-- compute bounding box
348                        if (!forcedBoundingBox)
349                                mBox.Include(ray->mTermination);
350                }
351
352                if ((mBox.IsInside(ray->mOrigin) || !forcedBoundingBox) &&
353                        ray->mOriginObject &&
354                        !ray->mOriginObject->Mailed())
355                {
356                        ray->mOriginObject->Mail();
357                        MeshInstance *obj = dynamic_cast<MeshInstance *>(ray->mOriginObject);
358                        AddMeshToPolygons(obj->GetMesh(), polys, obj);
359                        ++ numObj;
360
361                        //-- compute bounding box
362                        if (!forcedBoundingBox)
363                                mBox.Include(ray->mOrigin);
364                }
365        }
366       
367        Debug << "maximal pvs (i.e., pvs still considered as valid) : "
368                  << mViewCellsManager->GetMaxPvsSize() << endl;
369
370        //-- store rays
371        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
372        {
373                VssRay *ray = *rit;
374
375                float minT, maxT;
376
377                static Ray hray;
378                hray.Init(*ray);
379
380                // TODO: not very efficient to implictly cast between rays types
381                if (mBox.GetRaySegment(hray, minT, maxT))
382                {
383                        float len = ray->Length();
384
385                        if (!len)
386                                len = Limits::Small;
387
388                        rays->push_back(RayInfo(ray, minT / len, maxT / len));
389                }
390        }
391
392        // normalize
393        if (mUseAreaForPvs)
394                mTermMinProbability *= mBox.SurfaceArea();
395        else
396                mTermMinProbability *= mBox.GetVolume();
397
398        // throw out unnecessary polygons
399        PreprocessPolygons(polys);
400
401        mBspStats.polys = (int)polys.size();
402        mGlobalCostMisses = 0;
403
404        cout << "finished" << endl;
405
406        Debug << "\nPolygon extraction: " << (int)polys.size() << " polys extracted from "
407                  << (int)sampleRays.size() << " rays in "
408                  << TimeDiff(startTime, GetTime())*1e-3 << " secs" << endl << endl;
409
410        // use split cost priority queue
411        if (mUseSplitCostQueue)
412        {
413                ConstructWithSplitQueue(polys, rays);
414        }
415        else
416        {
417                Construct(polys, rays);
418        }
419
420        // clean up polygons
421        CLEAR_CONTAINER(polys);
422}
423
424
425// TODO: return memory usage in MB
426float VspBspTree::GetMemUsage() const
427{
428        return (float)
429                 (sizeof(VspBspTree) +
430                  mBspStats.Leaves() * sizeof(BspLeaf) +
431                  mCreatedViewCells * sizeof(BspViewCell) +
432                  mBspStats.pvs * sizeof(ObjectPvsData) +
433                  mBspStats.Interior() * sizeof(BspInterior) +
434                  mBspStats.accumRays * sizeof(RayInfo)) / (1024.0f * 1024.0f);
435}
436
437
438
439void VspBspTree::Construct(const PolygonContainer &polys, RayInfoContainer *rays)
440{
441        VspBspTraversalQueue tQueue;
442
443        mRoot = new BspLeaf();
444
445        // constrruct root node geometry
446        BspNodeGeometry *geom = new BspNodeGeometry();
447        ConstructGeometry(mRoot, *geom);
448
449        const float prop = mUseAreaForPvs ? geom->GetArea() : geom->GetVolume();
450
451        VspBspTraversalData tData(mRoot,
452                                                          new PolygonContainer(polys),
453                                                          0,
454                                                          rays,
455                              ComputePvsSize(*rays),
456                                                          prop,
457                                                          geom);
458
459        EvalPriority(tData);
460       
461
462        if (mSimulateOctree)
463        tData.mAxis = 0;
464
465        // first node is kd node, i.e. an axis aligned box
466        if (0)
467        tData.mIsKdNode = true;
468        else
469                tData.mIsKdNode = false;
470
471        tQueue.push(tData);
472
473
474        mTotalCost = tData.mPvs * tData.mProbability / mBox.GetVolume();
475        mTotalPvsSize = tData.mPvs;
476       
477        mSubdivisionStats
478                        << "#ViewCells\n1\n" <<  endl
479                        << "#RenderCostDecrease\n0\n" << endl
480                        << "#TotalRenderCost\n" << mTotalCost << endl
481                        << "#AvgRenderCost\n" << mTotalPvsSize << endl;
482
483        Debug << "total cost: " << mTotalCost << endl;
484       
485       
486        mBspStats.Start();
487        cout << "Constructing vsp bsp tree ... \n";
488
489        long startTime = GetTime();     
490        int nLeaves = 500;
491        int nViewCells = 500;
492
493        // used for intermediate time measurements and progress
494        long interTime = GetTime();     
495
496        mOutOfMemory = false;
497
498        mCreatedViewCells = 0;
499       
500        while (!tQueue.empty())
501        {
502                tData = tQueue.top();
503            tQueue.pop();               
504
505                if (0 && !mOutOfMemory)
506                {
507                        float mem = GetMemUsage();
508
509                        if (mem > mMaxMemory)
510                        {
511                                mOutOfMemory = true;
512                                Debug << "memory limit reached: " << mem << endl;
513                        }
514                }
515
516                // subdivide leaf node
517                BspNode *r = Subdivide(tQueue, tData);
518
519                if (r == mRoot)
520                        Debug << "VSP BSP tree construction time spent at root: "
521                                  << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
522
523                if (mBspStats.Leaves() >= nLeaves)
524                {
525                        nLeaves += 500;
526
527                        cout << "leaves=" << mBspStats.Leaves() << endl;
528                        Debug << "needed "
529                                  << TimeDiff(interTime, GetTime())*1e-3
530                                  << " secs to create 500 view cells" << endl;
531                        interTime = GetTime();
532                }
533
534                if (mCreatedViewCells >= nViewCells)
535                {
536                        nViewCells += 500;
537
538                        cout << "generated " << mCreatedViewCells << " viewcells" << endl;
539                }
540        }
541
542        Debug << "Used Memory: " << GetMemUsage() << " MB" << endl << endl;
543        cout << "finished\n";
544
545        mBspStats.Stop();
546}
547
548
549
550void VspBspTree::ConstructWithSplitQueue(const PolygonContainer &polys,
551                                                                                          RayInfoContainer *rays)
552{
553        VspBspSplitQueue tQueue;
554
555        mRoot = new BspLeaf();
556
557        // constrruct root node geometry
558        BspNodeGeometry *geom = new BspNodeGeometry();
559        ConstructGeometry(mRoot, *geom);
560
561        const float prop = mUseAreaForPvs ? geom->GetArea() : geom->GetVolume();
562
563        VspBspTraversalData tData(mRoot,
564                                                          new PolygonContainer(polys),
565                                                          0,
566                                                          rays,
567                              ComputePvsSize(*rays),
568                                                          prop,
569                                                          geom);
570
571       
572
573        // compute first split candidate
574        VspBspSplitCandidate splitCandidate;
575        EvalSplitCandidate(tData, splitCandidate);
576
577        tQueue.push(splitCandidate);
578
579        mTotalCost = tData.mPvs * tData.mProbability / mBox.GetVolume();
580        mTotalPvsSize = tData.mPvs;
581       
582        mSubdivisionStats
583                        << "#ViewCells\n1\n" <<  endl
584                        << "#RenderCostDecrease\n0\n" << endl
585                        << "#dummy\n0\n" << endl
586                        << "#TotalRenderCost\n" << mTotalCost << endl
587                        << "#AvgRenderCost\n" << mTotalPvsSize << endl;
588
589        Debug << "total cost: " << mTotalCost << endl;
590       
591       
592        mBspStats.Start();
593        cout << "Constructing vsp bsp tree ... \n";
594
595        long startTime = GetTime();     
596        int nLeaves = 500;
597        int nViewCells = 500;
598
599        // used for intermediate time measurements and progress
600        long interTime = GetTime();     
601
602        mOutOfMemory = false;
603
604        mCreatedViewCells = 0;
605       
606        while (!tQueue.empty())
607        {
608                splitCandidate = tQueue.top();
609            tQueue.pop();               
610
611                // cost ratio of cost decrease / totalCost
612                float costRatio = splitCandidate.GetCost() / mTotalCost;
613
614                //Debug << "cost ratio: " << costRatio << endl;
615
616                if (costRatio < mTermMinGlobalCostRatio)
617                        ++ mGlobalCostMisses;
618               
619                if (0 && !mOutOfMemory)
620                {
621                        float mem = GetMemUsage();
622
623                        if (mem > mMaxMemory)
624                        {
625                                mOutOfMemory = true;
626                                Debug << "memory limit reached: " << mem << endl;
627                        }
628                }
629
630                // subdivide leaf node
631                BspNode *r = Subdivide(tQueue, splitCandidate);
632
633                if (r == mRoot)
634                        Debug << "VSP BSP tree construction time spent at root: "
635                                  << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
636
637                if (mBspStats.Leaves() >= nLeaves)
638                {
639                        nLeaves += 500;
640
641                        cout << "leaves=" << mBspStats.Leaves() << endl;
642                        Debug << "needed "
643                                  << TimeDiff(interTime, GetTime())*1e-3
644                                  << " secs to create 500 view cells" << endl;
645                        interTime = GetTime();
646                }
647
648                if (mCreatedViewCells == nViewCells)
649                {
650                        nViewCells += 500;
651
652                        cout << "generated " << mCreatedViewCells << " viewcells" << endl;
653                }
654        }
655
656        Debug << "Used Memory: " << GetMemUsage() << " MB" << endl << endl;
657        cout << "finished\n";
658
659        mBspStats.Stop();
660}
661
662
663bool VspBspTree::LocalTerminationCriteriaMet(const VspBspTraversalData &data) const
664{
665        return
666                (((int)data.mRays->size() <= mTermMinRays) ||
667                 (data.mPvs <= mTermMinPvs)   ||
668                 (data.mProbability <= mTermMinProbability) ||
669                 (data.GetAvgRayContribution() > mTermMaxRayContribution) ||
670                 (data.mDepth >= mTermMaxDepth));
671}
672
673
674bool VspBspTree::GlobalTerminationCriteriaMet(const VspBspTraversalData &data) const
675{
676        return
677                (mOutOfMemory
678                || (mBspStats.Leaves() >= mMaxViewCells)
679                || (mGlobalCostMisses >= mTermGlobalCostMissTolerance)
680                 );
681}
682
683
684
685BspNode *VspBspTree::Subdivide(VspBspTraversalQueue &tQueue,
686                                                           VspBspTraversalData &tData)
687{
688        BspNode *newNode = tData.mNode;
689
690        if (!LocalTerminationCriteriaMet(tData) && !GlobalTerminationCriteriaMet(tData))
691        {
692                PolygonContainer coincident;
693
694                VspBspTraversalData tFrontData;
695                VspBspTraversalData tBackData;
696
697                if (mSimulateOctree)
698                {       // choose axes in circular motion
699                        tFrontData.mAxis = (tData.mAxis + 1) % 3;
700                        tBackData.mAxis = (tData.mAxis + 1) % 3;
701                }
702
703                // create new interior node and two leaf nodes
704                // or return leaf as it is (if maxCostRatio missed)
705                int splitAxis;
706                bool splitFurther = true;
707                int maxCostMisses = tData.mMaxCostMisses;
708               
709                Plane3 splitPlane;
710                BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
711
712                if (!SelectPlane(splitPlane, leaf, tData, tFrontData, tBackData, splitAxis))
713                {
714                        ++ maxCostMisses;
715
716                        if (maxCostMisses > mTermMissTolerance)
717                        {
718                                // terminate branch because of max cost
719                                ++ mBspStats.maxCostNodes;
720                                splitFurther = false;
721                        }
722                }
723       
724                if (splitFurther) //-- continue subdivision
725                {
726                        newNode = SubdivideNode(splitPlane, tData, tFrontData, tBackData, coincident);
727
728                        if (splitAxis < 3)
729                                ++ mBspStats.splits[splitAxis];
730                        else
731                                ++ mBspStats.polySplits;
732
733                        tFrontData.mIsKdNode = tBackData.mIsKdNode = (tData.mIsKdNode && splitAxis < 3);
734
735                        // how often was max cost ratio missed in this branch?
736                        tFrontData.mMaxCostMisses = maxCostMisses;
737                        tBackData.mMaxCostMisses = maxCostMisses;
738
739                        EvalPriority(tFrontData);
740                        EvalPriority(tBackData);
741
742                        if (1)
743                        {
744
745                                float cFront = (float)tFrontData.mPvs * tFrontData.mProbability;
746                                float cBack = (float)tBackData.mPvs * tBackData.mProbability;
747                                float cData = (float)tData.mPvs * tData.mProbability;;
748
749                float costDecr = (cFront + cBack - cData) / mBox.GetVolume();
750
751                                mTotalCost += costDecr;
752                                mTotalPvsSize += tFrontData.mPvs + tBackData.mPvs - tData.mPvs;
753
754                                mSubdivisionStats
755                                                << "#ViewCells\n" << mBspStats.Leaves() << endl
756                                                << "#RenderCostDecrease\n" << -costDecr << endl
757                                                << "#TotalRenderCost\n" << mTotalCost << endl
758                                                << "#AvgRenderCost\n" << (float)mTotalPvsSize / (float)mBspStats.Leaves() << endl;
759                        }
760
761                        // push the children on the stack
762                        tQueue.push(tFrontData);
763                        tQueue.push(tBackData);
764
765                        // delete old leaf node
766                        DEL_PTR(tData.mNode);
767                }
768        }
769
770        //-- terminate traversal and create new view cell
771        if (newNode->IsLeaf())
772        {
773                BspLeaf *leaf = dynamic_cast<BspLeaf *>(newNode);
774                BspViewCell *viewCell = new BspViewCell();
775               
776                leaf->SetViewCell(viewCell);
777       
778                //-- update pvs
779                int conSamp = 0;
780                float sampCon = 0.0f;
781                AddToPvs(leaf, *tData.mRays, sampCon, conSamp);
782
783                mBspStats.contributingSamples += conSamp;
784                mBspStats.sampleContributions +=(int) sampCon;
785
786                //-- store additional info
787                if (mStoreRays)
788                {
789                        RayInfoContainer::const_iterator it, it_end = tData.mRays->end();
790                        for (it = tData.mRays->begin(); it != it_end; ++ it)
791                        {
792                                (*it).mRay->Ref();                     
793                                leaf->mVssRays.push_back((*it).mRay);
794                        }
795                }
796
797                // should I check here?
798                if (0 && !mViewCellsManager->CheckValidity(viewCell, 0, mViewCellsManager->GetMaxPvsSize()))
799                {
800                        viewCell->SetValid(false);
801                        leaf->SetTreeValid(false);
802                        PropagateUpValidity(leaf);
803
804                        ++ mBspStats.invalidLeaves;
805                }
806               
807        viewCell->mLeaf = leaf;
808
809                if (mUseAreaForPvs)
810                        viewCell->SetArea(tData.mProbability);
811                else
812                        viewCell->SetVolume(tData.mProbability);
813
814                leaf->mProbability = tData.mProbability;
815
816                EvaluateLeafStats(tData);               
817        }
818
819        //-- cleanup
820        tData.Clear();
821
822        return newNode;
823}
824
825
826BspNode *VspBspTree::Subdivide(VspBspSplitQueue &tQueue,
827                                                           VspBspSplitCandidate &splitCandidate)
828{
829        VspBspTraversalData &tData = splitCandidate.mParentData;
830
831        BspNode *newNode = tData.mNode;
832
833        if (!LocalTerminationCriteriaMet(tData) && !GlobalTerminationCriteriaMet(tData))
834        {       
835                PolygonContainer coincident;
836
837                VspBspTraversalData tFrontData;
838                VspBspTraversalData tBackData;
839
840                //-- continue subdivision
841                // create new interior node and two leaf node
842                const Plane3 splitPlane = splitCandidate.mSplitPlane;
843                               
844                newNode = SubdivideNode(splitPlane, tData, tFrontData, tBackData, coincident);
845       
846                const int splitAxis = splitCandidate.mSplitAxis;
847                const int maxCostMisses = splitCandidate.mMaxCostMisses;
848
849                if (splitAxis < 3)
850                        ++ mBspStats.splits[splitAxis];
851                else
852                        ++ mBspStats.polySplits;
853
854                tFrontData.mIsKdNode = tBackData.mIsKdNode = (tData.mIsKdNode && splitAxis < 3);
855               
856                // how often was max cost ratio missed in this branch?
857                tFrontData.mMaxCostMisses = maxCostMisses;
858                tBackData.mMaxCostMisses = maxCostMisses;
859                       
860               
861                if (1)
862                {
863                        float cFront = (float)tFrontData.mPvs * tFrontData.mProbability;
864                        float cBack = (float)tBackData.mPvs * tBackData.mProbability;
865                        float cData = (float)tData.mPvs * tData.mProbability;
866
867                       
868                        float costDecr =
869                                (cFront + cBack - cData) / mBox.GetVolume();
870
871                        mTotalCost += costDecr;
872                        mTotalPvsSize += tFrontData.mPvs + tBackData.mPvs - tData.mPvs;
873
874                        mSubdivisionStats
875                                        << "#ViewCells\n" << mBspStats.Leaves() << endl
876                                        << "#RenderCostDecrease\n" << -costDecr << endl
877                                        << "#dummy\n" << splitCandidate.GetCost() << endl
878                                        << "#TotalRenderCost\n" << mTotalCost << endl
879                                        << "#AvgRenderCost\n" << (float)mTotalPvsSize / (float)mBspStats.Leaves() << endl;
880                }
881
882       
883                //-- push the new split candidates on the stack
884                VspBspSplitCandidate frontCandidate;
885                VspBspSplitCandidate backCandidate;
886
887                EvalSplitCandidate(tFrontData, frontCandidate);
888                EvalSplitCandidate(tBackData, backCandidate);
889       
890                tQueue.push(frontCandidate);
891                tQueue.push(backCandidate);
892       
893                // delete old leaf node
894                DEL_PTR(tData.mNode);
895        }
896
897
898        //-- terminate traversal and create new view cell
899        if (newNode->IsLeaf())
900        {
901                BspLeaf *leaf = dynamic_cast<BspLeaf *>(newNode);
902                BspViewCell *viewCell = new BspViewCell();
903               
904                leaf->SetViewCell(viewCell);
905       
906                //-- update pvs
907                int conSamp = 0;
908                float sampCon = 0.0f;
909                AddToPvs(leaf, *tData.mRays, sampCon, conSamp);
910
911                mBspStats.contributingSamples += conSamp;
912                mBspStats.sampleContributions +=(int) sampCon;
913
914                //-- store additional info
915                if (mStoreRays)
916                {
917                        RayInfoContainer::const_iterator it, it_end = tData.mRays->end();
918                        for (it = tData.mRays->begin(); it != it_end; ++ it)
919                        {
920                                (*it).mRay->Ref();                     
921                                leaf->mVssRays.push_back((*it).mRay);
922                        }
923                }
924
925                // should I check here?
926                viewCell->mLeaf = leaf;
927
928                if (mUseAreaForPvs)
929                        viewCell->SetArea(tData.mProbability);
930                else
931                        viewCell->SetVolume(tData.mProbability);
932
933        leaf->mProbability = tData.mProbability;
934
935                EvaluateLeafStats(tData);               
936        }
937
938        //-- cleanup
939        tData.Clear();
940
941        return newNode;
942}
943
944
945void VspBspTree::EvalPriority(VspBspTraversalData &tData) const
946{
947    tData.mPriority = mBreathFirstSplits ?
948                (float)-tData.mDepth : tData.mPvs * tData.mProbability;
949        //cout << "priority: " << tData.mPriority << endl;
950}
951
952
953void VspBspTree::EvalSplitCandidate(VspBspTraversalData &tData,
954                                                                        VspBspSplitCandidate &splitData)
955{
956        VspBspTraversalData frontData;
957        VspBspTraversalData backData;
958
959        BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
960
961        // compute locally best split plane
962        bool success = SelectPlane(splitData.mSplitPlane, leaf, tData,
963                                                           frontData, backData, splitData.mSplitAxis);
964
965        // TODO: reuse
966        DEL_PTR(frontData.mGeometry);
967        DEL_PTR(backData.mGeometry);
968       
969        // compute global decrease in render cost
970        splitData.mRenderCost = EvalRenderCostDecrease(splitData.mSplitPlane, tData);
971        splitData.mParentData = tData;
972        splitData.mMaxCostMisses = success ? tData.mMaxCostMisses : tData.mMaxCostMisses + 1;
973}
974
975
976BspInterior *VspBspTree::SubdivideNode(const Plane3 &splitPlane,
977                                                                           VspBspTraversalData &tData,
978                                                                           VspBspTraversalData &frontData,
979                                                                           VspBspTraversalData &backData,
980                                                                           PolygonContainer &coincident)
981{
982        BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
983       
984        //-- the front and back traversal data is filled with the new values
985        frontData.mDepth = tData.mDepth + 1;
986        frontData.mPolygons = new PolygonContainer();
987        frontData.mRays = new RayInfoContainer();
988       
989        backData.mDepth = tData.mDepth + 1;
990        backData.mPolygons = new PolygonContainer();
991        backData.mRays = new RayInfoContainer();
992       
993
994        //-- subdivide rays
995        SplitRays(splitPlane,
996                          *tData.mRays,
997                          *frontData.mRays,
998                          *backData.mRays);
999
1000
1001        // compute pvs
1002        frontData.mPvs = ComputePvsSize(*frontData.mRays);
1003        backData.mPvs = ComputePvsSize(*backData.mRays);
1004
1005        // split front and back node geometry and compute area
1006       
1007        // if geometry was not already computed
1008        if (!frontData.mGeometry && !backData.mGeometry)
1009        {
1010                frontData.mGeometry = new BspNodeGeometry();
1011                backData.mGeometry = new BspNodeGeometry();
1012
1013                tData.mGeometry->SplitGeometry(*frontData.mGeometry,
1014                                                                           *backData.mGeometry,
1015                                                                           splitPlane,
1016                                                                           mBox,
1017                                                                           //0.0f);
1018                                                                           mEpsilon);
1019               
1020                if (mUseAreaForPvs)
1021                {
1022                        frontData.mProbability = frontData.mGeometry->GetArea();
1023                        backData.mProbability = backData.mGeometry->GetArea();
1024                }
1025                else
1026                {
1027                        frontData.mProbability = frontData.mGeometry->GetVolume();
1028                        backData.mProbability = tData.mProbability - frontData.mProbability;
1029
1030                        if (frontData.mProbability < -0.00001)
1031                                Debug << "fatal error f: " << frontData.mProbability << endl;
1032                        if (backData.mProbability < -0.00001)
1033                                Debug << "fatal error b: " << backData.mProbability << endl;
1034
1035                        // clamp because of precision issues
1036                        if (0)
1037                        {
1038                                if (frontData.mProbability < 0) frontData.mProbability = 0;
1039                                if (backData.mProbability < 0) backData.mProbability = 0;
1040                        }
1041                }
1042        }
1043
1044       
1045    // subdivide polygons
1046        SplitPolygons(splitPlane,
1047                                  *tData.mPolygons,
1048                      *frontData.mPolygons,
1049                                  *backData.mPolygons,
1050                                  coincident);
1051
1052
1053
1054        ///////////////////////////////////////
1055        // subdivide further
1056
1057        mBspStats.nodes += 2;
1058
1059       
1060        BspInterior *interior = new BspInterior(splitPlane);
1061
1062#ifdef _DEBUG
1063        Debug << interior << endl;
1064#endif
1065
1066        //-- create front and back leaf
1067
1068        BspInterior *parent = leaf->GetParent();
1069
1070        // replace a link from node's parent
1071        if (parent)
1072        {
1073                parent->ReplaceChildLink(leaf, interior);
1074                interior->SetParent(parent);
1075        }
1076        else // new root
1077        {
1078                mRoot = interior;
1079        }
1080
1081        // and setup child links
1082        interior->SetupChildLinks(new BspLeaf(interior), new BspLeaf(interior));
1083
1084        frontData.mNode = interior->GetFront();
1085        backData.mNode = interior->GetBack();
1086
1087        interior->mTimeStamp = mTimeStamp ++;
1088       
1089        //DEL_PTR(leaf);
1090        return interior;
1091}
1092
1093
1094void VspBspTree::AddToPvs(BspLeaf *leaf,
1095                                                  const RayInfoContainer &rays,
1096                                                  float &sampleContributions,
1097                                                  int &contributingSamples)
1098{
1099  sampleContributions = 0;
1100  contributingSamples = 0;
1101 
1102  RayInfoContainer::const_iterator it, it_end = rays.end();
1103 
1104  ViewCell *vc = leaf->GetViewCell();
1105 
1106  // add contributions from samples to the PVS
1107  for (it = rays.begin(); it != it_end; ++ it)
1108        {
1109          float sc = 0.0f;
1110          VssRay *ray = (*it).mRay;
1111          bool madeContrib = false;
1112          float contribution;
1113          if (ray->mTerminationObject) {
1114                if (vc->GetPvs().AddSample(ray->mTerminationObject, ray->mPdf, contribution))
1115                  madeContrib = true;
1116                sc += contribution;
1117          }
1118         
1119          if (ray->mOriginObject) {
1120                if (vc->GetPvs().AddSample(ray->mOriginObject, ray->mPdf, contribution))
1121                  madeContrib = true;
1122                sc += contribution;
1123          }
1124         
1125          sampleContributions += sc;
1126          if (madeContrib)
1127                  ++ contributingSamples;
1128               
1129          //leaf->mVssRays.push_back(ray);
1130        }
1131}
1132
1133
1134void VspBspTree::SortSplitCandidates(const RayInfoContainer &rays, const int axis)
1135{
1136        mSplitCandidates->clear();
1137
1138        int requestedSize = 2 * (int)(rays.size());
1139        // creates a sorted split candidates array
1140        if (mSplitCandidates->capacity() > 500000 &&
1141                requestedSize < (int)(mSplitCandidates->capacity()  / 10) )
1142        {
1143        delete mSplitCandidates;
1144                mSplitCandidates = new vector<SortableEntry>;
1145        }
1146
1147        mSplitCandidates->reserve(requestedSize);
1148
1149        // insert all queries
1150        for (RayInfoContainer::const_iterator ri = rays.begin(); ri < rays.end(); ++ ri)
1151        {
1152                const bool positive = (*ri).mRay->HasPosDir(axis);
1153                mSplitCandidates->push_back(SortableEntry(positive ? SortableEntry::ERayMin : SortableEntry::ERayMax,
1154                                                                                                  (*ri).ExtrapOrigin(axis), (*ri).mRay));
1155
1156                mSplitCandidates->push_back(SortableEntry(positive ? SortableEntry::ERayMax : SortableEntry::ERayMin,
1157                                                                                                  (*ri).ExtrapTermination(axis), (*ri).mRay));
1158        }
1159
1160        stable_sort(mSplitCandidates->begin(), mSplitCandidates->end());
1161}
1162
1163
1164float VspBspTree::BestCostRatioHeuristics(const RayInfoContainer &rays,
1165                                                                                  const AxisAlignedBox3 &box,
1166                                                                                  const int pvsSize,
1167                                                                                  const int &axis,
1168                                          float &position)
1169{
1170        SortSplitCandidates(rays, axis);
1171
1172        // go through the lists, count the number of objects left and right
1173        // and evaluate the following cost funcion:
1174        // C = ct_div_ci  + (ql*rl + qr*rr)/queries
1175
1176        int pvsl = 0, pvsr = pvsSize;
1177
1178        int pvsBack = pvsl;
1179        int pvsFront = pvsr;
1180
1181        float minBox = box.Min(axis);
1182        float maxBox = box.Max(axis);
1183        float sizeBox = maxBox - minBox;
1184
1185        float minBand = minBox + 0.1f * (maxBox - minBox);
1186        float maxBand = minBox + 0.9f * (maxBox - minBox);
1187
1188        float sum = (float)pvsSize * sizeBox;
1189        float minSum = 1e20f;
1190
1191        Intersectable::NewMail();
1192
1193        RayInfoContainer::const_iterator ri, ri_end = rays.end();
1194
1195        // set all object as belonging to the front pvs
1196        for(ri = rays.begin(); ri != ri_end; ++ ri)
1197        {
1198                Intersectable *oObject = (*ri).mRay->mOriginObject;
1199                Intersectable *tObject = (*ri).mRay->mTerminationObject;
1200
1201                if (oObject)
1202                {
1203                        if (!oObject->Mailed())
1204                        {
1205                                oObject->Mail();
1206                                oObject->mCounter = 1;
1207                        }
1208                        else
1209                        {
1210                                ++ oObject->mCounter;
1211                        }
1212                }
1213                if (tObject)
1214                {
1215                        if (!tObject->Mailed())
1216                        {
1217                                tObject->Mail();
1218                                tObject->mCounter = 1;
1219                        }
1220                        else
1221                        {
1222                                ++ tObject->mCounter;
1223                        }
1224                }
1225        }
1226
1227        Intersectable::NewMail();
1228
1229        vector<SortableEntry>::const_iterator ci, ci_end = mSplitCandidates->end();
1230
1231        for (ci = mSplitCandidates->begin(); ci < ci_end; ++ ci)
1232        {
1233                VssRay *ray;
1234                ray = (*ci).ray;
1235               
1236                Intersectable *oObject = ray->mOriginObject;
1237                Intersectable *tObject = ray->mTerminationObject;
1238               
1239
1240                switch ((*ci).type)
1241                {
1242                        case SortableEntry::ERayMin:
1243                                {
1244                                        if (oObject && !oObject->Mailed())
1245                                        {
1246                                                oObject->Mail();
1247                                                ++ pvsl;
1248                                        }
1249                                        if (tObject && !tObject->Mailed())
1250                                        {
1251                                                tObject->Mail();
1252                                                ++ pvsl;
1253                                        }
1254                                        break;
1255                                }
1256                        case SortableEntry::ERayMax:
1257                                {
1258                                        if (oObject)
1259                                        {
1260                                                if (-- oObject->mCounter == 0)
1261                                                        -- pvsr;
1262                                        }
1263
1264                                        if (tObject)
1265                                        {
1266                                                if (-- tObject->mCounter == 0)
1267                                                        -- pvsr;
1268                                        }
1269
1270                                        break;
1271                                }
1272                }
1273
1274                // Note: sufficient to compare size of bounding boxes of front and back side?
1275                if ((*ci).value > minBand && (*ci).value < maxBand)
1276                {
1277                        sum = pvsl * ((*ci).value - minBox) + pvsr * (maxBox - (*ci).value);
1278
1279                        //  cout<<"pos="<<(*ci).value<<"\t q=("<<ql<<","<<qr<<")\t r=("<<rl<<","<<rr<<")"<<endl;
1280                        // cout<<"cost= "<<sum<<endl;
1281
1282                        if (sum < minSum)
1283                        {
1284                                minSum = sum;
1285                                position = (*ci).value;
1286
1287                                pvsBack = pvsl;
1288                                pvsFront = pvsr;
1289                        }
1290                }
1291        }
1292
1293        // -- compute cost
1294
1295        const int lowerPvsLimit = mViewCellsManager->GetMinPvsSize();
1296        const int upperPvsLimit = mViewCellsManager->GetMaxPvsSize();
1297
1298        const float pOverall = sizeBox;
1299
1300        const float pBack = position - minBox;
1301        const float pFront = maxBox - position;
1302       
1303        const float penaltyOld = EvalPvsPenalty(pvsSize, lowerPvsLimit, upperPvsLimit);
1304    const float penaltyFront = EvalPvsPenalty(pvsFront, lowerPvsLimit, upperPvsLimit);
1305        const float penaltyBack = EvalPvsPenalty(pvsBack, lowerPvsLimit, upperPvsLimit);
1306       
1307        const float oldRenderCost = penaltyOld * pOverall;
1308        const float newRenderCost = penaltyFront * pFront + penaltyBack * pBack;
1309
1310        float ratio = mPvsFactor * newRenderCost / (oldRenderCost + Limits::Small);
1311
1312        //Debug << "costRatio=" << ratio << " pos=" << position << " t=" << (position - minBox) / (maxBox - minBox)
1313        //     <<"\t q=(" << queriesBack << "," << queriesFront << ")\t r=(" << raysBack << "," << raysFront << ")" << endl;
1314
1315        return ratio;
1316}
1317
1318
1319float VspBspTree::SelectAxisAlignedPlane(Plane3 &plane,
1320                                                                                 const VspBspTraversalData &tData,
1321                                                                                 int &axis,
1322                                                                                 BspNodeGeometry **frontGeom,
1323                                                                                 BspNodeGeometry **backGeom,
1324                                                                                 float &pFront,
1325                                                                                 float &pBack,
1326                                                                                 const bool useKdSplit)
1327{
1328        float nPosition[3];
1329        float nCostRatio[3];
1330        float nProbFront[3];
1331        float nProbBack[3];
1332
1333        BspNodeGeometry *nFrontGeom[3];
1334        BspNodeGeometry *nBackGeom[3];
1335
1336        for (int i = 0; i < 3; ++ i)
1337        {
1338                nFrontGeom[i] = NULL;
1339                nBackGeom[i] = NULL;
1340        }
1341
1342        int bestAxis = -1;
1343
1344        // create bounding box of node geometry
1345        AxisAlignedBox3 box;
1346        box.Initialize();
1347       
1348        //TODO: for kd split geometry already is box => only take minmax vertices
1349        if (1)
1350        {
1351                PolygonContainer::const_iterator it, it_end = tData.mGeometry->GetPolys().end();
1352
1353                for(it = tData.mGeometry->GetPolys().begin(); it < it_end; ++ it)
1354                        box.Include(*(*it));
1355        }
1356        else
1357        {
1358                RayInfoContainer::const_iterator ri, ri_end = tData.mRays->end();
1359
1360                for(ri = tData.mRays->begin(); ri < ri_end; ++ ri)
1361                        box.Include((*ri).ExtrapTermination());
1362        }
1363
1364        int sAxis = 0;
1365
1366        bool useSpecialAxis = mOnlyDrivingAxis || mUseRandomAxis || mSimulateOctree;
1367
1368        // use some kind of specialised fixed axis
1369        if (mOnlyDrivingAxis)
1370                sAxis = box.Size().DrivingAxis();
1371        else if (mUseRandomAxis)
1372                sAxis = Random(3);
1373        else if (mSimulateOctree)
1374                sAxis = tData.mAxis;
1375               
1376        //Debug << "use special axis: " << useSpecialAxis << endl;
1377        //Debug << "axis: " << sAxis << " drivingaxis: " << box.Size().DrivingAxis();
1378
1379        for (axis = 0; axis < 3; ++ axis)
1380        {
1381                if (!useSpecialAxis || (axis == sAxis))
1382                {
1383                        if (!mUseCostHeuristics)
1384                        {
1385                                nFrontGeom[axis] = new BspNodeGeometry();
1386                                nBackGeom[axis] = new BspNodeGeometry();
1387
1388                                nPosition[axis] = (box.Min()[axis] + box.Max()[axis]) * 0.5f;
1389                                Vector3 normal(0,0,0); normal[axis] = 1.0f;
1390
1391                                // allows faster split because we have axis aligned kd tree boxes
1392                                if (0 && useKdSplit)
1393                                {
1394                                        nCostRatio[axis] = EvalAxisAlignedSplitCost(tData,
1395                                                                                                                                box,
1396                                                                                                                                axis,
1397                                                                                                                                nPosition[axis],
1398                                                                                                                                nProbFront[axis],
1399                                                                                                                                nProbBack[axis]);
1400                                       
1401                                        Vector3 pos;
1402                                       
1403                                        pos = box.Max(); pos[axis] = nPosition[axis];
1404                                        AxisAlignedBox3 bBox(box.Min(), pos);
1405                                        // TODO
1406#if 0
1407                                                bBox.ExtractPolys(nBackGeom[axis]->GetPolys());
1408#endif                                 
1409                                        pos = box.Min(); pos[axis] = nPosition[axis];
1410                                        AxisAlignedBox3 fBox(pos, box.Max());
1411                                        // TODO
1412#if 0
1413                                                fBox.ExtractPolys(nFrontGeom[axis]->GetPolys());
1414#endif
1415                                }
1416                                else
1417                                {
1418                                        nCostRatio[axis] =
1419                                                EvalSplitPlaneCost(Plane3(normal, nPosition[axis]),
1420                                                                                   tData, *nFrontGeom[axis], *nBackGeom[axis],
1421                                                                                   nProbFront[axis], nProbBack[axis]);
1422                                }
1423                        }
1424                        else
1425                        {
1426                                nCostRatio[axis] =
1427                                        BestCostRatioHeuristics(*tData.mRays,
1428                                                                                    box,
1429                                                                                        tData.mPvs,
1430                                                                                        axis,
1431                                                                                        nPosition[axis]);
1432                        }
1433
1434                        if (bestAxis == -1)
1435                        {
1436                                bestAxis = axis;
1437                        }
1438                        else if (nCostRatio[axis] < nCostRatio[bestAxis])
1439                        {
1440                                bestAxis = axis;
1441                        }
1442
1443                }
1444        }
1445
1446        //-- assign values
1447        axis = bestAxis;
1448        pFront = nProbFront[bestAxis];
1449        pBack = nProbBack[bestAxis];
1450
1451        // assign best split nodes geometry
1452        *frontGeom = nFrontGeom[bestAxis];
1453        *backGeom = nBackGeom[bestAxis];
1454
1455        // and delete other geometry
1456        DEL_PTR(nFrontGeom[(bestAxis + 1) % 3]);
1457        DEL_PTR(nBackGeom[(bestAxis + 2) % 3]);
1458
1459        //-- split plane
1460    Vector3 normal(0,0,0); normal[bestAxis] = 1;
1461        plane = Plane3(normal, nPosition[bestAxis]);
1462
1463        return nCostRatio[bestAxis];
1464}
1465
1466
1467bool VspBspTree::SelectPlane(Plane3 &bestPlane,
1468                                                         BspLeaf *leaf,
1469                                                         VspBspTraversalData &data,                                                     
1470                                                         VspBspTraversalData &frontData,
1471                                                         VspBspTraversalData &backData,
1472                                                         int &splitAxis)
1473{
1474        // simplest strategy: just take next polygon
1475        if (mSplitPlaneStrategy & RANDOM_POLYGON)
1476        {
1477        if (!data.mPolygons->empty())
1478                {
1479                        const int randIdx =
1480                                (int)RandomValue(0, (Real)((int)data.mPolygons->size() - 1));
1481                        Polygon3 *nextPoly = (*data.mPolygons)[randIdx];
1482
1483                        bestPlane = nextPoly->GetSupportingPlane();
1484                        return true;
1485                }
1486        }
1487
1488        //-- use heuristics to find appropriate plane
1489
1490        // intermediate plane
1491        Plane3 plane;
1492        float lowestCost = MAX_FLOAT;
1493       
1494        // decides if the first few splits should be only axisAligned
1495        const bool onlyAxisAligned  =
1496                (mSplitPlaneStrategy & AXIS_ALIGNED) &&
1497                ((int)data.mRays->size() > mTermMinRaysForAxisAligned) &&
1498                ((int)data.GetAvgRayContribution() < mTermMaxRayContriForAxisAligned);
1499       
1500        const int limit = onlyAxisAligned ? 0 :
1501                Min((int)data.mPolygons->size(), mMaxPolyCandidates);
1502
1503        float candidateCost;
1504
1505        int maxIdx = (int)data.mPolygons->size();
1506
1507        for (int i = 0; i < limit; ++ i)
1508        {
1509                // the already taken candidates are stored behind maxIdx
1510                // => assure that no index is taken twice
1511                const int candidateIdx = (int)RandomValue(0, (Real)(-- maxIdx));
1512                Polygon3 *poly = (*data.mPolygons)[candidateIdx];
1513
1514                // swap candidate to the end to avoid testing same plane
1515                std::swap((*data.mPolygons)[maxIdx], (*data.mPolygons)[candidateIdx]);
1516                //Polygon3 *poly = (*data.mPolygons)[(int)RandomValue(0, (int)polys.size() - 1)];
1517
1518                // evaluate current candidate
1519                BspNodeGeometry fGeom, bGeom;
1520                float fArea, bArea;
1521                plane = poly->GetSupportingPlane();
1522                candidateCost = EvalSplitPlaneCost(plane, data, fGeom, bGeom, fArea, bArea);
1523               
1524                if (candidateCost < lowestCost)
1525                {
1526                        bestPlane = plane;
1527                        lowestCost = candidateCost;
1528                }
1529        }
1530
1531        //-- evaluate axis aligned splits
1532        int axis;
1533        BspNodeGeometry *fGeom, *bGeom;
1534        float pFront, pBack;
1535
1536        candidateCost = 99999999.0f;
1537
1538        // option: axis aligned split only if no polygon available
1539        if (!mUsePolygonSplitIfAvailable || data.mPolygons->empty())
1540        {
1541                candidateCost = SelectAxisAlignedPlane(plane,
1542                                                                                           data,
1543                                                                                           axis,
1544                                                                                           &fGeom,
1545                                                                                           &bGeom,
1546                                                                                           pFront,
1547                                                                                           pBack,
1548                                                                                           data.mIsKdNode);     
1549        }
1550
1551        splitAxis = 3;
1552
1553        if (candidateCost < lowestCost)
1554        {       
1555                bestPlane = plane;
1556                lowestCost = candidateCost;
1557                splitAxis = axis;
1558       
1559                // assign already computed values
1560                // we can do this because we always save the
1561                // computed values from the axis aligned splits         
1562
1563                if (fGeom && bGeom)
1564                {
1565                        frontData.mGeometry = fGeom;
1566                        backData.mGeometry = bGeom;
1567       
1568                        frontData.mProbability = pFront;
1569                        backData.mProbability = pBack;
1570                }
1571        }
1572        else
1573        {
1574                DEL_PTR(fGeom);
1575                DEL_PTR(bGeom);
1576        }
1577   
1578
1579//      if (lowestCost > 10)
1580//              Debug << "warning!! lowest cost: " << lowestCost << endl;
1581       
1582#ifdef _DEBUG
1583        Debug << "plane lowest cost: " << lowestCost << endl;
1584#endif
1585
1586        if (lowestCost > mTermMaxCostRatio)
1587        {
1588                return false;
1589        }
1590
1591        return true;
1592}
1593
1594
1595Plane3 VspBspTree::ChooseCandidatePlane(const RayInfoContainer &rays) const
1596{
1597        const int candidateIdx = (int)RandomValue(0, (Real)((int)rays.size() - 1));
1598
1599        const Vector3 minPt = rays[candidateIdx].ExtrapOrigin();
1600        const Vector3 maxPt = rays[candidateIdx].ExtrapTermination();
1601
1602        const Vector3 pt = (maxPt + minPt) * 0.5;
1603        const Vector3 normal = Normalize(rays[candidateIdx].mRay->GetDir());
1604
1605        return Plane3(normal, pt);
1606}
1607
1608
1609Plane3 VspBspTree::ChooseCandidatePlane2(const RayInfoContainer &rays) const
1610{
1611        Vector3 pt[3];
1612
1613        int idx[3];
1614        int cmaxT = 0;
1615        int cminT = 0;
1616        bool chooseMin = false;
1617
1618        for (int j = 0; j < 3; ++ j)
1619        {
1620                idx[j] = (int)RandomValue(0, (Real)((int)rays.size() * 2 - 1));
1621
1622                if (idx[j] >= (int)rays.size())
1623                {
1624                        idx[j] -= (int)rays.size();
1625
1626                        chooseMin = (cminT < 2);
1627                }
1628                else
1629                        chooseMin = (cmaxT < 2);
1630
1631                RayInfo rayInf = rays[idx[j]];
1632                pt[j] = chooseMin ? rayInf.ExtrapOrigin() : rayInf.ExtrapTermination();
1633        }
1634
1635        return Plane3(pt[0], pt[1], pt[2]);
1636}
1637
1638
1639Plane3 VspBspTree::ChooseCandidatePlane3(const RayInfoContainer &rays) const
1640{
1641        Vector3 pt[3];
1642
1643        int idx1 = (int)RandomValue(0, (Real)((int)rays.size() - 1));
1644        int idx2 = (int)RandomValue(0, (Real)((int)rays.size() - 1));
1645
1646        // check if rays different
1647        if (idx1 == idx2)
1648                idx2 = (idx2 + 1) % (int)rays.size();
1649
1650        const RayInfo ray1 = rays[idx1];
1651        const RayInfo ray2 = rays[idx2];
1652
1653        // normal vector of the plane parallel to both lines
1654        const Vector3 norm = Normalize(CrossProd(ray1.mRay->GetDir(), ray2.mRay->GetDir()));
1655
1656        // vector from line 1 to line 2
1657        const Vector3 vd = ray2.ExtrapOrigin() - ray1.ExtrapOrigin();
1658
1659        // project vector on normal to get distance
1660        const float dist = DotProd(vd, norm);
1661
1662        // point on plane lies halfway between the two planes
1663        const Vector3 planePt = ray1.ExtrapOrigin() + norm * dist * 0.5;
1664
1665        return Plane3(norm, planePt);
1666}
1667
1668
1669inline void VspBspTree::GenerateUniqueIdsForPvs()
1670{
1671        Intersectable::NewMail(); sBackId = Intersectable::sMailId;
1672        Intersectable::NewMail(); sFrontId = Intersectable::sMailId;
1673        Intersectable::NewMail(); sFrontAndBackId = Intersectable::sMailId;
1674}
1675
1676
1677float VspBspTree::EvalRenderCostDecrease(const Plane3 &candidatePlane,
1678                                                                                 const VspBspTraversalData &data) const
1679{
1680        int pvsFront = 0;
1681        int pvsBack = 0;
1682        int totalPvs = 0;
1683
1684        // probability that view point lies in back / front node
1685        float pOverall = data.mProbability;
1686        float pFront = 0;
1687        float pBack = 0;
1688
1689
1690        // create unique ids for pvs heuristics
1691        GenerateUniqueIdsForPvs();
1692       
1693        for (int i = 0; i < data.mRays->size(); ++ i)
1694        {
1695                RayInfo rayInf = (*data.mRays)[i];
1696
1697                float t;
1698                VssRay *ray = rayInf.mRay;
1699                const int cf = rayInf.ComputeRayIntersection(candidatePlane, t);
1700
1701                // find front and back pvs for origing and termination object
1702                AddObjToPvs(ray->mTerminationObject, cf, pvsFront, pvsBack, totalPvs);
1703                AddObjToPvs(ray->mOriginObject, cf, pvsFront, pvsBack, totalPvs);
1704        }
1705
1706
1707        BspNodeGeometry geomFront;
1708        BspNodeGeometry geomBack;
1709
1710        // construct child geometry with regard to the candidate split plane
1711        data.mGeometry->SplitGeometry(geomFront,
1712                                                                  geomBack,
1713                                                                  candidatePlane,
1714                                                                  mBox,
1715                                                                  //0.0f);
1716                                                                  mEpsilon);
1717
1718        if (!mUseAreaForPvs) // use front and back cell areas to approximate volume
1719        {
1720                pFront = geomFront.GetVolume();
1721                pBack = pOverall - pFront;
1722
1723                if ((pFront < 0.0) || (pBack < 0.0))
1724                {
1725                        Debug << "vol f " << pFront << " b " << pBack << " p " << pOverall << endl;
1726                        Debug << "real vol f " << pFront << " b " << geomBack.GetVolume() << " p " << pOverall << endl;
1727                        Debug << "polys f " << geomFront.Size() << " b " << geomBack.Size() << " data " << data.mGeometry->Size() << endl;
1728                }
1729
1730                // clamp because of possible precision issues
1731                if (0)
1732                {
1733                        if (pFront < 0) pFront = 0;
1734                        if (pBack < 0) pBack = 0;
1735                }
1736        }
1737        else
1738        {
1739                pFront = geomFront.GetArea();
1740                pBack = geomBack.GetArea();
1741        }
1742       
1743
1744        // -- pvs rendering heuristics
1745        const int lowerPvsLimit = mViewCellsManager->GetMinPvsSize();
1746        const int upperPvsLimit = mViewCellsManager->GetMaxPvsSize();
1747
1748        // only render cost heuristics or combined with standard deviation
1749        const float penaltyOld = EvalPvsPenalty(totalPvs, lowerPvsLimit, upperPvsLimit);
1750    const float penaltyFront = EvalPvsPenalty(pvsFront, lowerPvsLimit, upperPvsLimit);
1751        const float penaltyBack = EvalPvsPenalty(pvsBack, lowerPvsLimit, upperPvsLimit);
1752                       
1753        const float oldRenderCost = pOverall * penaltyOld;
1754        const float newRenderCost = penaltyFront * pFront + penaltyBack * pBack;
1755
1756        //Debug << "decrease: " << oldRenderCost - newRenderCost << endl;
1757        return (oldRenderCost - newRenderCost) / mBox.GetVolume();
1758}
1759
1760
1761float VspBspTree::EvalSplitPlaneCost(const Plane3 &candidatePlane,
1762                                                                         const VspBspTraversalData &data,
1763                                                                         BspNodeGeometry &geomFront,
1764                                                                         BspNodeGeometry &geomBack,
1765                                                                         float &pFront,
1766                                                                         float &pBack) const
1767{
1768        float cost = 0;
1769
1770        int totalPvs = 0;
1771        int pvsFront = 0;
1772        int pvsBack = 0;
1773       
1774        // probability that view point lies in back / front node
1775        float pOverall = 0;
1776        pFront = 0;
1777        pBack = 0;
1778
1779
1780        int limit;
1781        bool useRand;
1782
1783        // create unique ids for pvs heuristics
1784        GenerateUniqueIdsForPvs();
1785
1786        // choose test rays randomly if too much
1787        if ((int)data.mRays->size() > mMaxTests)
1788        {
1789                useRand = true;
1790                limit = mMaxTests;
1791        }
1792        else
1793        {
1794                useRand = false;
1795                limit = (int)data.mRays->size();
1796        }
1797       
1798        for (int i = 0; i < limit; ++ i)
1799        {
1800                const int testIdx = useRand ?
1801                        (int)RandomValue(0, (Real)((int)data.mRays->size() - 1)) : i;
1802                RayInfo rayInf = (*data.mRays)[testIdx];
1803
1804                float t;
1805                VssRay *ray = rayInf.mRay;
1806                const int cf = rayInf.ComputeRayIntersection(candidatePlane, t);
1807
1808                // find front and back pvs for origing and termination object
1809                AddObjToPvs(ray->mTerminationObject, cf, pvsFront, pvsBack, totalPvs);
1810                AddObjToPvs(ray->mOriginObject, cf, pvsFront, pvsBack, totalPvs);
1811        }
1812
1813        // construct child geometry with regard to the candidate split plane
1814        bool splitSuccessFull = data.mGeometry->SplitGeometry(geomFront,
1815                                                                                                                  geomBack,
1816                                                                                                                  candidatePlane,
1817                                                                                                                  mBox,
1818                                                                                                                  //0.0f);
1819                                                                                                                  mEpsilon);
1820
1821        pOverall = data.mProbability;
1822
1823        if (!mUseAreaForPvs) // use front and back cell areas to approximate volume
1824        {
1825                pFront = geomFront.GetVolume();
1826                pBack = pOverall - pFront;
1827               
1828                // clamp because of possible precision issues
1829                if (1 &&
1830                        (!splitSuccessFull || (pFront <= 0) || (pBack <= 0) ||
1831                        !geomFront.Valid() || !geomBack.Valid()))
1832                {
1833                        Debug << "error f: " << pFront << " b: " << pBack << endl;
1834                        return 999;
1835                }
1836        }
1837        else
1838        {
1839                pFront = geomFront.GetArea();
1840                pBack = geomBack.GetArea();
1841        }
1842       
1843
1844        // -- pvs rendering heuristics
1845        const int lowerPvsLimit = mViewCellsManager->GetMinPvsSize();
1846        const int upperPvsLimit = mViewCellsManager->GetMaxPvsSize();
1847
1848        // only render cost heuristics or combined with standard deviation
1849        const float penaltyOld = EvalPvsPenalty(totalPvs, lowerPvsLimit, upperPvsLimit);
1850    const float penaltyFront = EvalPvsPenalty(pvsFront, lowerPvsLimit, upperPvsLimit);
1851        const float penaltyBack = EvalPvsPenalty(pvsBack, lowerPvsLimit, upperPvsLimit);
1852                       
1853        const float oldRenderCost = pOverall * penaltyOld;
1854        const float newRenderCost = penaltyFront * pFront + penaltyBack * pBack;
1855
1856        float oldCost, newCost;
1857
1858        // only render cost
1859        if (1)
1860        {
1861                oldCost = oldRenderCost;
1862                newCost = newRenderCost;
1863        }
1864        else // also considering standard deviation
1865        {
1866                // standard deviation is difference of back and front pvs
1867                const float expectedCost = 0.5f * (penaltyFront + penaltyBack);
1868
1869                const float newDeviation = 0.5f *
1870                        fabs(penaltyFront - expectedCost) + fabs(penaltyBack - expectedCost);
1871
1872                const float oldDeviation = penaltyOld;
1873
1874                newCost = mRenderCostWeight * newRenderCost + (1.0f - mRenderCostWeight) * newDeviation;
1875                oldCost = mRenderCostWeight * oldRenderCost + (1.0f - mRenderCostWeight) * oldDeviation;
1876        }
1877
1878        cost += mPvsFactor * newCost / (oldCost + Limits::Small);
1879               
1880
1881#ifdef _DEBUG
1882        Debug << "totalpvs: " << data.mPvs << " ptotal: " << pOverall
1883                  << " frontpvs: " << pvsFront << " pFront: " << pFront
1884                  << " backpvs: " << pvsBack << " pBack: " << pBack << endl << endl;
1885        Debug << "cost: " << cost << endl;
1886#endif
1887
1888        return cost;
1889}
1890
1891
1892int VspBspTree::ComputeBoxIntersections(const AxisAlignedBox3 &box,
1893                                                                                ViewCellContainer &viewCells) const
1894{
1895        stack<bspNodePair> nodeStack;
1896        BspNodeGeometry *rgeom = new BspNodeGeometry();
1897
1898        ConstructGeometry(mRoot, *rgeom);
1899
1900        nodeStack.push(bspNodePair(mRoot, rgeom));
1901 
1902        ViewCell::NewMail();
1903
1904        while (!nodeStack.empty())
1905        {
1906                BspNode *node = nodeStack.top().first;
1907                BspNodeGeometry *geom = nodeStack.top().second;
1908                nodeStack.pop();
1909
1910                const int side = geom->ComputeIntersection(box);
1911               
1912                switch (side)
1913                {
1914                case -1:
1915                        // node geometry is contained in box
1916                        CollectViewCells(node, true, viewCells, true);
1917                        break;
1918
1919                case 0:
1920                        if (node->IsLeaf())
1921                        {
1922                                BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
1923                       
1924                                if (!leaf->GetViewCell()->Mailed() && leaf->TreeValid())
1925                                {
1926                                        leaf->GetViewCell()->Mail();
1927                                        viewCells.push_back(leaf->GetViewCell());
1928                                }
1929                        }
1930                        else
1931                        {
1932                                BspInterior *interior = dynamic_cast<BspInterior *>(node);
1933                       
1934                                BspNode *first = interior->GetFront();
1935                                BspNode *second = interior->GetBack();
1936           
1937                                BspNodeGeometry *firstGeom = new BspNodeGeometry();
1938                                BspNodeGeometry *secondGeom = new BspNodeGeometry();
1939
1940                                geom->SplitGeometry(*firstGeom,
1941                                                                        *secondGeom,
1942                                                                        interior->GetPlane(),
1943                                                                        mBox,
1944                                                                        //0.0000001f);
1945                                                                        mEpsilon);
1946
1947                                nodeStack.push(bspNodePair(first, firstGeom));
1948                                nodeStack.push(bspNodePair(second, secondGeom));
1949                        }
1950                       
1951                        break;
1952                default:
1953                        // default: cull
1954                        break;
1955                }
1956               
1957                DEL_PTR(geom);
1958               
1959        }
1960
1961        return (int)viewCells.size();
1962}
1963
1964
1965float VspBspTree::EvalAxisAlignedSplitCost(const VspBspTraversalData &data,
1966                                                                                   const AxisAlignedBox3 &box,
1967                                                                                   const int axis,
1968                                                                                   const float &position,                                                                                 
1969                                                                                   float &pFront,
1970                                                                                   float &pBack) const
1971{
1972        int pvsTotal = 0;
1973        int pvsFront = 0;
1974        int pvsBack = 0;
1975       
1976        // create unique ids for pvs heuristics
1977        GenerateUniqueIdsForPvs();
1978
1979        const int pvsSize = data.mPvs;
1980
1981        RayInfoContainer::const_iterator rit, rit_end = data.mRays->end();
1982
1983        // this is the main ray classification loop!
1984        for(rit = data.mRays->begin(); rit != rit_end; ++ rit)
1985        {
1986                //if (!(*rit).mRay->IsActive()) continue;
1987
1988                // determine the side of this ray with respect to the plane
1989                float t;
1990                const int side = (*rit).ComputeRayIntersection(axis, position, t);
1991       
1992                AddObjToPvs((*rit).mRay->mTerminationObject, side, pvsFront, pvsBack, pvsTotal);
1993                AddObjToPvs((*rit).mRay->mOriginObject, side, pvsFront, pvsBack, pvsTotal);
1994        }
1995
1996        //-- pvs heuristics
1997        float pOverall;
1998
1999        //-- compute heurstics
2000        //   we take simplified computation for mid split
2001               
2002        pOverall = data.mProbability;
2003
2004        if (!mUseAreaForPvs)
2005        {   // volume
2006                pBack = pFront = pOverall * 0.5f;
2007               
2008#if 0
2009                // box length substitute for probability
2010                const float minBox = box.Min(axis);
2011                const float maxBox = box.Max(axis);
2012
2013                pBack = position - minBox;
2014                pFront = maxBox - position;
2015                pOverall = maxBox - minBox;
2016#endif
2017        }
2018        else //-- area substitute for probability
2019        {
2020                const int axis2 = (axis + 1) % 3;
2021                const int axis3 = (axis + 2) % 3;
2022
2023                const float faceArea =
2024                        (box.Max(axis2) - box.Min(axis2)) *
2025                        (box.Max(axis3) - box.Min(axis3));
2026
2027                pBack = pFront = pOverall * 0.5f + faceArea;
2028        }
2029
2030#ifdef _DEBUG
2031        Debug << axis << " " << pvsSize << " " << pvsBack << " " << pvsFront << endl;
2032        Debug << pFront << " " << pBack << " " << pOverall << endl;
2033#endif
2034
2035       
2036        const float newCost = pvsBack * pBack + pvsFront * pFront;
2037        const float oldCost = (float)pvsSize * pOverall + Limits::Small;
2038
2039        return  (mCtDivCi + newCost) / oldCost;
2040}
2041
2042
2043void VspBspTree::AddObjToPvs(Intersectable *obj,
2044                                                         const int cf,
2045                                                         int &frontPvs,
2046                                                         int &backPvs,
2047                                                         int &totalPvs) const
2048{
2049        if (!obj)
2050                return;
2051        // new object
2052        if ((obj->mMailbox != sFrontId) &&
2053                (obj->mMailbox != sBackId) &&
2054                (obj->mMailbox != sFrontAndBackId))
2055        {
2056                ++ totalPvs;
2057        }
2058
2059        // TODO: does this really belong to no pvs?
2060        //if (cf == Ray::COINCIDENT) return;
2061
2062        // object belongs to both PVS
2063        if (cf >= 0)
2064        {
2065                if ((obj->mMailbox != sFrontId) &&
2066                        (obj->mMailbox != sFrontAndBackId))
2067                {
2068                        ++ frontPvs;
2069               
2070                        if (obj->mMailbox == sBackId)
2071                                obj->mMailbox = sFrontAndBackId;
2072                        else
2073                                obj->mMailbox = sFrontId;
2074                }
2075        }
2076
2077        if (cf <= 0)
2078        {
2079                if ((obj->mMailbox != sBackId) &&
2080                        (obj->mMailbox != sFrontAndBackId))
2081                {
2082                        ++ backPvs;
2083               
2084                        if (obj->mMailbox == sFrontId)
2085                                obj->mMailbox = sFrontAndBackId;
2086                        else
2087                                obj->mMailbox = sBackId;
2088                }
2089        }
2090}
2091
2092
2093void VspBspTree::CollectLeaves(vector<BspLeaf *> &leaves,
2094                                                           const bool onlyUnmailed,
2095                                                           const int maxPvsSize) const
2096{
2097        stack<BspNode *> nodeStack;
2098        nodeStack.push(mRoot);
2099
2100        while (!nodeStack.empty())
2101        {
2102                BspNode *node = nodeStack.top();
2103                nodeStack.pop();
2104               
2105                if (node->IsLeaf())
2106                {
2107                        // test if this leaf is in valid view space
2108                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
2109                        if (leaf->TreeValid() &&
2110                                (!onlyUnmailed || !leaf->Mailed()) &&
2111                                ((maxPvsSize < 0) || (leaf->GetViewCell()->GetPvs().GetSize() <= maxPvsSize)))
2112                        {
2113                                leaves.push_back(leaf);
2114                        }
2115                }
2116                else
2117                {
2118                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2119
2120                        nodeStack.push(interior->GetBack());
2121                        nodeStack.push(interior->GetFront());
2122                }
2123        }
2124}
2125
2126
2127AxisAlignedBox3 VspBspTree::GetBoundingBox() const
2128{
2129        return mBox;
2130}
2131
2132
2133BspNode *VspBspTree::GetRoot() const
2134{
2135        return mRoot;
2136}
2137
2138
2139void VspBspTree::EvaluateLeafStats(const VspBspTraversalData &data)
2140{
2141        // the node became a leaf -> evaluate stats for leafs
2142        BspLeaf *leaf = dynamic_cast<BspLeaf *>(data.mNode);
2143
2144        // store maximal and minimal depth
2145        if (data.mDepth > mBspStats.maxDepth)
2146                mBspStats.maxDepth = data.mDepth;
2147
2148        if (data.mPvs > mBspStats.maxPvs)
2149                mBspStats.maxPvs = data.mPvs;
2150       
2151        mBspStats.pvs += data.mPvs;
2152
2153        if (data.mDepth < mBspStats.minDepth)
2154                mBspStats.minDepth = data.mDepth;
2155       
2156        if (data.mDepth >= mTermMaxDepth)
2157                ++ mBspStats.maxDepthNodes;
2158
2159        // accumulate rays to compute rays /  leaf
2160        mBspStats.accumRays += (int)data.mRays->size();
2161
2162        if (data.mPvs < mTermMinPvs)
2163                ++ mBspStats.minPvsNodes;
2164
2165        if ((int)data.mRays->size() < mTermMinRays)
2166                ++ mBspStats.minRaysNodes;
2167
2168        if (data.GetAvgRayContribution() > mTermMaxRayContribution)
2169                ++ mBspStats.maxRayContribNodes;
2170
2171        if (data.mProbability <= mTermMinProbability)
2172                ++ mBspStats.minProbabilityNodes;
2173       
2174        // accumulate depth to compute average depth
2175        mBspStats.accumDepth += data.mDepth;
2176
2177        ++ mCreatedViewCells;
2178
2179#ifdef _DEBUG
2180        Debug << "BSP stats: "
2181                  << "Depth: " << data.mDepth << " (max: " << mTermMaxDepth << "), "
2182                  << "PVS: " << data.mPvs << " (min: " << mTermMinPvs << "), "
2183          //              << "Area: " << data.mProbability << " (min: " << mTermMinProbability << "), "
2184                  << "#rays: " << (int)data.mRays->size() << " (max: " << mTermMinRays << "), "
2185                  << "#pvs: " << leaf->GetViewCell()->GetPvs().GetSize() << "=, "
2186                  << "#avg ray contrib (pvs): " << (float)data.mPvs / (float)data.mRays->size() << endl;
2187#endif
2188}
2189
2190
2191int VspBspTree::CastRay(Ray &ray)
2192{
2193        int hits = 0;
2194
2195        stack<BspRayTraversalData> tQueue;
2196
2197        float maxt, mint;
2198
2199        if (!mBox.GetRaySegment(ray, mint, maxt))
2200                return 0;
2201
2202        Intersectable::NewMail();
2203        ViewCell::NewMail();
2204        Vector3 entp = ray.Extrap(mint);
2205        Vector3 extp = ray.Extrap(maxt);
2206
2207        BspNode *node = mRoot;
2208        BspNode *farChild = NULL;
2209
2210        while (1)
2211        {
2212                if (!node->IsLeaf())
2213                {
2214                        BspInterior *in = dynamic_cast<BspInterior *>(node);
2215
2216                        Plane3 splitPlane = in->GetPlane();
2217                        const int entSide = splitPlane.Side(entp);
2218                        const int extSide = splitPlane.Side(extp);
2219
2220                        if (entSide < 0)
2221                        {
2222                                node = in->GetBack();
2223
2224                                if(extSide <= 0) // plane does not split ray => no far child
2225                                        continue;
2226
2227                                farChild = in->GetFront(); // plane splits ray
2228
2229                        } else if (entSide > 0)
2230                        {
2231                                node = in->GetFront();
2232
2233                                if (extSide >= 0) // plane does not split ray => no far child
2234                                        continue;
2235
2236                                farChild = in->GetBack(); // plane splits ray
2237                        }
2238                        else // ray and plane are coincident
2239                        {
2240                                // WHAT TO DO IN THIS CASE ?
2241                                //break;
2242                                node = in->GetFront();
2243                                continue;
2244                        }
2245
2246                        // push data for far child
2247                        tQueue.push(BspRayTraversalData(farChild, extp, maxt));
2248
2249                        // find intersection of ray segment with plane
2250                        float t;
2251                        extp = splitPlane.FindIntersection(ray.GetLoc(), extp, &t);
2252                        maxt *= t;
2253
2254                } else // reached leaf => intersection with view cell
2255                {
2256                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
2257
2258                        if (!leaf->GetViewCell()->Mailed())
2259                        {
2260                                //ray.bspIntersections.push_back(Ray::VspBspIntersection(maxt, leaf));
2261                                leaf->GetViewCell()->Mail();
2262                                ++ hits;
2263                        }
2264
2265                        //-- fetch the next far child from the stack
2266                        if (tQueue.empty())
2267                                break;
2268
2269                        entp = extp;
2270                        mint = maxt; // NOTE: need this?
2271
2272                        if (ray.GetType() == Ray::LINE_SEGMENT && mint > 1.0f)
2273                                break;
2274
2275                        BspRayTraversalData &s = tQueue.top();
2276
2277                        node = s.mNode;
2278                        extp = s.mExitPoint;
2279                        maxt = s.mMaxT;
2280
2281                        tQueue.pop();
2282                }
2283        }
2284
2285        return hits;
2286}
2287
2288
2289void VspBspTree::CollectViewCells(ViewCellContainer &viewCells, bool onlyValid) const
2290{
2291        ViewCell::NewMail();
2292       
2293        CollectViewCells(mRoot, onlyValid, viewCells, true);
2294}
2295
2296
2297void VspBspTree::CollapseViewCells()
2298{
2299// TODO
2300#if VC_HISTORY
2301        stack<BspNode *> nodeStack;
2302
2303        if (!mRoot)
2304                return;
2305
2306        nodeStack.push(mRoot);
2307       
2308        while (!nodeStack.empty())
2309        {
2310                BspNode *node = nodeStack.top();
2311                nodeStack.pop();
2312               
2313                if (node->IsLeaf())
2314        {
2315                        BspViewCell *viewCell = dynamic_cast<BspLeaf *>(node)->GetViewCell();
2316
2317                        if (!viewCell->GetValid())
2318                        {
2319                                BspViewCell *viewCell = dynamic_cast<BspLeaf *>(node)->GetViewCell();
2320       
2321                                ViewCellContainer leaves;
2322                                mViewCellsTree->CollectLeaves(viewCell, leaves);
2323
2324                                ViewCellContainer::const_iterator it, it_end = leaves.end();
2325
2326                                for (it = leaves.begin(); it != it_end; ++ it)
2327                                {
2328                                        BspLeaf *l = dynamic_cast<BspViewCell *>(*it)->mLeaf;
2329                                        l->SetViewCell(GetOrCreateOutOfBoundsCell());
2330                                        ++ mBspStats.invalidLeaves;
2331                                }
2332
2333                                // add to unbounded view cell
2334                                GetOrCreateOutOfBoundsCell()->GetPvs().AddPvs(viewCell->GetPvs());
2335                                DEL_PTR(viewCell);
2336                        }
2337                }
2338                else
2339                {
2340                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2341               
2342                        nodeStack.push(interior->GetFront());
2343                        nodeStack.push(interior->GetBack());
2344                }
2345        }
2346
2347        Debug << "invalid leaves: " << mBspStats.invalidLeaves << endl;
2348#endif
2349}
2350
2351
2352void VspBspTree::CollectRays(VssRayContainer &rays)
2353{
2354        vector<BspLeaf *> leaves;
2355
2356        vector<BspLeaf *>::const_iterator lit, lit_end = leaves.end();
2357
2358        for (lit = leaves.begin(); lit != lit_end; ++ lit)
2359        {
2360                BspLeaf *leaf = *lit;
2361                VssRayContainer::const_iterator rit, rit_end = leaf->mVssRays.end();
2362
2363                for (rit = leaf->mVssRays.begin(); rit != rit_end; ++ rit)
2364                        rays.push_back(*rit);
2365        }
2366}
2367
2368
2369void VspBspTree::ValidateTree()
2370{
2371        stack<BspNode *> nodeStack;
2372
2373        if (!mRoot)
2374                return;
2375
2376        nodeStack.push(mRoot);
2377       
2378        mBspStats.invalidLeaves = 0;
2379        while (!nodeStack.empty())
2380        {
2381                BspNode *node = nodeStack.top();
2382                nodeStack.pop();
2383               
2384                if (node->IsLeaf())
2385                {
2386                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
2387
2388                        if (!leaf->GetViewCell()->GetValid())
2389                                ++ mBspStats.invalidLeaves;
2390
2391                        // validity flags don't match => repair
2392                        if (leaf->GetViewCell()->GetValid() != leaf->TreeValid())
2393                        {
2394                                leaf->SetTreeValid(leaf->GetViewCell()->GetValid());
2395                                PropagateUpValidity(leaf);
2396                        }
2397                }
2398                else
2399                {
2400                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2401               
2402                        nodeStack.push(interior->GetFront());
2403                        nodeStack.push(interior->GetBack());
2404                }
2405        }
2406
2407        Debug << "invalid leaves: " << mBspStats.invalidLeaves << endl;
2408}
2409
2410
2411
2412void VspBspTree::CollectViewCells(BspNode *root,
2413                                                                  bool onlyValid,
2414                                                                  ViewCellContainer &viewCells,
2415                                                                  bool onlyUnmailed) const
2416{
2417        stack<BspNode *> nodeStack;
2418
2419        if (!root)
2420                return;
2421
2422        nodeStack.push(root);
2423       
2424        while (!nodeStack.empty())
2425        {
2426                BspNode *node = nodeStack.top();
2427                nodeStack.pop();
2428               
2429                if (node->IsLeaf())
2430                {
2431                        if (!onlyValid || node->TreeValid())
2432                        {
2433                                ViewCell *viewCell =
2434                                        mViewCellsTree->GetActiveViewCell(dynamic_cast<BspLeaf *>(node)->GetViewCell());
2435                                               
2436                                if (!onlyUnmailed || !viewCell->Mailed())
2437                                {
2438                                        viewCell->Mail();
2439                                        viewCells.push_back(viewCell);
2440                                }
2441                        }
2442                }
2443                else
2444                {
2445                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2446               
2447                        nodeStack.push(interior->GetFront());
2448                        nodeStack.push(interior->GetBack());
2449                }
2450        }
2451
2452}
2453
2454
2455void VspBspTree::PreprocessPolygons(PolygonContainer &polys)
2456{
2457        // preprocess: throw out polygons coincident to the view space box (not needed)
2458        PolygonContainer boxPolys;
2459        mBox.ExtractPolys(boxPolys);
2460        vector<Plane3> boxPlanes;
2461
2462        PolygonContainer::iterator pit, pit_end = boxPolys.end();
2463
2464        // extract planes of box
2465        // TODO: can be done more elegantly than first extracting polygons
2466        // and take their planes
2467        for (pit = boxPolys.begin(); pit != pit_end; ++ pit)
2468        {
2469                boxPlanes.push_back((*pit)->GetSupportingPlane());
2470        }
2471
2472        pit_end = polys.end();
2473
2474        for (pit = polys.begin(); pit != pit_end; ++ pit)
2475        {
2476                vector<Plane3>::const_iterator bit, bit_end = boxPlanes.end();
2477               
2478                for (bit = boxPlanes.begin(); (bit != bit_end) && (*pit); ++ bit)
2479                {
2480                        const int cf = (*pit)->ClassifyPlane(*bit, mEpsilon);
2481
2482                        if (cf == Polygon3::COINCIDENT)
2483                        {
2484                                DEL_PTR(*pit);
2485                                //Debug << "coincident!!" << endl;
2486                        }
2487                }
2488        }
2489
2490        // remove deleted entries
2491        for (int i = 0; i < (int)polys.size(); ++ i)
2492        {
2493                while (!polys[i] && (i < (int)polys.size()))
2494                {
2495                        swap(polys[i], polys.back());
2496                        polys.pop_back();
2497                }
2498        }
2499}
2500
2501
2502float VspBspTree::AccumulatedRayLength(const RayInfoContainer &rays) const
2503{
2504        float len = 0;
2505
2506        RayInfoContainer::const_iterator it, it_end = rays.end();
2507
2508        for (it = rays.begin(); it != it_end; ++ it)
2509                len += (*it).SegmentLength();
2510
2511        return len;
2512}
2513
2514
2515int VspBspTree::SplitRays(const Plane3 &plane,
2516                                                  RayInfoContainer &rays,
2517                                                  RayInfoContainer &frontRays,
2518                                                  RayInfoContainer &backRays) const
2519{
2520        int splits = 0;
2521
2522        RayInfoContainer::const_iterator it, it_end = rays.end();
2523
2524        for (it = rays.begin(); it != it_end; ++ it)
2525        {
2526                RayInfo bRay = *it;
2527               
2528                VssRay *ray = bRay.mRay;
2529                float t;
2530
2531                // get classification and receive new t
2532                const int cf = bRay.ComputeRayIntersection(plane, t);
2533
2534                switch (cf)
2535                {
2536                case -1:
2537                        backRays.push_back(bRay);
2538                        break;
2539                case 1:
2540                        frontRays.push_back(bRay);
2541                        break;
2542                case 0:
2543                        {
2544                                //-- split ray
2545                                //   test if start point behind or in front of plane
2546                                const int side = plane.Side(bRay.ExtrapOrigin());
2547
2548                                if (side <= 0)
2549                                {
2550                                        backRays.push_back(RayInfo(ray, bRay.GetMinT(), t));
2551                                        frontRays.push_back(RayInfo(ray, t, bRay.GetMaxT()));
2552                                }
2553                                else
2554                                {
2555                                        frontRays.push_back(RayInfo(ray, bRay.GetMinT(), t));
2556                                        backRays.push_back(RayInfo(ray, t, bRay.GetMaxT()));
2557                                }
2558                        }
2559                        break;
2560                default:
2561                        Debug << "Should not come here" << endl;
2562                        break;
2563                }
2564        }
2565
2566        return splits;
2567}
2568
2569
2570void VspBspTree::ExtractHalfSpaces(BspNode *n, vector<Plane3> &halfSpaces) const
2571{
2572        BspNode *lastNode;
2573
2574        do
2575        {
2576                lastNode = n;
2577
2578                // want to get planes defining geometry of this node => don't take
2579                // split plane of node itself
2580                n = n->GetParent();
2581
2582                if (n)
2583                {
2584                        BspInterior *interior = dynamic_cast<BspInterior *>(n);
2585                        Plane3 halfSpace = dynamic_cast<BspInterior *>(interior)->GetPlane();
2586
2587            if (interior->GetBack() != lastNode)
2588                                halfSpace.ReverseOrientation();
2589
2590                        halfSpaces.push_back(halfSpace);
2591                }
2592        }
2593        while (n);
2594}
2595
2596
2597void VspBspTree::ConstructGeometry(BspNode *n,
2598                                                                   BspNodeGeometry &geom) const
2599{
2600        vector<Plane3> halfSpaces;
2601        ExtractHalfSpaces(n, halfSpaces);
2602
2603        PolygonContainer candidatePolys;
2604        vector<Plane3> candidatePlanes;
2605
2606        // bounded planes are added to the polygons
2607        for (int i = 0; i < (int)halfSpaces.size(); ++ i)
2608        {
2609                Polygon3 *p = GetBoundingBox().CrossSection(halfSpaces[i]);
2610
2611                if (p->Valid(mEpsilon))
2612                {
2613                        candidatePolys.push_back(p);
2614                        candidatePlanes.push_back(halfSpaces[i]);
2615                }
2616        }
2617
2618        // add faces of bounding box (also could be faces of the cell)
2619        for (int i = 0; i < 6; ++ i)
2620        {
2621                VertexContainer vertices;
2622
2623                for (int j = 0; j < 4; ++ j)
2624                        vertices.push_back(mBox.GetFace(i).mVertices[j]);
2625
2626                Polygon3 *poly = new Polygon3(vertices);
2627
2628                candidatePolys.push_back(poly);
2629                candidatePlanes.push_back(poly->GetSupportingPlane());
2630        }
2631
2632        for (int i = 0; i < (int)candidatePolys.size(); ++ i)
2633        {
2634                // polygon is split by all other planes
2635                for (int j = 0; (j < (int)halfSpaces.size()) && candidatePolys[i]; ++ j)
2636                {
2637                        if (i == j) // polygon and plane are coincident
2638                                continue;
2639
2640                        VertexContainer splitPts;
2641                        Polygon3 *frontPoly, *backPoly;
2642
2643                        const int cf =
2644                                candidatePolys[i]->ClassifyPlane(halfSpaces[j],
2645                                                                                                 mEpsilon);
2646
2647                        switch (cf)
2648                        {
2649                                case Polygon3::SPLIT:
2650                                        frontPoly = new Polygon3();
2651                                        backPoly = new Polygon3();
2652
2653                                        candidatePolys[i]->Split(halfSpaces[j],
2654                                                                                         *frontPoly,
2655                                                                                         *backPoly,
2656                                                                                         mEpsilon);
2657
2658                                        DEL_PTR(candidatePolys[i]);
2659
2660                                        if (backPoly->Valid(mEpsilon))
2661                                                candidatePolys[i] = backPoly;
2662                                        else
2663                                                DEL_PTR(backPoly);
2664
2665                                        // outside, don't need this
2666                                        DEL_PTR(frontPoly);
2667                                        break;
2668                                // polygon outside of halfspace
2669                                case Polygon3::FRONT_SIDE:
2670                                        DEL_PTR(candidatePolys[i]);
2671                                        break;
2672                                // just take polygon as it is
2673                                case Polygon3::BACK_SIDE:
2674                                case Polygon3::COINCIDENT:
2675                                default:
2676                                        break;
2677                        }
2678                }
2679
2680                if (candidatePolys[i])
2681                {
2682                        geom.Add(candidatePolys[i], candidatePlanes[i]);
2683                        //      geom.mPolys.push_back(candidates[i]);
2684                }
2685        }
2686}
2687
2688
2689void VspBspTree::ConstructGeometry(ViewCell *vc,
2690                                                                   BspNodeGeometry &vcGeom) const
2691{
2692        ViewCellContainer leaves;
2693       
2694        mViewCellsTree->CollectLeaves(vc, leaves);
2695
2696        ViewCellContainer::const_iterator it, it_end = leaves.end();
2697
2698        for (it = leaves.begin(); it != it_end; ++ it)
2699        {
2700                BspLeaf *l = dynamic_cast<BspViewCell *>(*it)->mLeaf;
2701               
2702                ConstructGeometry(l, vcGeom);
2703        }
2704}
2705
2706
2707int VspBspTree::FindNeighbors(BspNode *n, vector<BspLeaf *> &neighbors,
2708                                                          const bool onlyUnmailed) const
2709{
2710        stack<bspNodePair> nodeStack;
2711       
2712        BspNodeGeometry nodeGeom;
2713        ConstructGeometry(n, nodeGeom);
2714       
2715        // split planes from the root to this node
2716        // needed to verify that we found neighbor leaf
2717        // TODO: really needed?
2718        vector<Plane3> halfSpaces;
2719        ExtractHalfSpaces(n, halfSpaces);
2720
2721
2722        BspNodeGeometry *rgeom = new BspNodeGeometry();
2723        ConstructGeometry(mRoot, *rgeom);
2724
2725        nodeStack.push(bspNodePair(mRoot, rgeom));
2726
2727        while (!nodeStack.empty())
2728        {
2729                BspNode *node = nodeStack.top().first;
2730                BspNodeGeometry *geom = nodeStack.top().second;
2731       
2732                nodeStack.pop();
2733
2734                if (node->IsLeaf())
2735                {
2736                        // test if this leaf is in valid view space
2737                        if (node->TreeValid() &&
2738                                (node != n) &&
2739                                (!onlyUnmailed || !node->Mailed()))
2740                        {
2741                                bool isAdjacent = true;
2742
2743                                if (1)
2744                                {
2745                                        // test all planes of current node if still adjacent
2746                                        for (int i = 0; (i < halfSpaces.size()) && isAdjacent; ++ i)
2747                                        {
2748                                                const int cf =
2749                                                        Polygon3::ClassifyPlane(geom->GetPolys(),
2750                                                                                                        halfSpaces[i],
2751                                                                                                        mEpsilon);
2752
2753                                                if (cf == Polygon3::FRONT_SIDE)
2754                                                {
2755                                                        isAdjacent = false;
2756                                                }
2757                                        }
2758                                }
2759                                else
2760                                {
2761                                        // TODO: why is this wrong??
2762                                        // test all planes of current node if still adjacent
2763                                        for (int i = 0; (i < nodeGeom.Size()) && isAdjacent; ++ i)
2764                                        {
2765                                                Polygon3 *poly = nodeGeom.GetPolys()[i];
2766
2767                                                const int cf =
2768                                                        Polygon3::ClassifyPlane(geom->GetPolys(),
2769                                                                                                        poly->GetSupportingPlane(),
2770                                                                                                        mEpsilon);
2771
2772                                                if (cf == Polygon3::FRONT_SIDE)
2773                                                {
2774                                                        isAdjacent = false;
2775                                                }
2776                                        }
2777                                }
2778                                // neighbor was found
2779                                if (isAdjacent)
2780                                {       
2781                                        neighbors.push_back(dynamic_cast<BspLeaf *>(node));
2782                                }
2783                        }
2784                }
2785                else
2786                {
2787                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2788
2789                        const int cf = Polygon3::ClassifyPlane(nodeGeom.GetPolys(),
2790                                                                                                   interior->GetPlane(),
2791                                                                                                   mEpsilon);
2792                       
2793                        BspNode *front = interior->GetFront();
2794                        BspNode *back = interior->GetBack();
2795           
2796                        BspNodeGeometry *fGeom = new BspNodeGeometry();
2797                        BspNodeGeometry *bGeom = new BspNodeGeometry();
2798
2799                        geom->SplitGeometry(*fGeom,
2800                                                                *bGeom,
2801                                                                interior->GetPlane(),
2802                                                                mBox,
2803                                                                //0.0000001f);
2804                                                                mEpsilon);
2805               
2806                        if (cf == Polygon3::BACK_SIDE)
2807                        {
2808                                nodeStack.push(bspNodePair(interior->GetBack(), bGeom));
2809                                DEL_PTR(fGeom);
2810                        }
2811                        else
2812                        {
2813                                if (cf == Polygon3::FRONT_SIDE)
2814                                {
2815                                        nodeStack.push(bspNodePair(interior->GetFront(), fGeom));
2816                                        DEL_PTR(bGeom);
2817                                }
2818                                else
2819                                {       // random decision
2820                                        nodeStack.push(bspNodePair(front, fGeom));
2821                                        nodeStack.push(bspNodePair(back, bGeom));
2822                                }
2823                        }
2824                }
2825       
2826                DEL_PTR(geom);
2827        }
2828
2829        return (int)neighbors.size();
2830}
2831
2832
2833
2834int VspBspTree::FindApproximateNeighbors(BspNode *n, vector<BspLeaf *> &neighbors,
2835                                                                                 const bool onlyUnmailed) const
2836{
2837        stack<bspNodePair> nodeStack;
2838       
2839        BspNodeGeometry nodeGeom;
2840        ConstructGeometry(n, nodeGeom);
2841       
2842        // split planes from the root to this node
2843        // needed to verify that we found neighbor leaf
2844        // TODO: really needed?
2845        vector<Plane3> halfSpaces;
2846        ExtractHalfSpaces(n, halfSpaces);
2847
2848
2849        BspNodeGeometry *rgeom = new BspNodeGeometry();
2850        ConstructGeometry(mRoot, *rgeom);
2851
2852        nodeStack.push(bspNodePair(mRoot, rgeom));
2853
2854        while (!nodeStack.empty())
2855        {
2856                BspNode *node = nodeStack.top().first;
2857                BspNodeGeometry *geom = nodeStack.top().second;
2858       
2859                nodeStack.pop();
2860
2861                if (node->IsLeaf())
2862                {
2863                        // test if this leaf is in valid view space
2864                        if (node->TreeValid() &&
2865                                (node != n) &&
2866                                (!onlyUnmailed || !node->Mailed()))
2867                        {
2868                                bool isAdjacent = true;
2869
2870                                // neighbor was found
2871                                if (isAdjacent)
2872                                {       
2873                                        neighbors.push_back(dynamic_cast<BspLeaf *>(node));
2874                                }
2875                        }
2876                }
2877                else
2878                {
2879                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2880
2881                        const int cf = Polygon3::ClassifyPlane(nodeGeom.GetPolys(),
2882                                                                                                   interior->GetPlane(),
2883                                                                                                   mEpsilon);
2884                       
2885                        BspNode *front = interior->GetFront();
2886                        BspNode *back = interior->GetBack();
2887           
2888                        BspNodeGeometry *fGeom = new BspNodeGeometry();
2889                        BspNodeGeometry *bGeom = new BspNodeGeometry();
2890
2891                        geom->SplitGeometry(*fGeom,
2892                                                                *bGeom,
2893                                                                interior->GetPlane(),
2894                                                                mBox,
2895                                                                //0.0000001f);
2896                                                                mEpsilon);
2897               
2898                        if (cf == Polygon3::BACK_SIDE)
2899                        {
2900                                nodeStack.push(bspNodePair(interior->GetBack(), bGeom));
2901                                DEL_PTR(fGeom);
2902                        }
2903                        else
2904                        {
2905                                if (cf == Polygon3::FRONT_SIDE)
2906                                {
2907                                        nodeStack.push(bspNodePair(interior->GetFront(), fGeom));
2908                                        DEL_PTR(bGeom);
2909                                }
2910                                else
2911                                {       // random decision
2912                                        nodeStack.push(bspNodePair(front, fGeom));
2913                                        nodeStack.push(bspNodePair(back, bGeom));
2914                                }
2915                        }
2916                }
2917       
2918                DEL_PTR(geom);
2919        }
2920
2921        return (int)neighbors.size();
2922}
2923
2924
2925
2926BspLeaf *VspBspTree::GetRandomLeaf(const Plane3 &halfspace)
2927{
2928    stack<BspNode *> nodeStack;
2929        nodeStack.push(mRoot);
2930
2931        int mask = rand();
2932
2933        while (!nodeStack.empty())
2934        {
2935                BspNode *node = nodeStack.top();
2936                nodeStack.pop();
2937
2938                if (node->IsLeaf())
2939                {
2940                        return dynamic_cast<BspLeaf *>(node);
2941                }
2942                else
2943                {
2944                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2945                        BspNode *next;
2946                        BspNodeGeometry geom;
2947
2948                        // todo: not very efficient: constructs full cell everytime
2949                        ConstructGeometry(interior, geom);
2950
2951                        const int cf =
2952                                Polygon3::ClassifyPlane(geom.GetPolys(), halfspace, mEpsilon);
2953
2954                        if (cf == Polygon3::BACK_SIDE)
2955                                next = interior->GetFront();
2956                        else
2957                                if (cf == Polygon3::FRONT_SIDE)
2958                                        next = interior->GetFront();
2959                        else
2960                        {
2961                                // random decision
2962                                if (mask & 1)
2963                                        next = interior->GetBack();
2964                                else
2965                                        next = interior->GetFront();
2966                                mask = mask >> 1;
2967                        }
2968
2969                        nodeStack.push(next);
2970                }
2971        }
2972
2973        return NULL;
2974}
2975
2976
2977BspLeaf *VspBspTree::GetRandomLeaf(const bool onlyUnmailed)
2978{
2979        stack<BspNode *> nodeStack;
2980
2981        nodeStack.push(mRoot);
2982
2983        int mask = rand();
2984
2985        while (!nodeStack.empty())
2986        {
2987                BspNode *node = nodeStack.top();
2988                nodeStack.pop();
2989
2990                if (node->IsLeaf())
2991                {
2992                        if ( (!onlyUnmailed || !node->Mailed()) )
2993                                return dynamic_cast<BspLeaf *>(node);
2994                }
2995                else
2996                {
2997                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2998
2999                        // random decision
3000                        if (mask & 1)
3001                                nodeStack.push(interior->GetBack());
3002                        else
3003                                nodeStack.push(interior->GetFront());
3004
3005                        mask = mask >> 1;
3006                }
3007        }
3008
3009        return NULL;
3010}
3011
3012
3013int VspBspTree::ComputePvsSize(const RayInfoContainer &rays) const
3014{
3015        int pvsSize = 0;
3016
3017        RayInfoContainer::const_iterator rit, rit_end = rays.end();
3018
3019        Intersectable::NewMail();
3020
3021        for (rit = rays.begin(); rit != rays.end(); ++ rit)
3022        {
3023                VssRay *ray = (*rit).mRay;
3024
3025                if (ray->mOriginObject)
3026                {
3027                        if (!ray->mOriginObject->Mailed())
3028                        {
3029                                ray->mOriginObject->Mail();
3030                                ++ pvsSize;
3031                        }
3032                }
3033                if (ray->mTerminationObject)
3034                {
3035                        if (!ray->mTerminationObject->Mailed())
3036                        {
3037                                ray->mTerminationObject->Mail();
3038                                ++ pvsSize;
3039                        }
3040                }
3041        }
3042
3043        return pvsSize;
3044}
3045
3046
3047float VspBspTree::GetEpsilon() const
3048{
3049        return mEpsilon;
3050}
3051
3052
3053int VspBspTree::SplitPolygons(const Plane3 &plane,
3054                                                          PolygonContainer &polys,
3055                                                          PolygonContainer &frontPolys,
3056                                                          PolygonContainer &backPolys,
3057                                                          PolygonContainer &coincident) const
3058{
3059        int splits = 0;
3060
3061        PolygonContainer::const_iterator it, it_end = polys.end();
3062
3063        for (it = polys.begin(); it != polys.end(); ++ it)     
3064        {
3065                Polygon3 *poly = *it;
3066
3067                // classify polygon
3068                const int cf = poly->ClassifyPlane(plane, mEpsilon);
3069
3070                switch (cf)
3071                {
3072                        case Polygon3::COINCIDENT:
3073                                coincident.push_back(poly);
3074                                break;
3075                        case Polygon3::FRONT_SIDE:
3076                                frontPolys.push_back(poly);
3077                                break;
3078                        case Polygon3::BACK_SIDE:
3079                                backPolys.push_back(poly);
3080                                break;
3081                        case Polygon3::SPLIT:
3082                                backPolys.push_back(poly);
3083                                frontPolys.push_back(poly);
3084                                ++ splits;
3085                                break;
3086                        default:
3087                Debug << "SHOULD NEVER COME HERE\n";
3088                                break;
3089                }
3090        }
3091
3092        return splits;
3093}
3094
3095
3096int VspBspTree::CastLineSegment(const Vector3 &origin,
3097                                                                const Vector3 &termination,
3098                                                                vector<ViewCell *> &viewcells)
3099{
3100        int hits = 0;
3101        stack<BspRayTraversalData> tQueue;
3102
3103        float mint = 0.0f, maxt = 1.0f;
3104
3105        Intersectable::NewMail();
3106        ViewCell::NewMail();
3107
3108        Vector3 entp = origin;
3109        Vector3 extp = termination;
3110
3111        BspNode *node = mRoot;
3112        BspNode *farChild = NULL;
3113
3114        float t;
3115        const float thresh = 1e-6f; // matt: change this
3116       
3117        while (1)
3118        {
3119                if (!node->IsLeaf())
3120                {
3121                        BspInterior *in = dynamic_cast<BspInterior *>(node);
3122
3123                        Plane3 splitPlane = in->GetPlane();
3124                       
3125                        const int entSide = splitPlane.Side(entp, thresh);
3126                        const int extSide = splitPlane.Side(extp, thresh);
3127
3128                        if (entSide < 0)
3129                        {
3130                                node = in->GetBack();
3131                               
3132                                // plane does not split ray => no far child
3133                                if (extSide <= 0)
3134                                        continue;
3135 
3136                                farChild = in->GetFront(); // plane splits ray
3137                        }
3138                        else if (entSide > 0)
3139                        {
3140                                node = in->GetFront();
3141
3142                                if (extSide >= 0) // plane does not split ray => no far child
3143                                        continue;
3144
3145                                farChild = in->GetBack(); // plane splits ray
3146                        }
3147                        else // one of the ray end points is on the plane
3148                        {       // NOTE: what to do if ray is coincident with plane?
3149                                if (extSide < 0)
3150                                        node = in->GetBack();
3151                                else //if (extSide > 0)
3152                                        node = in->GetFront();
3153                                //else break; // coincident => count no intersections
3154
3155                                continue; // no far child
3156                        }
3157
3158                        // push data for far child
3159                        tQueue.push(BspRayTraversalData(farChild, extp));
3160
3161                        // find intersection of ray segment with plane
3162                        extp = splitPlane.FindIntersection(origin, extp, &t);
3163                }
3164                else
3165                {
3166                        // reached leaf => intersection with view cell
3167                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
3168                        ViewCell *viewCell;
3169                       
3170                        viewCell = mViewCellsTree->GetActiveViewCell(leaf->GetViewCell());
3171                        //viewCell = leaf->GetViewCell();
3172
3173                        if (!viewCell->Mailed())
3174                        {
3175                                viewcells.push_back(viewCell);
3176                                viewCell->Mail();
3177                                ++ hits;
3178                        }
3179
3180                        //-- fetch the next far child from the stack
3181                        if (tQueue.empty())
3182                                break;
3183
3184                        entp = extp;
3185                       
3186                        BspRayTraversalData &s = tQueue.top();
3187
3188                        node = s.mNode;
3189                        extp = s.mExitPoint;
3190
3191                        tQueue.pop();
3192                }
3193        }
3194
3195        return hits;
3196}
3197
3198
3199
3200
3201int VspBspTree::TreeDistance(BspNode *n1, BspNode *n2) const
3202{
3203        std::deque<BspNode *> path1;
3204        BspNode *p1 = n1;
3205
3206        // create path from node 1 to root
3207        while (p1)
3208        {
3209                if (p1 == n2) // second node on path
3210                        return (int)path1.size();
3211
3212                path1.push_front(p1);
3213                p1 = p1->GetParent();
3214        }
3215
3216        int depth = n2->GetDepth();
3217        int d = depth;
3218
3219        BspNode *p2 = n2;
3220
3221        // compare with same depth
3222        while (1)
3223        {
3224                if ((d < (int)path1.size()) && (p2 == path1[d]))
3225                        return (depth - d) + ((int)path1.size() - 1 - d);
3226
3227                -- d;
3228                p2 = p2->GetParent();
3229        }
3230
3231        return 0; // never come here
3232}
3233
3234
3235BspNode *VspBspTree::CollapseTree(BspNode *node, int &collapsed)
3236{
3237// TODO
3238#if VC_HISTORY
3239        if (node->IsLeaf())
3240                return node;
3241
3242        BspInterior *interior = dynamic_cast<BspInterior *>(node);
3243
3244        BspNode *front = CollapseTree(interior->GetFront(), collapsed);
3245        BspNode *back = CollapseTree(interior->GetBack(), collapsed);
3246
3247        if (front->IsLeaf() && back->IsLeaf())
3248        {
3249                BspLeaf *frontLeaf = dynamic_cast<BspLeaf *>(front);
3250                BspLeaf *backLeaf = dynamic_cast<BspLeaf *>(back);
3251
3252                //-- collapse tree
3253                if (frontLeaf->GetViewCell() == backLeaf->GetViewCell())
3254                {
3255                        BspViewCell *vc = frontLeaf->GetViewCell();
3256
3257                        BspLeaf *leaf = new BspLeaf(interior->GetParent(), vc);
3258                        leaf->SetTreeValid(frontLeaf->TreeValid());
3259
3260                        // replace a link from node's parent
3261                        if (leaf->GetParent())
3262                                leaf->GetParent()->ReplaceChildLink(node, leaf);
3263                        else
3264                                mRoot = leaf;
3265
3266                        ++ collapsed;
3267                        delete interior;
3268
3269                        return leaf;
3270                }
3271        }
3272#endif
3273        return node;
3274}
3275
3276
3277int VspBspTree::CollapseTree()
3278{
3279        int collapsed = 0;
3280        //TODO
3281#if VC_HISTORY
3282        (void)CollapseTree(mRoot, collapsed);
3283
3284        // revalidate leaves
3285        RepairViewCellsLeafLists();
3286#endif
3287        return collapsed;
3288}
3289
3290
3291void VspBspTree::RepairViewCellsLeafLists()
3292{
3293// TODO
3294#if VC_HISTORY
3295        // list not valid anymore => clear
3296        stack<BspNode *> nodeStack;
3297        nodeStack.push(mRoot);
3298
3299        ViewCell::NewMail();
3300
3301        while (!nodeStack.empty())
3302        {
3303                BspNode *node = nodeStack.top();
3304                nodeStack.pop();
3305
3306                if (node->IsLeaf())
3307                {
3308                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
3309
3310                        BspViewCell *viewCell = leaf->GetViewCell();
3311
3312                        if (!viewCell->Mailed())
3313                        {
3314                                viewCell->mLeaves.clear();
3315                                viewCell->Mail();
3316                        }
3317       
3318                        viewCell->mLeaves.push_back(leaf);
3319
3320                }
3321                else
3322                {
3323                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
3324
3325                        nodeStack.push(interior->GetFront());
3326                        nodeStack.push(interior->GetBack());
3327                }
3328        }
3329// TODO
3330#endif
3331}
3332
3333
3334typedef pair<BspNode *, BspNodeGeometry *> bspNodePair;
3335
3336
3337int VspBspTree::CastBeam(Beam &beam)
3338{
3339    stack<bspNodePair> nodeStack;
3340        BspNodeGeometry *rgeom = new BspNodeGeometry();
3341        ConstructGeometry(mRoot, *rgeom);
3342
3343        nodeStack.push(bspNodePair(mRoot, rgeom));
3344 
3345        ViewCell::NewMail();
3346
3347        while (!nodeStack.empty())
3348        {
3349                BspNode *node = nodeStack.top().first;
3350                BspNodeGeometry *geom = nodeStack.top().second;
3351                nodeStack.pop();
3352               
3353                AxisAlignedBox3 box;
3354                geom->GetBoundingBox(box);
3355
3356                const int side = beam.ComputeIntersection(box);
3357               
3358                switch (side)
3359                {
3360                case -1:
3361                        CollectViewCells(node, true, beam.mViewCells, true);
3362                        break;
3363                case 0:
3364                       
3365                        if (node->IsLeaf())
3366                        {
3367                                BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
3368                       
3369                                if (!leaf->GetViewCell()->Mailed() && leaf->TreeValid())
3370                                {
3371                                        leaf->GetViewCell()->Mail();
3372                                        beam.mViewCells.push_back(leaf->GetViewCell());
3373                                }
3374                        }
3375                        else
3376                        {
3377                                BspInterior *interior = dynamic_cast<BspInterior *>(node);
3378                       
3379                                BspNode *first = interior->GetFront();
3380                                BspNode *second = interior->GetBack();
3381           
3382                                BspNodeGeometry *firstGeom = new BspNodeGeometry();
3383                                BspNodeGeometry *secondGeom = new BspNodeGeometry();
3384
3385                                geom->SplitGeometry(*firstGeom,
3386                                                                        *secondGeom,
3387                                                                        interior->GetPlane(),
3388                                                                        mBox,
3389                                                                        //0.0000001f);
3390                                                                        mEpsilon);
3391
3392                                // decide on the order of the nodes
3393                                if (DotProd(beam.mPlanes[0].mNormal,
3394                                        interior->GetPlane().mNormal) > 0)
3395                                {
3396                                        swap(first, second);
3397                                        swap(firstGeom, secondGeom);
3398                                }
3399
3400                                nodeStack.push(bspNodePair(first, firstGeom));
3401                                nodeStack.push(bspNodePair(second, secondGeom));
3402                        }
3403                       
3404                        break;
3405                default:
3406                        // default: cull
3407                        break;
3408                }
3409               
3410                DEL_PTR(geom);
3411               
3412        }
3413
3414        return (int)beam.mViewCells.size();
3415}
3416
3417
3418void VspBspTree::SetViewCellsManager(ViewCellsManager *vcm)
3419{
3420        mViewCellsManager = vcm;
3421}
3422
3423
3424int VspBspTree::CollectMergeCandidates(const vector<BspLeaf *> leaves,
3425                                                                           vector<MergeCandidate> &candidates)
3426{
3427        BspLeaf::NewMail();
3428       
3429        vector<BspLeaf *>::const_iterator it, it_end = leaves.end();
3430
3431        int numCandidates = 0;
3432
3433        // find merge candidates and push them into queue
3434        for (it = leaves.begin(); it != it_end; ++ it)
3435        {
3436                BspLeaf *leaf = *it;
3437               
3438                // the same leaves must not be part of two merge candidates
3439                leaf->Mail();
3440               
3441                vector<BspLeaf *> neighbors;
3442                if (1)
3443                        FindNeighbors(leaf, neighbors, true);
3444                else
3445                        FindApproximateNeighbors(leaf, neighbors, true);
3446                vector<BspLeaf *>::const_iterator nit, nit_end = neighbors.end();
3447
3448                // TODO: test if at least one ray goes from one leaf to the other
3449                for (nit = neighbors.begin(); nit != nit_end; ++ nit)
3450                {
3451                        if ((*nit)->GetViewCell() != leaf->GetViewCell())
3452                        {
3453                                MergeCandidate mc(leaf->GetViewCell(), (*nit)->GetViewCell());
3454
3455                                // dont't merge view cells if they are empty and not front and back leaf of the same parent
3456                                // in the tree?
3457                                if (mEmptyViewCellsMergeAllowed ||
3458                                        !leaf->GetViewCell()->GetPvs().Empty() || !(*nit)->GetViewCell()->GetPvs().Empty() ||
3459                    leaf->IsSibling(*nit))
3460                                {
3461                                        candidates.push_back(mc);
3462                                }
3463
3464                                ++ numCandidates;
3465                                if ((numCandidates % 1000) == 0)
3466                                {
3467                                        cout << "collected " << numCandidates << " merge candidates" << endl;
3468                                }
3469                        }
3470                }
3471        }
3472
3473        Debug << "merge queue: " << (int)candidates.size() << endl;
3474        Debug << "leaves in queue: " << numCandidates << endl;
3475       
3476
3477        return (int)leaves.size();
3478}
3479
3480
3481int VspBspTree::CollectMergeCandidates(const VssRayContainer &rays,
3482                                                                           vector<MergeCandidate> &candidates)
3483{
3484        ViewCell::NewMail();
3485        long startTime = GetTime();
3486       
3487        map<BspLeaf *, vector<BspLeaf*> > neighborMap;
3488        ViewCellContainer::const_iterator iit;
3489
3490        int numLeaves = 0;
3491       
3492        BspLeaf::NewMail();
3493
3494        for (int i = 0; i < (int)rays.size(); ++ i)
3495        { 
3496                VssRay *ray = rays[i];
3497       
3498                // traverse leaves stored in the rays and compare and
3499                // merge consecutive leaves (i.e., the neighbors in the tree)
3500                if (ray->mViewCells.size() < 2)
3501                        continue;
3502//TODO viewcellhierarchy
3503                iit = ray->mViewCells.begin();
3504                BspViewCell *bspVc = dynamic_cast<BspViewCell *>(*(iit ++));
3505                BspLeaf *leaf = bspVc->mLeaf;
3506               
3507                // traverse intersections
3508                // consecutive leaves are neighbors => add them to queue
3509                for (; iit != ray->mViewCells.end(); ++ iit)
3510                {
3511                        // next pair
3512                        BspLeaf *prevLeaf = leaf;
3513                        bspVc = dynamic_cast<BspViewCell *>(*iit);
3514            leaf = bspVc->mLeaf;
3515
3516                        // view space not valid or same view cell
3517                        if (!leaf->TreeValid() || !prevLeaf->TreeValid() ||
3518                                (leaf->GetViewCell() == prevLeaf->GetViewCell()))
3519                                continue;
3520
3521                vector<BspLeaf *> &neighbors = neighborMap[leaf];
3522                       
3523                        bool found = false;
3524
3525                        // both leaves inserted in queue already =>
3526                        // look if double pair already exists
3527                        if (leaf->Mailed() && prevLeaf->Mailed())
3528                        {
3529                                vector<BspLeaf *>::const_iterator it, it_end = neighbors.end();
3530                               
3531                for (it = neighbors.begin(); !found && (it != it_end); ++ it)
3532                                        if (*it == prevLeaf)
3533                                                found = true; // already in queue
3534                        }
3535               
3536                        if (!found)
3537                        {
3538                                // this pair is not in map yet
3539                                // => insert into the neighbor map and the queue
3540                                neighbors.push_back(prevLeaf);
3541                                neighborMap[prevLeaf].push_back(leaf);
3542
3543                                leaf->Mail();
3544                                prevLeaf->Mail();
3545               
3546                                MergeCandidate mc(leaf->GetViewCell(), prevLeaf->GetViewCell());
3547                               
3548                                candidates.push_back(mc);
3549
3550                                if (((int)candidates.size() % 1000) == 0)
3551                                {
3552                                        cout << "collected " << (int)candidates.size() << " merge candidates" << endl;
3553                                }
3554                        }
3555        }
3556        }
3557
3558        Debug << "neighbormap size: " << (int)neighborMap.size() << endl;
3559        Debug << "merge queue: " << (int)candidates.size() << endl;
3560        Debug << "leaves in queue: " << numLeaves << endl;
3561
3562
3563        //-- collect the leaves which haven't been found by ray casting
3564        if (0)
3565        {
3566                cout << "finding additional merge candidates using geometry" << endl;
3567                vector<BspLeaf *> leaves;
3568                CollectLeaves(leaves, true);
3569                Debug << "found " << (int)leaves.size() << " new leaves" << endl << endl;
3570                CollectMergeCandidates(leaves, candidates);
3571        }
3572
3573        return numLeaves;
3574}
3575
3576
3577
3578
3579ViewCell *VspBspTree::GetViewCell(const Vector3 &point)
3580{
3581  if (mRoot == NULL)
3582        return NULL;
3583 
3584  stack<BspNode *> nodeStack;
3585  nodeStack.push(mRoot);
3586 
3587  ViewCell *viewcell = NULL;
3588 
3589  while (!nodeStack.empty())  {
3590        BspNode *node = nodeStack.top();
3591        nodeStack.pop();
3592       
3593        if (node->IsLeaf()) {
3594          viewcell = dynamic_cast<BspLeaf *>(node)->GetViewCell();
3595          break;
3596        } else {
3597         
3598          BspInterior *interior = dynamic_cast<BspInterior *>(node);
3599               
3600          // random decision
3601          if (interior->GetPlane().Side(point) < 0)
3602                nodeStack.push(interior->GetBack());
3603          else
3604                nodeStack.push(interior->GetFront());
3605        }
3606  }
3607 
3608  return viewcell;
3609}
3610
3611
3612bool VspBspTree::ViewPointValid(const Vector3 &viewPoint) const
3613{
3614        BspNode *node = mRoot;
3615
3616        while (1)
3617        {
3618                // early exit
3619                if (node->TreeValid())
3620                        return true;
3621
3622                if (node->IsLeaf())
3623                        return false;
3624                       
3625                BspInterior *in = dynamic_cast<BspInterior *>(node);
3626                                       
3627                if (in->GetPlane().Side(viewPoint) <= 0)
3628                {
3629                        node = in->GetBack();
3630                }
3631                else
3632                {
3633                        node = in->GetFront();
3634                }
3635        }
3636
3637        // should never come here
3638        return false;
3639}
3640
3641
3642void VspBspTree::PropagateUpValidity(BspNode *node)
3643{
3644        const bool isValid = node->TreeValid();
3645
3646        // propagative up invalid flag until only invalid nodes exist over this node
3647        if (!isValid)
3648        {
3649                while (!node->IsRoot() && node->GetParent()->TreeValid())
3650                {
3651                        node = node->GetParent();
3652                        node->SetTreeValid(false);
3653                }
3654        }
3655        else
3656        {
3657                // propagative up valid flag until one of the subtrees is invalid
3658                while (!node->IsRoot() && !node->TreeValid())
3659                {
3660            node = node->GetParent();
3661                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
3662                       
3663                        // the parent is valid iff both leaves are valid
3664                        node->SetTreeValid(interior->GetBack()->TreeValid() &&
3665                                                           interior->GetFront()->TreeValid());
3666                }
3667        }
3668}
3669
3670
3671bool VspBspTree::Export(ofstream &stream)
3672{
3673        ExportNode(mRoot, stream);
3674
3675        return true;
3676}
3677
3678
3679void VspBspTree::ExportNode(BspNode *node, ofstream &stream)
3680{
3681        if (node->IsLeaf())
3682        {
3683                BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
3684                ViewCell *viewCell = mViewCellsTree->GetActiveViewCell(leaf->GetViewCell());
3685
3686                int id = -1;
3687                if (viewCell != mOutOfBoundsCell)
3688                        id = viewCell->GetId();
3689
3690                stream << "<Leaf viewCellId=\"" << id << "\" />" << endl;
3691        }
3692        else
3693        {
3694                BspInterior *interior = dynamic_cast<BspInterior *>(node);
3695       
3696                Plane3 plane = interior->GetPlane();
3697                stream << "<Interior plane=\"" << plane.mNormal.x << " "
3698                           << plane.mNormal.y << " " << plane.mNormal.z << " "
3699                           << plane.mD << "\">" << endl;
3700
3701                ExportNode(interior->GetBack(), stream);
3702                ExportNode(interior->GetFront(), stream);
3703
3704                stream << "</Interior>" << endl;
3705        }
3706}
Note: See TracBrowser for help on using the repository browser.