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

Revision 468, 65.9 KB checked in by mattausch, 19 years ago (diff)

updated rendersimulator

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