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

Revision 452, 62.4 KB checked in by mattausch, 19 years ago (diff)

started to add view cell merging to vsp kd tree

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