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

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