source: GTP/trunk/Lib/Vis/Preprocessing/src/ViewCell.cpp @ 801

Revision 801, 50.8 KB checked in by mattausch, 19 years ago (diff)

debug version for testing subdivision

RevLine 
[235]1#include "ViewCell.h"
2#include "Mesh.h"
[308]3#include "Intersectable.h"
[235]4#include "MeshKdTree.h"
5#include "Triangle3.h"
[580]6#include "common.h"
7#include "Environment.h"
8#include "ViewCellsManager.h"
9#include "Exporter.h"
10
[463]11#include <time.h>
[462]12#include <iomanip>
[580]13#include <stack>
[235]14
[580]15
16
17template <typename T> class myless
18{
19public:
20        bool operator() (T v1, T v2) const
21        {
[600]22                return (v1->GetMergeCost() < v2->GetMergeCost());
[580]23        }
24};
25
26
27typedef priority_queue<ViewCell *, vector<ViewCell *>, myless<vector<ViewCell *>::value_type> > TraversalQueue;
28
29int ViewCell::sMailId = 21843194198;
30int ViewCell::sReservedMailboxes = 1;
[660]31int ViewCell::sLastUpdated = 0;
[580]32
33//int upperPvsLimit = 120;
34//int lowerPvsLimit = 5;
35
36float MergeCandidate::sRenderCostWeight = 0;
37
38
39// pvs penalty can be different from pvs size
[752]40inline static float EvalPvsPenalty(const int pvs, const int lower, const int upper)
[580]41{
42        // clamp to minmax values
[728]43        if (pvs < lower)
[580]44                return (float)lower;
45        if (pvs > upper)
46                return (float)upper;
[752]47
[580]48        return (float)pvs;
49}
50
51
[585]52inline int CountDiffPvs(ViewCell *vc)
53{
54        int count = 0;
55
56        ObjectPvsMap::const_iterator it, it_end = vc->GetPvs().mEntries.end();
57        for (it = vc->GetPvs().mEntries.begin(); it != it_end; ++ it)
58        {
59                if (!(*it).first->Mailed())
60                {
61                        (*it).first->Mail();
62                        ++ count;
63                }
64        }
65
66        return count;
67}
68
[729]69/// Fast computation of merged pvs size
[752]70static int ComputeMergedPvsSize(const ObjectPvs &pvs1, const ObjectPvs &pvs2)
[580]71{
[729]72        // add first pvs
[580]73        int pvs = pvs1.GetSize();
74
75        ObjectPvsMap::const_iterator it, it_end =  pvs1.mEntries.end();
76
77        Intersectable::NewMail();
78
[729]79        // mail all objects in first pvs
[580]80        for (it = pvs1.mEntries.begin(); it != it_end; ++ it)
81        {
82                (*it).first->Mail();
83        }
84
85        it_end = pvs2.mEntries.end();
86
[729]87        // look if they are in second pvs
[580]88        for (it = pvs2.mEntries.begin(); it != it_end; ++ it)
89        {
90                Intersectable *obj = (*it).first;
91                if (!obj->Mailed())
92                        ++ pvs;
93        }
94
95        return pvs;
96}
97
[729]98// computet render cost of merge
[752]99float ViewCellsTree::ComputeMergedPvsCost(const ObjectPvs &pvs1,
100                                                                                  const ObjectPvs &pvs2) const
[729]101{
102        float renderCost = 0;
[580]103
[729]104        // compute new pvs size
105        ObjectPvsMap::const_iterator it, it_end =  pvs1.mEntries.end();
106
107        Intersectable::NewMail();
108
109        // first mail all objects in first pvs
110        for (it = pvs1.mEntries.begin(); it != it_end; ++ it)
111        {
112                Intersectable *obj = (*it).first;
113
114                obj->Mail();
115                renderCost += mViewCellsManager->EvalRenderCost(obj);
116        }
117
118        it_end = pvs2.mEntries.end();
119
120       
121        for (it = pvs2.mEntries.begin(); it != it_end; ++ it)
122        {
123                Intersectable *obj = (*it).first;
124
125                // test if object already considered   
126                if (!obj->Mailed())
127                {
128                        renderCost += mViewCellsManager->EvalRenderCost(obj);
129                }
130        }
131
132        return renderCost;
133}
134
135
[478]136ViewCell::ViewCell():
137MeshInstance(NULL),
138mPiercingRays(0),
[551]139mArea(-1),
140mVolume(-1),
[580]141mValid(true),
142mParent(NULL),
[600]143mMergeCost(0),
[752]144mIsActive(false),
145mPvsSize(0),
146mPvsSizeValid(false)
[265]147{
148}
[235]149
[478]150ViewCell::ViewCell(Mesh *mesh):
151MeshInstance(mesh),
152mPiercingRays(0),
[551]153mArea(-1),
154mVolume(-1),
[580]155mValid(true),
156mParent(NULL),
[600]157mMergeCost(0),
[660]158mIsActive(false),
[752]159mLastUpdated(sLastUpdated),
160mPvsSize(0),
161mPvsSizeValid(false)
[372]162{
163}
164
[503]165
[469]166const ObjectPvs &ViewCell::GetPvs() const
[419]167{
168        return mPvs;
169}
170
[752]171
[469]172ObjectPvs &ViewCell::GetPvs()
[372]173{
174        return mPvs;
175}
176
[503]177
[235]178int ViewCell::Type() const
179{
[308]180        return VIEW_CELL;
[235]181}
182
[503]183
[469]184float ViewCell::GetVolume() const
185{
186        return mVolume;
187}
188
[478]189
[469]190void ViewCell::SetVolume(float volume)
191{
192        mVolume = volume;
193}
194
[503]195
196void ViewCell::SetMesh(Mesh *mesh)
197{
198        mMesh = mesh;
199}
200
201
[478]202float ViewCell::GetArea() const
203{
204        return mArea;
205}
206
207
208void ViewCell::SetArea(float area)
209{
210        mArea = area;
211}
212
213
[752]214void ViewCell::SetColor(const RgbColor &color)
215{
216        mColor = color;
217}
218
219
220RgbColor ViewCell::GetColor() const
221{
222        return mColor;
223}
224
225
[547]226void ViewCell::SetValid(const bool valid)
227{
[564]228        mValid = valid;
[547]229}
230
231
232bool ViewCell::GetValid() const
233{
234        return mValid;
235}
236
237
[580]238void ViewCell::SetParent(ViewCellInterior *parent)
239{
240        mParent = parent;
241}
242
243
244bool ViewCell::IsRoot() const
245{
246        return !mParent;
247}
248
249
250ViewCellInterior *ViewCell::GetParent() const
251{
252        return mParent;
253}
254
255
[600]256void ViewCell::SetMergeCost(const float mergeCost)
[580]257{
[600]258        mMergeCost = mergeCost;
[580]259}
260
261
[728]262float ViewCell::GetRenderCost() const
263{
[801]264        //return (float)mPvs.GetSize() * GetVolume();
265        return (float)mPvsSize * GetVolume();
[728]266}
267
268
[600]269float ViewCell::GetMergeCost() const
[580]270{
[600]271        return mMergeCost;
[580]272}
273
274
[660]275void ViewCell::SetActive()
276{
277        mIsActive = true;
278        mLastUpdated = sLastUpdated;
279}
[581]280
[660]281
282bool ViewCell::IsActive() const
283{
284        return mIsActive  && (mLastUpdated == sLastUpdated);
285}
286
287
[462]288/************************************************************************/
[580]289/*                class ViewCellInterior implementation                 */
290/************************************************************************/
291
292
293ViewCellInterior::ViewCellInterior()
294{
295}
296
297
298ViewCellInterior::~ViewCellInterior()
299{
300        ViewCellContainer::const_iterator it, it_end = mChildren.end();
301
302        for (it = mChildren.begin(); it != it_end; ++ it)
303                delete (*it);
304}
305
306
307ViewCellInterior::ViewCellInterior(Mesh *mesh):
308ViewCell(mesh)
309{
310}
311
312
313bool ViewCellInterior::IsLeaf() const
314{
315        return false;
316}
317
318
[650]319void ViewCellInterior::SetupChildLink(ViewCell *vc)
[580]320{
[650]321    mChildren.push_back(vc);
322    vc->mParent = this;
[580]323}
324
325
[586]326void ViewCellInterior::RemoveChildLink(ViewCell *l)
327{
328        // erase leaf from old view cell
329        ViewCellContainer::iterator it = mChildren.begin();
[580]330
[586]331        for (; (*it) != l; ++ it);
332        if (it == mChildren.end())
333                Debug << "error" << endl;
334        else
335                mChildren.erase(it);
336}
337
[703]338
339
[580]340/************************************************************************/
[462]341/*                class ViewCellsStatistics implementation              */
342/************************************************************************/
343
[580]344
345
346
[463]347void ViewCellsStatistics::Print(ostream &app) const
348{
349        app << "=========== View Cells Statistics ===============\n";
350
351        app << setprecision(4);
352
353        //app << "#N_CTIME  ( Construction time [s] )\n" << Time() << " \n";
354
[613]355        app << "#N_OVERALLPVS ( objects in PVS )\n" << pvsSize << endl;
[463]356
357        app << "#N_PMAXPVS ( largest PVS )\n" << maxPvs << endl;
358
359        app << "#N_PMINPVS ( smallest PVS )\n" << minPvs << endl;
360
361        app << "#N_PAVGPVS ( average PVS )\n" << AvgPvs() << endl;
362
[485]363        app << "#N_PEMPTYPVS ( view cells with empty PVS )\n" << emptyPvs << endl;
[463]364
365        app << "#N_VIEWCELLS ( number of view cells)\n" << viewCells << endl;
366
367        app << "#N_AVGLEAVES (average number of leaves per view cell )\n" << AvgLeaves() << endl;
368
369        app << "#N_MAXLEAVES ( maximal number of leaves per view cell )\n" << maxLeaves << endl;
370       
[564]371        app << "#N_INVALID ( number of invalid view cells )\n" << invalid << endl;
372
[463]373        app << "========== End of View Cells Statistics ==========\n";
[580]374}
375
376
377/*************************************************************************/
378/*                    class ViewCellsTree implementation                 */
379/*************************************************************************/
380
381
382ViewCellsTree::ViewCellsTree(ViewCellsManager *vcm):
383mRoot(NULL),
384mUseAreaForPvs(false),
[584]385mViewCellsManager(vcm),
[752]386#if 0
387mViewCellsStorage(PVS_IN_INTERIORS)
388#else
389mViewCellsStorage(PVS_IN_LEAVES)
390#endif
[580]391{
392        environment->GetBoolValue("ViewCells.Visualization.exportMergedViewCells", mExportMergedViewCells);
[582]393        environment->GetFloatValue("ViewCells.maxStaticMemory", mMaxMemory);
[580]394
395        //-- merge options
396        environment->GetFloatValue("ViewCells.PostProcess.renderCostWeight", mRenderCostWeight);
397        environment->GetIntValue("ViewCells.PostProcess.minViewCells", mMergeMinViewCells);
398        environment->GetFloatValue("ViewCells.PostProcess.maxCostRatio", mMergeMaxCostRatio);
[586]399        environment->GetBoolValue("ViewCells.PostProcess.refine", mRefineViewCells);   
[582]400
[586]401
[582]402        Debug << "========= view cell tree options ================\n";
403        Debug << "minimum view cells: " << mMergeMinViewCells << endl;
404        Debug << "max cost ratio: " << mMergeMaxCostRatio << endl;
405        Debug << "max memory: " << mMaxMemory << endl;
[586]406        Debug << "refining view cells: " << mRefineViewCells << endl;
[694]407        Debug << "********* view cell tree options ***************\n";
[582]408
[580]409        MergeCandidate::sRenderCostWeight = mRenderCostWeight;
410}
411
412
[582]413// return memory usage in MB
414float ViewCellsTree::GetMemUsage() const
415{
[752]416        // TODO
[582]417        return 0;
418                /*(sizeof(ViewCellsTree) +
419                 mBspStats.Leaves() * sizeof(BspLeaf) +
420                 mBspStats.Interior() * sizeof(BspInterior) +
421                 mBspStats.accumRays * sizeof(RayInfo)) / (1024.0f * 1024.0f);*/
422}
423
424
[736]425int ViewCellsTree::GetNumInitialViewCells(ViewCell *vc) const
[580]426{
427        int vcSize = 0;
428
429        stack<ViewCell *> tstack;
430
431        tstack.push(vc);
432
433        while (!tstack.empty())
434        {
435                ViewCell *vc = tstack.top();
436                tstack.pop();
437
438                if (vc->IsLeaf())
439                {
440                        ++ vcSize;
441                }
442                else
443                {
444                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
445                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
446                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
447                                tstack.push(*it);
448                       
449                }
450        }
451
452        return vcSize;
453}
454
455
456void ViewCellsTree::CollectLeaves(ViewCell *vc, ViewCellContainer &leaves) const
457{
458        stack<ViewCell *> tstack;
459
460        tstack.push(vc);
461
462        while (!tstack.empty())
463        {
464                ViewCell *vc = tstack.top();
465                tstack.pop();
466
467                if (vc->IsLeaf())
468                {
469                        leaves.push_back(vc);
470                }
471                else
472                {
473                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
474                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
[664]475
[580]476                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
[664]477                        {
[580]478                                tstack.push(*it);
[664]479                        }
[580]480                       
481                }
482        }
483}
484
485
486ViewCellsTree::~ViewCellsTree()
487{
488        DEL_PTR(mRoot);
489}
490
491
492int ViewCellsTree::ConstructMergeTree(const VssRayContainer &rays,
493                                                                          const ObjectContainer &objects)
494{
[582]495        mNumActiveViewCells = (int)mViewCellsManager->GetViewCells().size();
[580]496
497        float variance = 0;
498        int totalPvs = 0;
[600]499        float totalRenderCost = 0;
[580]500
501        //-- compute statistics values of initial view cells
[600]502        mViewCellsManager->EvaluateRenderStatistics(totalRenderCost,
[580]503                                                                                                mExpectedCost,
504                                                                                                mDeviation,
505                                                                                                variance,
506                                                                                                totalPvs,
507                                                                                                mAvgRenderCost);
508
509
510        //-- fill merge queue
511        vector<MergeCandidate> candidates;
512
513        mViewCellsManager->CollectMergeCandidates(rays, candidates);
[703]514
[580]515        while(!candidates.empty())
516        {
517                MergeCandidate mc = candidates.back();
518                candidates.pop_back();
519                EvalMergeCost(mc);
520                mMergeQueue.push(mc);
521        }
522
523        Debug << "************************* merge ***********************************" << endl; 
524        Debug << "deviation: " << mDeviation << endl;
525        Debug << "avg render cost: " << mAvgRenderCost << endl;
[600]526        Debug << "expected cost: " << mExpectedCost << endl;
[580]527
528
529        ViewCellsManager::PvsStatistics pvsStats;
530        mViewCellsManager->GetPvsStatistics(pvsStats);
531
[605]532        //static float expectedValue = pvsStats.avgPvs;
[580]533       
534        // the current view cells are kept in this container
[720]535        // we start with the current view cells from the view cell manager.
536        // The active view cells will change with subsequent merges
[752]537       
[720]538        // todo: should rather take initial view cells
539    ViewCellContainer &activeViewCells = mViewCellsManager->GetViewCells();
540       
[752]541       
[580]542        ViewCell::NewMail();
543
544        MergeStatistics mergeStats;
545        mergeStats.Start();
546       
547        long startTime = GetTime();
548
549        mergeStats.collectTime = TimeDiff(startTime, GetTime());
550        mergeStats.candidates = (int)mMergeQueue.size();
551        startTime = GetTime();
552
553        // frequency stats are updated
[650]554        const int statsOut = 500;
[734]555       
[580]556        // passes are needed for statistics, because we don't want to record
557        // every merge
558        int pass = 0;
559        int mergedPerPass = 0;
560        float realExpectedCost = mExpectedCost;
561        float realAvgRenderCost = mAvgRenderCost;
[582]562        int realNumActiveViewCells = mNumActiveViewCells;
[580]563       
564        // maximal ratio of old expected render cost to expected render
565        // when the the render queue has to be reset.
566        float avgCostMaxDeviation;
567        int maxMergesPerPass;
[582]568        int numMergedViewCells = 0;
[734]569       
[580]570        environment->GetIntValue("ViewCells.PostProcess.maxMergesPerPass", maxMergesPerPass);
571        environment->GetFloatValue("ViewCells.PostProcess.avgCostMaxDeviation", avgCostMaxDeviation);
572
573        cout << "actual merge starts now ... " << endl;
[604]574
[580]575        //-- use priority queue to merge leaf pairs
[612]576
[600]577        //const float maxAvgCost = 350;
[801]578        while (!mMergeQueue.empty())//nmNumActiveViewCells > mMergeMinViewCells))
[580]579        {
580                //-- reset merge queue if the ratio of current expected cost / real expected cost
581                //   too small or after a given number of merges
582                if ((mergedPerPass > maxMergesPerPass) ||
583                        (avgCostMaxDeviation > mAvgRenderCost / realAvgRenderCost))
584                {
585                        Debug << "************ reset queue *****************\n"
586                                  << "ratios: " << avgCostMaxDeviation
587                                  << " real avg render cost " << realAvgRenderCost << " average render cost " << mAvgRenderCost
588                                  << " merged per pass : " << mergedPerPass << " of maximal " << maxMergesPerPass << endl;
589
590                        Debug << "Values before reset: " 
591                                  << " erc: " << mExpectedCost
592                                  << " avgrc: " << mAvgRenderCost
593                                  << " dev: " << mDeviation << endl;
594       
595                        // adjust render cost
596                        ++ pass;
597
598                        mergedPerPass = 0;
599                        mExpectedCost = realExpectedCost;
600                        mAvgRenderCost = realAvgRenderCost;
[582]601                        mNumActiveViewCells = realNumActiveViewCells;
[580]602                       
[582]603                        const int numMergedViewCells = UpdateActiveViewCells(activeViewCells);
604               
[720]605                       
606                        //-- resets / refines the view cells
607                        //-- priorities are recomputed
608                        //-- the candidates are put back into merge queue
[586]609                        if (mRefineViewCells)
610                                RefineViewCells(rays, objects);
611                        else
612                                ResetMergeQueue();
[580]613
614                        Debug << "Values after reset: " 
615                                  << " erc: " << mExpectedCost
616                                  << " avg: " << mAvgRenderCost
617                                  << " dev: " << mDeviation << endl;
618
619                        if (mExportMergedViewCells)
620                        {
[582]621                                ExportMergedViewCells(activeViewCells, objects, numMergedViewCells);
[580]622                        }
623                }
624
625
626                MergeCandidate mc = mMergeQueue.top();
627                mMergeQueue.pop();
628       
[710]629                // both view cells equal because of previous merges
[582]630                // NOTE: do I really still need this? probably cannot happen!!
[580]631                if (mc.mLeftViewCell == mc.mRightViewCell)
632                        continue;
633
634                if (mc.IsValid())
635                {
636                        ViewCell::NewMail();
[600]637
638                        //-- update statistical values
[582]639                        -- realNumActiveViewCells;
[580]640                        ++ mergeStats.merged;
641                        ++ mergedPerPass;
642
[600]643                        const float renderCostIncr = mc.GetRenderCost();
644                        const float mergeCostIncr = mc.GetMergeCost();
[580]645
[600]646                        totalRenderCost += renderCostIncr;
[580]647                        mDeviation += mc.GetDeviationIncr();
[710]648
649                                               
650                        //-- merge the view cells of leaf1 and leaf2
[580]651                        int pvsDiff;
652                        ViewCellInterior *mergedVc =
653                                MergeViewCells(mc.mLeftViewCell, mc.mRightViewCell, pvsDiff);
[710]654                       
[580]655
[600]656                        // total render cost and deviation has changed
657                        // real expected cost will be larger than expected cost used for the
658                        // cost heuristics, but cannot recompute costs on each increase of the
659                        // expected cost
[580]660                        totalPvs += pvsDiff;
[600]661                        realExpectedCost = totalRenderCost / (float)realNumActiveViewCells;
662                        realAvgRenderCost = (float)totalPvs / (float)realNumActiveViewCells;
663       
[801]664                        // set merge cost to this node for priority traversal
665                        //mergedVc->SetMergeCost(totalRenderCost);
666// HACK
667mergedVc->SetMergeCost(1.0f / (float)realNumActiveViewCells);
[582]668
[612]669                        //if (mViewCellsManager->EqualToSpatialNode(mergedVc))
670                        //      ++ mergeStats.siblings;
[608]671                        mergedVc->SetCost(realExpectedCost);
[607]672
[650]673                        if ((mergeStats.merged % statsOut) == 0)
[580]674                                cout << "merged " << mergeStats.merged << " view cells" << endl;
675
676                }
677                else
678                {
679                        // merge candidate not valid, because one of the leaves was already
680                        // merged with another one => validate and reinsert into queue
[582]681                        if (ValidateMergeCandidate(mc))
682                        {
683                                EvalMergeCost(mc);
684                                mMergeQueue.push(mc);
685                        }
[580]686                }
[612]687               
[580]688        }
689
690        // adjust stats and reset queue one final time
691        mExpectedCost = realExpectedCost;
692        mAvgRenderCost = realAvgRenderCost;
[582]693        mNumActiveViewCells = realNumActiveViewCells;
[580]694
[582]695        UpdateActiveViewCells(activeViewCells);
[580]696
[586]697        // refine view cells and reset costs
698        if (mRefineViewCells)
699                RefineViewCells(rays, objects);
700        else
701                ResetMergeQueue();
702
[708]703
[580]704        // create a root node if the merge was not done till root level,
705        // else take the single node as new root
[582]706        if ((int)activeViewCells.size() > 1)
[580]707        {
[587]708                Debug << "creating root of view cell hierarchy for "
709                          << (int)activeViewCells.size() << " view cells" << endl;
[612]710               
[582]711                ViewCellInterior *root = mViewCellsManager->MergeViewCells(activeViewCells);
[710]712       
713                ViewCellContainer::const_iterator it, it_end = root->mChildren.end();
714
715                for (it = root->mChildren.begin(); it != it_end; ++ it)
716                        (*it)->SetParent(root);
717       
[600]718                root->SetMergeCost(totalRenderCost);
[608]719                // $$JB keep this 0 temporarilly
720                root->SetCost(0.0f);
[612]721
[580]722                mRoot = root;
723        }
[710]724        // normal case
725        else if (!activeViewCells.empty())
[580]726        {
[582]727                Debug << "setting root of the merge history" << endl;
728                mRoot = activeViewCells[0];
[752]729                Debug << "volume of the root view cell: " << mRoot->GetVolume() << " " << mViewCellsManager->GetViewSpaceBox().GetVolume() << endl;
[580]730        }
[752]731        else
732        {
733                Debug << "big error, root is NULL" << endl;
734        }
[708]735       
[644]736        //-- empty merge queue just in case
[600]737        while (!mMergeQueue.empty())
738        {
739                mMergeQueue.pop();
740        }
[801]741//hack!!
742//mRoot->GetPvs().Clear();
[581]743        // TODO delete because makes no sense here
[580]744        mergeStats.expectedRenderCost = realExpectedCost;
745        mergeStats.deviation = mDeviation;
746
747        // we want to optimize this heuristics
748        mergeStats.heuristics =
749                mDeviation * (1.0f - mRenderCostWeight) +
750                mExpectedCost * mRenderCostWeight;
751
752        mergeStats.mergeTime = TimeDiff(startTime, GetTime());
753        mergeStats.Stop();
754        Debug << mergeStats << endl << endl;
755
[608]756        // assign colors for the view cells so that at least one is always consistent
757        AssignRandomColors();
[708]758
[580]759        //TODO: should return sample contributions?
760        return mergeStats.merged;
761}
762
763
[584]764ViewCell *ViewCellsTree::GetRoot() const
[581]765{
766        return mRoot;
767}
768
769
[580]770void ViewCellsTree::ResetMergeQueue()
771{
772        cout << "reset merge queue ... ";
773       
774        vector<MergeCandidate> buf;
775        buf.reserve(mMergeQueue.size());
776                       
777       
778        // store merge candidates in intermediate buffer
779        while (!mMergeQueue.empty())
780        {
781                MergeCandidate mc = mMergeQueue.top();
782                mMergeQueue.pop();
783               
784                // recalculate cost
[582]785                if (ValidateMergeCandidate(mc))
786                {
787                        EvalMergeCost(mc);
788                        buf.push_back(mc);                             
789                }
[580]790        }
791
792        vector<MergeCandidate>::const_iterator bit, bit_end = buf.end();
793
794        // reinsert back into queue
795        for (bit = buf.begin(); bit != bit_end; ++ bit)
796        {     
797                mMergeQueue.push(*bit);
798        }
799
800        cout << "finished" << endl;
801}
802
803
[582]804int ViewCellsTree::UpdateActiveViewCells(ViewCellContainer &viewCells)
[580]805{
[582]806        int numMergedViewCells = 0;
[580]807
[584]808        Debug << "updating active vc: " << (int)viewCells.size() << endl;
[580]809        // find all already merged view cells and remove them from view cells
[582]810               
811        // sort out all view cells which are not active anymore, i.e., they
812        // were already part of a merge
[580]813        int i = 0;
814
[582]815        ViewCell::NewMail();
816
[580]817        while (1)
818        {
[582]819                // remove all merged view cells from end of the vector
820                while (!viewCells.empty() && (viewCells.back()->GetParent()))
[580]821                {
822                        viewCells.pop_back();
823                }
824
825                // all merged view cells have been found
[710]826                if (i >= (int)viewCells.size())
[580]827                        break;
828
[710]829                // already merged this view cell, put it to end of vector
[582]830                if (viewCells[i]->GetParent())
[580]831                        swap(viewCells[i], viewCells.back());
832               
[752]833                // mail view cell that it has not been merged
[710]834                viewCells[i]->Mail();
835
836                // increase loop counter
837                ++ i;
[580]838        }
839
[582]840
[580]841        // add new view cells to container only if they don't have been
842        // merged in the mean time
[582]843        ViewCellContainer::const_iterator ait, ait_end = mMergedViewCells.end();
[752]844
[582]845        for (ait = mMergedViewCells.begin(); ait != ait_end; ++ ait)
[580]846        {
[582]847                ViewCell *vc = mMergedViewCells.back();
848                if (!vc->GetParent() && !vc->Mailed())
[580]849                {
[582]850                        vc->Mail();
851                        viewCells.push_back(vc);
852                        ++ numMergedViewCells;
[580]853                }
854        }
855
[752]856        // dispose old merged view cells
[582]857        mMergedViewCells.clear();
858
[580]859        // update standard deviation
860        ViewCellContainer::const_iterator vit, vit_end = viewCells.end();
861       
862        mDeviation = 0;
863
864        for (vit = viewCells.begin(); vit != vit_end; ++ vit)
865        {
[728]866                const int lower = mViewCellsManager->GetMinPvsSize();
867                const int upper = mViewCellsManager->GetMaxPvsSize();
[752]868
[728]869                const float penalty = EvalPvsPenalty((*vit)->GetPvs().GetSize(), lower, upper);
[580]870               
871                mDeviation += fabs(mAvgRenderCost - penalty);
872        }
873
874        mDeviation /= (float)viewCells.size();
[582]875       
876        return numMergedViewCells;
[580]877}
878
879
880void ViewCellsTree::ExportMergedViewCells(ViewCellContainer &viewCells,
881                                                                                  const ObjectContainer &objects,
[582]882                                                                                  const int numMergedViewCells)
[580]883{
884       
885
886        char s[64];
887
888        sprintf(s, "merged_viewcells%07d.x3d", (int)viewCells.size());
889        Exporter *exporter = Exporter::GetExporter(s);
890
891        if (exporter)
892        {
893                cout << "exporting " << (int)viewCells.size() << " merged view cells ... ";
894                exporter->ExportGeometry(objects);
895                //Debug << "vc size " << (int)viewCells.size() << " merge queue size: " << (int)mMergeQueue.size() << endl;
896                ViewCellContainer::const_iterator it, it_end = viewCells.end();
897
898                int i = 0;
899                for (it = viewCells.begin(); it != it_end; ++ it)
900                {
901                        Material m;
902                        // assign special material to new view cells
903                        // new view cells are on the back of container
[582]904                        if (i ++ >= (viewCells.size() - numMergedViewCells))
[580]905                        {
906                                //m = RandomMaterial();
907                                m.mDiffuseColor.r = RandomValue(0.5f, 1.0f);
908                                m.mDiffuseColor.g = RandomValue(0.5f, 1.0f);
909                                m.mDiffuseColor.b = RandomValue(0.5f, 1.0f);
910                        }
911                        else
912                        {
913                                float col = RandomValue(0.1f, 0.4f);
914                                m.mDiffuseColor.r = col;
915                                m.mDiffuseColor.g = col;
916                                m.mDiffuseColor.b = col;
917                        }
918
919                        exporter->SetForcedMaterial(m);
920                        mViewCellsManager->ExportViewCellGeometry(exporter, *it);
921                }
922                delete exporter;
923                cout << "finished" << endl;
924        }
925}
926
927
928// TODO: should be done in view cells manager
[586]929ViewCellInterior *ViewCellsTree::MergeViewCells(ViewCell *l,
930                                                                                                ViewCell *r,
931                                                                                                int &pvsDiff) //const
[580]932{
[582]933        ViewCellInterior *vc = mViewCellsManager->MergeViewCells(l, r);
[580]934
935        // if merge was unsuccessful
936        if (!vc) return NULL;
937
[752]938        // set to the new parent view cell
[710]939        l->SetParent(vc);
940        r->SetParent(vc);
941
[580]942        // set new size of view cell
943        if (mUseAreaForPvs)
[710]944        {
[580]945                vc->SetArea(l->GetArea() + l->GetArea());
[710]946        }
[580]947        else
[602]948        {
949                vc->SetVolume(r->GetVolume() + l->GetVolume());
950        }
[710]951
952       
[580]953        // important so other merge candidates sharing this view cell
954        // are notified that the merge cost must be updated!!
955        vc->Mail();
956
957        const int pvs1 = l->GetPvs().GetSize();
958        const int pvs2 = r->GetPvs().GetSize();
959
960
[582]961        // new view cells are stored in this vector
962        mMergedViewCells.push_back(vc);
[580]963
964        pvsDiff = vc->GetPvs().GetSize() - pvs1 - pvs2;
965
[752]966
967
968        //Ždon't store intermediate pvs
969        if (mViewCellsStorage == PVS_IN_LEAVES)
970        {
971                l->mPvsSize = l->GetPvs().GetSize();
972                l->mPvsSizeValid = true;
973               
974                if (!l->IsLeaf())
975                        l->GetPvs().Clear();
976               
977                r->mPvsSize = r->GetPvs().GetSize();
978                r->mPvsSizeValid = true;
979               
980                if (!r->IsLeaf())
981                        r->GetPvs().Clear();
982       
983}
984
985
[580]986        return vc;
987}
988
989
[586]990int ViewCellsTree::RefineViewCells(const VssRayContainer &rays,
991                                                                   const ObjectContainer &objects)
[580]992{
993        Debug << "refining " << (int)mMergeQueue.size() << " candidates " << endl;
[586]994
995        // intermediate buffer for shuffled view cells
996        vector<MergeCandidate> buf;
997        buf.reserve(mMergeQueue.size());
998                       
[580]999        // Use priority queue of remaining leaf pairs
1000        // The candidates either share the same view cells or
1001        // are border leaves which share a boundary.
1002        // We test if they can be shuffled, i.e.,
1003        // either one leaf is made part of one view cell or the other
1004        // leaf is made part of the other view cell. It is tested if the
1005        // remaining view cells are "better" than the old ones.
1006       
[586]1007        const int numPasses = 3;
[580]1008        int pass = 0;
1009        int passShuffled = 0;
1010        int shuffled = 0;
1011        int shuffledViewCells = 0;
1012
1013        ViewCell::NewMail();
1014       
[586]1015        while (!mMergeQueue.empty())
[580]1016        {
[586]1017                MergeCandidate mc = mMergeQueue.top();
1018                mMergeQueue.pop();
[580]1019
[586]1020                // both view cells equal or already shuffled
1021                if ((mc.GetLeftViewCell() == mc.GetRightViewCell()) ||
1022                        mc.GetLeftViewCell()->IsLeaf() || mc.GetRightViewCell()->IsLeaf())
1023                {                       
1024                        continue;
1025                }
[580]1026
[586]1027                // candidate for shuffling
1028                const bool wasShuffled = ShuffleLeaves(mc);
[580]1029               
[586]1030                // shuffled or put into other queue for further refine
1031                if (wasShuffled)
1032                {
1033                        ++ passShuffled;
1034
1035                        if (!mc.GetLeftViewCell()->Mailed())
[580]1036                        {
[586]1037                                mc.GetLeftViewCell()->Mail();
1038                                ++ shuffledViewCells;
[580]1039                        }
[586]1040                        if (!mc.GetRightViewCell()->Mailed())
[580]1041                        {
[586]1042                                mc.GetRightViewCell()->Mail();
1043                                ++ shuffledViewCells;
[580]1044                        }
1045                }
1046
[586]1047                // put back into intermediate vector
1048                buf.push_back(mc);
[580]1049        }
1050
[586]1051
1052        //-- in the end, the candidates must be in the mergequeue again
1053        //   with the correct cost
1054
1055        cout << "reset merge queue ... ";
1056       
1057       
1058        vector<MergeCandidate>::const_iterator bit, bit_end = buf.end();
1059       
1060        for (bit = buf.begin(); bit != bit_end; ++ bit)
1061        {   
1062                MergeCandidate mc = *bit;
1063                // recalculate cost
1064                if (ValidateMergeCandidate(mc))
1065                {
1066                        EvalMergeCost(mc);
1067                        mMergeQueue.push(mc);   
1068                }
[580]1069        }
1070
[586]1071        cout << "finished" << endl;
1072
[580]1073        return shuffledViewCells;
1074}
1075
1076
1077inline int AddedPvsSize(ObjectPvs pvs1, const ObjectPvs &pvs2)
1078{
1079        return pvs1.AddPvs(pvs2);
1080}
1081
1082
1083// recomputes pvs size minus pvs of leaf l
1084#if 0
1085inline int SubtractedPvsSize(BspViewCell *vc, BspLeaf *l, const ObjectPvs &pvs2)
1086{
1087        ObjectPvs pvs;
1088        vector<BspLeaf *>::const_iterator it, it_end = vc->mLeaves.end();
1089        for (it = vc->mLeaves.begin(); it != vc->mLeaves.end(); ++ it)
1090                if (*it != l)
1091                        pvs.AddPvs(*(*it)->mPvs);
1092        return pvs.GetSize();
1093}
1094#endif
1095
1096
1097// computes pvs1 minus pvs2
1098inline int SubtractedPvsSize(ObjectPvs pvs1, const ObjectPvs &pvs2)
1099{
1100        return pvs1.SubtractPvs(pvs2);
1101}
1102
1103
1104float ViewCellsTree::EvalShuffleCost(ViewCell *leaf,
[586]1105                                                                         ViewCellInterior *vc1,
1106                                                                         ViewCellInterior *vc2) const
[580]1107{
1108        //const int pvs1 = SubtractedPvsSize(vc1, leaf, *leaf->mPvs);
1109        const int pvs1 = SubtractedPvsSize(vc1->GetPvs(), leaf->GetPvs());
1110        const int pvs2 = AddedPvsSize(vc2->GetPvs(), leaf->GetPvs());
1111
1112        const int lowerPvsLimit = mViewCellsManager->GetMinPvsSize();
1113        const int upperPvsLimit = mViewCellsManager->GetMaxPvsSize();
1114
1115        const float pvsPenalty1 =
1116                EvalPvsPenalty(pvs1, lowerPvsLimit, upperPvsLimit);
1117
1118        const float pvsPenalty2 =
1119                EvalPvsPenalty(pvs2, lowerPvsLimit, upperPvsLimit);
1120
1121
1122        // don't shuffle leaves with pvs > max
1123        if (0 && (pvs1 + pvs2 > mViewCellsManager->GetMaxPvsSize()))
1124        {
1125                return 1e20f;
1126        }
1127
1128        float p1, p2;
1129
1130    if (mUseAreaForPvs)
1131        {
1132                p1 = vc1->GetArea() - leaf->GetArea();
1133                p2 = vc2->GetArea() + leaf->GetArea();
1134        }
1135        else
1136        {
1137                p1 = vc1->GetVolume() - leaf->GetVolume();
1138                p2 = vc2->GetVolume() + leaf->GetVolume();
1139        }
1140
1141        const float renderCost1 = pvsPenalty1 * p1;
1142        const float renderCost2 = pvsPenalty2 * p2;
1143
1144        float dev1, dev2;
1145
1146        if (1)
1147        {
1148                dev1 = fabs(mAvgRenderCost - pvsPenalty1);
1149                dev2 = fabs(mAvgRenderCost - pvsPenalty2);
1150        }
1151        else
1152        {
1153                dev1 = fabs(mExpectedCost - renderCost1);
1154                dev2 = fabs(mExpectedCost - renderCost2);
1155        }
1156       
1157        return mRenderCostWeight * (renderCost1 + renderCost2) +
[582]1158                  (1.0f - mRenderCostWeight) * (dev1 + dev2) / (float)mNumActiveViewCells;
[580]1159}
1160
1161
1162void ViewCellsTree::ShuffleLeaf(ViewCell *leaf,
[586]1163                                                                ViewCellInterior *vc1,
1164                                                                ViewCellInterior *vc2) const
[580]1165{
1166        // compute new pvs and area
[586]1167        // TODO change
[580]1168        vc1->GetPvs().SubtractPvs(leaf->GetPvs());
1169        vc2->GetPvs().AddPvs(leaf->GetPvs());
1170       
1171        if (mUseAreaForPvs)
1172        {
1173                vc1->SetArea(vc1->GetArea() - leaf->GetArea());
1174                vc2->SetArea(vc2->GetArea() + leaf->GetArea());
1175        }
1176        else
1177        {
1178                vc1->SetVolume(vc1->GetVolume() - leaf->GetVolume());
1179                vc2->SetVolume(vc2->GetVolume() + leaf->GetVolume());
1180        }
1181
[586]1182       
1183        ViewCellInterior *p = dynamic_cast<ViewCellInterior *>(leaf->GetParent());
[580]1184
[586]1185        p->RemoveChildLink(leaf);
1186        vc2->SetupChildLink(leaf);
[580]1187}
1188
1189
[586]1190bool ViewCellsTree::ShuffleLeaves(MergeCandidate &mc) const
[580]1191{
1192        float cost1, cost2;
1193
[586]1194        ViewCellInterior *vc1 = dynamic_cast<ViewCellInterior *>(mc.GetLeftViewCell());
1195        ViewCellInterior *vc2 = dynamic_cast<ViewCellInterior *>(mc.GetRightViewCell());
1196
1197        ViewCell *leaf1 = mc.GetInitialLeftViewCell();
1198        ViewCell *leaf2 = mc.GetInitialRightViewCell();
1199
[580]1200        //-- first test if shuffling would decrease cost
[586]1201        cost1 = GetCostHeuristics(vc1);
1202        cost2 = GetCostHeuristics(vc2);
[580]1203
1204        const float oldCost = cost1 + cost2;
1205       
1206        float shuffledCost1 = Limits::Infinity;
1207        float shuffledCost2 = Limits::Infinity;
1208
[586]1209        if (leaf1)
1210                shuffledCost1 = EvalShuffleCost(leaf1, vc1, vc2);       
1211        if (leaf2)
1212                shuffledCost2 = EvalShuffleCost(leaf2, vc2, vc1);
[580]1213
1214        // if cost of shuffle is less than old cost => shuffle
1215        if ((oldCost <= shuffledCost1) && (oldCost <= shuffledCost2))
1216                return false;
1217       
1218        if (shuffledCost1 < shuffledCost2)
1219        {
[586]1220                if (leaf1)
1221                        ShuffleLeaf(leaf1, vc1, vc2);
1222                mc.mInitialLeftViewCell = NULL;
[580]1223        }
1224        else
1225        {
[586]1226                if (leaf2)
1227                        ShuffleLeaf(leaf2, vc2, vc1);
1228                mc.mInitialRightViewCell = NULL;
[580]1229        }
[586]1230
[580]1231        return true;
1232}
1233
1234
1235float ViewCellsTree::GetVariance(ViewCell *vc) const
1236{
1237        const int upper = mViewCellsManager->GetMaxPvsSize();
1238        const int lower = mViewCellsManager->GetMinPvsSize();
1239
1240        if (1)
1241        {
[752]1242                const float penalty =
1243                        EvalPvsPenalty(vc->GetPvs().GetSize(), lower, upper);
1244                return (mAvgRenderCost - penalty) * (mAvgRenderCost - penalty) /
1245                        (float)mNumActiveViewCells;
[580]1246        }
1247
1248    const float leafCost = GetRenderCost(vc);
1249        return (mExpectedCost - leafCost) * (mExpectedCost - leafCost);
1250}
1251
1252
1253float ViewCellsTree::GetDeviation(ViewCell *vc) const
1254{
1255        const int upper = mViewCellsManager->GetMaxPvsSize();
1256        const int lower = mViewCellsManager->GetMinPvsSize();
1257
1258        if (1)
1259        {
1260                const float penalty = EvalPvsPenalty(vc->GetPvs().GetSize(), lower, upper);
[582]1261                return fabs(mAvgRenderCost - penalty) / (float)mNumActiveViewCells;
[580]1262        }
1263
1264    const float renderCost = GetRenderCost(vc);
1265        return fabs(mExpectedCost - renderCost);
1266}
1267
1268
1269
1270float ViewCellsTree::GetRenderCost(ViewCell *vc) const
1271{
1272        if (mUseAreaForPvs)
1273                return vc->GetPvs().GetSize() * vc->GetArea();
1274
1275        return vc->GetPvs().GetSize() * vc->GetVolume();
1276}
1277
1278
1279float ViewCellsTree::GetCostHeuristics(ViewCell *vc) const
1280{
1281        return GetRenderCost(vc) * mRenderCostWeight +
1282                   GetDeviation(vc) * (1.0f - mRenderCostWeight);
1283}
1284
1285
[582]1286bool ViewCellsTree::ValidateMergeCandidate(MergeCandidate &mc) const
[580]1287{
[752]1288        // propagate up so we have only the active view cells
[580]1289        while (mc.mLeftViewCell->mParent)
1290        {
1291                mc.mLeftViewCell = mc.mLeftViewCell->mParent;
1292        }
1293
1294        while (mc.mRightViewCell->mParent)
1295        {
1296                mc.mRightViewCell = mc.mRightViewCell->mParent;
1297        }
1298
[752]1299        // this view cell was already merged
1300        //return mc.mLeftViewCell && (mc.mLeftViewCell != mc.mRightViewCell);
[582]1301        return mc.mLeftViewCell != mc.mRightViewCell;
[580]1302}
1303
1304
1305void ViewCellsTree::EvalMergeCost(MergeCandidate &mc) const
1306{
1307        //-- compute pvs difference
[752]1308        int newPvs;
1309        if (1) // not valid if not using const cost per object!!
1310                newPvs = ComputeMergedPvsSize(mc.mLeftViewCell->GetPvs(), mc.mRightViewCell->GetPvs());
1311        else
1312                newPvs = (int)ComputeMergedPvsCost(mc.mLeftViewCell->GetPvs(), mc.mRightViewCell->GetPvs());
[580]1313
[729]1314        const float newPenalty = EvalPvsPenalty(newPvs,
1315                                                                                        mViewCellsManager->GetMinPvsSize(),
1316                                                                                        mViewCellsManager->GetMaxPvsSize());
1317
[580]1318        ViewCell *vc1 = mc.mLeftViewCell;
1319        ViewCell *vc2 = mc.mRightViewCell;
1320
1321        //-- compute ratio of old cost
1322        //   (i.e., added size of left and right view cell times pvs size)
1323        //   to new rendering cost (i.e, size of merged view cell times pvs size)
1324        const float oldCost = GetRenderCost(vc1) + GetRenderCost(vc2);
1325
1326    const float newCost = mUseAreaForPvs ?
1327                (float)newPenalty * (vc1->GetArea() + vc2->GetArea()) :
1328                (float)newPenalty * (vc1->GetVolume() + vc2->GetVolume());
1329
1330
1331        // strong penalty if pvs size too large
1332        if (0 && (newPvs > mViewCellsManager->GetMaxPvsSize()))
1333        {
1334                mc.mRenderCost = 1e20f;
1335        }
1336        else
1337        {
1338                mc.mRenderCost = (newCost - oldCost) /
1339                        mViewCellsManager->GetViewSpaceBox().GetVolume();
1340        }       
1341       
1342
1343        //-- merge cost also takes deviation into account
1344        float newDev, oldDev;
1345
1346        if (1)
[582]1347                newDev = fabs(mAvgRenderCost - newPenalty) / (float)mNumActiveViewCells;
[580]1348        else
[582]1349                newDev = fabs(mExpectedCost - newCost) / (float)mNumActiveViewCells;
[580]1350       
1351        oldDev = GetDeviation(vc1) + GetDeviation(vc2);
1352
1353        // compute deviation increase
1354        mc.mDeviationIncr = newDev - oldDev;
1355       
1356        //Debug << "render cost: " << mc.mRenderCost * mRenderCostWeight << endl;
1357        //Debug << "standard deviation: " << mc.mDeviationIncr * mRenderCostWeight << endl;
1358}
1359
[752]1360
1361void ViewCellsTree::SetViewCellsStorage(int stype)
[580]1362{
[752]1363        if (mViewCellsStorage == stype)
1364                return;
1365
1366        // TODO
1367        switch (stype)
[581]1368        {
[752]1369        case COMPRESSED:
[584]1370                CompressViewCellsPvs(mRoot);
[752]1371                break;
1372        default:
1373                break;
[584]1374        }
[752]1375
1376        mViewCellsStorage = stype;
[584]1377}
[580]1378
[752]1379
[584]1380void ViewCellsTree::CompressViewCellsPvs(ViewCell *root)
1381{
1382        if (!root->IsLeaf())
1383        {
1384                ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(root);
1385
1386        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
[585]1387               
[584]1388                // compress child sets first
1389                for (it = interior->mChildren.begin(); it != it_end; ++ it)
[581]1390                {
[584]1391                        CompressViewCellsPvs(*it);
[581]1392                }
[584]1393
[585]1394                // compress root node
[610]1395                PullUpVisibility(interior);
[581]1396        }
[580]1397}
1398
1399
[660]1400void ViewCellsTree::ExportStats(const string &mergeStats)
[650]1401{
1402        TraversalQueue tqueue;
1403
1404        tqueue.push(mRoot);
1405        int numViewCells = 1;
1406       
1407        const AxisAlignedBox3 box = mViewCellsManager->GetViewSpaceBox();
1408        const float vol = box.GetVolume();
1409
[752]1410        const int rootPvs = GetPvsSize(mRoot);
1411
[693]1412        Debug << "vsb volume: " << vol << endl;
1413        Debug << "root volume: " << mRoot->GetVolume() << endl;
[752]1414        Debug << "root pvs: " << rootPvs << endl;
[693]1415
[650]1416        int totalPvs;
[651]1417        float totalRenderCost, avgRenderCost, expectedCost;
[650]1418
1419        float deviation = 0;
[752]1420        totalPvs = rootPvs;
1421        totalRenderCost = avgRenderCost = expectedCost = (float)rootPvs;
[650]1422
1423        ofstream stats;
[660]1424        stats.open(mergeStats.c_str());
[650]1425
[752]1426        //-- first view cell
[650]1427        stats
1428                << "#Pass\n" << 0 << endl
1429                //<< "#Merged\n" << mergeStats.merged << endl
1430                << "#ViewCells\n" << numViewCells << endl
1431        << "#RenderCostDecrease\n" << 0 << endl // TODO
1432                << "#TotalRenderCost\n" << totalRenderCost << endl
[752]1433                << "#CurrentPvs\n" << rootPvs << endl
[650]1434                << "#ExpectedCost\n" << expectedCost << endl
1435                << "#AvgRenderCost\n" << avgRenderCost << endl
1436                << "#Deviation\n" << deviation << endl
1437                << "#TotalPvs\n" << totalPvs << endl
1438                << "#PvsSizeDecrease\n" << 0 << endl // TODO
1439                << "#Volume\n" << mRoot->GetVolume() << endl
1440                << endl;
1441
1442
1443        while (!tqueue.empty())
1444        {
1445                ViewCell *vc = tqueue.top();
1446                tqueue.pop();
1447
1448                if (!vc->IsLeaf())
1449                {       
1450                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
1451                       
[752]1452                        const int parentPvs = GetPvsSize(interior);
[650]1453                        const float parentCost = (float)parentPvs * interior->GetVolume();
1454                        float childCost = 0;
1455                        int childPvs = 0;
1456
1457                        -- numViewCells;
1458
1459                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1460
1461                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1462                        {
[752]1463                                int pvsSize = GetPvsSize(*it);
1464                                childCost += (float) pvsSize * (*it)->GetVolume();
1465                                childPvs += pvsSize;
[650]1466
1467                                tqueue.push(*it);
1468                                ++ numViewCells;
1469                        }
1470
1471               
1472                        const float costDecr = (parentCost - childCost) / vol;
1473
1474                        totalRenderCost -= costDecr;
1475                        totalPvs += childPvs - parentPvs;
1476                       
1477                        expectedCost = totalRenderCost / (float)numViewCells;
1478                        avgRenderCost = (float)totalPvs / (float)numViewCells;
1479
1480                        stats
1481                                << "#Pass\n" << 0 << endl
1482                                //<< "#Merged\n" << mergeStats.merged << endl
1483                                << "#ViewCells\n" << numViewCells << endl
1484                                << "#RenderCostDecrease\n" << costDecr << endl // TODO
1485                                << "#TotalRenderCost\n" << totalRenderCost << endl
[752]1486                                << "#CurrentPvs\n" << parentPvs << endl
[650]1487                                << "#ExpectedCost\n" << expectedCost << endl
1488                                << "#AvgRenderCost\n" << avgRenderCost << endl
1489                                << "#Deviation\n" << deviation << endl
1490                                << "#TotalPvs\n" << totalPvs << endl
1491                                << "#PvsSizeDecrease\n" << childPvs - parentPvs << endl // TODO
1492                                << "#Volume\n" << vc->GetVolume() << endl
1493                                << endl;
1494
1495                }
1496        }
1497
1498        stats.close();
1499}
1500
1501
1502void ViewCellsTree::SetRoot(ViewCell *root)
1503{
1504        mRoot = root;
1505}
1506
[660]1507
[581]1508void ViewCellsTree::CollectBestViewCellSet(ViewCellContainer &viewCells,
1509                                                                                   const int numViewCells)
[580]1510{
1511        TraversalQueue tqueue;
[581]1512        tqueue.push(mRoot);
[582]1513       
[580]1514        while (!tqueue.empty())
1515        {
1516                ViewCell *vc = tqueue.top();
[650]1517                tqueue.pop();
1518
[582]1519                // save the view cells if it is a leaf or if enough view cells have already been traversed
1520                // because of the priority queue, this will be the optimal set of v
[650]1521                if (vc->IsLeaf() || ((viewCells.size() + tqueue.size() + 1) >= numViewCells))
[581]1522                {
1523                        viewCells.push_back(vc);
1524                }
[582]1525                else
1526                {       
[581]1527                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
[580]1528
[581]1529                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1530
1531                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1532                        {
1533                                tqueue.push(*it);
1534                        }
1535                }
[580]1536        }
1537}
[581]1538       
[580]1539
[610]1540void ViewCellsTree::PullUpVisibility(ViewCellInterior *interior)
[581]1541{
[584]1542        Intersectable::NewMail((int)interior->mChildren.size());
[580]1543
[581]1544        ViewCellContainer::const_iterator cit, cit_end = interior->mChildren.end();
1545
1546        ObjectPvsMap::const_iterator oit;
1547
1548        // mail all objects in the leaf sets
[584]1549        // we are interested in the objects which are present in all leaves
1550        // => count how often an object is part of a child set
[581]1551        for (cit = interior->mChildren.begin(); cit != cit_end; ++ cit)
1552        {
1553                ViewCell *vc = *cit;
1554
1555                ObjectPvsMap::const_iterator oit_end = vc->GetPvs().mEntries.end();
1556
1557                for (oit = vc->GetPvs().mEntries.begin(); oit != oit_end; ++ oit)
1558                {
[584]1559                        Intersectable *obj = (*oit).first;
1560                        if ((cit == interior->mChildren.begin()) && !obj->Mailed())
1561                                obj->Mail();
[581]1562                       
[584]1563                        int incm = obj->IncMail();
[581]1564                }
1565        }
1566
1567        interior->GetPvs().mEntries.clear();
1568       
[584]1569       
[581]1570        // only the objects which are present in all leaf pvs
1571        // should remain in the parent pvs
[584]1572        // these are the objects which have been mailed in all children
[581]1573        for (cit = interior->mChildren.begin(); cit != cit_end; ++ cit)
1574        {
1575                ViewCell *vc = *cit;
1576
1577                ObjectPvsMap::const_iterator oit_end = vc->GetPvs().mEntries.end();
1578
1579                for (oit = vc->GetPvs().mEntries.begin(); oit != oit_end; ++ oit)
[585]1580                {               
[581]1581                        if ((*oit).first->Mailed((int)interior->mChildren.size()))
1582                        {       
1583                                interior->GetPvs().AddSample((*oit).first, (*oit).second.mSumPdf);
1584                        }
1585                }
1586        }
1587
[584]1588
1589
1590        // delete all the objects from the leaf sets which were moved to parent pvs
[581]1591        ObjectPvsMap::const_iterator oit_end = interior->GetPvs().mEntries.end();
1592
1593        for (oit = interior->GetPvs().mEntries.begin(); oit != oit_end; ++ oit)
1594        {
1595                for (cit = interior->mChildren.begin(); cit != cit_end; ++ cit)
1596                {
[584]1597                        if (!(*cit)->GetPvs().RemoveSample((*oit).first, Limits::Infinity))
1598                                Debug << "should not come here!" << endl;
[581]1599                }
1600        }
[585]1601
[752]1602        /*int dummy = interior->GetPvs().GetSize();
[585]1603
1604        for (cit = interior->mChildren.begin(); cit != cit_end; ++ cit)
1605        {
1606                dummy += (*cit)->GetPvs().GetSize();
[752]1607        }*/
[585]1608
[581]1609}
1610
[752]1611// TODO
[584]1612void ViewCellsTree::GetPvs(ViewCell *vc, ObjectPvs &pvs) const
1613{
[801]1614        // pvs is stored in each cell
[752]1615        if (mViewCellsStorage == PVS_IN_INTERIORS)
[801]1616        {
[585]1617                pvs = vc->GetPvs();
[801]1618                return;
1619        }
[585]1620
[801]1621        Intersectable::NewMail();
1622
1623
[585]1624        int pvsSize = 0;
1625        ViewCell *root = vc;
1626       
1627        // also add pvs from this view cell to root
1628        while (root->GetParent())
[584]1629        {
[585]1630                root = root->GetParent();
1631                pvs.AddPvs(root->GetPvs());
1632        }
[584]1633
[585]1634        stack<ViewCell *> tstack;
1635        tstack.push(vc);
[584]1636
[585]1637        while (!tstack.empty())
1638        {
1639                vc = tstack.top();
1640                tstack.pop();
1641
[801]1642                // add new pvs
[585]1643                pvs.AddPvs(vc->GetPvs());
1644
[584]1645                if (!vc->IsLeaf())
1646                {
1647                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
[585]1648
[584]1649                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1650
1651                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1652                        {
[585]1653                                tstack.push(*it);
1654                        }               
[584]1655                }
1656        }
1657}
1658
1659
1660int ViewCellsTree::GetPvsSize(ViewCell *vc) const
1661{
[752]1662        int pvsSize = 0;
1663
1664        if (vc->IsLeaf())
1665        {
1666                pvsSize = vc->GetPvs().GetSize();
1667        }
1668       
1669
[585]1670        Intersectable::NewMail();
[584]1671
[752]1672        //////////////////////////
1673        switch (mViewCellsStorage)
1674        {
1675        case PVS_IN_LEAVES: //-- store pvs only in leaves
1676                {                       
1677                        if (vc->mPvsSizeValid)
1678                        {
1679                                pvsSize = vc->mPvsSize;
1680                                break;
1681                        }
1682       
1683                        //-- if no valid pvs size stored as a scalar=> compute new pvs size
1684                        ViewCellContainer leaves;
1685                        CollectLeaves(vc, leaves);
[585]1686
[752]1687                        ViewCellContainer::const_iterator it, it_end = leaves.end();
[719]1688
[752]1689                        Intersectable::NewMail();
1690
1691                        // sum different intersectables
1692                        for (it = leaves.begin(); it != it_end; ++ it)
1693                        {
1694                                ObjectPvsMap::iterator oit, oit_end = (*it)->GetPvs().mEntries.end();
1695
1696                                // mail all from first pvs
1697                                for (oit = (*it)->GetPvs().mEntries.begin(); oit != oit_end; ++ oit)
1698                                {
1699                                        Intersectable *intersect = (*oit).first;
1700
1701                                        if (!intersect->Mailed())
1702                                        {
1703                                                ++ pvsSize;
1704                                                intersect->Mail();                                     
1705                                        }
1706                                }
1707                        }
1708
1709                        break;
1710                }
1711        case COMPRESSED:
1712                {
1713                        ////////////////////////
[719]1714        //-- compressed pvs
1715
[752]1716                        if (vc->mPvsSizeValid)
1717                                return vc->mPvsSize;
1718
1719                        // if no pvs size stored: compute
[585]1720        int pvsSize = 0;
1721        ViewCell *root = vc;
1722       
1723        // also add pvs from this view cell to root
1724        while (root->GetParent())
[584]1725        {
[585]1726                root = root->GetParent();
1727                pvsSize += CountDiffPvs(root);
1728        }
[584]1729
[585]1730        stack<ViewCell *> tstack;
1731        tstack.push(vc);
[584]1732
[585]1733        while (!tstack.empty())
1734        {
1735                vc = tstack.top();
1736                tstack.pop();
1737
1738                pvsSize += CountDiffPvs(vc);
1739
1740                if (!vc->IsLeaf())
[584]1741                {
[585]1742                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
1743
1744                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1745
1746                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1747                        {
1748                                tstack.push(*it);
1749                        }               
[584]1750                }
1751        }
[752]1752                        break;
1753                }
1754        case PVS_IN_INTERIORS:
1755        default:Debug << "in interiors: " << vc->mPvsSize << " $$ " << vc->GetPvs().GetSize() << endl;
1756                pvsSize = vc->GetPvs().GetSize();               
1757        }
[584]1758
1759        return pvsSize; 
1760}
1761
1762
1763float ViewCellsTree::GetMemoryCost(ViewCell *vc) const
1764{
1765        const float entrySize =
1766                sizeof(PvsData<Intersectable *>) + sizeof(Intersectable *);
1767
1768        return (float)GetNumPvsEntries(vc) * entrySize;
1769}
1770
1771
1772int ViewCellsTree::GetNumPvsEntries(ViewCell *vc) const
1773{
[590]1774        int pvsSize = 0;
1775        // only count leaves for uncompressed method for fairness
[752]1776        if ((mViewCellsStorage == PVS_IN_INTERIORS) || vc->IsLeaf())
1777        {
[590]1778                pvsSize = vc->GetPvs().GetSize();
[752]1779        }
[584]1780
1781        if (!vc->IsLeaf())
1782        {
1783                ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
1784
1785                ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1786
1787                for (it = interior->mChildren.begin(); it != it_end; ++ it)
1788                {
1789                        pvsSize += GetNumPvsEntries(*it);
1790                }
1791        }
1792
1793        return pvsSize;         
1794}
1795
1796
[752]1797int ViewCellsTree::ViewCellsStorage() const
[584]1798{
[752]1799        return mViewCellsStorage;
[584]1800}
1801
1802
[590]1803ViewCell *ViewCellsTree::GetActiveViewCell(ViewCell *vc) const
1804{
[660]1805        while (vc->GetParent() && !vc->IsActive())
[590]1806        {
1807                vc = vc->GetParent();
1808        }
1809
1810        return vc;
1811}
1812
1813
[651]1814void ViewCellsTree::PropagatePvs(ViewCell *vc)
[664]1815{       
1816        ViewCell *viewCell = vc;
1817
[660]1818        // propagate pvs up
[664]1819        while (viewCell->GetParent())
[610]1820        {
[664]1821                viewCell->GetParent()->GetPvs().Merge(vc->GetPvs());
1822                viewCell = viewCell->GetParent();
[610]1823        }
[651]1824
1825        if (vc->IsLeaf())
1826                return;
1827
[660]1828        // propagate pvs to the leaves
[651]1829        stack<ViewCell *> tstack;
1830        tstack.push(vc);
1831
1832        while (!tstack.empty())
1833        {
1834                ViewCell *viewCell = tstack.top();
1835                tstack.pop();
1836
1837                if (viewCell != vc)
1838                {
1839                        viewCell->GetPvs().Merge(vc->GetPvs());
1840                }
1841
1842                if (!viewCell->IsLeaf())
1843                {
1844                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(viewCell);
[664]1845
[651]1846                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1847
1848                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1849                        {
1850                                tstack.push(*it);
1851                        }
1852                }
1853        }
[610]1854}
[605]1855
[610]1856
[650]1857void ViewCellsTree::AssignRandomColors()
[608]1858{
[675]1859        TraversalQueue tqueue;
1860       
1861        tqueue.push(mRoot);
[708]1862       
[675]1863        mRoot->SetColor(RandomColor(0.3f, 1.0f));
1864       
1865        while (!tqueue.empty())
[608]1866        {
[675]1867                ViewCell *vc = tqueue.top();
1868                tqueue.pop();
[650]1869
[675]1870                // save the view cells if it is a leaf or if enough view cells have already been traversed
1871                // because of the priority queue, this will be the optimal set of v
1872                if (!vc->IsLeaf())
1873                {       
1874                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
1875                 
1876                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1877                 
1878                        float maxProbability = -1.0f;
1879                 
1880                        ViewCell *maxViewCell = NULL;
1881                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1882                        {
1883                                ViewCell *v = *it;
1884                         
1885                                // set random color
1886                                v->SetColor(RandomColor(0.3f, 1.0f));
1887                         
1888                                if (v->GetVolume() > maxProbability)
1889                                {
1890                                        maxProbability = v->GetVolume();
1891                                        maxViewCell = v;
1892                                }
1893
1894                                if (maxViewCell)
[708]1895                                {
[675]1896                                        maxViewCell->SetColor(vc->GetColor());
[708]1897                                }
[675]1898                               
1899                                tqueue.push(v);
1900                        }
1901                }       
[608]1902        }
1903}
1904
[675]1905
[608]1906/** Get costs resulting from each merge step. */
1907void
1908ViewCellsTree::GetCostFunction(vector<float> &costFunction)
1909{
1910  TraversalQueue tqueue;
1911  tqueue.push(mRoot);
1912  while (!tqueue.empty()) {
1913        ViewCell *vc = tqueue.top();
[650]1914        tqueue.pop();
[608]1915        // save the view cells if it is a leaf or if enough view cells have already been traversed
1916        // because of the priority queue, this will be the optimal set of v
1917        if (!vc->IsLeaf()) {   
1918          ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
[746]1919          costFunction.push_back(interior->GetMergeCost());
[608]1920         
1921          ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1922         
1923          for (it = interior->mChildren.begin(); it != it_end; ++ it) {
1924                tqueue.push(*it);
1925          }
1926         
1927        }
1928  }
1929}
1930
1931
[605]1932void  ViewCellsTree::UpdateViewCellsStats(ViewCell *vc, ViewCellsStatistics &vcStat)
1933{
1934        ++ vcStat.viewCells;
1935               
1936        const int pvsSize = GetPvsSize(vc);
1937
[613]1938        vcStat.pvsSize += pvsSize;
[605]1939
1940        if (pvsSize == 0)
1941                ++ vcStat.emptyPvs;
1942
1943        if (pvsSize > vcStat.maxPvs)
1944                vcStat.maxPvs = pvsSize;
1945
1946        if (pvsSize < vcStat.minPvs)
1947                vcStat.minPvs = pvsSize;
1948
1949        if (!vc->GetValid())
1950                ++ vcStat.invalid;
1951}
1952
1953
[610]1954bool ViewCellsTree::Export(ofstream &stream)
1955{
1956        ExportViewCell(mRoot, stream);
[605]1957
[610]1958        return true;
1959}
1960
1961
[651]1962void ViewCellsTree::CreateUniqueViewCellsIds()
[610]1963{
[651]1964        stack<ViewCell *> tstack;
[610]1965
[651]1966        int currentId = 0;
1967
1968        tstack.push(mRoot);
1969
1970        while (!tstack.empty())
[610]1971        {
[651]1972                ViewCell *vc = tstack.top();
1973                tstack.pop();
1974
1975                vc->SetId(currentId ++);
1976
1977                if (!vc->IsLeaf())
1978                {
1979                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
1980                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
1981                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
1982                        {
1983                                tstack.push(*it);
1984                        }
1985                }
[610]1986        }
[651]1987}
[610]1988
1989
[651]1990void ViewCellsTree::ExportViewCell(ViewCell *viewCell, ofstream &stream)
1991{
1992        ObjectPvsMap::iterator it, it_end = viewCell->GetPvs().mEntries.end();
1993
[610]1994        if (viewCell->IsLeaf())
1995        {
1996                stream << "<Leaf ";
1997                stream << "id=\"" << viewCell->GetId() << "\" ";
[660]1998                stream << "active=\"" << viewCell->IsActive() << "\" ";
[610]1999                stream << "mergecost=\"" << viewCell->GetMergeCost() << "\" ";
2000                stream << "pvs=\"";
[651]2001                if (0)// test with empty pvs
2002                        for (it = viewCell->GetPvs().mEntries.begin(); it != it_end; ++ it)
2003                        {
2004                                stream << (*it).first->GetId() << " ";
2005                        }
2006
2007       
2008                stream << "\" />" << endl;
2009                //stream << " </Leaf>" << endl;
[610]2010        }
2011        else
2012        {
2013                ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(viewCell);
2014       
2015                stream << "<Interior ";
2016                stream << "id=\"" << viewCell->GetId() << "\" ";
[660]2017                stream << "active=\"" << viewCell->IsActive() << "\" ";
[610]2018                stream << "mergecost=\"" << viewCell->GetMergeCost() << "\" ";
2019                stream << "pvs=\"";
2020
[651]2021                if (0)// test with empty pvs
2022                        for (it = viewCell->GetPvs().mEntries.begin(); it != it_end; ++ it)
2023                        {
2024                                stream << (*it).first->GetId() << " ";
2025                        }
2026
2027                stream << "\" >" << endl;
2028
[610]2029                ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
2030
2031                for (it = interior->mChildren.begin(); it != it_end; ++ it)
2032                {
2033                        ExportViewCell(*it, stream);
2034                }
2035
2036                stream << "</Interior>" << endl;
2037        }
2038}
2039
2040
[660]2041void ViewCellsTree::ResetPvs()
2042{
2043        stack<ViewCell *> tstack;
[610]2044
[660]2045        tstack.push(mRoot);
2046
2047        while (!tstack.empty())
2048        {
2049                ViewCell *vc = tstack.top();
2050                tstack.pop();
2051
2052                vc->GetPvs().mEntries.clear();
[666]2053               
[660]2054                if (!vc->IsLeaf())
2055                {
2056                        ViewCellInterior *interior = dynamic_cast<ViewCellInterior *>(vc);
2057                        ViewCellContainer::const_iterator it, it_end = interior->mChildren.end();
[666]2058
[660]2059                        for (it = interior->mChildren.begin(); it != it_end; ++ it)
2060                        {
2061                                tstack.push(*it);
2062                        }
2063                }
2064        }
2065}
2066
2067
2068void ViewCellsTree::SetActiveSetToLeaves()
2069{
[752]2070        // todo
[660]2071}
2072
[580]2073/**************************************************************************/
2074/*                     MergeCandidate implementation                      */
2075/**************************************************************************/
2076
2077
2078MergeCandidate::MergeCandidate(ViewCell *l, ViewCell *r):
2079mRenderCost(0),
2080mDeviationIncr(0),
2081mLeftViewCell(l),
[586]2082mRightViewCell(r),
2083mInitialLeftViewCell(l),
2084mInitialRightViewCell(r)
[580]2085{
2086        //EvalMergeCost();
2087}
2088
2089
2090void MergeCandidate::SetRightViewCell(ViewCell *v)
2091{
2092        mRightViewCell = v;
2093}
2094
2095
2096void MergeCandidate::SetLeftViewCell(ViewCell *v)
2097{
2098        mLeftViewCell = v;
2099}
2100
2101
2102ViewCell *MergeCandidate::GetRightViewCell() const
2103{
2104        return mRightViewCell;
2105}
2106
2107
2108ViewCell *MergeCandidate::GetLeftViewCell() const
2109{
2110        return mLeftViewCell;
2111}
2112
2113
2114ViewCell *MergeCandidate::GetInitialRightViewCell() const
2115{
2116        return mInitialRightViewCell;
2117}
2118
2119
2120ViewCell *MergeCandidate::GetInitialLeftViewCell() const
2121{
2122        return mInitialLeftViewCell;
2123}
2124
2125
2126bool MergeCandidate::IsValid() const
2127{
[752]2128        // if one has a parent, it was already merged
[582]2129        return !(mLeftViewCell->mParent || mRightViewCell->mParent);
[580]2130}
2131
2132
2133float MergeCandidate::GetRenderCost() const
2134{
2135        return mRenderCost;
2136}
2137
2138
2139float MergeCandidate::GetDeviationIncr() const
2140{
2141        return mDeviationIncr;
2142}
2143
2144
2145float MergeCandidate::GetMergeCost() const
2146{
2147        return mRenderCost * sRenderCostWeight +
2148                   mDeviationIncr * (1.0f - sRenderCostWeight);
2149}
2150
2151
[605]2152
[610]2153
2154
2155
[580]2156/************************************************************************/
2157/*                    MergeStatistics implementation                    */
2158/************************************************************************/
2159
2160
2161void MergeStatistics::Print(ostream &app) const
2162{
2163        app << "===== Merge statistics ===============\n";
2164
2165        app << setprecision(4);
2166
2167        app << "#N_CTIME ( Overall time [s] )\n" << Time() << " \n";
2168
2169        app << "#N_CCTIME ( Collect candidates time [s] )\n" << collectTime * 1e-3f << " \n";
2170
2171        app << "#N_MTIME ( Merge time [s] )\n" << mergeTime * 1e-3f << " \n";
2172
2173        app << "#N_NODES ( Number of nodes before merge )\n" << nodes << "\n";
2174
2175        app << "#N_CANDIDATES ( Number of merge candidates )\n" << candidates << "\n";
2176
2177        app << "#N_MERGEDSIBLINGS ( Number of merged siblings )\n" << siblings << "\n";
2178
2179        app << "#OVERALLCOST ( overall merge cost )\n" << overallCost << "\n";
2180
2181        app << "#N_MERGEDNODES ( Number of merged nodes )\n" << merged << "\n";
2182
2183        app << "#MAX_TREEDIST ( Maximal distance in tree of merged leaves )\n" << maxTreeDist << "\n";
2184
2185        app << "#AVG_TREEDIST ( Average distance in tree of merged leaves )\n" << AvgTreeDist() << "\n";
2186
2187        app << "#EXPECTEDCOST ( expected render cost )\n" << expectedRenderCost << "\n";
2188
2189        app << "#DEVIATION ( deviation )\n" << deviation << "\n";
2190
2191        app << "#HEURISTICS ( heuristics )\n" << heuristics << "\n";
2192       
2193
2194        app << "===== END OF BspTree statistics ==========\n";
[608]2195}
Note: See TracBrowser for help on using the repository browser.