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

Revision 483, 66.0 KB checked in by mattausch, 19 years ago (diff)
Line 
1#include "Plane3.h"
2#include "ViewCellBsp.h"
3#include "Mesh.h"
4#include "common.h"
5#include "ViewCell.h"
6#include "Environment.h"
7#include "Polygon3.h"
8#include "Ray.h"
9#include "AxisAlignedBox3.h"
10#include <stack>
11
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
59
60BspInterior *BspNode::GetParent()
61{
62        return mParent;
63}
64
65
66void BspNode::SetParent(BspInterior *parent)
67{
68        mParent = parent;
69}
70
71
72bool BspNode::IsSibling(BspNode *n) const
73{
74        return  ((this != n) && mParent &&
75                         (mParent->GetFront() == n) || (mParent->GetBack() == n));
76}
77
78int BspNode::GetDepth() const
79{
80        int depth = 0;
81        BspNode *p = mParent;
82       
83        while (p)
84        {
85                p = p->mParent;
86                ++ depth;
87        }
88
89        return depth;
90}
91
92/****************************************************************/
93/*              class BspInterior implementation                */
94/****************************************************************/
95
96
97BspInterior::BspInterior(const Plane3 &plane):
98mPlane(plane), mFront(NULL), mBack(NULL)
99{}
100
101BspInterior::~BspInterior()
102{
103        DEL_PTR(mFront);
104        DEL_PTR(mBack);
105}
106
107bool BspInterior::IsLeaf() const
108{
109        return false;
110}
111
112BspNode *BspInterior::GetBack()
113{
114        return mBack;
115}
116
117BspNode *BspInterior::GetFront()
118{
119        return mFront;
120}
121
122Plane3 BspInterior::GetPlane() const
123{
124        return mPlane;
125}
126
127void BspInterior::ReplaceChildLink(BspNode *oldChild, BspNode *newChild)
128{
129        if (mBack == oldChild)
130                mBack = newChild;
131        else
132                mFront = newChild;
133}
134
135void BspInterior::SetupChildLinks(BspNode *b, BspNode *f)
136{
137    mBack = b;
138    mFront = f;
139}
140
141/****************************************************************/
142/*                  class BspLeaf implementation                */
143/****************************************************************/
144
145
146BspLeaf::BspLeaf(): mViewCell(NULL)
147{
148}
149
150
151BspLeaf::BspLeaf(BspViewCell *viewCell):
152mViewCell(viewCell)
153{
154}
155
156
157BspLeaf::BspLeaf(BspInterior *parent):
158BspNode(parent), mViewCell(NULL)
159{}
160
161
162
163BspLeaf::BspLeaf(BspInterior *parent, BspViewCell *viewCell):
164BspNode(parent), mViewCell(viewCell)
165{
166}
167
168BspViewCell *BspLeaf::GetViewCell() const
169{
170        return mViewCell;
171}
172
173void BspLeaf::SetViewCell(BspViewCell *viewCell)
174{
175        mViewCell = viewCell;
176}
177
178bool BspLeaf::IsLeaf() const
179{
180        return true;
181}
182
183
184/****************************************************************/
185/*                  class BspTree implementation                */
186/****************************************************************/
187
188BspTree::BspTree(): 
189mRoot(NULL),
190mPvsUseArea(true),
191mGenerateViewCells(true)
192{
193        Randomize(); // initialise random generator for heuristics
194
195        // the view cell corresponding to unbounded space
196        mRootCell = new BspViewCell();
197
198        //-- termination criteria for autopartition
199        environment->GetIntValue("BspTree.Termination.maxDepth", mTermMaxDepth);
200        environment->GetIntValue("BspTree.Termination.minPvs", mTermMinPvs);
201        environment->GetIntValue("BspTree.Termination.minPolygons", mTermMinPolys);
202        environment->GetIntValue("BspTree.Termination.minRays", mTermMinRays);
203        environment->GetFloatValue("BspTree.Termination.minArea", mTermMinArea);       
204        environment->GetFloatValue("BspTree.Termination.maxRayContribution", mTermMaxRayContribution);
205        environment->GetFloatValue("BspTree.Termination.minAccRayLenght", mTermMinAccRayLength);
206
207        //-- factors for bsp tree split plane heuristics
208        environment->GetFloatValue("BspTree.Factor.verticalSplits", mVerticalSplitsFactor);
209        environment->GetFloatValue("BspTree.Factor.largestPolyArea", mLargestPolyAreaFactor);
210        environment->GetFloatValue("BspTree.Factor.blockedRays", mBlockedRaysFactor);
211        environment->GetFloatValue("BspTree.Factor.leastRaySplits", mLeastRaySplitsFactor);
212        environment->GetFloatValue("BspTree.Factor.balancedRays", mBalancedRaysFactor);
213        environment->GetFloatValue("BspTree.Factor.pvs", mPvsFactor);
214        environment->GetFloatValue("BspTree.Factor.leastSplits" , mLeastSplitsFactor);
215        environment->GetFloatValue("BspTree.Factor.balancedPolys", mBalancedPolysFactor);
216        environment->GetFloatValue("BspTree.Factor.balancedViewCells", mBalancedViewCellsFactor);
217        environment->GetFloatValue("BspTree.Termination.ct_div_ci", mCtDivCi);
218
219        //-- termination criteria for axis aligned split
220        environment->GetFloatValue("BspTree.Termination.AxisAligned.ct_div_ci", mAxisAlignedCtDivCi);
221        environment->GetFloatValue("BspTree.Termination.maxCostRatio", mMaxCostRatio);
222        environment->GetIntValue("BspTree.Termination.AxisAligned.minPolys",
223                                                         mTermMinPolysForAxisAligned);
224        environment->GetIntValue("BspTree.Termination.AxisAligned.minRays",
225                                                         mTermMinRaysForAxisAligned);
226        environment->GetIntValue("BspTree.Termination.AxisAligned.minObjects",
227                                                         mTermMinObjectsForAxisAligned);
228        //-- partition criteria
229        environment->GetIntValue("BspTree.maxPolyCandidates", mMaxPolyCandidates);
230        environment->GetIntValue("BspTree.maxRayCandidates", mMaxRayCandidates);
231        environment->GetIntValue("BspTree.splitPlaneStrategy", mSplitPlaneStrategy);
232        environment->GetFloatValue("BspTree.AxisAligned.splitBorder", mSplitBorder);
233        environment->GetIntValue("BspTree.maxTests", mMaxTests);
234
235        environment->GetFloatValue("BspTree.Construction.epsilon", mEpsilon);
236       
237    Debug << "BSP max depth: " << mTermMaxDepth << endl;
238        Debug << "BSP min PVS: " << mTermMinPvs << endl;
239        Debug << "BSP min area: " << mTermMinArea << endl;
240        Debug << "BSP max polys: " << mTermMinPolys << endl;
241        Debug << "BSP max rays: " << mTermMinRays << endl;
242        Debug << "BSP max polygon candidates: " << mMaxPolyCandidates << endl;
243        Debug << "BSP max plane candidates: " << mMaxRayCandidates << endl;
244
245        Debug << "Split plane strategy: ";
246        if (mSplitPlaneStrategy & RANDOM_POLYGON)
247                Debug << "random polygon ";
248        if (mSplitPlaneStrategy & AXIS_ALIGNED)
249                Debug << "axis aligned ";
250        if (mSplitPlaneStrategy & LEAST_SPLITS)     
251                Debug << "least splits ";
252        if (mSplitPlaneStrategy & BALANCED_POLYS)
253                Debug << "balanced polygons ";
254        if (mSplitPlaneStrategy & BALANCED_VIEW_CELLS)
255                Debug << "balanced view cells ";
256        if (mSplitPlaneStrategy & LARGEST_POLY_AREA)
257                Debug << "largest polygon area ";
258        if (mSplitPlaneStrategy & VERTICAL_AXIS)
259                Debug << "vertical axis ";
260        if (mSplitPlaneStrategy & BLOCKED_RAYS)
261                Debug << "blocked rays ";
262        if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
263                Debug << "least ray splits ";
264        if (mSplitPlaneStrategy & BALANCED_RAYS)
265                Debug << "balanced rays ";
266        if (mSplitPlaneStrategy & PVS)
267                Debug << "pvs";
268
269        Debug << endl;
270}
271
272
273const BspTreeStatistics &BspTree::GetStatistics() const
274{
275        return mStat;
276}
277
278
279int BspTree::SplitPolygons(const Plane3 &plane,
280                                                   PolygonContainer &polys,
281                                                   PolygonContainer &frontPolys,
282                                                   PolygonContainer &backPolys,
283                                                   PolygonContainer &coincident) const
284{
285        int splits = 0;
286
287#ifdef _Debug
288        Debug << "splitting polygons of node " << this << " with plane " << mPlane << endl;
289#endif
290        while (!polys.empty())
291        {
292                Polygon3 *poly = polys.back();
293                polys.pop_back();
294
295                //Debug << "New polygon with plane: " << poly->GetSupportingPlane() << "\n";
296
297                // classify polygon
298                const int cf = poly->ClassifyPlane(plane, mEpsilon);
299
300                switch (cf)
301                {
302                        case Polygon3::COINCIDENT:
303                                coincident.push_back(poly);
304                                break;                 
305                        case Polygon3::FRONT_SIDE:     
306                                frontPolys.push_back(poly);
307                                break;
308                        case Polygon3::BACK_SIDE:
309                                backPolys.push_back(poly);
310                                break;
311                        case Polygon3::SPLIT:
312                                {
313                                        Polygon3 *front_piece = new Polygon3(poly->mParent);
314                                        Polygon3 *back_piece = new Polygon3(poly->mParent);
315
316                                        //-- split polygon into front and back part
317                                        poly->Split(plane,
318                                                                *front_piece,
319                                                                *back_piece,
320                                                                mEpsilon);
321                                       
322                                        ++ splits; // increase number of splits
323
324                                        //-- inherit rays from parent polygon for blocked ray criterium
325                                        poly->InheritRays(*front_piece, *back_piece);
326                               
327                                        // check if polygons still valid
328                                        if (front_piece->Valid(mEpsilon))
329                                                frontPolys.push_back(front_piece);
330                                        else
331                                                DEL_PTR(front_piece);
332                               
333                                        if (back_piece->Valid(mEpsilon))
334                                                backPolys.push_back(back_piece);
335                                        else                           
336                                                DEL_PTR(back_piece);
337                               
338#ifdef _DEBUG
339                                        Debug << "split " << *poly << endl << *front_piece << endl << *back_piece << endl;
340#endif
341                                        DEL_PTR(poly);
342                                }
343                                break;
344                        default:
345                Debug << "SHOULD NEVER COME HERE\n";
346                                break;
347                }
348        }
349
350        return splits;
351}
352
353
354void BspTreeStatistics::Print(ostream &app) const
355{
356        app << "===== BspTree statistics ===============\n";
357
358        app << setprecision(4);
359
360        app << "#N_CTIME  ( Construction time [s] )\n" << Time() << " \n";
361
362        app << "#N_NODES ( Number of nodes )\n" << nodes << "\n";
363
364        app << "#N_INTERIORS ( Number of interior nodes )\n" << Interior() << "\n";
365
366        app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n";
367
368        app << "#N_SPLITS ( Number of splits )\n" << splits << "\n";
369
370        app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maximum depth )\n"
371                <<      maxDepthNodes * 100 / (double)Leaves() << endl;
372
373        app << "#N_PMINPVSLEAVES  ( Percentage of leaves with mininimal PVS )\n"
374                << minPvsNodes * 100 / (double)Leaves() << endl;
375
376        app << "#N_PMINRAYSLEAVES  ( Percentage of leaves with minimal number of rays)\n"
377                << minRaysNodes * 100 / (double)Leaves() << endl;
378
379        app << "#N_MAXCOSTNODES  ( Percentage of leaves with terminated because of max cost ratio )\n"
380                << maxCostNodes * 100 / (double)Leaves() << endl;
381
382        app << "#N_PMINAREALEAVES  ( Percentage of leaves with mininum area )\n"
383                << minAreaNodes * 100 / (double)Leaves() << endl;
384
385        app << "#N_PMAXRAYCONTRIBLEAVES  ( Percentage of leaves with maximal ray contribution )\n"
386                <<      maxRayContribNodes * 100 / (double)Leaves() << endl;
387
388        app << "#N_PMAXDEPTH ( Maximal reached depth )\n" << maxDepth << endl;
389
390        app << "#N_PMINDEPTH ( Minimal reached depth )\n" << minDepth << endl;
391
392        app << "#AVGDEPTH ( average depth )\n" << AvgDepth() << endl;
393
394        app << "#N_INPUT_POLYGONS (number of input polygons )\n" <<     polys << endl;
395
396        //app << "#N_PVS: " << pvs << endl;
397
398        app << "#N_ROUTPUT_INPUT_POLYGONS ( ratio polygons after subdivision / input polygons )\n" <<
399                 (polys + splits) / (double)polys << endl;
400       
401        app << "===== END OF BspTree statistics ==========\n";
402}
403
404
405BspTree::~BspTree()
406{
407        DEL_PTR(mRoot);
408        DEL_PTR(mRootCell);
409}
410
411BspViewCell *BspTree::GetRootCell() const
412{
413        return mRootCell;
414}
415
416void BspTree::InsertViewCell(ViewCell *viewCell)
417{
418        PolygonContainer *polys = new PolygonContainer();
419
420        // don't generate new view cell, insert this one
421        mGenerateViewCells = false;
422        // extract polygons that guide the split process
423        mStat.polys += AddMeshToPolygons(viewCell->GetMesh(), *polys, viewCell);
424        mBox.Include(viewCell->GetBox()); // add to BSP aabb
425
426        InsertPolygons(polys);
427}
428
429void BspTree::InsertPolygons(PolygonContainer *polys)
430{       
431        BspTraversalStack tStack;
432
433        // traverse existing tree or create new tree
434    if (!mRoot)
435                mRoot = new BspLeaf();
436
437        tStack.push(BspTraversalData(mRoot,
438                                                                 polys,
439                                                                 0,
440                                                                 mRootCell,
441                                                                 new BoundedRayContainer(),
442                                                                 0,
443                                                                 mBox.SurfaceArea(),
444                                                                 new BspNodeGeometry()));
445
446        while (!tStack.empty())
447        {
448                // filter polygons donw the tree
449                BspTraversalData tData = tStack.top();
450            tStack.pop();
451                       
452                if (!tData.mNode->IsLeaf())
453                {
454                        BspInterior *interior = dynamic_cast<BspInterior *>(tData.mNode);
455
456                        //-- filter view cell polygons down the tree until a leaf is reached
457                        if (!tData.mPolygons->empty())
458                        {
459                                PolygonContainer *frontPolys = new PolygonContainer();
460                                PolygonContainer *backPolys = new PolygonContainer();
461                                PolygonContainer coincident;
462
463                                int splits = 0;
464               
465                                // split viewcell polygons with respect to split plane
466                                splits += SplitPolygons(interior->GetPlane(),
467                                                                                *tData.mPolygons,
468                                                                                *frontPolys,
469                                                                                *backPolys,
470                                                                                coincident);
471                               
472                                // extract view cells associated with the split polygons
473                                ViewCell *frontViewCell = mRootCell;
474                                ViewCell *backViewCell = mRootCell;
475                       
476                                BspTraversalData frontData(interior->GetFront(),
477                                                                                   frontPolys,
478                                                                                   tData.mDepth + 1,
479                                                                                   mRootCell,   
480                                                                                   tData.mRays,
481                                                                                   tData.mPvs,
482                                                                                   mBox.SurfaceArea(),
483                                                                                   new BspNodeGeometry());
484
485                                BspTraversalData backData(interior->GetBack(),
486                                                                                  backPolys,
487                                                                                  tData.mDepth + 1,
488                                                                                  mRootCell,   
489                                                                                  tData.mRays,
490                                                                                  tData.mPvs,
491                                                                                  mBox.SurfaceArea(),
492                                                                                  new BspNodeGeometry());
493
494                                if (!mGenerateViewCells)
495                                {
496                                        ExtractViewCells(frontData,
497                                                                         backData,
498                                                                         coincident,
499                                                                         interior->mPlane);
500                                }
501
502                                // don't need coincident polygons anymore
503                                CLEAR_CONTAINER(coincident);
504
505                                mStat.splits += splits;
506
507                                // push the children on the stack
508                                tStack.push(frontData);
509                                tStack.push(backData);
510                        }
511
512                        // cleanup
513                        DEL_PTR(tData.mPolygons);
514                        DEL_PTR(tData.mRays);
515                }
516                else
517                {
518                        // reached leaf => subdivide current viewcell
519                        BspNode *subRoot = Subdivide(tStack, tData);
520                }
521        }
522}
523
524int BspTree::AddMeshToPolygons(Mesh *mesh,
525                                                           PolygonContainer &polys,
526                                                           MeshInstance *parent)
527{
528        FaceContainer::const_iterator fi;
529       
530        // copy the face data to polygons
531        for (fi = mesh->mFaces.begin(); fi != mesh->mFaces.end(); ++ fi)
532        {
533                Polygon3 *poly = new Polygon3((*fi), mesh);
534               
535                if (poly->Valid(mEpsilon))
536                {
537                        poly->mParent = parent; // set parent intersectable
538                        polys.push_back(poly);
539                }
540                else
541                        DEL_PTR(poly);
542        }
543        return (int)mesh->mFaces.size();
544}
545
546int BspTree::AddToPolygonSoup(const ViewCellContainer &viewCells,
547                                                          PolygonContainer &polys,
548                                                          int maxObjects)
549{
550        int limit = (maxObjects > 0) ?
551                Min((int)viewCells.size(), maxObjects) : (int)viewCells.size();
552 
553        int polysSize = 0;
554
555        for (int i = 0; i < limit; ++ i)
556        {
557                if (viewCells[i]->GetMesh()) // copy the mesh data to polygons
558                {
559                        mBox.Include(viewCells[i]->GetBox()); // add to BSP tree aabb
560                        polysSize += AddMeshToPolygons(viewCells[i]->GetMesh(), polys, viewCells[i]);
561                }
562        }
563
564        return polysSize;
565}
566
567int BspTree::AddToPolygonSoup(const ObjectContainer &objects, PolygonContainer &polys, int maxObjects)
568{
569        int limit = (maxObjects > 0) ? Min((int)objects.size(), maxObjects) : (int)objects.size();
570 
571        for (int i = 0; i < limit; ++i)
572        {
573                Intersectable *object = objects[i];//*it;
574                Mesh *mesh = NULL;
575
576                switch (object->Type()) // extract the meshes
577                {
578                case Intersectable::MESH_INSTANCE:
579                        mesh = dynamic_cast<MeshInstance *>(object)->GetMesh();
580                        break;
581                case Intersectable::VIEW_CELL:
582                        mesh = dynamic_cast<ViewCell *>(object)->GetMesh();
583                        break;
584                        // TODO: handle transformed mesh instances
585                default:
586                        Debug << "intersectable type not supported" << endl;
587                        break;
588                }
589               
590        if (mesh) // copy the mesh data to polygons
591                {
592                        mBox.Include(object->GetBox()); // add to BSP tree aabb
593                        AddMeshToPolygons(mesh, polys, mRootCell);
594                }
595        }
596
597        return (int)polys.size();
598}
599
600void BspTree::Construct(const ViewCellContainer &viewCells)
601{
602        mStat.nodes = 1;
603        mBox.Initialize();      // initialise bsp tree bounding box
604
605        // copy view cell meshes into one big polygon soup
606        PolygonContainer *polys = new PolygonContainer();
607        mStat.polys = AddToPolygonSoup(viewCells, *polys);
608
609        // view cells are given
610        mGenerateViewCells = false;
611        // construct tree from the view cell polygons
612        Construct(polys, new BoundedRayContainer());
613}
614
615
616void BspTree::Construct(const ObjectContainer &objects)
617{
618        mStat.nodes = 1;
619        mBox.Initialize();      // initialise bsp tree bounding box
620       
621        PolygonContainer *polys = new PolygonContainer();
622
623        mGenerateViewCells = true;
624        // copy mesh instance polygons into one big polygon soup
625        mStat.polys = AddToPolygonSoup(objects, *polys);
626
627        // construct tree from polygon soup
628        Construct(polys, new BoundedRayContainer());
629}
630
631void BspTree::Construct(const RayContainer &sampleRays)
632{
633    mStat.nodes = 1;
634        mBox.Initialize();      // initialise BSP tree bounding box
635       
636        PolygonContainer *polys = new PolygonContainer();
637        BoundedRayContainer *rays = new BoundedRayContainer();
638
639        RayContainer::const_iterator rit, rit_end = sampleRays.end();
640
641        // generate view cells
642        mGenerateViewCells = true;
643
644        long startTime = GetTime();
645
646        Debug << "**** Extracting polygons from rays ****\n";
647
648        std::map<Face *, Polygon3 *> facePolyMap;
649
650        //-- extract polygons intersected by the rays
651        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
652        {
653                Ray *ray = *rit;
654       
655                // get ray-face intersection. Store polygon representing the rays together
656                // with rays intersecting the face.
657                if (!ray->intersections.empty())
658                {
659                        MeshInstance *obj = dynamic_cast<MeshInstance *>(ray->intersections[0].mObject);
660                        Face *face = obj->GetMesh()->mFaces[ray->intersections[0].mFace];
661
662                        std::map<Face *, Polygon3 *>::iterator it = facePolyMap.find(face);
663
664                        if (it != facePolyMap.end())
665                        {
666                                //store rays if needed for heuristics
667                                if (mSplitPlaneStrategy & BLOCKED_RAYS)
668                                        (*it).second->mPiercingRays.push_back(ray);
669                        }
670                        else
671                        {       //store rays if needed for heuristics
672                                Polygon3 *poly = new Polygon3(face, obj->GetMesh());
673                                poly->mParent = obj;
674                                polys->push_back(poly);
675
676                                if (mSplitPlaneStrategy & BLOCKED_RAYS)
677                                        poly->mPiercingRays.push_back(ray);
678
679                                facePolyMap[face] = poly;
680                        }
681                }
682        }
683       
684        facePolyMap.clear();
685
686        // compute bounding box
687        Polygon3::IncludeInBox(*polys, mBox);
688
689        //-- store rays
690        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
691        {
692                Ray *ray = *rit;
693        ray->SetId(-1); // reset id
694
695                float minT, maxT;
696                if (mBox.GetRaySegment(*ray, minT, maxT))
697                        rays->push_back(new BoundedRay(ray, minT, maxT));
698        }
699
700        mStat.polys = (int)polys->size();
701
702        Debug << "**** Finished polygon extraction ****" << endl;
703        Debug << (int)polys->size() << " polys extracted from " << (int)sampleRays.size() << " rays" << endl;
704        Debug << "extraction time: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
705
706        Construct(polys, rays);
707}
708
709void BspTree::Construct(const ObjectContainer &objects, const RayContainer &sampleRays)
710{
711    mStat.nodes = 1;
712        mBox.Initialize();      // initialise BSP tree bounding box
713       
714        BoundedRayContainer *rays = new BoundedRayContainer();
715        PolygonContainer *polys = new PolygonContainer();
716       
717        mGenerateViewCells = true;
718
719        // copy mesh instance polygons into one big polygon soup
720        mStat.polys = AddToPolygonSoup(objects, *polys);
721
722        RayContainer::const_iterator rit, rit_end = sampleRays.end();
723
724        //-- store rays
725        for (rit = sampleRays.begin(); rit != rit_end; ++ rit)
726        {
727                Ray *ray = *rit;
728        ray->SetId(-1); // reset id
729
730                float minT, maxT;
731                if (mBox.GetRaySegment(*ray, minT, maxT))
732                        rays->push_back(new BoundedRay(ray, minT, maxT));
733        }
734
735        Debug << "tree has " << (int)polys->size() << " polys, " << (int)sampleRays.size() << " rays" << endl;
736        Construct(polys, rays);
737}
738
739void BspTree::Construct(PolygonContainer *polys, BoundedRayContainer *rays)
740{
741        BspTraversalStack tStack;
742
743        mRoot = new BspLeaf();
744
745        // constrruct root node geometry
746        BspNodeGeometry *cell = new BspNodeGeometry();
747        ConstructGeometry(mRoot, *cell);
748
749        BspTraversalData tData(mRoot, polys, 0, mRootCell, rays,
750                                                   ComputePvsSize(*rays), cell->GetArea(), cell);
751
752        tStack.push(tData);
753
754        mStat.Start();
755        cout << "Contructing bsp tree ... ";
756        long startTime = GetTime();
757        while (!tStack.empty())
758        {
759                tData = tStack.top();
760
761            tStack.pop();
762
763                // subdivide leaf node
764                BspNode *r = Subdivide(tStack, tData);
765
766                if (r == mRoot)
767                        Debug << "BSP tree construction time spent at root: " << TimeDiff(startTime, GetTime())*1e-3 << " secs" << endl;
768        }
769
770        cout << "finished\n";
771
772        mStat.Stop();
773}
774
775bool BspTree::TerminationCriteriaMet(const BspTraversalData &data) const
776{
777        return
778                (((int)data.mPolygons->size() <= mTermMinPolys) ||
779                 ((int)data.mRays->size() <= mTermMinRays) ||
780                 (data.mPvs <= mTermMinPvs) ||
781                 (data.mArea <= mTermMinArea) ||
782                 (data.mDepth >= mTermMaxDepth) ||
783                 (data.GetAvgRayContribution() < mTermMaxRayContribution));
784}
785
786BspNode *BspTree::Subdivide(BspTraversalStack &tStack, BspTraversalData &tData)
787{
788        //-- terminate traversal 
789        if (TerminationCriteriaMet(tData))             
790        {
791                BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
792       
793                BspViewCell *viewCell;
794
795                // generate new view cell for each leaf
796                if (mGenerateViewCells)
797                {
798                        viewCell = new BspViewCell();
799                }
800                else
801                {
802                        // add view cell to leaf
803                        viewCell = dynamic_cast<BspViewCell *>(tData.mViewCell);
804                }
805
806                leaf->SetViewCell(viewCell);
807                viewCell->mLeaves.push_back(leaf);
808
809                //-- add pvs
810                if (viewCell != mRootCell)
811                {
812                        int conSamp = 0, sampCon = 0;
813                        AddToPvs(leaf, *tData.mRays, conSamp, sampCon);
814                       
815                        mStat.contributingSamples += conSamp;
816                        mStat.sampleContributions += sampCon;
817                }
818
819                EvaluateLeafStats(tData);
820               
821                //-- clean up
822               
823                // discard polygons
824                CLEAR_CONTAINER(*tData.mPolygons);
825                // discard rays
826                CLEAR_CONTAINER(*tData.mRays);
827
828                DEL_PTR(tData.mPolygons);
829                DEL_PTR(tData.mRays);
830                DEL_PTR(tData.mGeometry);
831
832                return leaf;
833        }
834
835        //-- continue subdivision
836        PolygonContainer coincident;
837       
838        BspTraversalData tFrontData(NULL, new PolygonContainer(), tData.mDepth + 1, mRootCell,
839                                                                new BoundedRayContainer(), 0, 0, new BspNodeGeometry());
840        BspTraversalData tBackData(NULL, new PolygonContainer(), tData.mDepth + 1, mRootCell,
841                                                           new BoundedRayContainer(), 0, 0, new BspNodeGeometry());
842
843        // create new interior node and two leaf nodes
844        BspInterior *interior =
845                SubdivideNode(tData, tFrontData, tBackData, coincident);
846
847#ifdef _DEBUG   
848//      if (frontPolys->empty() && backPolys->empty() && (coincident.size() > 2))
849//      {       for (PolygonContainer::iterator it = coincident.begin(); it != coincident.end(); ++it)
850//                      Debug << (*it) << " " << (*it)->GetArea() << " " << (*it)->mParent << endl ;
851//              Debug << endl;}
852#endif
853
854        // extract view cells from coincident polygons according to plane normal
855    // only if front or back polygons are empty
856        if (!mGenerateViewCells)
857        {
858                ExtractViewCells(tFrontData,
859                                                 tBackData,
860                                                 coincident,
861                                                 interior->mPlane);                     
862        }
863
864        // don't need coincident polygons anymory
865        CLEAR_CONTAINER(coincident);
866
867        // push the children on the stack
868        tStack.push(tFrontData);
869        tStack.push(tBackData);
870
871        // cleanup
872        DEL_PTR(tData.mNode);
873
874        DEL_PTR(tData.mPolygons);
875        DEL_PTR(tData.mRays);
876        DEL_PTR(tData.mGeometry);               
877       
878        return interior;
879}
880
881void BspTree::ExtractViewCells(BspTraversalData &frontData,
882                                                           BspTraversalData &backData,
883                                                           const PolygonContainer &coincident,
884                                                           const Plane3 &splitPlane) const
885{
886        // if not empty, tree is further subdivided => don't have to find view cell
887        bool foundFront = !frontData.mPolygons->empty();
888        bool foundBack = !frontData.mPolygons->empty();
889
890        PolygonContainer::const_iterator it =
891                coincident.begin(), it_end = coincident.end();
892
893        //-- find first view cells in front and back leafs
894        for (; !(foundFront && foundBack) && (it != it_end); ++ it)
895        {
896                if (DotProd((*it)->GetNormal(), splitPlane.mNormal) > 0)
897                {
898                        backData.mViewCell = dynamic_cast<ViewCell *>((*it)->mParent);
899                        foundBack = true;
900                }
901                else
902                {
903                        frontData.mViewCell = dynamic_cast<ViewCell *>((*it)->mParent);
904                        foundFront = true;
905                }
906        }
907}
908
909BspInterior *BspTree::SubdivideNode(BspTraversalData &tData,
910                                                                        BspTraversalData &frontData,
911                                                                        BspTraversalData &backData,
912                                                                        PolygonContainer &coincident)
913{
914        mStat.nodes += 2;
915       
916        BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
917
918        Debug << "*********************" << endl;
919        long startTime = GetTime();
920       
921        // select subdivision plane
922        BspInterior *interior =
923                new BspInterior(SelectPlane(leaf, tData));
924        Debug << "time used for split plane selection: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
925#ifdef _DEBUG
926        Debug << interior << endl;
927#endif
928       
929
930        Debug << "number of rays: " << (int)tData.mRays->size() << endl;
931        Debug << "number of polys: " << (int)tData.mPolygons->size() << endl;
932
933        startTime = GetTime();
934       
935        // subdivide rays into front and back rays
936        SplitRays(interior->mPlane, *tData.mRays, *frontData.mRays, *backData.mRays);
937       
938        Debug << "time used for rays splitting: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
939
940        startTime = GetTime();
941        // subdivide polygons with plane
942        mStat.splits += SplitPolygons(interior->GetPlane(),
943                                                                  *tData.mPolygons,
944                                          *frontData.mPolygons,
945                                                                  *backData.mPolygons,
946                                                                  coincident);
947
948        Debug << "time used for polygon splitting: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
949
950    // compute pvs
951        frontData.mPvs = ComputePvsSize(*frontData.mRays);
952        backData.mPvs = ComputePvsSize(*backData.mRays);
953
954        // split geometry and compute area
955        if (1)
956        {
957                tData.mGeometry->SplitGeometry(*frontData.mGeometry,
958                                                                           *backData.mGeometry,
959                                                                           interior->mPlane,
960                                                                           mBox,
961                                                                           mEpsilon);
962       
963               
964                frontData.mArea = frontData.mGeometry->GetArea();
965                backData.mArea = backData.mGeometry->GetArea();
966        }
967
968        // compute accumulated ray length
969        //frontData.mAccRayLength = AccumulatedRayLength(*frontData.mRays);
970        //backData.mAccRayLength = AccumulatedRayLength(*backData.mRays);
971
972        //-- create front and back leaf
973
974        BspInterior *parent = leaf->GetParent();
975
976        // replace a link from node's parent
977        if (!leaf->IsRoot())
978        {
979                parent->ReplaceChildLink(leaf, interior);
980                interior->SetParent(parent);
981        }
982        else // new root
983        {
984                mRoot = interior;
985        }
986
987        // and setup child links
988        interior->SetupChildLinks(new BspLeaf(interior), new BspLeaf(interior));
989       
990        frontData.mNode = interior->GetFront();
991        backData.mNode = interior->GetBack();
992       
993        //DEL_PTR(leaf);
994        return interior;
995}
996
997void BspTree::SortSplitCandidates(const PolygonContainer &polys,
998                                                                  const int axis,
999                                                                  vector<SortableEntry> &splitCandidates) const
1000{
1001        splitCandidates.clear();
1002
1003        int requestedSize = 2 * (int)polys.size();
1004        // creates a sorted split candidates array 
1005        splitCandidates.reserve(requestedSize);
1006
1007        PolygonContainer::const_iterator it, it_end = polys.end();
1008
1009        AxisAlignedBox3 box;
1010
1011        // insert all queries
1012        for(it = polys.begin(); it != it_end; ++ it)
1013        {
1014                box.Initialize();
1015                box.Include(*(*it));
1016               
1017                splitCandidates.push_back(SortableEntry(SortableEntry::POLY_MIN, box.Min(axis), *it));
1018                splitCandidates.push_back(SortableEntry(SortableEntry::POLY_MAX, box.Max(axis), *it));
1019        }
1020
1021        stable_sort(splitCandidates.begin(), splitCandidates.end());
1022}
1023
1024
1025float BspTree::BestCostRatio(const PolygonContainer &polys,
1026                                                         const AxisAlignedBox3 &box,
1027                                                         const int axis,
1028                                                         float &position,
1029                                                         int &objectsBack,
1030                                                         int &objectsFront) const
1031{
1032        vector<SortableEntry> splitCandidates;
1033
1034        SortSplitCandidates(polys, axis, splitCandidates);
1035       
1036        // go through the lists, count the number of objects left and right
1037        // and evaluate the following cost funcion:
1038        // C = ct_div_ci  + (ol + or)/queries
1039       
1040        int objectsLeft = 0, objectsRight = (int)polys.size();
1041       
1042        float minBox = box.Min(axis);
1043        float maxBox = box.Max(axis);
1044        float boxArea = box.SurfaceArea();
1045 
1046        float minBand = minBox + mSplitBorder * (maxBox - minBox);
1047        float maxBand = minBox + (1.0f - mSplitBorder) * (maxBox - minBox);
1048       
1049        float minSum = 1e20f;
1050        vector<SortableEntry>::const_iterator ci, ci_end = splitCandidates.end();
1051
1052        for(ci = splitCandidates.begin(); ci != ci_end; ++ ci)
1053        {
1054                switch ((*ci).type)
1055                {
1056                        case SortableEntry::POLY_MIN:
1057                                ++ objectsLeft;
1058                                break;
1059                        case SortableEntry::POLY_MAX:
1060                            -- objectsRight;
1061                                break;
1062                        default:
1063                                break;
1064                }
1065               
1066                if ((*ci).value > minBand && (*ci).value < maxBand)
1067                {
1068                        AxisAlignedBox3 lbox = box;
1069                        AxisAlignedBox3 rbox = box;
1070                        lbox.SetMax(axis, (*ci).value);
1071                        rbox.SetMin(axis, (*ci).value);
1072
1073                        const float sum = objectsLeft * lbox.SurfaceArea() +
1074                                                          objectsRight * rbox.SurfaceArea();
1075     
1076                        if (sum < minSum)
1077                        {
1078                                minSum = sum;
1079                                position = (*ci).value;
1080
1081                                objectsBack = objectsLeft;
1082                                objectsFront = objectsRight;
1083                        }
1084                }
1085        }
1086 
1087        const float oldCost = (float)polys.size();
1088        const float newCost = mAxisAlignedCtDivCi + minSum / boxArea;
1089        const float ratio = newCost / oldCost;
1090
1091
1092#if 0
1093  Debug << "====================" << endl;
1094  Debug << "costRatio=" << ratio << " pos=" << position<<" t=" << (position - minBox)/(maxBox - minBox)
1095      << "\t o=(" << objectsBack << "," << objectsFront << ")" << endl;
1096#endif
1097  return ratio;
1098}
1099
1100bool BspTree::SelectAxisAlignedPlane(Plane3 &plane,
1101                                                                         const PolygonContainer &polys) const
1102{
1103        AxisAlignedBox3 box;
1104        box.Initialize();
1105       
1106        // create bounding box of region
1107        Polygon3::IncludeInBox(polys, box);
1108       
1109        int objectsBack = 0, objectsFront = 0;
1110        int axis = 0;
1111        float costRatio = MAX_FLOAT;
1112        Vector3 position;
1113
1114        //-- area subdivision
1115        for (int i = 0; i < 3; ++ i)
1116        {
1117                float p = 0;
1118                float r = BestCostRatio(polys, box, i, p, objectsBack, objectsFront);
1119               
1120                if (r < costRatio)
1121                {
1122                        costRatio = r;
1123                        axis = i;
1124                        position = p;
1125                }
1126        }
1127       
1128        if (costRatio >= mMaxCostRatio)
1129                return false;
1130
1131        Vector3 norm(0,0,0); norm[axis] = 1.0f;
1132        plane = Plane3(norm, position);
1133
1134        return true;
1135}
1136
1137Plane3 BspTree::SelectPlane(BspLeaf *leaf, BspTraversalData &data)
1138{
1139        if (data.mPolygons->empty() && data.mRays->empty())
1140        {
1141                Debug << "Warning: No autopartition polygon candidate available\n";
1142       
1143                // return axis aligned split
1144                AxisAlignedBox3 box;
1145                box.Initialize();
1146       
1147                // create bounding box of region
1148                Polygon3::IncludeInBox(*data.mPolygons, box);
1149
1150                const int axis = box.Size().DrivingAxis();
1151                const Vector3 position = (box.Min()[axis] + box.Max()[axis])*0.5f;
1152
1153                Vector3 norm(0,0,0); norm[axis] = 1.0f;
1154                return Plane3(norm, position);
1155        }
1156       
1157        if ((mSplitPlaneStrategy & AXIS_ALIGNED) &&
1158                ((int)data.mPolygons->size() > mTermMinPolysForAxisAligned) &&
1159                ((int)data.mRays->size() > mTermMinRaysForAxisAligned) &&
1160                ((mTermMinObjectsForAxisAligned < 0) ||
1161                  (Polygon3::ParentObjectsSize(*data.mPolygons) > mTermMinObjectsForAxisAligned)))
1162        {
1163                Plane3 plane;
1164                if (SelectAxisAlignedPlane(plane, *data.mPolygons))
1165                        return plane;
1166        }
1167
1168        // simplest strategy: just take next polygon
1169        if (mSplitPlaneStrategy & RANDOM_POLYGON)
1170        {
1171        if (!data.mPolygons->empty())
1172                {
1173                        Polygon3 *nextPoly =
1174                                (*data.mPolygons)[(int)RandomValue(0, (Real)((int)data.mPolygons->size() - 1))];
1175                        return nextPoly->GetSupportingPlane();
1176                }
1177                else
1178                {
1179                        const int candidateIdx = (int)RandomValue(0, (Real)((int)data.mRays->size() - 1));
1180                        BoundedRay *bRay = (*data.mRays)[candidateIdx];
1181
1182                        Ray *ray = bRay->mRay;
1183                                               
1184                        const Vector3 minPt = ray->Extrap(bRay->mMinT);
1185                        const Vector3 maxPt = ray->Extrap(bRay->mMaxT);
1186
1187                        const Vector3 pt = (maxPt + minPt) * 0.5;
1188
1189                        const Vector3 normal = ray->GetDir();
1190                       
1191                        return Plane3(normal, pt);
1192                }
1193
1194                return Plane3();
1195        }
1196
1197        // use heuristics to find appropriate plane
1198        return SelectPlaneHeuristics(leaf, data);
1199}
1200
1201
1202Plane3 BspTree::ChooseCandidatePlane(const BoundedRayContainer &rays) const
1203{       
1204        const int candidateIdx = (int)RandomValue(0, (Real)((int)rays.size() - 1));
1205        BoundedRay *bRay = rays[candidateIdx];
1206        Ray *ray = bRay->mRay;
1207
1208        const Vector3 minPt = ray->Extrap(bRay->mMinT);
1209        const Vector3 maxPt = ray->Extrap(bRay->mMaxT);
1210
1211        const Vector3 pt = (maxPt + minPt) * 0.5;
1212
1213        const Vector3 normal = ray->GetDir();
1214                       
1215        return Plane3(normal, pt);
1216}
1217
1218Plane3 BspTree::ChooseCandidatePlane2(const BoundedRayContainer &rays) const
1219{       
1220        Vector3 pt[3];
1221        int idx[3];
1222        int cmaxT = 0;
1223        int cminT = 0;
1224        bool chooseMin = false;
1225
1226        for (int j = 0; j < 3; j ++)
1227        {
1228                idx[j] = (int)RandomValue(0, Real((int)rays.size() * 2 - 1));
1229                               
1230                if (idx[j] >= (int)rays.size())
1231                {
1232                        idx[j] -= (int)rays.size();             
1233                        chooseMin = (cminT < 2);
1234                }
1235                else
1236                        chooseMin = (cmaxT < 2);
1237
1238                BoundedRay *bRay = rays[idx[j]];
1239                pt[j] = chooseMin ? bRay->mRay->Extrap(bRay->mMinT) :
1240                                                        bRay->mRay->Extrap(bRay->mMaxT);
1241        }       
1242                       
1243        return Plane3(pt[0], pt[1], pt[2]);
1244}
1245
1246Plane3 BspTree::ChooseCandidatePlane3(const BoundedRayContainer &rays) const
1247{       
1248        Vector3 pt[3];
1249       
1250        int idx1 = (int)RandomValue(0, (Real)((int)rays.size() - 1));
1251        int idx2 = (int)RandomValue(0, (Real)((int)rays.size() - 1));
1252
1253        // check if rays different
1254        if (idx1 == idx2)
1255                idx2 = (idx2 + 1) % (int)rays.size();
1256
1257        const BoundedRay *ray1 = rays[idx1];
1258        const BoundedRay *ray2 = rays[idx2];
1259
1260        // normal vector of the plane parallel to both lines
1261        const Vector3 norm =
1262                Normalize(CrossProd(ray1->mRay->GetDir(), ray2->mRay->GetDir()));
1263
1264        const Vector3 orig1 = ray1->mRay->Extrap(ray1->mMinT);
1265        const Vector3 orig2 = ray2->mRay->Extrap(ray2->mMinT);
1266
1267        // vector from line 1 to line 2
1268        const Vector3 vd = orig1 - orig2;
1269       
1270        // project vector on normal to get distance
1271        const float dist = DotProd(vd, norm);
1272
1273        // point on plane lies halfway between the two planes
1274        const Vector3 planePt = orig1 + norm * dist * 0.5;
1275
1276        return Plane3(norm, planePt);
1277}
1278
1279
1280Plane3 BspTree::SelectPlaneHeuristics(BspLeaf *leaf, BspTraversalData &data)
1281{
1282        float lowestCost = MAX_FLOAT;
1283        Plane3 bestPlane;
1284        // intermediate plane
1285        Plane3 plane;
1286
1287        const int limit = Min((int)data.mPolygons->size(), mMaxPolyCandidates);
1288        int maxIdx = (int)data.mPolygons->size();
1289       
1290        for (int i = 0; i < limit; ++ i)
1291        {
1292                // assure that no index is taken twice
1293                const int candidateIdx = (int)RandomValue(0, (Real)(-- maxIdx));
1294                //Debug << "current Idx: " << maxIdx << " cand idx " << candidateIdx << endl;
1295               
1296                Polygon3 *poly = (*data.mPolygons)[candidateIdx];
1297
1298                // swap candidate to the end to avoid testing same plane
1299                std::swap((*data.mPolygons)[maxIdx], (*data.mPolygons)[candidateIdx]);
1300       
1301                //Polygon3 *poly = (*data.mPolygons)[(int)RandomValue(0, (int)polys.size() - 1)];
1302
1303                // evaluate current candidate
1304                const float candidateCost =
1305                        SplitPlaneCost(poly->GetSupportingPlane(), data);
1306
1307                if (candidateCost < lowestCost)
1308                {
1309                        bestPlane = poly->GetSupportingPlane();
1310                        lowestCost = candidateCost;
1311                }
1312        }
1313       
1314        //-- choose candidate planes extracted from rays
1315        //-- different methods are available
1316        for (int i = 0; i < mMaxRayCandidates; ++ i)
1317        {
1318                plane = ChooseCandidatePlane3(*data.mRays);
1319                const float candidateCost = SplitPlaneCost(plane, data);
1320
1321                if (candidateCost < lowestCost)
1322                {
1323                        bestPlane = plane;     
1324                        lowestCost = candidateCost;
1325                }
1326        }
1327
1328#ifdef _DEBUG
1329        Debug << "plane lowest cost: " << lowestCost << endl;
1330#endif
1331
1332        return bestPlane;
1333}
1334
1335
1336float BspTree::SplitPlaneCost(const Plane3 &candidatePlane,
1337                                                          const PolygonContainer &polys) const
1338{
1339        float val = 0;
1340
1341        float sumBalancedPolys = 0;
1342        float sumSplits = 0;
1343        float sumPolyArea = 0;
1344        float sumBalancedViewCells = 0;
1345        float sumBlockedRays = 0;
1346        float totalBlockedRays = 0;
1347        //float totalArea = 0;
1348        int totalViewCells = 0;
1349
1350        // need three unique ids for each type of view cell
1351        // for balanced view cells criterium
1352        ViewCell::NewMail();
1353        const int backId = ViewCell::sMailId;
1354        ViewCell::NewMail();
1355        const int frontId = ViewCell::sMailId;
1356        ViewCell::NewMail();
1357        const int frontAndBackId = ViewCell::sMailId;
1358
1359        bool useRand;;
1360        int limit;
1361
1362        // choose test polyongs randomly if over threshold
1363        if ((int)polys.size() > mMaxTests)
1364        {
1365                useRand = true;
1366                limit = mMaxTests;
1367        }
1368        else
1369        {
1370                useRand = false;
1371                limit = (int)polys.size();
1372        }
1373
1374        for (int i = 0; i < limit; ++ i)
1375        {
1376                const int testIdx = useRand ? (int)RandomValue(0, (Real)(limit - 1)) : i;
1377
1378                Polygon3 *poly = polys[testIdx];
1379
1380        const int classification =
1381                        poly->ClassifyPlane(candidatePlane, mEpsilon);
1382
1383                if (mSplitPlaneStrategy & BALANCED_POLYS)
1384                        sumBalancedPolys += sBalancedPolysTable[classification];
1385               
1386                if (mSplitPlaneStrategy & LEAST_SPLITS)
1387                        sumSplits += sLeastPolySplitsTable[classification];
1388
1389                if (mSplitPlaneStrategy & LARGEST_POLY_AREA)
1390                {
1391                        if (classification == Polygon3::COINCIDENT)
1392                                sumPolyArea += poly->GetArea();
1393                        //totalArea += area;
1394                }
1395               
1396                if (mSplitPlaneStrategy & BLOCKED_RAYS)
1397                {
1398                        const float blockedRays = (float)poly->mPiercingRays.size();
1399               
1400                        if (classification == Polygon3::COINCIDENT)
1401                                sumBlockedRays += blockedRays;
1402                       
1403                        totalBlockedRays += blockedRays;
1404                }
1405
1406                // assign view cells to back or front according to classificaion
1407                if (mSplitPlaneStrategy & BALANCED_VIEW_CELLS)
1408                {
1409                        MeshInstance *viewCell = poly->mParent;
1410               
1411                        // assure that we only count a view cell
1412                        // once for the front and once for the back side of the plane
1413                        if (classification == Polygon3::FRONT_SIDE)
1414                        {
1415                                if ((viewCell->mMailbox != frontId) &&
1416                                        (viewCell->mMailbox != frontAndBackId))
1417                                {
1418                                        sumBalancedViewCells += 1.0;
1419
1420                                        if (viewCell->mMailbox != backId)
1421                                                viewCell->mMailbox = frontId;
1422                                        else
1423                                                viewCell->mMailbox = frontAndBackId;
1424                                       
1425                                        ++ totalViewCells;
1426                                }
1427                        }
1428                        else if (classification == Polygon3::BACK_SIDE)
1429                        {
1430                                if ((viewCell->mMailbox != backId) &&
1431                                    (viewCell->mMailbox != frontAndBackId))
1432                                {
1433                                        sumBalancedViewCells -= 1.0;
1434
1435                                        if (viewCell->mMailbox != frontId)
1436                                                viewCell->mMailbox = backId;
1437                                        else
1438                                                viewCell->mMailbox = frontAndBackId;
1439
1440                                        ++ totalViewCells;
1441                                }
1442                        }
1443                }
1444        }
1445
1446        const float polysSize = (float)polys.size() + Limits::Small;
1447
1448        // all values should be approx. between 0 and 1 so they can be combined
1449        // and scaled with the factors according to their importance
1450        if (mSplitPlaneStrategy & BALANCED_POLYS)
1451                val += mBalancedPolysFactor * fabs(sumBalancedPolys) / polysSize;
1452       
1453        if (mSplitPlaneStrategy & LEAST_SPLITS) 
1454                val += mLeastSplitsFactor * sumSplits / polysSize;
1455
1456        if (mSplitPlaneStrategy & LARGEST_POLY_AREA)
1457                // HACK: polys.size should be total area so scaling is between 0 and 1
1458                val += mLargestPolyAreaFactor * (float)polys.size() / sumPolyArea;
1459
1460        if (mSplitPlaneStrategy & BLOCKED_RAYS)
1461                if (totalBlockedRays != 0)
1462                        val += mBlockedRaysFactor * (totalBlockedRays - sumBlockedRays) / totalBlockedRays;
1463
1464        if (mSplitPlaneStrategy & BALANCED_VIEW_CELLS)
1465                val += mBalancedViewCellsFactor * fabs(sumBalancedViewCells) /
1466                        ((float)totalViewCells + Limits::Small);
1467       
1468        return val;
1469}
1470
1471
1472inline void BspTree::GenerateUniqueIdsForPvs()
1473{
1474        Intersectable::NewMail(); sBackId = ViewCell::sMailId;
1475        Intersectable::NewMail(); sFrontId = ViewCell::sMailId;
1476        Intersectable::NewMail(); sFrontAndBackId = ViewCell::sMailId;
1477}
1478
1479float BspTree::SplitPlaneCost(const Plane3 &candidatePlane,
1480                                                          const BoundedRayContainer &rays,
1481                                                          const int pvs,
1482                                                          const float area,
1483                                                          const BspNodeGeometry &cell) const
1484{
1485        float val = 0;
1486
1487        float sumBalancedRays = 0;
1488        float sumRaySplits = 0;
1489
1490        int frontPvs = 0;
1491        int backPvs = 0;
1492
1493        // probability that view point lies in child
1494        float pOverall = 0;
1495        float pFront = 0;
1496        float pBack = 0;
1497
1498        const bool pvsUseLen = false;
1499
1500        if (mSplitPlaneStrategy & PVS)
1501        {
1502                // create unique ids for pvs heuristics
1503                GenerateUniqueIdsForPvs();
1504
1505                if (mPvsUseArea) // use front and back cell areas to approximate volume
1506                {
1507                        // construct child geometry with regard to the candidate split plane
1508                        BspNodeGeometry frontCell;
1509                        BspNodeGeometry backCell;
1510
1511                        cell.SplitGeometry(frontCell,
1512                                                           backCell,
1513                                                           candidatePlane,
1514                                                           mBox,
1515                                                           mEpsilon);
1516               
1517                        pFront = frontCell.GetArea();
1518                        pBack = backCell.GetArea();
1519
1520                        pOverall = area;
1521                }
1522        }
1523                       
1524        bool useRand;;
1525        int limit;
1526
1527        // choose test polyongs randomly if over threshold
1528        if ((int)rays.size() > mMaxTests)
1529        {
1530                useRand = true;
1531                limit = mMaxTests;
1532        }
1533        else
1534        {
1535                useRand = false;
1536                limit = (int)rays.size();
1537        }
1538
1539        for (int i = 0; i < limit; ++ i)
1540        {
1541                const int testIdx = useRand ? (int)RandomValue(0, (Real)(limit - 1)) : i;
1542       
1543                BoundedRay *bRay = rays[testIdx];
1544
1545                Ray *ray = bRay->mRay;
1546                const float minT = bRay->mMinT;
1547                const float maxT = bRay->mMaxT;
1548
1549                Vector3 entP, extP;
1550
1551                const int cf =
1552                        ray->ClassifyPlane(candidatePlane, minT, maxT, entP, extP);
1553
1554                if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
1555                {
1556                        sumBalancedRays += sBalancedRaysTable[cf];
1557                }
1558               
1559                if (mSplitPlaneStrategy & BALANCED_RAYS)
1560                {
1561                        sumRaySplits += sLeastRaySplitsTable[cf];
1562                }
1563
1564                if (mSplitPlaneStrategy & PVS)
1565                {
1566                        // in case the ray intersects an object
1567                        // assure that we only count the object
1568                        // once for the front and once for the back side of the plane
1569                       
1570                        // add the termination object
1571                        if (!ray->intersections.empty())
1572                                AddObjToPvs(ray->intersections[0].mObject, cf, frontPvs, backPvs);
1573                       
1574                        // add the source object
1575                        AddObjToPvs(ray->sourceObject.mObject, cf, frontPvs, backPvs);
1576                       
1577                        if (mPvsUseArea)
1578                        {
1579                                float len = 1;
1580                       
1581                                if (pvsUseLen)
1582                                        len = SqrDistance(entP, extP);
1583       
1584                                // use length of rays to approximate volume
1585                                if (Ray::BACK && Ray::COINCIDENT)
1586                                        pBack += len;
1587                                if (Ray::FRONT && Ray::COINCIDENT)
1588                                        pFront += len;
1589                                if (Ray::FRONT_BACK || Ray::BACK_FRONT)
1590                                {
1591                                        if (pvsUseLen)
1592                                        {
1593                                                const Vector3 extp = ray->Extrap(maxT);
1594                                                const float t = candidatePlane.FindT(ray->GetLoc(), extp);
1595                               
1596                                                const float newT = t * maxT;
1597                                                const float newLen = SqrDistance(ray->Extrap(newT), extp);
1598
1599                                                if (Ray::FRONT_BACK)
1600                                                {
1601                                                        pFront += len - newLen;
1602                                                        pBack += newLen;
1603                                                }
1604                                                else
1605                                                {
1606                                                        pBack += len - newLen;
1607                                                        pFront += newLen;
1608                                                }
1609                                        }
1610                                        else
1611                                        {
1612                                                ++ pFront;
1613                                                ++ pBack;
1614                                        }
1615                                }
1616                        }
1617                }
1618        }
1619
1620        const float raysSize = (float)rays.size() + Limits::Small;
1621
1622        if (mSplitPlaneStrategy & LEAST_RAY_SPLITS)
1623                val += mLeastRaySplitsFactor * sumRaySplits / raysSize;
1624
1625        if (mSplitPlaneStrategy & BALANCED_RAYS)
1626                val += mBalancedRaysFactor * fabs(sumBalancedRays) /  raysSize;
1627
1628        const float denom = pOverall * (float)pvs * 2.0f + Limits::Small;
1629
1630        if (mSplitPlaneStrategy & PVS)
1631        {
1632                val += mPvsFactor * (frontPvs * pFront + (backPvs * pBack)) / denom;
1633
1634                // give penalty to unbalanced split
1635                if (0)
1636                if (((pFront * 0.2 + Limits::Small) > pBack) ||
1637                        (pFront < (pBack * 0.2 + Limits::Small)))
1638                        val += 0.5;
1639        }
1640
1641       
1642#ifdef _DEBUG
1643        Debug << "totalpvs: " << pvs << " ptotal: " << pOverall
1644                  << " frontpvs: " << frontPvs << " pFront: " << pFront
1645                  << " backpvs: " << backPvs << " pBack: " << pBack << endl << endl;
1646#endif
1647       
1648        return val;
1649}
1650
1651void BspTree::AddObjToPvs(Intersectable *obj,
1652                                                  const int cf,
1653                                                  int &frontPvs,
1654                                                  int &backPvs) const
1655{
1656        if (!obj)
1657                return;
1658        // TODO: does this really belong to no pvs?
1659        //if (cf == Ray::COINCIDENT) return;
1660
1661        // object belongs to both PVS
1662        const bool bothSides = (cf == Ray::FRONT_BACK) ||
1663                                                   (cf == Ray::BACK_FRONT) ||
1664                                                   (cf == Ray::COINCIDENT);
1665
1666        if ((cf == Ray::FRONT) || bothSides)
1667        {
1668                if ((obj->mMailbox != sFrontId) &&
1669                        (obj->mMailbox != sFrontAndBackId))
1670                {
1671                        ++ frontPvs;
1672
1673                        if (obj->mMailbox == sBackId)
1674                                obj->mMailbox = sFrontAndBackId;       
1675                        else
1676                                obj->mMailbox = sFrontId;                                                               
1677                }
1678        }
1679       
1680        if ((cf == Ray::BACK) || bothSides)
1681        {
1682                if ((obj->mMailbox != sBackId) &&
1683                        (obj->mMailbox != sFrontAndBackId))
1684                {
1685                        ++ backPvs;
1686
1687                        if (obj->mMailbox == sFrontId)
1688                                obj->mMailbox = sFrontAndBackId;
1689                        else
1690                                obj->mMailbox = sBackId;                               
1691                }
1692        }
1693}
1694
1695float BspTree::SplitPlaneCost(const Plane3 &candidatePlane,
1696                                                          BspTraversalData &data) const
1697{
1698        float val = 0;
1699
1700        if (mSplitPlaneStrategy & VERTICAL_AXIS)
1701        {
1702                Vector3 tinyAxis(0,0,0); tinyAxis[mBox.Size().TinyAxis()] = 1.0f;
1703                // we put a penalty on the dot product between the "tiny" vertical axis
1704                // and the split plane axis
1705                val += mVerticalSplitsFactor *
1706                           fabs(DotProd(candidatePlane.mNormal, tinyAxis));
1707        }
1708
1709        // the following criteria loop over all polygons to find the cost value
1710        if ((mSplitPlaneStrategy & BALANCED_POLYS)      ||
1711                (mSplitPlaneStrategy & LEAST_SPLITS)        ||
1712                (mSplitPlaneStrategy & LARGEST_POLY_AREA)   ||
1713                (mSplitPlaneStrategy & BALANCED_VIEW_CELLS) ||
1714                (mSplitPlaneStrategy & BLOCKED_RAYS))
1715        {
1716                val += SplitPlaneCost(candidatePlane, *data.mPolygons);
1717        }
1718
1719        // the following criteria loop over all rays to find the cost value
1720        if ((mSplitPlaneStrategy & BALANCED_RAYS)      ||
1721                (mSplitPlaneStrategy & LEAST_RAY_SPLITS)   ||
1722                (mSplitPlaneStrategy & PVS))
1723        {
1724                val += SplitPlaneCost(candidatePlane, *data.mRays, data.mPvs,
1725                                                          data.mArea, *data.mGeometry);
1726        }
1727
1728        // return linear combination of the sums
1729        return val;
1730}
1731
1732void BspTree::CollectLeaves(vector<BspLeaf *> &leaves) const
1733{
1734        stack<BspNode *> nodeStack;
1735        nodeStack.push(mRoot);
1736 
1737        while (!nodeStack.empty())
1738        {
1739                BspNode *node = nodeStack.top();
1740   
1741                nodeStack.pop();
1742   
1743                if (node->IsLeaf())
1744                {
1745                        BspLeaf *leaf = (BspLeaf *)node;               
1746                        leaves.push_back(leaf);
1747                }
1748                else
1749                {
1750                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
1751
1752                        nodeStack.push(interior->GetBack());
1753                        nodeStack.push(interior->GetFront());
1754                }
1755        }
1756}
1757
1758AxisAlignedBox3 BspTree::GetBoundingBox() const
1759{
1760        return mBox;
1761}
1762
1763BspNode *BspTree::GetRoot() const
1764{
1765        return mRoot;
1766}
1767
1768void BspTree::EvaluateLeafStats(const BspTraversalData &data)
1769{
1770        // the node became a leaf -> evaluate stats for leafs
1771        BspLeaf *leaf = dynamic_cast<BspLeaf *>(data.mNode);
1772
1773        // store maximal and minimal depth
1774        if (data.mDepth > mStat.maxDepth)
1775                mStat.maxDepth = data.mDepth;
1776
1777        if (data.mDepth < mStat.minDepth)
1778                mStat.minDepth = data.mDepth;
1779
1780        // accumulate depth to compute average depth
1781        mStat.accumDepth += data.mDepth;
1782       
1783
1784        if (data.mDepth >= mTermMaxDepth)
1785                ++ mStat.maxDepthNodes;
1786
1787        if (data.mPvs < mTermMinPvs)
1788                ++ mStat.minPvsNodes;
1789
1790        if ((int)data.mRays->size() < mTermMinRays)
1791                ++ mStat.minRaysNodes;
1792
1793        if (data.GetAvgRayContribution() > mTermMaxRayContribution)
1794                ++ mStat.maxRayContribNodes;
1795       
1796        if (data.mGeometry->GetArea() <= mTermMinArea)
1797                ++ mStat.minAreaNodes;
1798
1799#ifdef _DEBUG
1800        Debug << "BSP stats: "
1801                  << "Depth: " << data.mDepth << " (max: " << mTermMaxDepth << "), "
1802                  << "PVS: " << data.mPvs << " (min: " << mTermMinPvs << "), "
1803                  << "Area: " << data.mArea << " (min: " << mTermMinArea << "), "
1804                  << "#polygons: " << (int)data.mPolygons->size() << " (max: " << mTermMinPolys << "), "
1805                  << "#rays: " << (int)data.mRays->size() << " (max: " << mTermMinRays << "), "
1806                  << "#pvs: " << leaf->GetViewCell()->GetPvs().GetSize() << "=, "
1807                  << "#avg ray contrib (pvs): " << (float)data.mPvs / (float)data.mRays->size() << endl;
1808#endif
1809}
1810
1811int
1812BspTree::_CastRay(Ray &ray)
1813{
1814        int hits = 0;
1815 
1816        stack<BspRayTraversalData> tStack;
1817 
1818        float maxt, mint;
1819
1820        if (!mBox.GetRaySegment(ray, mint, maxt))
1821                return 0;
1822
1823        Intersectable::NewMail();
1824
1825        Vector3 entp = ray.Extrap(mint);
1826        Vector3 extp = ray.Extrap(maxt);
1827 
1828        BspNode *node = mRoot;
1829        BspNode *farChild = NULL;
1830       
1831        while (1)
1832        {
1833                if (!node->IsLeaf())
1834                {
1835                        BspInterior *in = dynamic_cast<BspInterior *>(node);
1836                       
1837                        Plane3 splitPlane = in->GetPlane();
1838                        const int entSide = splitPlane.Side(entp);
1839                        const int extSide = splitPlane.Side(extp);
1840
1841                        if (entSide < 0)
1842                        {
1843                                node = in->GetBack();
1844
1845                                if(extSide <= 0) // plane does not split ray => no far child
1846                                        continue;
1847                                       
1848                                farChild = in->GetFront(); // plane splits ray
1849
1850                        } else if (entSide > 0)
1851                        {
1852                                node = in->GetFront();
1853
1854                                if (extSide >= 0) // plane does not split ray => no far child
1855                                        continue;
1856
1857                                farChild = in->GetBack(); // plane splits ray                   
1858                        }
1859                        else // ray and plane are coincident
1860                        {
1861                                // WHAT TO DO IN THIS CASE ?
1862                                //break;
1863                                node = in->GetFront();
1864                                continue;
1865                        }
1866
1867                        // push data for far child
1868                        tStack.push(BspRayTraversalData(farChild, extp, maxt));
1869
1870                        // find intersection of ray segment with plane
1871                        float t;
1872                        extp = splitPlane.FindIntersection(ray.GetLoc(), extp, &t);
1873                        maxt *= t;
1874                       
1875                } else // reached leaf => intersection with view cell
1876                {
1877                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
1878     
1879                        if (!leaf->mViewCell->Mailed())
1880                        {
1881                          //                            ray.bspIntersections.push_back(Ray::BspIntersection(maxt, leaf));
1882                                leaf->mViewCell->Mail();
1883                                ++ hits;
1884                        }
1885                       
1886                        //-- fetch the next far child from the stack
1887                        if (tStack.empty())
1888                                break;
1889     
1890                        entp = extp;
1891                        mint = maxt; // NOTE: need this?
1892
1893                        if (ray.GetType() == Ray::LINE_SEGMENT && mint > 1.0f)
1894                                break;
1895
1896                        BspRayTraversalData &s = tStack.top();
1897
1898                        node = s.mNode;
1899                        extp = s.mExitPoint;
1900                        maxt = s.mMaxT;
1901
1902                        tStack.pop();
1903                }
1904        }
1905
1906        return hits;
1907}
1908
1909
1910int BspTree::CastLineSegment(const Vector3 &origin,
1911                                                         const Vector3 &termination,
1912                                                         vector<ViewCell *> &viewcells)
1913{
1914        int hits = 0;
1915        stack<BspRayTraversalData> tStack;
1916
1917        float mint = 0.0f, maxt = 1.0f;
1918
1919        Intersectable::NewMail();
1920
1921        Vector3 entp = origin;
1922        Vector3 extp = termination;
1923
1924        BspNode *node = mRoot;
1925        BspNode *farChild = NULL;
1926
1927        while (1)
1928        {
1929                if (!node->IsLeaf()) 
1930                {
1931                        BspInterior *in = dynamic_cast<BspInterior *>(node);
1932               
1933                        Plane3 splitPlane = in->GetPlane();
1934                       
1935                        const int entSide = splitPlane.Side(entp);
1936                        const int extSide = splitPlane.Side(extp);
1937
1938                        if (entSide < 0)
1939                        {
1940                                node = in->GetBack();
1941               
1942                                if(extSide <= 0) // plane does not split ray => no far child
1943                                        continue;
1944               
1945                                farChild = in->GetFront(); // plane splits ray
1946               
1947                        }
1948                        else if (entSide > 0)
1949                        {
1950                                node = in->GetFront();
1951                       
1952                                if (extSide >= 0) // plane does not split ray => no far child
1953                                        continue;
1954                       
1955                                farChild = in->GetBack(); // plane splits ray                   
1956                        }
1957                        else // ray and plane are coincident
1958                        {
1959                                // WHAT TO DO IN THIS CASE ?
1960                                //break;
1961                                node = in->GetFront();
1962                                continue;
1963                        }
1964               
1965                        // push data for far child
1966                        tStack.push(BspRayTraversalData(farChild, extp, maxt));
1967               
1968                        // find intersection of ray segment with plane
1969                        float t;
1970                        extp = splitPlane.FindIntersection(origin, extp, &t);
1971                        maxt *= t; 
1972                }
1973                else
1974                {
1975                        // reached leaf => intersection with view cell
1976                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
1977               
1978                        if (!leaf->mViewCell->Mailed())
1979                        {
1980                                viewcells.push_back(leaf->mViewCell);
1981                                leaf->mViewCell->Mail();
1982                                hits++;
1983                        }
1984               
1985                        //-- fetch the next far child from the stack
1986                        if (tStack.empty())
1987                                break;
1988           
1989                        entp = extp;
1990                        mint = maxt; // NOTE: need this?
1991               
1992                        BspRayTraversalData &s = tStack.top();
1993               
1994                        node = s.mNode;
1995                        extp = s.mExitPoint;
1996                        maxt = s.mMaxT;
1997               
1998                        tStack.pop();
1999                }
2000        }
2001        return hits;
2002}
2003
2004bool BspTree::Export(const string filename)
2005{
2006        Exporter *exporter = Exporter::GetExporter(filename);
2007
2008        if (exporter)
2009        {
2010                exporter->ExportBspTree(*this);
2011                return true;
2012        }       
2013
2014        return false;
2015}
2016
2017void BspTree::CollectViewCells(ViewCellContainer &viewCells) const
2018{
2019        stack<BspNode *> nodeStack;
2020        nodeStack.push(mRoot);
2021
2022        ViewCell::NewMail();
2023
2024        while (!nodeStack.empty())
2025        {
2026                BspNode *node = nodeStack.top();
2027                nodeStack.pop();
2028
2029                if (node->IsLeaf())
2030                {
2031                        ViewCell *viewCell = dynamic_cast<BspLeaf *>(node)->mViewCell;
2032
2033                        if (!viewCell->Mailed())
2034                        {
2035                                viewCell->Mail();
2036                                viewCells.push_back(viewCell);
2037                        }
2038                }
2039                else
2040                {
2041                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
2042
2043                        nodeStack.push(interior->GetFront());
2044                        nodeStack.push(interior->GetBack());
2045                }
2046        }
2047}
2048
2049
2050BspTreeStatistics &BspTree::GetStat()
2051{
2052        return mStat;
2053}
2054
2055
2056float BspTree::AccumulatedRayLength(BoundedRayContainer &rays) const
2057{
2058        float len = 0;
2059
2060        BoundedRayContainer::const_iterator it, it_end = rays.end();
2061
2062        for (it = rays.begin(); it != it_end; ++ it)
2063        {
2064                len += SqrDistance((*it)->mRay->Extrap((*it)->mMinT),
2065                                                   (*it)->mRay->Extrap((*it)->mMaxT));
2066        }
2067
2068        return len;
2069}
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->mLeaves;
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}
2644
2645void BspNodeGeometry::ComputeBoundingBox(AxisAlignedBox3 &box)
2646{
2647        Polygon3::IncludeInBox(mPolys, box);
2648}
Note: See TracBrowser for help on using the repository browser.