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

Revision 304, 33.8 KB checked in by mattausch, 19 years ago (diff)

bsp candidate selection heuristics tryout

Line 
1#include "Plane3.h"
2#include "ViewCellBsp.h"
3#include "Mesh.h"
4#include "common.h"
5#include "ViewCell.h"
6#include "Environment.h"
7#include "Polygon3.h"
8#include "Ray.h"
9#include "AxisAlignedBox3.h"
10#include <stack>
11#include <time.h>
12#include <iomanip>
13#include "Exporter.h"
14
15int BspTree::sTermMaxPolygons = 10;
16int BspTree::sTermMaxDepth = 20;
17int BspTree::sMaxCandidates = 10;
18int BspTree::sSplitPlaneStrategy = NEXT_POLYGON;
19int BspTree::sConstructionMethod = VIEW_CELLS;
20int BspTree::sTermMaxPolysForAxisAligned = 50;
21
22float BspTree::sCt_div_ci = 1.0f;
23float BspTree::sSplitBorder = 1.0f;
24float BspTree::sMaxCostRatio = 0.9f;
25
26//-- factors for bsp tree split plane heuristics
27float BspTree::sLeastSplitsFactor = 1.0f;
28float BspTree::sBalancedPolysFactor = 2.0f;
29float BspTree::sBalancedViewCellsFactor = 2.0f;
30float BspTree::sVerticalSplitsFactor = 1.0f; // very important criterium for 2.5d scenes
31
32
33/** Evaluates split plane classification with respect to the plane's
34        contribution for a balanced tree.
35*/
36float BspTree::sLeastSplitsTable[] = {0, 0, 1, 0};
37/** Evaluates split plane classification with respect to the plane's
38        contribution for a minimum number splits in the tree.
39*/
40float BspTree::sBalancedPolysTable[] = {-1, 1, 0, 0};
41
42//int counter = 0;
43
44/****************************************************************/
45/*                  class BspNode implementation                */
46/****************************************************************/
47
48BspNode::BspNode(): mParent(NULL), mPolygons(NULL)//,mViewCellIdx(0)
49{}
50
51BspNode::BspNode(BspInterior *parent): mParent(parent), mPolygons(NULL)//,mViewCellIdx(0)
52{}
53
54BspNode::~BspNode()
55{
56        if (mPolygons)
57        {
58                CLEAR_CONTAINER(*mPolygons);
59                DEL_PTR(mPolygons);
60        }
61}
62
63bool BspNode::IsRoot() const
64{
65        return mParent == NULL;
66}
67
68BspInterior *BspNode::GetParent()
69{
70        return mParent;
71}
72
73void BspNode::SetParent(BspInterior *parent)
74{
75        mParent = parent;
76}
77
78PolygonContainer *BspNode::GetPolygons()
79{
80        if (!mPolygons)
81                mPolygons = new PolygonContainer();
82
83        return mPolygons;
84}
85
86void BspNode::ProcessPolygons(PolygonContainer *polys, bool storePolys)
87{
88        if (storePolys)
89        {
90                while (!polys->empty())
91                {
92                        GetPolygons()->push_back(polys->back());
93                        polys->pop_back();
94                }
95        }
96        else CLEAR_CONTAINER(*polys);
97}
98
99
100/****************************************************************/
101/*              class BspInterior implementation                */
102/****************************************************************/
103
104
105BspInterior::BspInterior(const Plane3 &plane):
106mPlane(plane), mFront(NULL), mBack(NULL)
107{}
108
109bool BspInterior::IsLeaf() const
110{
111        return false;
112}
113
114BspNode *BspInterior::GetBack()
115{
116        return mBack;
117}
118
119BspNode *BspInterior::GetFront()
120{
121        return mFront;
122}
123
124Plane3 *BspInterior::GetPlane()
125{
126        return &mPlane;
127}
128
129void BspInterior::ReplaceChildLink(BspNode *oldChild, BspNode *newChild)
130{
131        if (mBack == oldChild)
132                mBack = newChild;
133        else
134                mFront = newChild;
135}
136
137void BspInterior::SetupChildLinks(BspNode *b, BspNode *f)
138{
139    mBack = b;
140    mFront = f;
141}
142
143void BspInterior::ProcessPolygon(Polygon3 **poly, const bool storePolys)
144{
145        if (storePolys)
146                GetPolygons()->push_back(*poly);
147        else
148                DEL_PTR(*poly);
149}
150
151void BspInterior::SplitPolygons(PolygonContainer *polys,
152                                                                PolygonContainer *frontPolys,
153                                                                PolygonContainer *backPolys,
154                                                                PolygonContainer *coincident,
155                                                                int &splits,
156                                                                bool storePolys)
157{
158        Polygon3 *splitPoly = NULL;
159#ifdef _Debug
160        Debug << "splitting polygons of node " << this << " with plane " << mPlane << endl;
161#endif
162        while (!polys->empty())
163        {
164                Polygon3 *poly = polys->back();
165                polys->pop_back();
166
167                //Debug << "New polygon with plane: " << poly->GetSupportingPlane() << "\n";
168
169                // get polygon classification
170                int classification = poly->ClassifyPlane(mPlane);
171
172                Polygon3 *front_piece = NULL;
173                Polygon3 *back_piece = NULL;
174       
175                switch (classification)
176                {
177                        case Polygon3::COINCIDENT:
178                                //Debug << "coincident" << endl;       
179                                coincident->push_back(poly);
180                                break;                 
181                        case Polygon3::FRONT_SIDE:     
182                                //Debug << "front" << endl;
183                                frontPolys->push_back(poly);
184                                break;
185                        case Polygon3::BACK_SIDE:
186                                //Debug << "back" << endl;
187                                backPolys->push_back(poly);
188                                break;
189                        case Polygon3::SPLIT:
190                                front_piece = new Polygon3(poly->mParent);
191                                back_piece = new Polygon3(poly->mParent);
192
193                                //-- split polygon into front and back part
194                                poly->Split(&mPlane, front_piece, back_piece);
195
196                                ++ splits; // increase number of splits
197
198                                // check if polygons still valid
199                                if (front_piece->Valid())
200                                        frontPolys->push_back(front_piece);
201                                else
202                                        DEL_PTR(front_piece);
203                               
204                                if (back_piece->Valid())
205                                        backPolys->push_back(back_piece);
206                                else                           
207                                        DEL_PTR(back_piece);
208                               
209#ifdef _DEBUG
210                                Debug << "split " << *poly << endl << *front_piece << endl << *back_piece << endl;
211#endif
212                                ProcessPolygon(&poly, storePolys);
213                               
214                                break;
215                        default:
216                Debug << "SHOULD NEVER COME HERE\n";
217                                break;
218                }
219        }
220}
221
222/****************************************************************/
223/*                  class BspLeaf implementation                */
224/****************************************************************/
225BspLeaf::BspLeaf(): mViewCell(NULL)
226{
227}
228
229BspLeaf::BspLeaf(ViewCell *viewCell): mViewCell(viewCell)
230{
231}
232
233BspLeaf::BspLeaf(BspInterior *parent): BspNode(parent), mViewCell(NULL)
234{}
235
236BspLeaf::BspLeaf(BspInterior *parent, ViewCell *viewCell):
237BspNode(parent), mViewCell(viewCell)
238{
239}
240ViewCell *BspLeaf::GetViewCell()
241{
242        return mViewCell;
243}
244
245void BspLeaf::SetViewCell(ViewCell *viewCell)
246{
247        mViewCell = viewCell;
248}
249
250bool BspLeaf::IsLeaf() const
251{
252        return true;
253}
254
255/****************************************************************/
256/*                  class BspTree implementation                */
257/****************************************************************/
258
259BspTree::BspTree(ViewCell *viewCell):
260mRoot(NULL),
261mViewCell(viewCell),
262mStoreSplitPolys(true)
263//mStoreSplitPolys(false)
264{
265        Randomize(); // initialise random generator for heuristics
266}
267 
268const BspTreeStatistics &BspTree::GetStatistics() const
269{
270        return mStat;
271}
272
273void BspTreeStatistics::Print(ostream &app) const
274{
275        app << "===== BspTree statistics ===============\n";
276
277        app << setprecision(4);
278
279        app << "#N_RAYS Number of rays )\n"
280                << rays << endl;
281        app << "#N_DOMAINS  ( Number of query domains )\n"
282                << queryDomains <<endl;
283
284        app << "#N_NODES ( Number of nodes )\n" << nodes << "\n";
285
286        app << "#N_INTERIORS ( Number of interior nodes )\n" << Interior() << "\n";
287
288        app << "#N_LEAVES ( Number of leaves )\n" << Leaves() << "\n";
289
290        app << "#N_SPLITS ( Number of splits )\n" << splits << "\n";
291
292        app << "#N_RAYREFS ( Number of rayRefs )\n" <<
293        rayRefs << "\n";
294
295        app << "#N_RAYRAYREFS  ( Number of rayRefs / ray )\n" <<
296        rayRefs/(double)rays << "\n";
297
298        app << "#N_LEAFRAYREFS  ( Number of rayRefs / leaf )\n" <<
299        rayRefs/(double)Leaves() << "\n";
300
301        app << "#N_MAXOBJECTREFS  ( Max number of rayRefs / leaf )\n" <<
302        maxObjectRefs << "\n";
303
304        app << "#N_NONEMPTYRAYREFS  ( Number of rayRefs in nonEmpty leaves / non empty leaf )\n" <<
305        rayRefsNonZeroQuery/(double)(Leaves() - zeroQueryNodes) << "\n";
306
307        app << "#N_LEAFDOMAINREFS  ( Number of query domain Refs / leaf )\n" <<
308        objectRefs/(double)Leaves() << "\n";
309
310        app << "#N_PEMPTYLEAVES  ( Percentage of leaves with zero query domains )\n"<<
311        zeroQueryNodes*100/(double)Leaves() << endl;
312
313        app << "#N_PMAXDEPTHLEAVES ( Percentage of leaves at maxdepth )\n"<<
314        maxDepthNodes * 100 / (double)Leaves() << endl;
315
316        app << "#N_PMAXDEPTH ( Maximal reached depth )\n" << maxDepth << endl;
317
318        app << "#N_PMINDEPTH ( Minimal reached depth )\n" << minDepth << endl;
319
320        app << "#AVGDEPTH ( average depth )\n" << AvgDepth() << endl;
321
322        app << "#N_ADDED_RAYREFS  ( Number of dynamically added ray references )\n"<<
323        addedRayRefs << endl;
324
325        app << "#N_REMOVED_RAYREFS  ( Number of dynamically removed ray references )\n" <<
326        removedRayRefs << endl;
327
328        app << "#N_INPUT_POLYGONS (number of input polygons )\n" <<
329                polys << endl;
330
331        app << "#N_ROUTPUT_INPUT_POLYGONS ( ratio polygons after subdivision / input polygons )\n" <<
332                 (polys + splits) / (double)polys << endl;
333       
334        app << "===== END OF BspTree statistics ==========\n";
335}
336
337BspTree::~BspTree()
338{
339        std::stack<BspNode *> tStack;
340
341        tStack.push(mRoot);
342
343        while (!tStack.empty())
344        {
345                BspNode *node = tStack.top();
346
347            tStack.pop();
348       
349                if (!node->IsLeaf())
350                {
351                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
352
353                        // push the children on the stack (there are always two children)
354                        tStack.push(interior->GetBack());
355                        tStack.push(interior->GetFront());
356                }
357
358                DEL_PTR(node);
359        }
360}
361
362void BspTree::InsertViewCell(ViewCell *viewCell)
363{
364        PolygonContainer *polys = new PolygonContainer();
365
366        // extract polygons that guide the split process
367        mStat.polys += AddMesh2Polygons(viewCell->GetMesh(), *polys);
368        mBox.Include(viewCell->GetBox()); // add to BSP aabb
369
370        InsertPolygons(polys);
371}
372
373void BspTree::InsertPolygons(PolygonContainer *polys, ViewCellContainer *viewCells)
374{       
375        std::stack<BspTraversalData> tStack;
376               
377        // traverse tree or create new one
378        BspNode *firstNode = mRoot ? mRoot : new BspLeaf();
379
380        tStack.push(BspTraversalData(firstNode, polys, 0, mViewCell));
381
382        while (!tStack.empty())
383        {
384                // filter polygons donw the tree
385                BspTraversalData tData = tStack.top();
386            tStack.pop();
387                       
388                if (!tData.mNode->IsLeaf())
389                {
390                        BspInterior *interior = dynamic_cast<BspInterior *>(tData.mNode);
391
392                        //-- filter view cell polygons down the tree until a leaf is reached
393                        if ((int)tData.mPolygons->size() > 0)
394                        {
395                                PolygonContainer *frontPolys = new PolygonContainer();
396                                PolygonContainer *backPolys = new PolygonContainer();
397                                PolygonContainer coincident;
398
399                                int splits = 0;
400               
401                                // split viecell polygons with respect to split plane
402                                interior->SplitPolygons(tData.mPolygons,
403                                                                                frontPolys,
404                                                                                backPolys,
405                                                                                &coincident,
406                                                                                splits,
407                                                                                mStoreSplitPolys);
408                               
409                                // extract view cells associated with the split polygons
410                                ViewCell *frontViewCell = mViewCell;
411                                ViewCell *backViewCell = mViewCell;
412       
413                                if (!viewCells)
414                                {
415                                        ExtractViewCells(&backViewCell,
416                                                                         &frontViewCell,
417                                                                         coincident,
418                                                                         interior->mPlane,
419                                                                         frontPolys->empty(),
420                                                                         backPolys->empty());
421                                }
422
423                                // don't need coincident polygons anymore
424                                interior->ProcessPolygons(&coincident, mStoreSplitPolys);
425
426                                //Debug << "split node on level " << tData.mDepth << " bk: " << backPolys->size() << " frnt: " << frontPolys->size() << endl;
427                                mStat.splits += splits;
428
429                                // push the children on the stack
430                                tStack.push(BspTraversalData(interior->GetFront(), frontPolys, tData.mDepth + 1, frontViewCell));
431                                tStack.push(BspTraversalData(interior->GetBack(), backPolys, tData.mDepth + 1, backViewCell));
432                        }
433
434                        // cleanup
435                        DEL_PTR(tData.mPolygons);
436                }
437                else // reached leaf => subdivide current viewcell
438                {
439                        //if (tData.mPolygons->size() > 0)
440                        //      Debug << "Subdividing further: polygon size: " << (int)tData.mPolygons->size() << ", view cell: " << tData.mViewCell << endl;
441
442                        BspNode *root = Subdivide(tStack, tData, viewCells);           
443
444                        if (!mRoot) // tree empty => new root
445                                mRoot = root;
446                }
447        }
448}
449
450int BspTree::AddMesh2Polygons(Mesh *mesh, PolygonContainer &polys, Intersectable *parent)
451{
452        FaceContainer::const_iterator fi;
453        int polysNum = 0;
454
455        // copy the face data to polygons
456        for (fi = mesh->mFaces.begin(); fi != mesh->mFaces.end(); ++ fi)
457        {
458                Polygon3 *poly = new Polygon3((*fi), mesh);
459                poly->mParent = parent; // set parent intersectable
460                polys.push_back(poly);
461
462                if (!poly->Valid())
463                        Debug << "Input polygon not valid: " << *poly
464                                  << endl << "Area: " << poly->GetArea() << endl;
465               
466                ++ polysNum;
467        }
468        return polysNum;
469}
470
471int BspTree::Copy2PolygonSoup(const ViewCellContainer &viewCells, PolygonContainer &polys, int maxObjects)
472{
473        int limit = (maxObjects > 0) ? Min((int)viewCells.size(), maxObjects) : (int)viewCells.size();
474 
475        for (int i = 0; i < limit; ++i)
476        {//if ((i == 12) ||(i==14))
477                if (viewCells[i]->GetMesh()) // copy the mesh data to polygons
478                {
479                        mBox.Include(viewCells[i]->GetBox()); // add to BSP tree aabb
480                        AddMesh2Polygons(viewCells[i]->GetMesh(), polys, viewCells[i]);
481                }
482        }
483
484        return (int)polys.size();
485}
486
487int BspTree::Copy2PolygonSoup(const ObjectContainer &objects, PolygonContainer &polys, int maxObjects)
488{
489        int limit = (maxObjects > 0) ? Min((int)objects.size(), maxObjects) : (int)objects.size();
490 
491        for (int i = 0; i < limit; ++i)
492        {
493                Intersectable *object = objects[i];//*it;
494                Mesh *mesh = NULL;
495
496                switch (object->Type()) // extract the meshes
497                {
498                case Intersectable::MESH_INSTANCE:
499                        mesh = dynamic_cast<MeshInstance *>(object)->GetMesh();
500                        break;
501                case Intersectable::VIEWCELL:
502                        mesh = dynamic_cast<ViewCell *>(object)->GetMesh();
503                        break;
504                        // TODO: handle transformed mesh instances
505                default:
506                        Debug << "intersectable type not supported" << endl;
507                        break;
508                }
509               
510        if (mesh) // copy the mesh data to polygons
511                {
512                        mBox.Include(object->GetBox()); // add to BSP tree aabb
513                        AddMesh2Polygons(mesh, polys);
514                }
515        }
516
517        return (int)polys.size();
518}
519
520void BspTree::Construct(const ViewCellContainer &viewCells)
521{
522        mStat.nodes = 1;
523        mBox.Initialize();      // initialise bsp tree bounding box
524
525        // copy view cell meshes into one big polygon soup
526        PolygonContainer *polys = new PolygonContainer();
527        mStat.polys = Copy2PolygonSoup(viewCells, *polys);
528
529        // construct tree from viewcell polygons
530        Construct(polys);
531}
532
533
534void BspTree::Construct(const ObjectContainer &objects, ViewCellContainer *viewCells)
535{
536        mStat.nodes = 1;
537        mBox.Initialize();      // initialise bsp tree bounding box
538       
539        PolygonContainer *polys = new PolygonContainer();
540       
541        // copy mesh instance polygons into one big polygon soup
542        mStat.polys = Copy2PolygonSoup(objects, *polys);
543
544        bool tmpStorePolys = mStoreSplitPolys;
545        mStoreSplitPolys = false;
546
547        // construct tree from polygon soup
548        Construct(polys, viewCells);
549
550        mStoreSplitPolys = tmpStorePolys;
551        // filter scene bounding box down the tree in order to store split polygons
552        if (mStoreSplitPolys)
553        {
554                Mesh *polyMesh = new Mesh();
555
556                for (int i = 0; i < 6; ++i)
557                        polyMesh->AddRectangle(mBox.GetFace(i));
558
559                PolygonContainer *polys = new PolygonContainer();
560                                AddMesh2Polygons(polyMesh, *polys, NULL);
561               
562                InsertPolygons(polys);
563
564                delete polyMesh;
565        }
566}
567
568void BspTree::Construct(const RayContainer &rays, ViewCellContainer *viewCells)
569{
570        // TODO
571}
572
573void BspTree::Construct(PolygonContainer *polys, ViewCellContainer *viewCells)
574{
575        std::stack<BspTraversalData> tStack;
576       
577        BspTraversalData tData(new BspLeaf(), polys, 0, mViewCell);
578
579        tStack.push(tData);
580
581        long startTime = GetTime();
582        Debug << "**** Contructing tree using objects ****\n";
583        while (!tStack.empty())
584        {
585                tData = tStack.top();
586            tStack.pop();
587
588                // subdivide leaf node
589                BspNode *root = Subdivide(tStack, tData, viewCells);
590
591                // empty tree => new root corresponding to unbounded space
592                if (!mRoot)
593                        mRoot = root;
594        }
595
596        Debug << "**** Finished tree construction ****\n";
597        Debug << "BSP tree contruction time: " << TimeDiff(startTime, GetTime())*1e-3 << "s" << endl;
598}
599
600
601BspNode *BspTree::Subdivide(BspTraversalStack &tStack, BspTraversalData &tData,
602                                                        ViewCellContainer *viewCells)
603{
604        //-- terminate traversal 
605        if ((tData.mPolygons->size() <= sTermMaxPolygons) || (tData.mDepth >= sTermMaxDepth))
606        {
607#ifdef _DEBUG
608                Debug << "subdivision terminated at depth " << tData.mDepth << ", #polys: " << (int)tData.mPolygons->size() << endl;
609#endif
610
611                EvaluateLeafStats(tData);
612
613                BspLeaf *leaf = dynamic_cast<BspLeaf *>(tData.mNode);
614
615                //-- new viewcells are generated and added to each leaf
616                if (viewCells)
617                {
618                        ViewCell *viewCell = new ViewCell();
619                        leaf->SetViewCell(viewCell);
620                        viewCells->push_back(viewCell);
621                        // Debug << "creating new viewcell" << endl;
622                }
623                //-- add viewcell stored in split polygon
624                else if (leaf->GetViewCell())
625                        leaf->SetViewCell(tData.mViewCell);
626                //else Debug << "Leaf already has view cell " << endl;
627
628                // remaining polygons are discarded or added to node
629                tData.mNode->ProcessPolygons(tData.mPolygons, mStoreSplitPolys);
630                DEL_PTR(tData.mPolygons);
631
632                return tData.mNode;
633        }
634
635        //-- continue subdivision
636        PolygonContainer *backPolys = new PolygonContainer();
637        PolygonContainer *frontPolys = new PolygonContainer();
638        PolygonContainer coincident;
639       
640        // create new interior node and two leaf nodes
641        BspInterior *interior = SubdivideNode(dynamic_cast<BspLeaf *>(tData.mNode),
642                                                                                  tData.mPolygons,
643                                                                                  frontPolys,
644                                                                                  backPolys,
645                                                                                  &coincident);
646
647        //-- extract view cells from coincident polygons according to plane normal
648        ViewCell *frontViewCell = mViewCell;
649        ViewCell *backViewCell = mViewCell;
650       
651    if (!viewCells)
652        {       // extract the pointer to the view cells
653                ExtractViewCells(&backViewCell,
654                                                 &frontViewCell,
655                                                 coincident,
656                                                 interior->mPlane,
657                                                 frontPolys->empty(),
658                                                 backPolys->empty());
659        }
660       
661        // don't need coincident polygons anymore
662        interior->ProcessPolygons(&coincident, mStoreSplitPolys);
663
664        // push the children on the stack
665        // split polygon is only needed in the back leaf
666        tStack.push(BspTraversalData(interior->GetFront(), frontPolys, tData.mDepth + 1, frontViewCell));
667        tStack.push(BspTraversalData(interior->GetBack(), backPolys, tData.mDepth + 1, backViewCell));
668
669        // cleanup
670        DEL_PTR(tData.mNode);
671        DEL_PTR(tData.mPolygons);
672
673        return interior;
674}
675
676void BspTree::ExtractViewCells(ViewCell **backViewCell,
677                                                           ViewCell **frontViewCell,
678                                                           const PolygonContainer &coincident,
679                                                           const Plane3 splitPlane,
680                                                           const bool extractFront,
681                                                           const bool extractBack) const
682{
683        bool foundFront = !extractFront, foundBack = !extractBack;
684
685        PolygonContainer::const_iterator it, it_end = coincident.end();
686
687        for (it = coincident.begin(); !(foundFront && foundBack) &&
688                (it < coincident.end()); ++it)
689        {
690                if (DotProd((*it)->GetSupportingPlane().mNormal, splitPlane.mNormal > 0))
691                {
692                        *backViewCell = dynamic_cast<ViewCell *>((*it)->mParent);
693                        foundBack = true;
694                }
695                else
696                {
697                        *frontViewCell = dynamic_cast<ViewCell *>((*it)->mParent);
698                        foundFront = true;
699                }
700                //if (foundFront) Debug << "front viewCell: " << *frontViewCell << endl;
701                //if (foundBack) Debug << "back viewCell: " << *backViewCell << endl;
702        }
703}
704
705BspInterior *BspTree::SubdivideNode(BspLeaf *leaf,
706                                                                        PolygonContainer *polys,
707                                                                        PolygonContainer *frontPolys,
708                                                                        PolygonContainer *backPolys,
709                                                                        PolygonContainer *coincident)
710{
711        mStat.nodes += 2;
712
713        // add the new nodes to the tree + select subdivision plane
714        BspInterior *interior = new BspInterior(SelectPlane(leaf, *polys));
715
716#ifdef _DEBUG
717        Debug << interior << endl;
718#endif
719
720        // split polygon according to current plane
721        int splits = 0;
722       
723        interior->SplitPolygons(polys, frontPolys, backPolys, coincident, splits, mStoreSplitPolys);
724       
725        mStat.splits += splits;
726
727        BspInterior *parent = leaf->GetParent();
728
729        // replace a link from node's parent
730        if (parent)
731        {
732                parent->ReplaceChildLink(leaf, interior);
733                interior->SetParent(parent);
734        }
735
736        // and setup child links
737        interior->SetupChildLinks(new BspLeaf(interior), new BspLeaf(interior));
738       
739        return interior;
740}
741
742void BspTree::SortSplitCandidates(const PolygonContainer &polys,
743                                                                  const int axis,
744                                                                  vector<SortableEntry> &splitCandidates) const
745{
746  splitCandidates.clear();
747 
748  int requestedSize = 2 * (int)polys.size();
749  // creates a sorted split candidates array 
750  splitCandidates.reserve(requestedSize);
751 
752  PolygonContainer::const_iterator it, it_end = polys.end();
753
754  AxisAlignedBox3 box;
755
756  // insert all queries
757  for(it = polys.begin(); it != it_end; ++ it)
758  {
759          box.Initialize();
760          box.Include(*(*it));
761         
762          splitCandidates.push_back(SortableEntry(SortableEntry::POLY_MIN, box.Min(axis), *it));
763      splitCandidates.push_back(SortableEntry(SortableEntry::POLY_MAX, box.Max(axis), *it));
764  }
765 
766  stable_sort(splitCandidates.begin(), splitCandidates.end());
767}
768
769
770float BspTree::BestCostRatio(const PolygonContainer &polys,
771                                                         const AxisAlignedBox3 &box,
772                                                         const int axis,
773                                                         float &position,
774                                                         int &objectsBack,
775                                                         int &objectsFront) const
776{
777        vector<SortableEntry> splitCandidates;
778
779        SortSplitCandidates(polys, axis, splitCandidates);
780       
781        // go through the lists, count the number of objects left and right
782        // and evaluate the following cost funcion:
783        // C = ct_div_ci  + (ol + or)/queries
784       
785        int objectsLeft = 0, objectsRight = (int)polys.size();
786       
787        float minBox = box.Min(axis);
788        float maxBox = box.Max(axis);
789        float boxArea = box.SurfaceArea();
790 
791        float minBand = minBox + sSplitBorder * (maxBox - minBox);
792        float maxBand = minBox + (1.0f - sSplitBorder) * (maxBox - minBox);
793       
794        float minSum = 1e20f;
795        vector<SortableEntry>::const_iterator ci, ci_end = splitCandidates.end();
796
797        for(ci = splitCandidates.begin(); ci != ci_end; ++ ci)
798        {
799                switch ((*ci).type)
800                {
801                        case SortableEntry::POLY_MIN:
802                                ++ objectsLeft;
803                                break;
804                        case SortableEntry::POLY_MAX:
805                            -- objectsRight;
806                                break;
807                        default:
808                                break;
809                }
810               
811                if ((*ci).value > minBand && (*ci).value < maxBand)
812                {
813                        AxisAlignedBox3 lbox = box;
814                        AxisAlignedBox3 rbox = box;
815                        lbox.SetMax(axis, (*ci).value);
816                        rbox.SetMin(axis, (*ci).value);
817
818                        float sum = objectsLeft * lbox.SurfaceArea() +
819                                                objectsRight * rbox.SurfaceArea();
820     
821                        if (sum < minSum)
822                        {
823                                minSum = sum;
824                                position = (*ci).value;
825
826                                objectsBack = objectsLeft;
827                                objectsFront = objectsRight;
828                        }
829                }
830        }
831 
832        float oldCost = (float)polys.size();
833        float newCost = sCt_div_ci + minSum / boxArea;
834        float ratio = newCost / oldCost;
835
836
837#if 0
838  Debug << "====================" << endl;
839  Debug << "costRatio=" << ratio << " pos=" << position<<" t=" << (position - minBox)/(maxBox - minBox)
840      << "\t o=(" << objectsBack << "," << objectsFront << ")" << endl;
841#endif
842  return ratio;
843}
844
845Plane3 BspTree::SelectPlane(BspLeaf *leaf, const PolygonContainer &polys)  const
846{
847        if (polys.size() == 0)
848        {
849                Debug << "Warning: No split candidate available\n";
850                return Plane3();
851        }
852
853        if ((sSplitPlaneStrategy & AXIS_ALIGNED) &&
854                ((int)polys.size() > sTermMaxPolysForAxisAligned))
855        {
856                AxisAlignedBox3 box;
857                box.Initialize();
858       
859                // todo: area subdivision
860                Polygon3::IncludeInBox(polys, box);
861       
862                int objectsBack = 0, objectsFront = 0;
863                int axis = 0;
864                float costRatio = MAX_FLOAT;
865                Vector3 position;
866
867                for (int i = 0; i < 3; ++ i)
868                {
869                        float p = 0;
870                        float r = BestCostRatio(polys, box, i, p, objectsBack, objectsFront);
871                       
872                        if (r < costRatio)
873                        {
874                                costRatio = r;
875                                axis = i;
876                                position = p;
877                        }
878                }
879               
880                if (costRatio < sMaxCostRatio)
881                {
882                        Vector3 norm(0,0,0); norm[axis] = 1.0f;
883                        return Plane3(norm, position);
884                }
885                /*int axis = box.Size().DrivingAxis();
886                Vector3 norm(0,0,0); norm[axis] = 1;
887                Vector3 position = (box.Min()[axis] + box.Max()[axis]) * 0.5f;
888                return Plane3(norm, position);*/
889        }
890
891        // simplest strategy: just take next polygon
892        if (sSplitPlaneStrategy & NEXT_POLYGON)
893        {
894                return polys.front()->GetSupportingPlane();
895        }
896
897        // use heuristics to find appropriate plane
898        return SelectPlaneHeuristics(polys, sMaxCandidates);
899}
900
901Plane3 BspTree::SelectPlaneHeuristics(const PolygonContainer &polys, int maxTests) const
902{
903        float lowestCost = MAX_FLOAT;
904        int bestPlaneIdx = 0;
905       
906        int limit = maxTests > 0 ? Min((int)polys.size(), maxTests) : (int)polys.size();
907       
908        int candidateIdx = limit;
909
910        for (int i = 0; i < limit; ++ i)
911        {
912                candidateIdx = GetNextCandidateIdx(candidateIdx, polys, (int)polys.size() - i - i);
913
914                Plane3 candidatePlane = polys[candidateIdx]->GetSupportingPlane();
915               
916               
917                // evaluate current candidate
918                float candidateCost = EvalSplitPlane(polys, candidatePlane);
919                       
920                if (candidateCost < lowestCost)
921                {
922                        bestPlaneIdx = candidateIdx;
923                        lowestCost = candidateCost;
924                }
925        }
926        //Debug << "Plane lowest cost: " << lowestCost << endl;
927       
928        return polys[bestPlaneIdx]->GetSupportingPlane();
929}
930
931int BspTree::GetNextCandidateIdx(int currentIdx, PolygonContainer &polys, int max) const
932{
933#if 0
934        int candidateIdx = Random(max); // TODO: avoid testing same plane 2 times
935
936        // swap candidates
937        Polygon *p = polys[candidateIdx];
938        polys[candidateIdx] = polys[max];
939        polys[max] = p;
940
941        return max;
942#else
943        return ++ currentIdx;
944#endif
945}
946
947float BspTree::EvalSplitPlane(const PolygonContainer &polys, const Plane3 &candidatePlane) const
948{
949        float val = 0;
950       
951        if (sSplitPlaneStrategy & VERTICAL_AXIS)
952        {
953                Vector3 tinyAxis(0,0,0); tinyAxis[mBox.Size().TinyAxis()] = 1.0f;
954                // we put a penalty on the dot product between the "tiny" vertical axis
955                // and the split plane axis
956                // NOTE: weight with poly size because vertical split more important if too many polygons
957                val += sVerticalSplitsFactor *
958                           fabs(DotProd(candidatePlane.mNormal, tinyAxis)) * (float)polys.size();
959        }
960        //-- strategies where the effect of the split plane on the polygons is tested
961        if ((sSplitPlaneStrategy & BALANCED_POLYS)     ||
962                (sSplitPlaneStrategy & LEAST_SPLITS)       ||
963                (sSplitPlaneStrategy & LARGEST_POLY_AREA))
964        {
965                PolygonContainer::const_iterator it, it_end = polys.end();
966                float sumBalancedPolys = 0;
967                float sumSplits = 0;
968
969                for (it = polys.begin(); it != it_end; ++ it)
970                {
971                        int classification = (*it)->ClassifyPlane(candidatePlane);
972
973                        if (sSplitPlaneStrategy & BALANCED_POLYS)
974                                sumBalancedPolys += sBalancedPolysTable[classification];
975                               
976                        if (sSplitPlaneStrategy & LEAST_SPLITS)
977                                sumSplits += sLeastSplitsTable[classification];
978
979                        if (sSplitPlaneStrategy & LARGEST_POLY_AREA)
980                        {
981                                if (classification == Polygon3::COINCIDENT)
982                                {       
983                                        float area = (*it)->GetArea();
984                                        //Debug << "polygon area: " << area << " ";
985                                        if (area) val += 100.0f/area;
986                                }
987                        }
988                }
989                val += sBalancedPolysFactor * fabs(sumBalancedPolys) +
990                           sLeastSplitsFactor * sumSplits;
991        }
992       
993        if (sSplitPlaneStrategy & BALANCED_VIEW_CELLS)
994        {
995                // store view cells
996                ObjectContainer frontViewCells;
997                ObjectContainer backViewCells;
998
999                float sumBalancedViewCells = 0;
1000
1001                PolygonContainer::const_iterator it, it_end = polys.end();
1002               
1003                for (it = polys.begin(); it != it_end; ++ it)
1004                {
1005                        int classification = (*it)->ClassifyPlane(candidatePlane);
1006                        Intersectable *viewCell = (*it)->mParent;
1007                       
1008                        if (viewCell && (classification == Polygon3::FRONT_SIDE))
1009                                frontViewCells.push_back(viewCell);
1010                        else if (viewCell && (classification == Polygon3::BACK_SIDE))
1011                                backViewCells.push_back(viewCell);
1012                }
1013
1014                // count number of unique view cells
1015                sort(frontViewCells.begin(), frontViewCells.end());
1016                sort(backViewCells.begin(), backViewCells.end());
1017
1018                ObjectContainer::const_iterator frontIt, frontIt_end = frontViewCells.end();
1019               
1020                Intersectable *intersect = NULL;
1021                // increase counter for view cells in front of plane
1022        for (frontIt = frontViewCells.begin(); frontIt != frontIt_end; ++frontIt)
1023                {
1024                        if (*frontIt != intersect)
1025                        {
1026                                intersect = *frontIt;
1027                                sumBalancedViewCells += 1.0f;
1028                        }
1029                }
1030
1031                ObjectContainer::const_iterator backIt, backIt_end = backViewCells.end();
1032                intersect = NULL;
1033                // decrease counter for view cells on back side of plane
1034        for (backIt = backViewCells.begin(); backIt != backIt_end; ++backIt)
1035                {
1036                        if (*backIt != intersect)
1037                        {
1038                                intersect = *backIt;
1039                                sumBalancedViewCells -= 1;
1040                        }
1041                }
1042
1043                 val += sBalancedViewCellsFactor * fabs(sumBalancedViewCells);
1044        }
1045
1046        // return linear combination of the sums
1047        return val;
1048}
1049
1050void BspTree::ParseEnvironment()
1051{
1052        environment->GetIntValue("BspTree.Termination.maxDepth", sTermMaxDepth);
1053        environment->GetIntValue("BspTree.Termination.maxPolygons", sTermMaxPolygons);
1054        environment->GetIntValue("BspTree.Termination.maxPolysForAxisAligned",
1055                                                         sTermMaxPolysForAxisAligned);
1056        environment->GetIntValue("BspTree.maxCandidates", sMaxCandidates);
1057        environment->GetIntValue("BspTree.splitPlaneStrategy", sSplitPlaneStrategy);
1058
1059        // need kdtree criteria for axis aligned split
1060        environment->GetFloatValue("MeshKdTree.Termination.ct_div_ci", sCt_div_ci);
1061        environment->GetFloatValue("MeshKdTree.splitBorder", sSplitBorder);
1062        environment->GetFloatValue("MeshKdTree.Termination.maxCostRatio", sMaxCostRatio);
1063
1064    Debug << "BSP max depth: " << sTermMaxDepth << endl;
1065        Debug << "BSP max polys: " << sTermMaxPolygons << endl;
1066        Debug << "BSP max candidates: " << sMaxCandidates << endl;
1067       
1068        Debug << "Split plane strategy: ";
1069        if (sSplitPlaneStrategy & NEXT_POLYGON)
1070                Debug << "next polygon ";
1071        if (sSplitPlaneStrategy & AXIS_ALIGNED)
1072                Debug << "axis aligned ";
1073        if (sSplitPlaneStrategy & LEAST_SPLITS)     
1074                Debug << "least splits ";
1075        if (sSplitPlaneStrategy & BALANCED_POLYS)
1076                Debug << "balanced polygons ";
1077        if (sSplitPlaneStrategy & BALANCED_VIEW_CELLS)
1078                Debug << "balanced view cells ";
1079        if (sSplitPlaneStrategy & LARGEST_POLY_AREA)
1080                Debug << "largest polygon area ";
1081        if (sSplitPlaneStrategy & VERTICAL_AXIS)
1082                Debug << "vertical axis ";
1083
1084        Debug << endl;
1085
1086        //-- parse BSP tree construction method
1087        char constructionMethodStr[60];
1088       
1089        environment->GetStringValue("BspTree.constructionMethod", constructionMethodStr);
1090
1091        sConstructionMethod = BspTree::VIEW_CELLS;
1092       
1093        if (strcmp(constructionMethodStr, "viewCells") == 0)
1094                sConstructionMethod = BspTree::VIEW_CELLS;
1095        else if (strcmp(constructionMethodStr, "sceneGeometry") == 0)
1096                sConstructionMethod = BspTree::SCENE_GEOMETRY;
1097        else if (strcmp(constructionMethodStr, "rays") == 0)
1098                sConstructionMethod = BspTree::RAYS;
1099        else
1100        {
1101                cerr << "Wrong bsp construction method " << constructionMethodStr << endl;
1102                exit(1);
1103    }
1104
1105        Debug << "Construction method: " << constructionMethodStr << endl;
1106}
1107
1108void BspTree::CollectLeaves(vector<BspLeaf *> &leaves)
1109{
1110        stack<BspNode *> nodeStack;
1111        nodeStack.push(mRoot);
1112 
1113        while (!nodeStack.empty())
1114        {
1115                BspNode *node = nodeStack.top();
1116   
1117                nodeStack.pop();
1118   
1119                if (node->IsLeaf())
1120                {
1121                        BspLeaf *leaf = (BspLeaf *)node;
1122                       
1123                        leaves.push_back(leaf);
1124                } else
1125                {
1126                        BspInterior *interior = dynamic_cast<BspInterior *>(node);
1127                        nodeStack.push(interior->GetBack());
1128                        nodeStack.push(interior->GetFront());
1129                }
1130        }
1131}
1132
1133int BspTree::CollectLeafPvs()
1134{
1135        int totalPvsSize = 0;
1136 
1137        stack<BspNode *> nodeStack;
1138 
1139        nodeStack.push(mRoot);
1140 
1141        while (!nodeStack.empty())
1142        {
1143                BspNode *node = nodeStack.top();
1144               
1145                nodeStack.pop();
1146               
1147                if (node->IsLeaf())
1148                {
1149                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
1150
1151                        ViewCell *viewcell = leaf->GetViewCell();
1152                       
1153                        if (!viewcell->Mailed())
1154                        {
1155                                viewcell->Mail(); // what does mail mean?
1156                                       
1157                                // add this node to pvs of all nodes it can see
1158                                BspPvsMap::iterator ni;
1159         
1160        /*                      for (ni = object->mBspPvs.mEntries.begin(); ni != object->mKdPvs.mEntries.end(); ni++)
1161                                {
1162                                        BspNode *node = (*ni).first;
1163           
1164                                        // $$ JB TEMPORARY solution -> should add object PVS or explictly computed
1165                                        // BSP tree PVS
1166                                if (leaf->mBspPvs.AddNodeSample(node))
1167                                                totalPvsSize++;
1168                                }*/
1169                        }
1170                } else
1171                {
1172                        // traverse tree
1173                        BspInterior *interior = (BspInterior *)node;
1174     
1175                        nodeStack.push(interior->GetFront());
1176                        nodeStack.push(interior->GetBack());
1177                }
1178        }
1179
1180        return totalPvsSize;
1181}
1182
1183AxisAlignedBox3 BspTree::GetBoundingBox() const
1184{
1185        return mBox;
1186}
1187
1188BspNode *BspTree::GetRoot() const
1189{
1190        return mRoot;
1191}
1192
1193bool BspTree::StorePolys() const
1194{
1195        return mStoreSplitPolys;
1196}
1197
1198void BspTree::EvaluateLeafStats(const BspTraversalData &data)
1199{
1200        // the node became a leaf -> evaluate stats for leafs
1201        BspLeaf *leaf = dynamic_cast<BspLeaf *>(data.mNode);
1202
1203        if (data.mDepth >= sTermMaxDepth)
1204        {
1205                ++ mStat.maxDepthNodes;
1206        }
1207
1208        // store maximal and minimal depth
1209        if (data.mDepth > mStat.maxDepth)
1210                mStat.maxDepth = data.mDepth;
1211        if (data.mDepth < mStat.minDepth)
1212                mStat.minDepth = data.mDepth;
1213
1214        // accumulate depth to compute average
1215        mStat.accumDepth += data.mDepth;
1216
1217        if (leaf->mViewCell)
1218                ++ mStat.viewCellLeaves;
1219
1220#ifdef _DEBUG
1221        Debug << "BSP Traversal data. Depth: " << data.mDepth << " (max: " << mTermMaxDepth << "), #polygons: " <<
1222          data.mPolygons->size() << " (max: " << sTermMaxPolygons << ")" << endl;
1223#endif
1224}
1225
1226int BspTree::CastRay(Ray &ray)
1227{
1228        int hits = 0;
1229 
1230        stack<BspRayTraversalData> tStack;
1231 
1232        float maxt = 1e6;
1233        float mint = 0;
1234
1235        Intersectable::NewMail();
1236
1237        // test with tree bounding box
1238        if (!mBox.GetMinMaxT(ray, &mint, &maxt))
1239                return 0;
1240
1241        if (mint < 0)
1242                mint = 0;
1243 
1244        maxt += Limits::Threshold;
1245 
1246        Vector3 entp = ray.Extrap(mint);
1247        Vector3 extp = ray.Extrap(maxt);
1248 
1249        BspNode *node = mRoot;
1250        BspNode *farChild;
1251       
1252        while (1)
1253        {
1254                if (!node->IsLeaf())
1255                {
1256                        BspInterior *in = (BspInterior *) node;
1257                       
1258                        Plane3 *splitPlane = in->GetPlane();
1259
1260                        float t = 0;
1261                        bool coplanar = false;
1262               
1263                        int entSide = splitPlane->Side(entp);
1264                        int extSide = splitPlane->Side(extp);
1265
1266                        Vector3 intersection;
1267
1268                        if (entSide < 0)
1269                        {
1270                                node = in->GetBack();
1271
1272                                if(extSide <= 0)
1273                                        continue;
1274                                       
1275                                farChild = in->GetFront(); // plane splits ray
1276
1277                        } else if (entSide > 0)
1278                        {
1279                                node = in->GetFront();
1280
1281                                if (extSide >= 0)
1282                                        continue;
1283
1284                                farChild = in->GetBack();        // plane splits ray                   
1285                        }
1286                        else // ray and plane are coincident // WHAT TO DO IN THIS CASE ?
1287                        {
1288                                node = in->GetFront();
1289                                continue;
1290                        }
1291
1292                        //-- split
1293
1294                        // push data for far child
1295                        tStack.push(BspRayTraversalData(farChild, extp, maxt));
1296
1297                        // find intersection with plane between ray origin and exit point
1298                        extp = splitPlane->FindIntersection(ray.GetLoc(), extp, &maxt);
1299
1300                } else // compute intersections with objects in leaf
1301                {
1302                        BspLeaf *leaf = dynamic_cast<BspLeaf *>(node);
1303                        //ray.leaves.push_back(leaf);
1304      // TODO
1305                        /*ObjectContainer::const_iterator mi;
1306                        for (mi = leaf->mObjects.begin(); mi != leaf->mObjects.end(); ++mi)
1307                        {
1308                                Intersectable *object = *mi;
1309                                if (!object->Mailed())
1310                                {
1311                                        object->Mail();
1312                                        //ray.meshes.push_back(mesh);
1313                                        hits += object->CastRay(ray);
1314                                }
1315                        }*/
1316
1317                        if (hits && ray.GetType() == Ray::LOCAL_RAY)
1318                                if (ray.intersections[0].mT <= maxt)
1319                                        break;
1320     
1321                        // get the next node from the stack
1322                        if (tStack.empty())
1323                                break;
1324     
1325                        entp = extp;
1326                        mint = maxt;
1327                        BspRayTraversalData &s  = tStack.top();
1328
1329                        node = s.mNode;
1330                        extp = s.mExitPoint;
1331                        maxt = s.mMaxT;
1332
1333                        tStack.pop();
1334                }
1335        }
1336
1337        return hits;
1338}
1339
1340bool BspTree::Export(const string filename)
1341{
1342        Exporter *exporter = Exporter::GetExporter(filename);
1343
1344        if (exporter)
1345        {
1346                exporter->ExportBspTree(*this);
1347                return true;
1348        }       
1349
1350        return false;
1351}
1352//} // GtpVisibilityPreprocessor
Note: See TracBrowser for help on using the repository browser.