source: trunk/VUT/GtpVisibilityPreprocessor/src/ViewCellBsp.cpp @ 442

Revision 442, 62.2 KB checked in by mattausch, 19 years ago (diff)
Line 
1#include "Plane3.h"
2#include "ViewCellBsp.h"
3#include "Mesh.h"
4#include "common.h"
5#include "ViewCell.h"
6#include "Environment.h"
7#include "Polygon3.h"
8#include "Ray.h"
9#include "AxisAlignedBox3.h"
10#include <stack>
11#include <time.h>
12#include <iomanip>
13#include "Exporter.h"
14#include "Plane3.h"
15
16//-- static members
17
18int BspNode::sMailId = 1;
19
20/** Evaluates split plane classification with respect to the plane's
21        contribution for a minimum number splits in the tree.
22*/
23const float BspTree::sLeastPolySplitsTable[] = {0, 0, 1, 0};
24/** Evaluates split plane classification with respect to the plane's
25        contribution for a balanced tree.
26*/
27const float BspTree::sBalancedPolysTable[] = {1, -1, 0, 0};
28
29/** Evaluates split plane classification with respect to the plane's
30        contribution for a minimum number of ray splits.
31*/
32const float BspTree::sLeastRaySplitsTable[] = {0, 0, 1, 1, 0};
33/** Evaluates split plane classification with respect to the plane's
34        contribution for balanced rays.
35*/
36const float BspTree::sBalancedRaysTable[] = {1, -1, 0, 0, 0};
37
38int BspTree::sFrontId = 0;
39int BspTree::sBackId = 0;
40int BspTree::sFrontAndBackId = 0;
41
42/****************************************************************/
43/*                  class BspNode implementation                */
44/****************************************************************/
45
46BspNode::BspNode():
47mParent(NULL)
48{}
49
50BspNode::BspNode(BspInterior *parent):
51mParent(parent)
52{}
53
54
55bool BspNode::IsRoot() const
56{
57        return mParent == NULL;
58}
59
60BspInterior *BspNode::GetParent()
61{
62        return mParent;
63}
64
65void BspNode::SetParent(BspInterior *parent)
66{
67        mParent = parent;
68}
69
70/****************************************************************/
71/*              class BspInterior implementation                */
72/****************************************************************/
73
74
75BspInterior::BspInterior(const Plane3 &plane):
76mPlane(plane), mFront(NULL), mBack(NULL)
77{}
78
79BspInterior::~BspInterior()
80{
81        DEL_PTR(mFront);
82        DEL_PTR(mBack);
83}
84
85bool BspInterior::IsLeaf() const
86{
87        return false;
88}
89
90BspNode *BspInterior::GetBack()
91{
92        return mBack;
93}
94
95BspNode *BspInterior::GetFront()
96{
97        return mFront;
98}
99
100Plane3 *BspInterior::GetPlane()
101{
102        return &mPlane;
103}
104
105void BspInterior::ReplaceChildLink(BspNode *oldChild, BspNode *newChild)
106{
107        if (mBack == oldChild)
108                mBack = newChild;
109        else
110                mFront = newChild;
111}
112
113void BspInterior::SetupChildLinks(BspNode *b, BspNode *f)
114{
115    mBack = b;
116    mFront = f;
117}
118
119int BspInterior::SplitPolygons(PolygonContainer &polys,
120                                                           PolygonContainer &frontPolys,
121                                                           PolygonContainer &backPolys,
122                                                           PolygonContainer &coincident)
123{
124        int splits = 0;
125
126#ifdef _Debug
127        Debug << "splitting polygons of node " << this << " with plane " << mPlane << endl;
128#endif
129        while (!polys.empty())
130        {
131                Polygon3 *poly = polys.back();
132                polys.pop_back();
133
134                //Debug << "New polygon with plane: " << poly->GetSupportingPlane() << "\n";
135
136                // classify polygon
137                const int cf = poly->ClassifyPlane(mPlane);
138
139                switch (cf)
140                {
141                        case Polygon3::COINCIDENT:
142                                coincident.push_back(poly);
143                                break;                 
144                        case Polygon3::FRONT_SIDE:     
145                                frontPolys.push_back(poly);
146                                break;
147                        case Polygon3::BACK_SIDE:
148                                backPolys.push_back(poly);
149                                break;
150                        case Polygon3::SPLIT:
151                                {
152                                        Polygon3 *front_piece = new Polygon3(poly->mParent);
153                                        Polygon3 *back_piece = new Polygon3(poly->mParent);
154
155                                        //-- split polygon into front and back part
156                                        poly->Split(mPlane, *front_piece, *back_piece);
157                                       
158                                        ++ splits; // increase number of splits
159
160                                        //-- inherit rays from parent polygon for blocked ray criterium
161                                        poly->InheritRays(*front_piece, *back_piece);
162                                        //Debug << "p: " << poly->mPiercingRays.size() << " f: " << front_piece->mPiercingRays.size() << " b: " << back_piece->mPiercingRays.size() << endl;
163                               
164                                        // check if polygons still valid
165                                        if (front_piece->Valid())
166                                                frontPolys.push_back(front_piece);
167                                        else
168                                                DEL_PTR(front_piece);
169                               
170                                        if (back_piece->Valid())
171                                                backPolys.push_back(back_piece);
172                                        else                           
173                                                DEL_PTR(back_piece);
174                               
175#ifdef _DEBUG
176                                        Debug << "split " << *poly << endl << *front_piece << endl << *back_piece << endl;
177#endif
178                                        DEL_PTR(poly);
179                                }
180                                break;
181                        default:
182                Debug << "SHOULD NEVER COME HERE\n";
183                                break;
184                }
185        }
186
187        return splits;
188}
189
190/****************************************************************/
191/*                  class BspLeaf implementation                */
192/****************************************************************/
193BspLeaf::BspLeaf(): mViewCell(NULL)
194{
195}
196
197BspLeaf::BspLeaf(BspViewCell *viewCell):
198mViewCell(viewCell)
199{
200}
201
202BspLeaf::BspLeaf(BspInterior *parent):
203BspNode(parent), mViewCell(NULL)
204{}
205
206BspLeaf::BspLeaf(BspInterior *parent, BspViewCell *viewCell):
207BspNode(parent), mViewCell(viewCell)
208{
209}
210
211BspViewCell *BspLeaf::GetViewCell() const
212{
213        return mViewCell;
214}
215
216void BspLeaf::SetViewCell(BspViewCell *viewCell)
217{
218        mViewCell = viewCell;
219}
220
221bool BspLeaf::IsLeaf() const
222{
223        return true;
224}
225
226void BspLeaf::AddToPvs(const BoundedRayContainer &rays,
227                                           int &sampleContributions,
228                                           int &contributingSamples,
229                                           bool storeLeavesWithRays)
230{
231        sampleContributions = 0;
232        contributingSamples = 0;
233
234    BoundedRayContainer::const_iterator it, it_end = rays.end();
235
236        // add contributions from samples to the PVS
237        for (it = rays.begin(); it != it_end; ++ it)
238        {
239                int contribution = 0;
240                Ray *ray = (*it)->mRay;
241                       
242                if (!ray->intersections.empty())
243                        contribution += mViewCell->GetPvs().AddSample(ray->intersections[0].mObject);
244               
245                if (ray->sourceObject.mObject)
246                        contribution += mViewCell->GetPvs().AddSample(ray->sourceObject.mObject);
247
248                if (contribution > 0)
249                {
250                        sampleContributions += contribution;
251                        ++ contributingSamples;
252                }
253               
254                if (storeLeavesWithRays)
255                        // warning: intersections not ordered
256                        ray->bspIntersections.push_back(Ray::BspIntersection((*it)->mMinT, this));
257        }
258}
259
260
261/****************************************************************/
262/*                  class BspTree implementation                */
263/****************************************************************/
264
265BspTree::BspTree(): 
266mRoot(NULL),
267mPvsUseArea(true),
268mGenerateViewCells(true)
269{
270        Randomize(); // initialise random generator for heuristics
271
272        // the view cell corresponding to unbounded space
273        mRootCell = new BspViewCell();
274
275        //-- termination criteria for autopartition
276        environment->GetIntValue("BspTree.Termination.maxDepth", mTermMaxDepth);
277        environment->GetIntValue("BspTree.Termination.minPvs", mTermMinPvs);
278        environment->GetIntValue("BspTree.Termination.minPolygons", mTermMinPolys);
279        environment->GetIntValue("BspTree.Termination.minRays", mTermMinRays);
280        environment->GetFloatValue("BspTree.Termination.minArea", mTermMinArea);       
281        environment->GetFloatValue("BspTree.Termination.maxRayContribution", mTermMaxRayContribution);
282        environment->GetFloatValue("BspTree.Termination.minAccRayLenght", mTermMinAccRayLength);
283
284        //-- factors for bsp tree split plane heuristics
285        environment->GetFloatValue("BspTree.Factor.verticalSplits", mVerticalSplitsFactor);
286        environment->GetFloatValue("BspTree.Factor.largestPolyArea", mLargestPolyAreaFactor);
287        environment->GetFloatValue("BspTree.Factor.blockedRays", mBlockedRaysFactor);
288        environment->GetFloatValue("BspTree.Factor.leastRaySplits", mLeastRaySplitsFactor);
289        environment->GetFloatValue("BspTree.Factor.balancedRays", mBalancedRaysFactor);
290        environment->GetFloatValue("BspTree.Factor.pvsFactor", mPvsFactor);
291        environment->GetFloatValue("BspTree.Factor.leastSplits" , mLeastSplitsFactor);
292        environment->GetFloatValue("BspTree.Factor.balancedPolys", mBalancedPolysFactor);
293        environment->GetFloatValue("BspTree.Factor.balancedViewCells", mBalancedViewCellsFactor);
294        environment->GetFloatValue("BspTree.Termination.ct_div_ci", mCtDivCi);
295
296        //-- termination criteria for axis aligned split
297        environment->GetFloatValue("BspTree.Termination.AxisAligned.ct_div_ci", mAaCtDivCi);
298        environment->GetFloatValue("BspTree.Termination.AxisAligned.maxCostRatio", mMaxCostRatio);
299        environment->GetIntValue("BspTree.Termination.AxisAligned.minPolys",
300                                                         mTermMinPolysForAxisAligned);
301        environment->GetIntValue("BspTree.Termination.AxisAligned.minRays",
302                                                         mTermMinRaysForAxisAligned);
303        environment->GetIntValue("BspTree.Termination.AxisAligned.minObjects",
304                                                         mTermMinObjectsForAxisAligned);
305        //-- partition criteria
306        environment->GetIntValue("BspTree.maxPolyCandidates", mMaxPolyCandidates);
307        environment->GetIntValue("BspTree.maxRayCandidates", mMaxRayCandidates);
308        environment->GetIntValue("BspTree.splitPlaneStrategy", mSplitPlaneStrategy);
309        environment->GetFloatValue("BspTree.AxisAligned.splitBorder", mSplitBorder);
310        environment->GetIntValue("BspTree.maxTests", mMaxTests);
311
312        environment->GetFloatValue("BspTree.Construction.sideTolerance", Vector3::sDistTolerance);
313        Vector3::sDistToleranceSqrt = Vector3::sDistTolerance * Vector3::sDistTolerance;
314
315
316    Debug << "BSP max depth: " << mTermMaxDepth << endl;
317        Debug << "BSP min PVS: " << mTermMinPvs << endl;
318        Debug << "BSP min area: " << mTermMinArea << endl;
319        Debug << "BSP max polys: " << mTermMinPolys << endl;
320        Debug << "BSP max rays: " << mTermMinRays << endl;
321        Debug << "BSP max polygon candidates: " << mMaxPolyCandidates << endl;
322        Debug << "BSP max plane candidates: " << mMaxRayCandidates << endl;
323
324        Debug << "Split plane strategy: ";
325        if (mSplitPlaneStrategy & RANDOM_POLYGON)
326                Debug << "random polygon ";
327        if (mSplitPlaneStrategy & AXIS_ALIGNED)
328                Debug << "axis aligned ";
329        if (mSplitPlaneStrategy & LEAST_SPLITS)     
330                Debug << "least splits ";
331        if (mSplitPlaneStrategy & BALANCED_POLYS)
332                Debug << "balanced polygons ";
333        if (mSplitPlaneStrategy & BALANCED_VIEW_CELLS)
334                Debug << "balanced view cells ";
335        if (mSplitPlaneStrategy & LARGEST_POLY_AREA)
336                Debug << "largest polygon area ";
337        if (mSplitPlaneStrategy & VERTICAL_AXIS)
338                Debug << "vertical axis ";
339        if (mSplitPlaneStrategy & BLOCKED_RAYS)
340                Debug << "blocked rays ";
341        if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
342                Debug << "least ray splits ";
343        if (mSplitPlaneStrategy & BALANCED_RAYS)
344                Debug << "balanced rays ";
345        if (mSplitPlaneStrategy & PVS)
346                Debug << "pvs";
347
348        Debug << endl;
349}
350
351const BspTreeStatistics &BspTree::GetStatistics() const
352{
353        return mStat;
354}
355
356void BspTreeStatistics::Print(ostream &app) const
357{
358        app << "===== BspTree statistics ===============\n";
359
360        app << setprecision(4);
361
362        app << "#N_CTIME  ( Construction time [s] )\n" << Time() << " \n";
363
364        app << "#N_NODES ( Number of nodes )\n" << nodes << "\n";
365
366        app << "#N_INTERIORS ( Number of interior nodes )\n" << Interior() << "\n";
367
368        app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n";
369
370        app << "#N_SPLITS ( Number of splits )\n" << splits << "\n";
371
372        app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maximum depth )\n"
373                <<      maxDepthNodes * 100 / (double)Leaves() << endl;
374
375        app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maximum depth )\n"
376                <<      maxDepthNodes * 100 / (double)Leaves() << endl;
377
378        app << "#N_PMINPVSLEAVES  ( Percentage of leaves with mininimal PVS )\n"
379                << minPvsNodes * 100 / (double)Leaves() << endl;
380
381        app << "#N_PMINRAYSLEAVES  ( Percentage of leaves with minimal number of rays)\n"
382                <<      minRaysNodes * 100 / (double)Leaves() << endl;
383
384        app << "#N_PMINAREALEAVES  ( Percentage of leaves with mininum area )\n"
385                << minAreaNodes * 100 / (double)Leaves() << endl;
386
387        app << "#N_PMAXRAYCONTRIBLEAVES  ( Percentage of leaves with maximal ray contribution )\n"
388                <<      maxRayContribNodes * 100 / (double)Leaves() << endl;
389
390        app << "#N_PMAXDEPTH ( Maximal reached depth )\n" << maxDepth << endl;
391
392        app << "#N_PMINDEPTH ( Minimal reached depth )\n" << minDepth << endl;
393
394        app << "#AVGDEPTH ( average depth )\n" << AvgDepth() << endl;
395
396        app << "#N_INPUT_POLYGONS (number of input polygons )\n" <<     polys << endl;
397
398        //app << "#N_PVS: " << pvs << endl;
399
400        app << "#N_ROUTPUT_INPUT_POLYGONS ( ratio polygons after subdivision / input polygons )\n" <<
401                 (polys + splits) / (double)polys << endl;
402       
403        app << "===== END OF BspTree statistics ==========\n";
404}
405
406
407BspTree::~BspTree()
408{
409        DEL_PTR(mRoot);
410        DEL_PTR(mRootCell);
411}
412
413BspViewCell *BspTree::GetRootCell() const
414{
415        return mRootCell;
416}
417
418void BspTree::InsertViewCell(ViewCell *viewCell)
419{
420        PolygonContainer *polys = new PolygonContainer();
421
422        // don't generate new view cell, insert this one
423        mGenerateViewCells = false;
424        // extract polygons that guide the split process
425        mStat.polys += AddMeshToPolygons(viewCell->GetMesh(), *polys, viewCell);
426        mBox.Include(viewCell->GetBox()); // add to BSP aabb
427
428        InsertPolygons(polys);
429}
430
431void BspTree::InsertPolygons(PolygonContainer *polys)
432{       
433        std::stack<BspTraversalData> tStack;
434
435        // traverse existing tree or create new tree
436    if (!mRoot)
437                mRoot = new BspLeaf();
438
439        tStack.push(BspTraversalData(mRoot, polys, 0, mRootCell, new BoundedRayContainer(), 0,
440                                                                 mBox.SurfaceArea(), new BspNodeGeometry()));
441
442        while (!tStack.empty())
443        {
444                // filter polygons donw the tree
445                BspTraversalData tData = tStack.top();
446            tStack.pop();
447                       
448                if (!tData.mNode->IsLeaf())
449                {
450                        BspInterior *interior = dynamic_cast<BspInterior *>(tData.mNode);
451
452                        //-- filter view cell polygons down the tree until a leaf is reached
453                        if (!tData.mPolygons->empty())
454                        {
455                                PolygonContainer *frontPolys = new PolygonContainer();
456                                PolygonContainer *backPolys = new PolygonContainer();
457                                PolygonContainer coincident;
458
459                                int splits = 0;
460               
461                                // split viewcell polygons with respect to split plane
462                                splits += interior->SplitPolygons(*tData.mPolygons,
463                                                                                                  *frontPolys,
464                                                                                                  *backPolys,
465                                                                                                  coincident);
466                               
467                                // extract view cells associated with the split polygons
468                                ViewCell *frontViewCell = mRootCell;
469                                ViewCell *backViewCell = mRootCell;
470                       
471                                BspTraversalData frontData(interior->GetFront(),
472                                                                                   frontPolys,
473                                                                                   tData.mDepth + 1,
474                                                                                   mRootCell,   
475                                                                                   tData.mRays,
476                                                                                   tData.mPvs,
477                                                                                   mBox.SurfaceArea(),
478                                                                                   new BspNodeGeometry());
479
480                                BspTraversalData backData(interior->GetBack(),
481                                                                                  backPolys,
482                                                                                  tData.mDepth + 1,
483                                                                                  mRootCell,   
484                                                                                  tData.mRays,
485                                                                                  tData.mPvs,
486                                                                                  mBox.SurfaceArea(),
487                                                                                  new BspNodeGeometry());
488
489                                if (!mGenerateViewCells)
490                                {
491                                        ExtractViewCells(frontData,
492                                                                         backData,
493                                                                         coincident,
494                                                                         interior->mPlane);
495                                }
496
497                                // don't need coincident polygons anymore
498                                CLEAR_CONTAINER(coincident);
499
500                                mStat.splits += splits;
501
502                                // push the children on the stack
503                                tStack.push(frontData);
504                                tStack.push(backData);
505                        }
506
507                        // cleanup
508                        DEL_PTR(tData.mPolygons);
509                }
510                else
511                {
512                        // reached leaf => subdivide current viewcell
513                        BspNode *subRoot = Subdivide(tStack, tData);
514                }
515        }
516}
517
518int BspTree::AddMeshToPolygons(Mesh *mesh, PolygonContainer &polys, MeshInstance *parent)
519{
520        FaceContainer::const_iterator fi;
521       
522        // copy the face data to polygons
523        for (fi = mesh->mFaces.begin(); fi != mesh->mFaces.end(); ++ fi)
524        {
525                Polygon3 *poly = new Polygon3((*fi), mesh);
526               
527                if (poly->Valid())
528                {
529                        poly->mParent = parent; // set parent intersectable
530                        polys.push_back(poly);
531                }
532                else
533                        DEL_PTR(poly);
534        }
535        return (int)mesh->mFaces.size();
536}
537
538int BspTree::AddToPolygonSoup(const ViewCellContainer &viewCells,
539                                                          PolygonContainer &polys,
540                                                          int maxObjects)
541{
542        int limit = (maxObjects > 0) ?
543                Min((int)viewCells.size(), maxObjects) : (int)viewCells.size();
544 
545        int polysSize = 0;
546
547        for (int i = 0; i < limit; ++ i)
548        {
549                if (viewCells[i]->GetMesh()) // copy the mesh data to polygons
550                {
551                        mBox.Include(viewCells[i]->GetBox()); // add to BSP tree aabb
552                        polysSize += AddMeshToPolygons(viewCells[i]->GetMesh(), polys, viewCells[i]);
553                }
554        }
555
556        return polysSize;
557}
558
559int BspTree::AddToPolygonSoup(const ObjectContainer &objects, PolygonContainer &polys, int maxObjects)
560{
561        int limit = (maxObjects > 0) ? Min((int)objects.size(), maxObjects) : (int)objects.size();
562 
563        for (int i = 0; i < limit; ++i)
564        {
565                Intersectable *object = objects[i];//*it;
566                Mesh *mesh = NULL;
567
568                switch (object->Type()) // extract the meshes
569                {
570                case Intersectable::MESH_INSTANCE:
571                        mesh = dynamic_cast<MeshInstance *>(object)->GetMesh();
572                        break;
573                case Intersectable::VIEW_CELL:
574                        mesh = dynamic_cast<ViewCell *>(object)->GetMesh();
575                        break;
576                        // TODO: handle transformed mesh instances
577                default:
578                        Debug << "intersectable type not supported" << endl;
579                        break;
580                }
581               
582        if (mesh) // copy the mesh data to polygons
583                {
584                        mBox.Include(object->GetBox()); // add to BSP tree aabb
585                        AddMeshToPolygons(mesh, polys, mRootCell);
586                }
587        }
588
589        return (int)polys.size();
590}
591
592void BspTree::Construct(const ViewCellContainer &viewCells)
593{
594        mStat.nodes = 1;
595        mBox.Initialize();      // initialise bsp tree bounding box
596
597        // copy view cell meshes into one big polygon soup
598        PolygonContainer *polys = new PolygonContainer();
599        mStat.polys = AddToPolygonSoup(viewCells, *polys);
600
601        // view cells are given
602        mGenerateViewCells = false;
603        // construct tree from the view cell polygons
604        Construct(polys, new BoundedRayContainer());
605}
606
607
608void BspTree::Construct(const ObjectContainer &objects)
609{
610        mStat.nodes = 1;
611        mBox.Initialize();      // initialise bsp tree bounding box
612       
613        PolygonContainer *polys = new PolygonContainer();
614
615        mGenerateViewCells = true;
616        // copy mesh instance polygons into one big polygon soup
617        mStat.polys = AddToPolygonSoup(objects, *polys);
618
619        // construct tree from polygon soup
620        Construct(polys, new BoundedRayContainer());
621}
622
623void BspTree::Construct(const RayContainer &sampleRays)
624{
625    mStat.nodes = 1;
626        mBox.Initialize();      // initialise BSP tree bounding box
627       
628        PolygonContainer *polys = new PolygonContainer();
629        BoundedRayContainer *rays = new BoundedRayContainer();
630
631        RayContainer::const_iterator rit, rit_end = sampleRays.end();
632
633        // generate view cells
634        mGenerateViewCells = false;
635
636        long startTime = GetTime();
637
638        Debug << "**** Extracting polygons from rays ****\n";
639
640        std::map<Face *, Polygon3 *> facePolyMap;
641
642        //-- extract polygons intersected by the rays
643        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
644        {
645                Ray *ray = *rit;
646       
647                // get ray-face intersection. Store polygon representing the rays together
648                // with rays intersecting the face.
649                if (!ray->intersections.empty())
650                {
651                        MeshInstance *obj = dynamic_cast<MeshInstance *>(ray->intersections[0].mObject);
652                        Face *face = obj->GetMesh()->mFaces[ray->intersections[0].mFace];
653
654                        std::map<Face *, Polygon3 *>::iterator it = facePolyMap.find(face);
655
656                        if (it != facePolyMap.end())
657                        {
658                                //store rays if needed for heuristics
659                                if (mSplitPlaneStrategy & BLOCKED_RAYS)
660                                        (*it).second->mPiercingRays.push_back(ray);
661                        }
662                        else
663                        {       //store rays if needed for heuristics
664                                Polygon3 *poly = new Polygon3(face, obj->GetMesh());
665                                poly->mParent = obj;
666                                polys->push_back(poly);
667
668                                if (mSplitPlaneStrategy & BLOCKED_RAYS)
669                                        poly->mPiercingRays.push_back(ray);
670
671                                facePolyMap[face] = poly;
672                        }
673                }
674        }
675       
676        facePolyMap.clear();
677
678        // compute bounding box
679        Polygon3::IncludeInBox(*polys, mBox);
680
681        //-- store rays
682        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
683        {
684                Ray *ray = *rit;
685        ray->SetId(-1); // reset id
686
687                float minT, maxT;
688                if (BoundRay(*ray, minT, maxT))
689                        rays->push_back(new BoundedRay(ray, minT, maxT));
690        }
691
692        mStat.polys = (int)polys->size();
693
694        Debug << "**** Finished polygon extraction ****" << endl;
695        Debug << (int)polys->size() << " polys extracted from " << (int)sampleRays.size() << " rays" << endl;
696        Debug << "extraction time: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
697
698        Construct(polys, rays);
699}
700
701void BspTree::Construct(const ObjectContainer &objects, const RayContainer &sampleRays)
702{
703    mStat.nodes = 1;
704        mBox.Initialize();      // initialise BSP tree bounding box
705       
706        BoundedRayContainer *rays = new BoundedRayContainer();
707        PolygonContainer *polys = new PolygonContainer();
708       
709        mGenerateViewCells = true;
710
711        // copy mesh instance polygons into one big polygon soup
712        mStat.polys = AddToPolygonSoup(objects, *polys);
713
714        RayContainer::const_iterator rit, rit_end = sampleRays.end();
715
716        //-- store rays
717        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
718        {
719                Ray *ray = *rit;
720        ray->SetId(-1); // reset id
721
722                float minT, maxT;
723                if (BoundRay(*ray, minT, maxT))
724                        rays->push_back(new BoundedRay(ray, minT, maxT));
725        }
726
727        Debug << "tree has " << (int)polys->size() << " polys, " << (int)sampleRays.size() << " rays" << endl;
728        Construct(polys, rays);
729}
730
731void BspTree::Construct(PolygonContainer *polys, BoundedRayContainer *rays)
732{
733        std::stack<BspTraversalData> tStack;
734
735        mRoot = new BspLeaf();
736
737        // constrruct root node geometry
738        BspNodeGeometry *cell = new BspNodeGeometry();
739        ConstructGeometry(mRoot, *cell);
740
741        BspTraversalData tData(mRoot, polys, 0, mRootCell, rays,
742                                                   ComputePvsSize(*rays), cell->GetArea(), cell);
743
744        tStack.push(tData);
745
746        mStat.Start();
747        cout << "Contructing bsp tree ... ";
748        long startTime = GetTime();
749        while (!tStack.empty())
750        {
751                tData = tStack.top();
752
753            tStack.pop();
754
755                // subdivide leaf node
756                BspNode *r = Subdivide(tStack, tData);
757
758                if (r == mRoot)
759                        Debug << "BSP tree construction time spent at root: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
760        }
761
762        cout << "finished\n";
763
764        mStat.Stop();
765}
766
767bool BspTree::TerminationCriteriaMet(const BspTraversalData &data) const
768{
769        return
770                (((int)data.mPolygons->size() <= mTermMinPolys) ||
771                 ((int)data.mRays->size() <= mTermMinRays) ||
772                 (data.mPvs <= mTermMinPvs) ||
773                 (data.mArea <= mTermMinArea) ||
774                 (data.mDepth >= mTermMaxDepth) ||
775                 (data.GetAvgRayContribution() < mTermMaxRayContribution));
776}
777
778BspNode *BspTree::Subdivide(BspTraversalStack &tStack, BspTraversalData &tData)
779{
780        //-- terminate traversal 
781        if (TerminationCriteriaMet(tData))             
782        {
783                BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
784       
785                BspViewCell *viewCell;
786
787                // generate new view cell for each leaf
788                if (mGenerateViewCells)
789                {
790                        viewCell = new BspViewCell();
791                }
792                else
793                {
794                        // add view cell to leaf
795                        viewCell = dynamic_cast<BspViewCell *>(tData.mViewCell);
796                }
797
798                leaf->SetViewCell(viewCell);
799                viewCell->mBspLeaves.push_back(leaf);
800
801                //-- add pvs
802                if (viewCell != mRootCell)
803                {
804                        int conSamp = 0, sampCon = 0;
805                        leaf->AddToPvs(*tData.mRays, conSamp, sampCon);
806                       
807                        mStat.contributingSamples += conSamp;
808                        mStat.sampleContributions += sampCon;
809                }
810
811                EvaluateLeafStats(tData);
812               
813                //-- clean up
814               
815                // discard polygons
816                CLEAR_CONTAINER(*tData.mPolygons);
817                // discard rays
818                CLEAR_CONTAINER(*tData.mRays);
819
820                DEL_PTR(tData.mPolygons);
821                DEL_PTR(tData.mRays);
822                       
823                return leaf;
824        }
825
826        //-- continue subdivision
827        PolygonContainer coincident;
828       
829        BspTraversalData tFrontData(NULL, new PolygonContainer(), tData.mDepth + 1, mRootCell,
830                                                                new BoundedRayContainer(), 0, 0, new BspNodeGeometry());
831        BspTraversalData tBackData(NULL, new PolygonContainer(), tData.mDepth + 1, mRootCell,
832                                                           new BoundedRayContainer(), 0, 0, new BspNodeGeometry());
833
834        // create new interior node and two leaf nodes
835        BspInterior *interior =
836                SubdivideNode(tData, tFrontData, tBackData, coincident);
837
838#ifdef _DEBUG   
839        if (frontPolys->empty() && backPolys->empty() && (coincident.size() > 2))
840        {       for (PolygonContainer::iterator it = coincident.begin(); it != coincident.end(); ++it)
841                        Debug << (*it) << " " << (*it)->GetArea() << " " << (*it)->mParent << endl ;
842                Debug << endl;}
843#endif
844
845        // extract view cells from coincident polygons according to plane normal
846    // only if front or back polygons are empty
847        if (!mGenerateViewCells)
848        {
849                ExtractViewCells(tFrontData,
850                                                 tBackData,
851                                                 coincident,
852                                                 interior->mPlane);                     
853        }
854
855        // don't need coincident polygons anymory
856        CLEAR_CONTAINER(coincident);
857
858        // push the children on the stack
859        tStack.push(tFrontData);
860        tStack.push(tBackData);
861
862        // cleanup
863        DEL_PTR(tData.mNode);
864        DEL_PTR(tData.mPolygons);
865        DEL_PTR(tData.mRays);
866        DEL_PTR(tData.mGeometry);               
867       
868        return interior;
869}
870
871void BspTree::ExtractViewCells(BspTraversalData &frontData,
872                                                           BspTraversalData &backData,
873                                                           const PolygonContainer &coincident,
874                                                           const Plane3 &splitPlane) const
875{
876        // if not empty, tree is further subdivided => don't have to find view cell
877        bool foundFront = !frontData.mPolygons->empty();
878        bool foundBack = !frontData.mPolygons->empty();
879
880        PolygonContainer::const_iterator it =
881                coincident.begin(), it_end = coincident.end();
882
883        //-- find first view cells in front and back leafs
884        for (; !(foundFront && foundBack) && (it != it_end); ++ it)
885        {
886                if (DotProd((*it)->GetNormal(), splitPlane.mNormal) > 0)
887                {
888                        backData.mViewCell = dynamic_cast<ViewCell *>((*it)->mParent);
889                        foundBack = true;
890                }
891                else
892                {
893                        frontData.mViewCell = dynamic_cast<ViewCell *>((*it)->mParent);
894                        foundFront = true;
895                }
896        }
897}
898
899BspInterior *BspTree::SubdivideNode(BspTraversalData &tData,
900                                                                        BspTraversalData &frontData,
901                                                                        BspTraversalData &backData,
902                                                                        PolygonContainer &coincident)
903{
904        mStat.nodes += 2;
905       
906        BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
907
908        Debug << "*********************" << endl;
909        long startTime = GetTime();
910       
911        // select subdivision plane
912        BspInterior *interior =
913                new BspInterior(SelectPlane(leaf, tData));
914        Debug << "time used for split plane selection: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
915#ifdef _DEBUG
916        Debug << interior << endl;
917#endif
918       
919
920        Debug << "number of rays: " << tData.mRays->size() << endl;
921        Debug << "number of polys: " << tData.mPolygons->size() << endl;
922
923        startTime = GetTime();
924       
925        // subdivide rays into front and back rays
926        SplitRays(interior->mPlane, *tData.mRays, *frontData.mRays, *backData.mRays);
927       
928        Debug << "time used for rays splitting: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
929
930        startTime = GetTime();
931        // subdivide polygons with plane
932        mStat.splits += interior->SplitPolygons(*tData.mPolygons,
933                                                    *frontData.mPolygons,
934                                                                                        *backData.mPolygons,
935                                                                                        coincident);
936
937        Debug << "time used for polygon splitting: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
938
939    // compute pvs
940        frontData.mPvs = ComputePvsSize(*frontData.mRays);
941        backData.mPvs = ComputePvsSize(*backData.mRays);
942
943        // split geometry and compute area
944        if (1)
945        {
946                tData.mGeometry->SplitGeometry(*frontData.mGeometry,
947                                                                           *backData.mGeometry,
948                                                                           *this,
949                                                                           interior->mPlane);
950       
951               
952                frontData.mArea = frontData.mGeometry->GetArea();
953                backData.mArea = backData.mGeometry->GetArea();
954        }
955
956        // compute accumulated ray length
957        //frontData.mAccRayLength = AccumulatedRayLength(*frontData.mRays);
958        //backData.mAccRayLength = AccumulatedRayLength(*backData.mRays);
959
960        //-- create front and back leaf
961
962        BspInterior *parent = leaf->GetParent();
963
964        // replace a link from node's parent
965        if (!leaf->IsRoot())
966        {
967                parent->ReplaceChildLink(leaf, interior);
968                interior->SetParent(parent);
969        }
970        else // new root
971        {
972                mRoot = interior;
973        }
974
975        // and setup child links
976        interior->SetupChildLinks(new BspLeaf(interior), new BspLeaf(interior));
977       
978        frontData.mNode = interior->mFront;
979        backData.mNode = interior->mBack;
980       
981        //DEL_PTR(leaf);
982        return interior;
983}
984
985void BspTree::SortSplitCandidates(const PolygonContainer &polys,
986                                                                  const int axis,
987                                                                  vector<SortableEntry> &splitCandidates) const
988{
989  splitCandidates.clear();
990 
991  int requestedSize = 2 * (int)polys.size();
992  // creates a sorted split candidates array 
993  splitCandidates.reserve(requestedSize);
994 
995  PolygonContainer::const_iterator it, it_end = polys.end();
996
997  AxisAlignedBox3 box;
998
999  // insert all queries
1000  for(it = polys.begin(); it != it_end; ++ it)
1001  {
1002          box.Initialize();
1003          box.Include(*(*it));
1004         
1005          splitCandidates.push_back(SortableEntry(SortableEntry::POLY_MIN, box.Min(axis), *it));
1006      splitCandidates.push_back(SortableEntry(SortableEntry::POLY_MAX, box.Max(axis), *it));
1007  }
1008 
1009  stable_sort(splitCandidates.begin(), splitCandidates.end());
1010}
1011
1012
1013float BspTree::BestCostRatio(const PolygonContainer &polys,
1014                                                         const AxisAlignedBox3 &box,
1015                                                         const int axis,
1016                                                         float &position,
1017                                                         int &objectsBack,
1018                                                         int &objectsFront) const
1019{
1020        vector<SortableEntry> splitCandidates;
1021
1022        SortSplitCandidates(polys, axis, splitCandidates);
1023       
1024        // go through the lists, count the number of objects left and right
1025        // and evaluate the following cost funcion:
1026        // C = ct_div_ci  + (ol + or)/queries
1027       
1028        int objectsLeft = 0, objectsRight = (int)polys.size();
1029       
1030        float minBox = box.Min(axis);
1031        float maxBox = box.Max(axis);
1032        float boxArea = box.SurfaceArea();
1033 
1034        float minBand = minBox + mSplitBorder * (maxBox - minBox);
1035        float maxBand = minBox + (1.0f - mSplitBorder) * (maxBox - minBox);
1036       
1037        float minSum = 1e20f;
1038        vector<SortableEntry>::const_iterator ci, ci_end = splitCandidates.end();
1039
1040        for(ci = splitCandidates.begin(); ci != ci_end; ++ ci)
1041        {
1042                switch ((*ci).type)
1043                {
1044                        case SortableEntry::POLY_MIN:
1045                                ++ objectsLeft;
1046                                break;
1047                        case SortableEntry::POLY_MAX:
1048                            -- objectsRight;
1049                                break;
1050                        default:
1051                                break;
1052                }
1053               
1054                if ((*ci).value > minBand && (*ci).value < maxBand)
1055                {
1056                        AxisAlignedBox3 lbox = box;
1057                        AxisAlignedBox3 rbox = box;
1058                        lbox.SetMax(axis, (*ci).value);
1059                        rbox.SetMin(axis, (*ci).value);
1060
1061                        float sum = objectsLeft * lbox.SurfaceArea() +
1062                                                objectsRight * rbox.SurfaceArea();
1063     
1064                        if (sum < minSum)
1065                        {
1066                                minSum = sum;
1067                                position = (*ci).value;
1068
1069                                objectsBack = objectsLeft;
1070                                objectsFront = objectsRight;
1071                        }
1072                }
1073        }
1074 
1075        float oldCost = (float)polys.size();
1076        float newCost = mAaCtDivCi + minSum / boxArea;
1077        float ratio = newCost / oldCost;
1078
1079
1080#if 0
1081  Debug << "====================" << endl;
1082  Debug << "costRatio=" << ratio << " pos=" << position<<" t=" << (position - minBox)/(maxBox - minBox)
1083      << "\t o=(" << objectsBack << "," << objectsFront << ")" << endl;
1084#endif
1085  return ratio;
1086}
1087
1088bool BspTree::SelectAxisAlignedPlane(Plane3 &plane,
1089                                                                         const PolygonContainer &polys) const
1090{
1091        AxisAlignedBox3 box;
1092        box.Initialize();
1093       
1094        // create bounding box of region
1095        Polygon3::IncludeInBox(polys, box);
1096       
1097        int objectsBack = 0, objectsFront = 0;
1098        int axis = 0;
1099        float costRatio = MAX_FLOAT;
1100        Vector3 position;
1101
1102        //-- area subdivision
1103        for (int i = 0; i < 3; ++ i)
1104        {
1105                float p = 0;
1106                float r = BestCostRatio(polys, box, i, p, objectsBack, objectsFront);
1107               
1108                if (r < costRatio)
1109                {
1110                        costRatio = r;
1111                        axis = i;
1112                        position = p;
1113                }
1114        }
1115       
1116        if (costRatio >= mMaxCostRatio)
1117                return false;
1118
1119        Vector3 norm(0,0,0); norm[axis] = 1.0f;
1120        plane = Plane3(norm, position);
1121
1122        return true;
1123}
1124
1125Plane3 BspTree::SelectPlane(BspLeaf *leaf, BspTraversalData &data)
1126{
1127        if (data.mPolygons->empty() && data.mRays->empty())
1128        {
1129                Debug << "Warning: No autopartition polygon candidate available\n";
1130       
1131                // return axis aligned split
1132                AxisAlignedBox3 box;
1133                box.Initialize();
1134       
1135                // create bounding box of region
1136                Polygon3::IncludeInBox(*data.mPolygons, box);
1137
1138                const int axis = box.Size().DrivingAxis();
1139                const Vector3 position = (box.Min()[axis] + box.Max()[axis])*0.5f;
1140
1141                Vector3 norm(0,0,0); norm[axis] = 1.0f;
1142                return Plane3(norm, position);
1143        }
1144       
1145        if ((mSplitPlaneStrategy & AXIS_ALIGNED) &&
1146                ((int)data.mPolygons->size() > mTermMinPolysForAxisAligned) &&
1147                ((int)data.mRays->size() > mTermMinRaysForAxisAligned) &&
1148                ((mTermMinObjectsForAxisAligned < 0) ||
1149                  (Polygon3::ParentObjectsSize(*data.mPolygons) > mTermMinObjectsForAxisAligned)))
1150        {
1151                Plane3 plane;
1152                if (SelectAxisAlignedPlane(plane, *data.mPolygons))
1153                        return plane;
1154        }
1155
1156        // simplest strategy: just take next polygon
1157        if (mSplitPlaneStrategy & RANDOM_POLYGON)
1158        {
1159        if (!data.mPolygons->empty())
1160                {
1161                        Polygon3 *nextPoly = (*data.mPolygons)[Random((int)data.mPolygons->size())];
1162                        return nextPoly->GetSupportingPlane();
1163                }
1164                else
1165                {
1166                        const int candidateIdx = Random((int)data.mRays->size());
1167                        BoundedRay *bRay = (*data.mRays)[candidateIdx];
1168
1169                        Ray *ray = bRay->mRay;
1170                                               
1171                        const Vector3 minPt = ray->Extrap(bRay->mMinT);
1172                        const Vector3 maxPt = ray->Extrap(bRay->mMaxT);
1173
1174                        const Vector3 pt = (maxPt + minPt) * 0.5;
1175
1176                        const Vector3 normal = ray->GetDir();
1177                       
1178                        return Plane3(normal, pt);
1179                }
1180
1181                return Plane3();
1182        }
1183
1184        // use heuristics to find appropriate plane
1185        return SelectPlaneHeuristics(leaf, data);
1186}
1187
1188Plane3 BspTree::SelectPlaneHeuristics(BspLeaf *leaf, BspTraversalData &data)
1189{
1190        float lowestCost = MAX_FLOAT;
1191        Plane3 bestPlane;
1192        Plane3 plane;
1193
1194        int limit = Min((int)data.mPolygons->size(), mMaxPolyCandidates);
1195       
1196        int candidateIdx = limit;
1197
1198        for (int i = 0; i < limit; ++ i)
1199        {
1200                candidateIdx = GetNextCandidateIdx(candidateIdx, *data.mPolygons);
1201               
1202                Polygon3 *poly = (*data.mPolygons)[candidateIdx];
1203                // evaluate current candidate
1204                const float candidateCost =
1205                        SplitPlaneCost(poly->GetSupportingPlane(), data);
1206
1207                if (candidateCost < lowestCost)
1208                {
1209                        bestPlane = poly->GetSupportingPlane();
1210                        lowestCost = candidateCost;
1211                }
1212        }
1213       
1214        //Debug << "lowest: " << lowestCost << endl;
1215
1216        //-- choose candidate planes extracted from rays
1217        // we currently use two methods
1218        // 1) take 3 ray endpoints, where two are minimum and one a maximum
1219        //    point or the other way round
1220        // 2) take plane normal as plane normal and the midpoint of the ray.
1221        //    PROBLEM: does not resemble any point where visibility is likely to change
1222        const BoundedRayContainer *rays = data.mRays;
1223
1224        for (int i = 0; i < mMaxRayCandidates / 2; ++ i)
1225        {
1226                candidateIdx = Random((int)rays->size());
1227                BoundedRay *bRay = (*rays)[candidateIdx];
1228
1229                Ray *ray = bRay->mRay;
1230                                               
1231                const Vector3 minPt = ray->Extrap(bRay->mMinT);
1232                const Vector3 maxPt = ray->Extrap(bRay->mMaxT);
1233
1234                const Vector3 pt = (maxPt + minPt) * 0.5;
1235
1236                const Vector3 normal = ray->GetDir();
1237                       
1238                plane = Plane3(normal, pt);
1239       
1240                const float candidateCost = SplitPlaneCost(plane, data);
1241
1242                if (candidateCost < lowestCost)
1243                {
1244                        bestPlane = plane;
1245                       
1246                        lowestCost = candidateCost;
1247                }
1248        }
1249
1250        //Debug << "lowest: " << lowestCost << endl;
1251        for (int i = 0; i < mMaxRayCandidates / 2; ++ i)
1252        {
1253                Vector3 pt[3];
1254                int idx[3];
1255                int cmaxT = 0;
1256                int cminT = 0;
1257                bool chooseMin = false;
1258
1259                for (int j = 0; j < 3; j ++)
1260                {
1261                        idx[j] = Random((int)rays->size() * 2);
1262                               
1263                        if (idx[j] >= (int)rays->size())
1264                        {
1265                                idx[j] -= (int)rays->size();
1266                               
1267                                chooseMin = (cminT < 2);
1268                        }
1269                        else
1270                                chooseMin = (cmaxT < 2);
1271
1272                        BoundedRay *bRay = (*rays)[idx[j]];
1273                        pt[j] = chooseMin ? bRay->mRay->Extrap(bRay->mMinT) : bRay->mRay->Extrap(bRay->mMaxT);
1274                }       
1275                       
1276                plane = Plane3(pt[0], pt[1], pt[2]);
1277
1278                const float candidateCost = SplitPlaneCost(plane, data);
1279
1280                if (candidateCost < lowestCost)
1281                {
1282                        //Debug << "choose ray plane 2: " << candidateCost << endl;
1283                        bestPlane = plane;
1284                       
1285                        lowestCost = candidateCost;
1286                }
1287        }       
1288
1289#ifdef _DEBUG
1290        Debug << "plane lowest cost: " << lowestCost << endl;
1291#endif
1292        return bestPlane;
1293}
1294
1295int BspTree::GetNextCandidateIdx(int currentIdx, PolygonContainer &polys)
1296{
1297        const int candidateIdx = Random(currentIdx --);
1298
1299        // swap candidates to avoid testing same plane 2 times
1300        std::swap(polys[currentIdx], polys[candidateIdx]);
1301       
1302        return currentIdx;
1303        //return Random((int)polys.size());
1304}
1305
1306float BspTree::SplitPlaneCost(const Plane3 &candidatePlane,
1307                                                          const PolygonContainer &polys) const
1308{
1309        float val = 0;
1310
1311        float sumBalancedPolys = 0;
1312        float sumSplits = 0;
1313        float sumPolyArea = 0;
1314        float sumBalancedViewCells = 0;
1315        float sumBlockedRays = 0;
1316        float totalBlockedRays = 0;
1317        //float totalArea = 0;
1318        int totalViewCells = 0;
1319
1320        // need three unique ids for each type of view cell
1321        // for balanced view cells criterium
1322        ViewCell::NewMail();
1323        const int backId = ViewCell::sMailId;
1324        ViewCell::NewMail();
1325        const int frontId = ViewCell::sMailId;
1326        ViewCell::NewMail();
1327        const int frontAndBackId = ViewCell::sMailId;
1328
1329        bool useRand;;
1330        int limit;
1331
1332        // choose test polyongs randomly if over threshold
1333        if ((int)polys.size() > mMaxTests)
1334        {
1335                useRand = true;
1336                limit = mMaxTests;
1337        }
1338        else
1339        {
1340                useRand = false;
1341                limit = (int)polys.size();
1342        }
1343       
1344
1345        for (int i = 0; i < limit; ++ i)
1346        {
1347                const int testIdx = useRand ? Random(limit) : i;
1348
1349                Polygon3 *poly = polys[testIdx];
1350
1351        const int classification = poly->ClassifyPlane(candidatePlane);
1352
1353                if (mSplitPlaneStrategy & BALANCED_POLYS)
1354                        sumBalancedPolys += sBalancedPolysTable[classification];
1355               
1356                if (mSplitPlaneStrategy & LEAST_SPLITS)
1357                        sumSplits += sLeastPolySplitsTable[classification];
1358
1359                if (mSplitPlaneStrategy & LARGEST_POLY_AREA)
1360                {
1361                        if (classification == Polygon3::COINCIDENT)
1362                                sumPolyArea += poly->GetArea();
1363                        //totalArea += area;
1364                }
1365               
1366                if (mSplitPlaneStrategy & BLOCKED_RAYS)
1367                {
1368                        const float blockedRays = (float)poly->mPiercingRays.size();
1369               
1370                        if (classification == Polygon3::COINCIDENT)
1371                                sumBlockedRays += blockedRays;
1372                       
1373                        totalBlockedRays += blockedRays;
1374                }
1375
1376                // assign view cells to back or front according to classificaion
1377                if (mSplitPlaneStrategy & BALANCED_VIEW_CELLS)
1378                {
1379                        MeshInstance *viewCell = poly->mParent;
1380               
1381                        // assure that we only count a view cell
1382                        // once for the front and once for the back side of the plane
1383                        if (classification == Polygon3::FRONT_SIDE)
1384                        {
1385                                if ((viewCell->mMailbox != frontId) &&
1386                                        (viewCell->mMailbox != frontAndBackId))
1387                                {
1388                                        sumBalancedViewCells += 1.0;
1389
1390                                        if (viewCell->mMailbox != backId)
1391                                                viewCell->mMailbox = frontId;
1392                                        else
1393                                                viewCell->mMailbox = frontAndBackId;
1394                                       
1395                                        ++ totalViewCells;
1396                                }
1397                        }
1398                        else if (classification == Polygon3::BACK_SIDE)
1399                        {
1400                                if ((viewCell->mMailbox != backId) &&
1401                                    (viewCell->mMailbox != frontAndBackId))
1402                                {
1403                                        sumBalancedViewCells -= 1.0;
1404
1405                                        if (viewCell->mMailbox != frontId)
1406                                                viewCell->mMailbox = backId;
1407                                        else
1408                                                viewCell->mMailbox = frontAndBackId;
1409
1410                                        ++ totalViewCells;
1411                                }
1412                        }
1413                }
1414        }
1415
1416        const float polysSize = (float)polys.size() + Limits::Small;
1417
1418        // all values should be approx. between 0 and 1 so they can be combined
1419        // and scaled with the factors according to their importance
1420        if (mSplitPlaneStrategy & BALANCED_POLYS)
1421                val += mBalancedPolysFactor * fabs(sumBalancedPolys) / polysSize;
1422       
1423        if (mSplitPlaneStrategy & LEAST_SPLITS) 
1424                val += mLeastSplitsFactor * sumSplits / polysSize;
1425
1426        if (mSplitPlaneStrategy & LARGEST_POLY_AREA)
1427                // HACK: polys.size should be total area so scaling is between 0 and 1
1428                val += mLargestPolyAreaFactor * (float)polys.size() / sumPolyArea;
1429
1430        if (mSplitPlaneStrategy & BLOCKED_RAYS)
1431                if (totalBlockedRays != 0)
1432                        val += mBlockedRaysFactor * (totalBlockedRays - sumBlockedRays) / totalBlockedRays;
1433
1434        if (mSplitPlaneStrategy & BALANCED_VIEW_CELLS)
1435                val += mBalancedViewCellsFactor * fabs(sumBalancedViewCells) /
1436                        ((float)totalViewCells + Limits::Small);
1437       
1438        return val;
1439}
1440
1441bool BspTree::BoundRay(const Ray &ray, float &minT, float &maxT) const
1442{
1443        maxT = 1e6;
1444        minT = 0;
1445       
1446        // test with tree bounding box
1447        if (!mBox.GetMinMaxT(ray, &minT, &maxT))
1448                return false;
1449
1450        if (minT < 0) // start ray from origin
1451                minT = 0;
1452
1453        // bound ray or line segment
1454        if ((ray.GetType() == Ray::LOCAL_RAY) &&
1455            !ray.intersections.empty() &&
1456                (ray.intersections[0].mT <= maxT))
1457        {
1458                maxT = ray.intersections[0].mT;
1459        }
1460
1461        return true;
1462}
1463
1464inline void BspTree::GenerateUniqueIdsForPvs()
1465{
1466        Intersectable::NewMail(); sBackId = ViewCell::sMailId;
1467        Intersectable::NewMail(); sFrontId = ViewCell::sMailId;
1468        Intersectable::NewMail(); sFrontAndBackId = ViewCell::sMailId;
1469}
1470
1471float BspTree::SplitPlaneCost(const Plane3 &candidatePlane,
1472                                                          const BoundedRayContainer &rays,
1473                                                          const int pvs,
1474                                                          const float area,
1475                                                          const BspNodeGeometry &cell) const
1476{
1477        float val = 0;
1478
1479        float sumBalancedRays = 0;
1480        float sumRaySplits = 0;
1481
1482        int frontPvs = 0;
1483        int backPvs = 0;
1484
1485        // probability that view point lies in child
1486        float pOverall = 0;
1487        float pFront = 0;
1488        float pBack = 0;
1489
1490        const bool pvsUseLen = false;
1491
1492        if (mSplitPlaneStrategy & PVS)
1493        {
1494                // create unique ids for pvs heuristics
1495                GenerateUniqueIdsForPvs();
1496
1497                if (mPvsUseArea) // use front and back cell areas to approximate volume
1498                {
1499                        // construct child geometry with regard to the candidate split plane
1500                        BspNodeGeometry frontCell;
1501                        BspNodeGeometry backCell;
1502
1503                        cell.SplitGeometry(frontCell, backCell, *this, candidatePlane);
1504               
1505                        pFront = frontCell.GetArea();
1506                        pBack = backCell.GetArea();
1507
1508                        pOverall = area;
1509                }
1510        }
1511                       
1512        bool useRand;;
1513        int limit;
1514
1515        // choose test polyongs randomly if over threshold
1516        if ((int)rays.size() > mMaxTests)
1517        {
1518                useRand = true;
1519                limit = mMaxTests;
1520        }
1521        else
1522        {
1523                useRand = false;
1524                limit = (int)rays.size();
1525        }
1526
1527        for (int i = 0; i < limit; ++ i)
1528        {
1529                const int testIdx = useRand ? Random(limit) : i;
1530       
1531                BoundedRay *bRay = rays[testIdx];
1532
1533                Ray *ray = bRay->mRay;
1534                const float minT = bRay->mMinT;
1535                const float maxT = bRay->mMaxT;
1536
1537                Vector3 entP, extP;
1538
1539                const int cf =
1540                        ray->ClassifyPlane(candidatePlane, minT, maxT, entP, extP);
1541
1542                if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
1543                {
1544                        sumBalancedRays += sBalancedRaysTable[cf];
1545                }
1546               
1547                if (mSplitPlaneStrategy & BALANCED_RAYS)
1548                {
1549                        sumRaySplits += sLeastRaySplitsTable[cf];
1550                }
1551
1552                if (mSplitPlaneStrategy & PVS)
1553                {
1554                        // in case the ray intersects an object
1555                        // assure that we only count the object
1556                        // once for the front and once for the back side of the plane
1557                       
1558                        // add the termination object
1559                        if (!ray->intersections.empty())
1560                                AddObjToPvs(ray->intersections[0].mObject, cf, frontPvs, backPvs);
1561                       
1562                        // add the source object
1563                        AddObjToPvs(ray->sourceObject.mObject, cf, frontPvs, backPvs);
1564                       
1565                        if (mPvsUseArea)
1566                        {
1567                                float len = 1;
1568                       
1569                                if (pvsUseLen)
1570                                        len = SqrDistance(entP, extP);
1571       
1572                                // use length of rays to approximate volume
1573                                if (Ray::BACK && Ray::COINCIDENT)
1574                                        pBack += len;
1575                                if (Ray::FRONT && Ray::COINCIDENT)
1576                                        pFront += len;
1577                                if (Ray::FRONT_BACK || Ray::BACK_FRONT)
1578                                {
1579                                        if (pvsUseLen)
1580                                        {
1581                                                const Vector3 extp = ray->Extrap(maxT);
1582                                                const float t = candidatePlane.FindT(ray->GetLoc(), extp);
1583                               
1584                                                const float newT = t * maxT;
1585                                                const float newLen = SqrDistance(ray->Extrap(newT), extp);
1586
1587                                                if (Ray::FRONT_BACK)
1588                                                {
1589                                                        pFront += len - newLen;
1590                                                        pBack += newLen;
1591                                                }
1592                                                else
1593                                                {
1594                                                        pBack += len - newLen;
1595                                                        pFront += newLen;
1596                                                }
1597                                        }
1598                                        else
1599                                        {
1600                                                ++ pFront;
1601                                                ++ pBack;
1602                                        }
1603                                }
1604                        }
1605                }
1606        }
1607
1608        const float raysSize = (float)rays.size() + Limits::Small;
1609
1610        if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
1611                val += mLeastRaySplitsFactor * sumRaySplits / raysSize;
1612
1613        if (mSplitPlaneStrategy & BALANCED_RAYS)
1614                val += mBalancedRaysFactor * fabs(sumBalancedRays) /  raysSize;
1615
1616        const float denom = pOverall * (float)pvs * 2.0f + Limits::Small;
1617
1618        if (mSplitPlaneStrategy & PVS)
1619        {
1620                val += mPvsFactor * (frontPvs * pFront + (backPvs * pBack)) / denom;
1621
1622                // give penalty to unbalanced split
1623                if (1)
1624                if (((pFront * 0.2 + Limits::Small) > pBack) ||
1625                        (pFront < (pBack * 0.2 + Limits::Small)))
1626                        val += 0.5;
1627        }
1628
1629       
1630#ifdef _DEBUG
1631        Debug << "totalpvs: " << pvs << " ptotal: " << pOverall
1632                  << " frontpvs: " << frontPvs << " pFront: " << pFront
1633                  << " backpvs: " << backPvs << " pBack: " << pBack << endl << endl;
1634#endif
1635       
1636        return val;
1637}
1638
1639void BspTree::AddObjToPvs(Intersectable *obj,
1640                                                  const int cf,
1641                                                  int &frontPvs,
1642                                                  int &backPvs) const
1643{
1644        if (!obj)
1645                return;
1646        // TODO: does this really belong to no pvs?
1647        //if (cf == Ray::COINCIDENT) return;
1648
1649        // object belongs to both PVS
1650        const bool bothSides = (cf == Ray::FRONT_BACK) ||
1651                                                   (cf == Ray::BACK_FRONT) ||
1652                                                   (cf == Ray::COINCIDENT);
1653
1654        if ((cf == Ray::FRONT) || bothSides)
1655        {
1656                if ((obj->mMailbox != sFrontId) &&
1657                        (obj->mMailbox != sFrontAndBackId))
1658                {
1659                        ++ frontPvs;
1660
1661                        if (obj->mMailbox == sBackId)
1662                                obj->mMailbox = sFrontAndBackId;       
1663                        else
1664                                obj->mMailbox = sFrontId;                                                               
1665                }
1666        }
1667       
1668        if ((cf == Ray::BACK) || bothSides)
1669        {
1670                if ((obj->mMailbox != sBackId) &&
1671                        (obj->mMailbox != sFrontAndBackId))
1672                {
1673                        ++ backPvs;
1674
1675                        if (obj->mMailbox == sFrontId)
1676                                obj->mMailbox = sFrontAndBackId;
1677                        else
1678                                obj->mMailbox = sBackId;                               
1679                }
1680        }
1681}
1682
1683float BspTree::SplitPlaneCost(const Plane3 &candidatePlane,
1684                                                          BspTraversalData &data) const
1685{
1686        float val = 0;
1687
1688        if (mSplitPlaneStrategy & VERTICAL_AXIS)
1689        {
1690                Vector3 tinyAxis(0,0,0); tinyAxis[mBox.Size().TinyAxis()] = 1.0f;
1691                // we put a penalty on the dot product between the "tiny" vertical axis
1692                // and the split plane axis
1693                val += mVerticalSplitsFactor *
1694                           fabs(DotProd(candidatePlane.mNormal, tinyAxis));
1695        }
1696
1697        // the following criteria loop over all polygons to find the cost value
1698        if ((mSplitPlaneStrategy & BALANCED_POLYS)      ||
1699                (mSplitPlaneStrategy & LEAST_SPLITS)        ||
1700                (mSplitPlaneStrategy & LARGEST_POLY_AREA)   ||
1701                (mSplitPlaneStrategy & BALANCED_VIEW_CELLS) ||
1702                (mSplitPlaneStrategy & BLOCKED_RAYS))
1703        {
1704                val += SplitPlaneCost(candidatePlane, *data.mPolygons);
1705        }
1706
1707        // the following criteria loop over all rays to find the cost value
1708        if ((mSplitPlaneStrategy & BALANCED_RAYS)      ||
1709                (mSplitPlaneStrategy & LEAST_RAY_SPLITS)   ||
1710                (mSplitPlaneStrategy & PVS))
1711        {
1712                val += SplitPlaneCost(candidatePlane, *data.mRays, data.mPvs,
1713                                                          data.mArea, *data.mGeometry);
1714        }
1715
1716        // return linear combination of the sums
1717        return val;
1718}
1719
1720void BspTree::CollectLeaves(vector<BspLeaf *> &leaves) const
1721{
1722        stack<BspNode *> nodeStack;
1723        nodeStack.push(mRoot);
1724 
1725        while (!nodeStack.empty())
1726        {
1727                BspNode *node = nodeStack.top();
1728   
1729                nodeStack.pop();
1730   
1731                if (node->IsLeaf())
1732                {
1733                        BspLeaf *leaf = (BspLeaf *)node;               
1734                        leaves.push_back(leaf);
1735                }
1736                else
1737                {
1738                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
1739
1740                        nodeStack.push(interior->GetBack());
1741                        nodeStack.push(interior->GetFront());
1742                }
1743        }
1744}
1745
1746AxisAlignedBox3 BspTree::GetBoundingBox() const
1747{
1748        return mBox;
1749}
1750
1751BspNode *BspTree::GetRoot() const
1752{
1753        return mRoot;
1754}
1755
1756void BspTree::EvaluateLeafStats(const BspTraversalData &data)
1757{
1758        // the node became a leaf -> evaluate stats for leafs
1759        BspLeaf *leaf = dynamic_cast<BspLeaf *>(data.mNode);
1760
1761        // store maximal and minimal depth
1762        if (data.mDepth > mStat.maxDepth)
1763                mStat.maxDepth = data.mDepth;
1764
1765        if (data.mDepth < mStat.minDepth)
1766                mStat.minDepth = data.mDepth;
1767
1768        // accumulate depth to compute average depth
1769        mStat.accumDepth += data.mDepth;
1770       
1771
1772        if (data.mDepth >= mTermMaxDepth)
1773                ++ mStat.maxDepthNodes;
1774
1775        if (data.mPvs < mTermMinPvs)
1776                ++ mStat.minPvsNodes;
1777
1778        if ((int)data.mRays->size() < mTermMinRays)
1779                ++ mStat.minRaysNodes;
1780
1781        if (data.GetAvgRayContribution() > mTermMaxRayContribution)
1782                ++ mStat.maxRayContribNodes;
1783       
1784        if (data.mGeometry->GetArea() <= mTermMinArea)
1785                ++ mStat.minAreaNodes;
1786
1787#ifdef _DEBUG
1788        Debug << "BSP stats: "
1789                  << "Depth: " << data.mDepth << " (max: " << mTermMaxDepth << "), "
1790                  << "PVS: " << data.mPvs << " (min: " << mTermMinPvs << "), "
1791                  << "Area: " << data.mArea << " (min: " << mTermMinArea << "), "
1792                  << "#polygons: " << (int)data.mPolygons->size() << " (max: " << mTermMinPolys << "), "
1793                  << "#rays: " << (int)data.mRays->size() << " (max: " << mTermMinRays << "), "
1794                  << "#pvs: " << leaf->GetViewCell()->GetPvs().GetSize() << "=, "
1795                  << "#avg ray contrib (pvs): " << (float)data.mPvs / (float)data.mRays->size() << endl;
1796#endif
1797}
1798
1799int BspTree::CastRay(Ray &ray)
1800{
1801        int hits = 0;
1802 
1803        stack<BspRayTraversalData> tStack;
1804 
1805        float maxt, mint;
1806
1807        if (!BoundRay(ray, mint, maxt))
1808                return 0;
1809
1810        Intersectable::NewMail();
1811
1812        Vector3 entp = ray.Extrap(mint);
1813        Vector3 extp = ray.Extrap(maxt);
1814 
1815        BspNode *node = mRoot;
1816        BspNode *farChild = NULL;
1817       
1818        while (1)
1819        {
1820                if (!node->IsLeaf())
1821                {
1822                        BspInterior *in = (BspInterior *) node;
1823                       
1824                        Plane3 *splitPlane = in->GetPlane();
1825
1826                        int entSide = splitPlane->Side(entp);
1827                        int extSide = splitPlane->Side(extp);
1828
1829                        Vector3 intersection;
1830
1831                        if (entSide < 0)
1832                        {
1833                                node = in->GetBack();
1834
1835                                if(extSide <= 0) // plane does not split ray => no far child
1836                                        continue;
1837                                       
1838                                farChild = in->GetFront(); // plane splits ray
1839
1840                        } else if (entSide > 0)
1841                        {
1842                                node = in->GetFront();
1843
1844                                if (extSide >= 0) // plane does not split ray => no far child
1845                                        continue;
1846
1847                                farChild = in->GetBack(); // plane splits ray                   
1848                        }
1849                        else // ray and plane are coincident
1850                        {
1851                                // WHAT TO DO IN THIS CASE ?
1852                                //break;
1853                                node = in->GetFront();
1854                                continue;
1855                        }
1856
1857                        // push data for far child
1858                        tStack.push(BspRayTraversalData(farChild, extp, maxt));
1859
1860                        // find intersection of ray segment with plane
1861                        float t;
1862                        extp = splitPlane->FindIntersection(ray.GetLoc(), extp, &t);
1863                        maxt *= t;
1864                       
1865                } else // reached leaf => intersection with view cell
1866                {
1867                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
1868     
1869                        if (!leaf->mViewCell->Mailed())
1870                        {
1871                                ray.bspIntersections.push_back(Ray::BspIntersection(maxt, leaf));
1872                                leaf->mViewCell->Mail();
1873                                ++ hits;
1874                        }
1875                       
1876                        //-- fetch the next far child from the stack
1877                        if (tStack.empty())
1878                                break;
1879     
1880                        entp = extp;
1881                        mint = maxt; // NOTE: need this?
1882
1883                        if (ray.GetType() == Ray::LINE_SEGMENT && mint > 1.0f)
1884                                break;
1885
1886                        BspRayTraversalData &s = tStack.top();
1887
1888                        node = s.mNode;
1889                        extp = s.mExitPoint;
1890                        maxt = s.mMaxT;
1891
1892                        tStack.pop();
1893                }
1894        }
1895
1896        return hits;
1897}
1898
1899bool BspTree::Export(const string filename)
1900{
1901        Exporter *exporter = Exporter::GetExporter(filename);
1902
1903        if (exporter)
1904        {
1905                exporter->ExportBspTree(*this);
1906                return true;
1907        }       
1908
1909        return false;
1910}
1911
1912void BspTree::CollectViewCells(ViewCellContainer &viewCells) const
1913{
1914        stack<BspNode *> nodeStack;
1915        nodeStack.push(mRoot);
1916
1917        ViewCell::NewMail();
1918
1919        while (!nodeStack.empty())
1920        {
1921                BspNode *node = nodeStack.top();
1922                nodeStack.pop();
1923
1924                if (node->IsLeaf())
1925                {
1926                        ViewCell *viewCell = dynamic_cast<BspLeaf *>(node)->mViewCell;
1927
1928                        if (!viewCell->Mailed())
1929                        {
1930                                viewCell->Mail();
1931                                viewCells.push_back(viewCell);
1932                        }
1933                }
1934                else
1935                {
1936                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
1937
1938                        nodeStack.push(interior->mFront);
1939                        nodeStack.push(interior->mBack);
1940                }
1941        }
1942}
1943
1944void BspTree::EvaluateViewCellsStats(BspViewCellsStatistics &stat) const
1945{
1946        stat.Reset();
1947
1948        stack<BspNode *> nodeStack;
1949        nodeStack.push(mRoot);
1950
1951        ViewCell::NewMail();
1952
1953        // exclude root cell
1954        mRootCell->Mail();
1955
1956        while (!nodeStack.empty())
1957        {
1958                BspNode *node = nodeStack.top();
1959                nodeStack.pop();
1960
1961                if (node->IsLeaf())
1962                {
1963                        ++ stat.bspLeaves;
1964
1965                        BspViewCell *viewCell = dynamic_cast<BspLeaf *>(node)->mViewCell;
1966
1967                        if (!viewCell->Mailed())
1968                        {
1969                                viewCell->Mail();
1970                               
1971                                ++ stat.viewCells;
1972                                const int pvsSize = viewCell->GetPvs().GetSize();
1973
1974                stat.pvs += pvsSize;
1975
1976                                if (pvsSize < 1)
1977                                        ++ stat.emptyPvs;
1978
1979                                if (pvsSize > stat.maxPvs)
1980                                        stat.maxPvs = pvsSize;
1981
1982                                if (pvsSize < stat.minPvs)
1983                                        stat.minPvs = pvsSize;
1984
1985                                if ((int)viewCell->mBspLeaves.size() > stat.maxBspLeaves)
1986                                        stat.maxBspLeaves = (int)viewCell->mBspLeaves.size();           
1987                        }
1988                }
1989                else
1990                {
1991                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
1992
1993                        nodeStack.push(interior->mFront);
1994                        nodeStack.push(interior->mBack);
1995                }
1996        }
1997}
1998
1999BspTreeStatistics &BspTree::GetStat()
2000{
2001        return mStat;
2002}
2003
2004float BspTree::AccumulatedRayLength(BoundedRayContainer &rays) const
2005{
2006        float len = 0;
2007
2008        BoundedRayContainer::const_iterator it, it_end = rays.end();
2009
2010        for (it = rays.begin(); it != it_end; ++ it)
2011        {
2012                len += SqrDistance((*it)->mRay->Extrap((*it)->mMinT),
2013                                                   (*it)->mRay->Extrap((*it)->mMaxT));
2014        }
2015
2016        return len;
2017}
2018
2019int BspTree::SplitRays(const Plane3 &plane,
2020                                           BoundedRayContainer &rays,
2021                                           BoundedRayContainer &frontRays,
2022                                           BoundedRayContainer &backRays)
2023{
2024        int splits = 0;
2025
2026        while (!rays.empty())
2027        {
2028                BoundedRay *bRay = rays.back();
2029                Ray *ray = bRay->mRay;
2030                float minT = bRay->mMinT;
2031                float maxT = bRay->mMaxT;
2032
2033                rays.pop_back();
2034       
2035                Vector3 entP, extP;
2036
2037                const int cf =
2038                        ray->ClassifyPlane(plane, minT, maxT, entP, extP);
2039               
2040                // set id to ray classification
2041                ray->SetId(cf);
2042
2043                switch (cf)
2044                {
2045                case Ray::COINCIDENT: // TODO: should really discard ray?
2046                        frontRays.push_back(bRay);
2047                        //DEL_PTR(bRay);
2048                        break;
2049                case Ray::BACK:
2050                        backRays.push_back(bRay);
2051                        break;
2052                case Ray::FRONT:
2053                        frontRays.push_back(bRay);
2054                        break;
2055                case Ray::FRONT_BACK:
2056                        {
2057                                // find intersection of ray segment with plane
2058                                const float t = plane.FindT(ray->GetLoc(), extP);
2059                               
2060                                const float newT = t * maxT;
2061
2062                                frontRays.push_back(new BoundedRay(ray, minT, newT));
2063                                backRays.push_back(new BoundedRay(ray, newT, maxT));
2064
2065                                DEL_PTR(bRay);
2066                        }
2067                        break;
2068                case Ray::BACK_FRONT:
2069                        {
2070                                // find intersection of ray segment with plane
2071                                const float t = plane.FindT(ray->GetLoc(), extP);
2072                                const float newT = t * bRay->mMaxT;
2073
2074                                backRays.push_back(new BoundedRay(ray, bRay->mMinT, newT));
2075                                frontRays.push_back(new BoundedRay(ray, newT, bRay->mMaxT));
2076                                DEL_PTR(bRay);
2077
2078                                ++ splits;
2079                        }
2080                        break;
2081                default:
2082                        Debug << "Should not come here 4" << endl;
2083                        break;
2084                }
2085        }
2086
2087        return splits;
2088}
2089
2090void BspTree::ExtractHalfSpaces(BspNode *n, vector<Plane3> &halfSpaces) const
2091{
2092        BspNode *lastNode;
2093        do
2094        {
2095                lastNode = n;
2096
2097                // want to get planes defining geometry of this node => don't take
2098                // split plane of node itself
2099                n = n->GetParent();
2100               
2101                if (n)
2102                {
2103                        BspInterior *interior = dynamic_cast<BspInterior *>(n);
2104                        Plane3 halfSpace = *dynamic_cast<BspInterior *>(interior)->GetPlane();
2105
2106            if (interior->mFront != lastNode)
2107                                halfSpace.ReverseOrientation();
2108
2109                        halfSpaces.push_back(halfSpace);
2110                }
2111        }
2112        while (n);
2113}
2114
2115void BspTree::ConstructGeometry(BspNode *n, BspNodeGeometry &cell) const
2116{
2117        PolygonContainer polys;
2118        ConstructGeometry(n, polys);
2119        cell.mPolys = polys;
2120}
2121
2122void BspTree::ConstructGeometry(BspViewCell *vc, PolygonContainer &cell) const
2123{
2124        vector<BspLeaf *> leaves = vc->mBspLeaves;
2125
2126        vector<BspLeaf *>::const_iterator it, it_end = leaves.end();
2127
2128        for (it = leaves.begin(); it != it_end; ++ it)
2129                ConstructGeometry(*it, cell);
2130}
2131
2132
2133void BspTree::ConstructGeometry(BspNode *n, PolygonContainer &cell) const
2134{
2135        vector<Plane3> halfSpaces;
2136        ExtractHalfSpaces(n, halfSpaces);
2137
2138        PolygonContainer candidatePolys;
2139
2140        // bounded planes are added to the polygons (reverse polygons
2141        // as they have to be outfacing
2142        for (int i = 0; i < (int)halfSpaces.size(); ++ i)
2143        {
2144                Polygon3 *p = GetBoundingBox().CrossSection(halfSpaces[i]);
2145               
2146                if (p->Valid())
2147                {
2148                        candidatePolys.push_back(p->CreateReversePolygon());
2149                        DEL_PTR(p);
2150                }
2151        }
2152
2153        // add faces of bounding box (also could be faces of the cell)
2154        for (int i = 0; i < 6; ++ i)
2155        {
2156                VertexContainer vertices;
2157       
2158                for (int j = 0; j < 4; ++ j)
2159                        vertices.push_back(mBox.GetFace(i).mVertices[j]);
2160
2161                candidatePolys.push_back(new Polygon3(vertices));
2162        }
2163
2164        for (int i = 0; i < (int)candidatePolys.size(); ++ i)
2165        {
2166                // polygon is split by all other planes
2167                for (int j = 0; (j < (int)halfSpaces.size()) && candidatePolys[i]; ++ j)
2168                {
2169                        if (i == j) // polygon and plane are coincident
2170                                continue;
2171
2172                        VertexContainer splitPts;
2173                        Polygon3 *frontPoly, *backPoly;
2174
2175                        const int cf = candidatePolys[i]->ClassifyPlane(halfSpaces[j]);
2176                       
2177                        switch (cf)
2178                        {
2179                                case Polygon3::SPLIT:
2180                                        frontPoly = new Polygon3();
2181                                        backPoly = new Polygon3();
2182
2183                                        candidatePolys[i]->Split(halfSpaces[j],
2184                                                                                         *frontPoly,
2185                                                                                         *backPoly);
2186
2187                                        DEL_PTR(candidatePolys[i]);
2188
2189                                        if (frontPoly->Valid())
2190                                                candidatePolys[i] = frontPoly;
2191                                        else
2192                                                DEL_PTR(frontPoly);
2193
2194                                        DEL_PTR(backPoly);
2195                                        break;
2196                                case Polygon3::BACK_SIDE:
2197                                        DEL_PTR(candidatePolys[i]);
2198                                        break;
2199                                // just take polygon as it is
2200                                case Polygon3::FRONT_SIDE:
2201                                case Polygon3::COINCIDENT:
2202                                default:
2203                                        break;
2204                        }
2205                }
2206               
2207                if (candidatePolys[i])
2208                        cell.push_back(candidatePolys[i]);
2209        }
2210}
2211
2212
2213int BspTree::FindNeighbors(BspNode *n, vector<BspLeaf *> &neighbors,
2214                                                   const bool onlyUnmailed) const
2215{
2216        PolygonContainer cell;
2217
2218        ConstructGeometry(n, cell);
2219
2220        stack<BspNode *> nodeStack;
2221        nodeStack.push(mRoot);
2222               
2223        // planes needed to verify that we found neighbor leaf.
2224        vector<Plane3> halfSpaces;
2225        ExtractHalfSpaces(n, halfSpaces);
2226
2227        while (!nodeStack.empty())
2228        {
2229                BspNode *node = nodeStack.top();
2230                nodeStack.pop();
2231
2232                if (node->IsLeaf())
2233                {
2234            if (node != n && (!onlyUnmailed || !node->Mailed()))
2235                        {
2236                                // test all planes of current node if candidate really
2237                                // is neighbour
2238                                PolygonContainer neighborCandidate;
2239                                ConstructGeometry(node, neighborCandidate);
2240                               
2241                                bool isAdjacent = true;
2242                                for (int i = 0; (i < halfSpaces.size()) && isAdjacent; ++ i)
2243                                {
2244                                        const int cf =
2245                                                Polygon3::ClassifyPlane(neighborCandidate, halfSpaces[i]);
2246
2247                                        if (cf == Polygon3::BACK_SIDE)
2248                                                isAdjacent = false;
2249                                }
2250
2251                                if (isAdjacent)
2252                                        neighbors.push_back(dynamic_cast<BspLeaf *>(node));
2253
2254                                CLEAR_CONTAINER(neighborCandidate);
2255                        }
2256                }
2257                else
2258                {
2259                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2260       
2261                        const int cf = Polygon3::ClassifyPlane(cell, interior->mPlane);
2262
2263                        if (cf == Polygon3::FRONT_SIDE)
2264                                nodeStack.push(interior->mFront);
2265                        else
2266                                if (cf == Polygon3::BACK_SIDE)
2267                                        nodeStack.push(interior->mBack);
2268                                else
2269                                {
2270                                        // random decision
2271                                        nodeStack.push(interior->mBack);
2272                                        nodeStack.push(interior->mFront);
2273                                }
2274                }
2275        }
2276       
2277        CLEAR_CONTAINER(cell);
2278        return (int)neighbors.size();
2279}
2280
2281BspLeaf *BspTree::GetRandomLeaf(const Plane3 &halfspace)
2282{
2283    stack<BspNode *> nodeStack;
2284        nodeStack.push(mRoot);
2285       
2286        int mask = rand();
2287 
2288        while (!nodeStack.empty())
2289        {
2290                BspNode *node = nodeStack.top();
2291                nodeStack.pop();
2292         
2293                if (node->IsLeaf())
2294                {
2295                        return dynamic_cast<BspLeaf *>(node);
2296                }
2297                else
2298                {
2299                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2300                       
2301                        BspNode *next;
2302       
2303                        PolygonContainer cell;
2304
2305                        // todo: not very efficient: constructs full cell everytime
2306                        ConstructGeometry(interior, cell);
2307
2308                        const int cf = Polygon3::ClassifyPlane(cell, halfspace);
2309
2310                        if (cf == Polygon3::BACK_SIDE)
2311                                next = interior->mFront;
2312                        else
2313                                if (cf == Polygon3::FRONT_SIDE)
2314                                        next = interior->mFront;
2315                        else
2316                        {
2317                                // random decision
2318                                if (mask & 1)
2319                                        next = interior->mBack;
2320                                else
2321                                        next = interior->mFront;
2322                                mask = mask >> 1;
2323                        }
2324
2325                        nodeStack.push(next);
2326                }
2327        }
2328       
2329        return NULL;
2330}
2331
2332BspLeaf *BspTree::GetRandomLeaf(const bool onlyUnmailed)
2333{
2334        stack<BspNode *> nodeStack;
2335       
2336        nodeStack.push(mRoot);
2337
2338        int mask = rand();
2339       
2340        while (!nodeStack.empty())
2341        {
2342                BspNode *node = nodeStack.top();
2343                nodeStack.pop();
2344               
2345                if (node->IsLeaf())
2346                {
2347                        if ( (!onlyUnmailed || !node->Mailed()) )
2348                                return dynamic_cast<BspLeaf *>(node);
2349                }
2350                else
2351                {
2352                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2353
2354                        // random decision
2355                        if (mask & 1)
2356                                nodeStack.push(interior->mBack);
2357                        else
2358                                nodeStack.push(interior->mFront);
2359
2360                        mask = mask >> 1;
2361                }
2362        }
2363       
2364        return NULL;
2365}
2366
2367int BspTree::ComputePvsSize(const BoundedRayContainer &rays) const
2368{
2369        int pvsSize = 0;
2370
2371        BoundedRayContainer::const_iterator rit, rit_end = rays.end();
2372
2373        Intersectable::NewMail();
2374
2375        for (rit = rays.begin(); rit != rays.end(); ++ rit)
2376        {
2377                Ray *ray = (*rit)->mRay;
2378               
2379                if (!ray->intersections.empty())
2380                {
2381                        if (!ray->intersections[0].mObject->Mailed())
2382                        {
2383                                ray->intersections[0].mObject->Mail();
2384                                ++ pvsSize;
2385                        }
2386                }
2387                if (ray->sourceObject.mObject)
2388                {
2389                        if (!ray->sourceObject.mObject->Mailed())
2390                        {
2391                                ray->sourceObject.mObject->Mail();
2392                                ++ pvsSize;
2393                        }
2394                }
2395        }
2396
2397        return pvsSize;
2398}
2399
2400/*************************************************************
2401 *            BspNodeGeometry Implementation                 *
2402 *************************************************************/
2403
2404BspNodeGeometry::~BspNodeGeometry()
2405{
2406        CLEAR_CONTAINER(mPolys);
2407}
2408
2409float BspNodeGeometry::GetArea() const
2410{
2411        return Polygon3::GetArea(mPolys);
2412}
2413
2414void BspNodeGeometry::SplitGeometry(BspNodeGeometry &front,
2415                                                                        BspNodeGeometry &back,
2416                                                                        const BspTree &tree,                                             
2417                                                                        const Plane3 &splitPlane) const
2418{       
2419        // get cross section of new polygon
2420        Polygon3 *planePoly = tree.GetBoundingBox().CrossSection(splitPlane);
2421
2422        planePoly = SplitPolygon(planePoly, tree);
2423
2424        //-- plane poly splits all other cell polygons
2425        for (int i = 0; i < (int)mPolys.size(); ++ i)
2426        {
2427                const int cf = mPolys[i]->ClassifyPlane(splitPlane, 0.00001f);
2428                       
2429                // split new polygon with all previous planes
2430                switch (cf)
2431                {
2432                        case Polygon3::SPLIT:
2433                                {
2434                                        Polygon3 *poly = new Polygon3(mPolys[i]->mVertices);
2435
2436                                        Polygon3 *frontPoly = new Polygon3();
2437                                        Polygon3 *backPoly = new Polygon3();
2438                               
2439                                        poly->Split(splitPlane, *frontPoly, *backPoly);
2440
2441                                        DEL_PTR(poly);
2442
2443                                        if (frontPoly->Valid())
2444                                                front.mPolys.push_back(frontPoly);
2445                                        else
2446                                                DEL_PTR(frontPoly);
2447
2448                                        if (backPoly->Valid())
2449                                                back.mPolys.push_back(backPoly);
2450                                        else
2451                                                DEL_PTR(backPoly);
2452                                }
2453                               
2454                                break;
2455                        case Polygon3::BACK_SIDE:
2456                                back.mPolys.push_back(new Polygon3(mPolys[i]->mVertices));                     
2457                                break;
2458                        case Polygon3::FRONT_SIDE:
2459                                front.mPolys.push_back(new Polygon3(mPolys[i]->mVertices));     
2460                                break;
2461                        case Polygon3::COINCIDENT:
2462                                //front.mPolys.push_back(CreateReversePolygon(mPolys[i]));
2463                                back.mPolys.push_back(new Polygon3(mPolys[i]->mVertices));
2464                                break;
2465                        default:
2466                                break;
2467                }
2468        }
2469
2470        //-- finally add the new polygon to the child cells
2471        if (planePoly)
2472        {
2473                // add polygon with normal pointing into positive half space to back cell
2474                back.mPolys.push_back(planePoly);
2475                // add polygon with reverse orientation to front cell
2476                front.mPolys.push_back(planePoly->CreateReversePolygon());
2477        }
2478
2479        //Debug << "returning new geometry " << mPolys.size() << " f: " << front.mPolys.size() << " b: " << back.mPolys.size() << endl;
2480        //Debug << "old area " << GetArea() << " f: " << front.GetArea() << " b: " << back.GetArea() << endl;
2481}
2482
2483Polygon3 *BspNodeGeometry::SplitPolygon(Polygon3 *planePoly,
2484                                                                                const BspTree &tree) const
2485{
2486        // polygon is split by all other planes
2487        for (int i = 0; (i < (int)mPolys.size()) && planePoly; ++ i)
2488        {
2489                Plane3 plane = mPolys[i]->GetSupportingPlane();
2490
2491                const int cf =
2492                        planePoly->ClassifyPlane(plane, 0.00001f);
2493                       
2494                // split new polygon with all previous planes
2495                switch (cf)
2496                {
2497                        case Polygon3::SPLIT:
2498                                {
2499                                        Polygon3 *frontPoly = new Polygon3();
2500                                        Polygon3 *backPoly = new Polygon3();
2501
2502                                        planePoly->Split(plane, *frontPoly, *backPoly);
2503                                       
2504                                        // don't need anymore
2505                                        DEL_PTR(planePoly);
2506                                        DEL_PTR(frontPoly);
2507
2508                                        // back polygon is belonging to geometry
2509                                        if (backPoly->Valid())
2510                                                planePoly = backPoly;
2511                                        else
2512                                                DEL_PTR(backPoly);
2513                                }
2514                                break;
2515                        case Polygon3::FRONT_SIDE:
2516                                DEL_PTR(planePoly);
2517                break;
2518                        // polygon is taken as it is
2519                        case Polygon3::BACK_SIDE:
2520                        case Polygon3::COINCIDENT:
2521                        default:
2522                                break;
2523                }
2524        }
2525
2526        return planePoly;
2527}
2528
2529void BspViewCellsStatistics::Print(ostream &app) const
2530{
2531        app << "===== BspViewCells statistics ===============\n";
2532
2533        app << setprecision(4);
2534
2535        //app << "#N_CTIME  ( Construction time [s] )\n" << Time() << " \n";
2536
2537        app << "#N_OVERALLPVS ( objects in PVS )\n" << pvs << endl;
2538
2539        app << "#N_PMAXPVS ( largest PVS )\n" << maxPvs << endl;
2540
2541        app << "#N_PMINPVS ( smallest PVS )\n" << minPvs << endl;
2542
2543        app << "#N_PAVGPVS ( average PVS )\n" << AvgPvs() << endl;
2544
2545        app << "#N_PEMPTYPVS ( view cells with PVS smaller 2 )\n" << emptyPvs << endl;
2546
2547        app << "#N_VIEWCELLS ( number of view cells)\n" << viewCells << endl;
2548
2549        app << "#N_AVGBSPLEAVES (average number of BSP leaves per view cell )\n" << AvgBspLeaves() << endl;
2550
2551        app << "#N_MAXBSPLEAVES ( maximal number of BSP leaves per view cell )\n" << maxBspLeaves << endl;
2552       
2553        app << "===== END OF BspViewCells statistics ==========\n";
2554}
Note: See TracBrowser for help on using the repository browser.