source: GTP/trunk/App/Demos/Vis/CHC_revisited/Bvh.cpp @ 2760

Revision 2760, 17.5 KB checked in by mattausch, 16 years ago (diff)
Line 
1#include <queue>
2#include <stack>
3#include <fstream>
4#include <iostream>
5#include <iomanip>
6
7#include "Bvh.h"
8#include "Camera.h"
9#include "Plane3.h"
10#include "glInterface.h"
11#include "Triangle3.h"
12#include "SceneEntity.h"
13#include "Geometry.h"
14
15namespace CHCDemo
16{
17
18#define INVALID_TEST ((unsigned int)-1)
19
20const static bool useVbos = true;
21unsigned int Bvh::sCurrentVboId = -1;
22
23using namespace std;
24
25
26
27/*
283 x---------x 2
29  |\         \
30  | \         \
31  |  \         \
32  |     4 x---------x 5
33  |   |         |
340 x   |     x 1 |
35   \  |         |
36    \ |         |
37     \|         |
38        7 x---------x 6             
39*/
40
41static unsigned int sIndices[] =       
42{7, // degenerated
43 7, 6, 4, 5, 3, 2, 0, 1,
44 1, 4, // degenerated
45 4, 3, 7, 0, 6, 1, 5, 2,
46 2 // degenerated
47};
48
49
50const static int sNumIndicesPerBox = 20;
51
52/*      Order of vertices
53        0 = (0, 0, 0)
54        1 = (1, 0, 0)
55        2 = (1, 1, 0)
56        3 = (0, 1, 0)
57        4 = (0, 1, 1)
58        5 = (1, 1, 1)
59        6 = (1, 0, 1)
60        7 = (0, 0, 1)
61*/
62
63static Plane3 sNearPlane;
64static Camera::Frustum sFrustum;
65
66/// these values are valid for all nodes
67static int sClipPlaneAABBVertexIndices[12];
68
69
70BvhNode::BvhNode(BvhNode *parent):
71mParent(parent),
72mAxis(-1),
73mDepth(0),
74mPlaneMask(0),
75mPreferredPlane(0),
76mLastRenderedFrame(-999),
77mFirst(-1),
78mLast(-1),
79mNumTestNodes(1),
80mTestNodesIdx(-1),
81mIndicesPtr(-1),
82mIndices(NULL),
83mId(0)
84{
85}
86
87
88void BvhNode::ResetVisibility()
89{
90        mVisibility.Reset();
91
92        mLastRenderedFrame = -999;
93}
94
95
96BvhNode::~BvhNode()
97{
98}
99
100
101void BvhNode::VisibilityInfo::Reset()
102{
103        mIsVisible = false;
104       
105        mAssumedVisibleFrames = 0;
106
107        mLastVisitedFrame = -1;
108       
109        mTimesInvisible = 0;
110        mIsFrustumCulled = false;
111
112        mIsNew = true;
113}
114
115
116BvhLeaf::~BvhLeaf()
117{
118}
119
120
121/***********************************************************/
122/*                class Bvh implementation                 */
123/***********************************************************/
124
125
126Bvh::Bvh():
127mCamera(NULL),
128mFrameId(-1),
129mRoot(NULL),
130mVertices(NULL),
131mIndices(NULL),
132mTestIndices(NULL),
133mCurrentIndicesPtr(0),
134mNumNodes(0),
135mMaxDepthForTestingChildren(4),
136mAreaRatioThreshold(1.4f)
137{}
138
139
140Bvh::Bvh(const SceneEntityContainer &entities):
141mCamera(NULL),
142mFrameId(-1),
143mRoot(NULL),
144mVertices(NULL),
145mIndices(NULL),
146mTestIndices(NULL),
147mCurrentIndicesPtr(0),
148mNumNodes(0),
149mMaxDepthForTestingChildren(4),
150mAreaRatioThreshold(1.4f)
151{
152        mGeometrySize = entities.size();
153        mGeometry = new SceneEntity*[mGeometrySize];
154       
155        mBox.Initialize();
156
157        SceneEntityContainer::const_iterator it, it_end = entities.end();
158
159        int i = 0;
160        for (it = entities.begin(); it != it_end; ++ it, ++ i)
161        {
162                mGeometry[i] = (*it);
163                mBox.Include((*it)->GetBoundingBox());
164        }
165
166        cout << "scene box: " << mBox << endl;
167}
168
169
170Bvh::~Bvh()
171{
172        if (mVertices) delete []mVertices;
173        if (mIndices) delete [] mIndices;
174        if (mTestIndices) delete [] mTestIndices;
175        if (mGeometry) delete [] mGeometry;
176
177        if (mRoot) delete mRoot;
178}
179
180
181void Bvh::PullUpLastVisited(BvhNode *node, const int frameId) const
182{               
183        BvhNode *parent = node->GetParent();
184
185        while (parent && (parent->GetLastVisitedFrame() != frameId))
186        {
187                parent->SetLastVisitedFrame(frameId);
188                parent = parent->GetParent();
189        }
190}
191
192
193void Bvh::MakeParentsVisible(BvhNode *node)
194{
195        BvhNode *parent = node->GetParent();
196
197        while (parent && (!parent->IsVisible()))
198        {
199                parent->SetVisible(true);
200                parent = parent->GetParent();
201        }
202}
203
204
205void Bvh::CollectLeaves(BvhNode *node, BvhLeafContainer &leaves)
206{
207        stack<BvhNode *> tStack;
208        tStack.push(node);
209
210        while (!tStack.empty())
211        {
212                BvhNode *node = tStack.top();
213
214                tStack.pop();
215
216                if (!node->IsLeaf())
217                {
218                        BvhInterior *interior = static_cast<BvhInterior *>(node);
219
220                        tStack.push(interior->mFront);
221                        tStack.push(interior->mBack);
222                }
223                else
224                {
225                        leaves.push_back(static_cast<BvhLeaf *>(node));
226                }
227        }
228}
229
230
231void Bvh::CollectNodes(BvhNode *node, BvhNodeContainer &nodes)
232{
233        stack<BvhNode *> tStack;
234
235        tStack.push(node);
236
237        while (!tStack.empty())
238        {
239                BvhNode *node = tStack.top();
240                tStack.pop();
241
242                nodes.push_back(node);
243
244                if (!node->IsLeaf())
245                {
246                        BvhInterior *interior = static_cast<BvhInterior *>(node);
247
248                        tStack.push(interior->mFront);
249                        tStack.push(interior->mBack);
250                }
251        }
252}
253
254
255typedef pair<BvhNode *, int> tPair;
256
257void Bvh::CollectNodes(BvhNode *root, BvhNodeContainer &nodes, int depth)
258{
259        stack<tPair> tStack;
260        tStack.push(tPair(root, 0));
261
262        while (!tStack.empty())
263        {
264                BvhNode *node = tStack.top().first;
265                const int d = tStack.top().second;
266
267                tStack.pop();
268
269                // found depth => take this node
270                if ((d == depth) || (node->IsLeaf()))
271                {
272                        nodes.push_back(node);
273                }
274                else
275                {
276                        BvhInterior *interior = static_cast<BvhInterior *>(node);
277
278                        tStack.push(tPair(interior->mFront, d + 1));
279                        tStack.push(tPair(interior->mBack, d + 1));
280                }
281        }
282}
283
284
285SceneEntity **Bvh::GetGeometry(BvhNode *node, int &geometrySize)
286{
287        geometrySize = node->CountPrimitives();
288        return mGeometry + node->mFirst;
289}
290
291
292int     Bvh::IsWithinViewFrustum(BvhNode *node)
293{
294        bool bIntersect = false;
295
296        if (node->GetParent())
297                node->mPlaneMask = node->GetParent()->mPlaneMask;
298
299
300        ////////
301        //-- do the view frustum culling for the planes [mPreferredPlane - 5]
302
303        for (int i = node->mPreferredPlane; i < 6; ++ i)
304        {
305                //-- do the test only if necessary
306                if (node->mPlaneMask & (1 << i))
307                {
308                        //-- test the n-vertex
309                        if (node->mBox.GetDistance(sClipPlaneAABBVertexIndices[i * 2 + 0], sFrustum.mClipPlanes[i]) > 0.0f)
310                        {
311                                //-- outside
312                                node->mPreferredPlane = i;
313                                return 0;
314                        }
315
316                        //-- test the p-vertex
317                        if (node->mBox.GetDistance(sClipPlaneAABBVertexIndices[i * 2 + 1], sFrustum.mClipPlanes[i]) <= 0.0f)
318                        {
319                                //-- completely inside: children don't need to check against this plane no more
320                                node->mPlaneMask^=      1 << i;
321                        }
322                        else
323                        {
324                                bIntersect = true;
325                        }
326                }
327        }
328
329        //////////
330        //-- do the view frustum culling for the planes [0 - m_iPreferredPlane)
331
332        for (int i = 0; i < node->mPreferredPlane; ++ i)
333        {
334                // do the test only if necessary
335                if (node->mPlaneMask & (1 << i))
336                {
337                        //-- test the n-vertex
338                        if (node->mBox.GetDistance(sClipPlaneAABBVertexIndices[i * 2 + 0], sFrustum.mClipPlanes[i]) > 0.0f)
339                        {
340                                // outside
341                                node->mPreferredPlane = i;
342                                return 0;
343                        }
344
345                        //-- test the p-vertex
346                        if (node->mBox.GetDistance(sClipPlaneAABBVertexIndices[i * 2 + 1], sFrustum.mClipPlanes[i]) <= 0.0f)
347                        {
348                                // completely inside: children don't need to check against this plane no more
349                                node->mPlaneMask^= 1 << i;
350                        }
351                        else
352                        {
353                                bIntersect = true;
354                        }
355                }
356        }
357
358        return bIntersect ? -1 : 1;
359}
360
361
362void Bvh::InitFrame(Camera *camera, int currentFrameId)
363{
364        // = 0011 1111 which means that at the beginning, all six planes have to frustum culled
365        mRoot->mPlaneMask = 0x3f;
366        mCamera = camera;
367
368        mFrameId = currentFrameId;
369
370        mCamera->CalcFrustum(sFrustum);
371        sFrustum.CalcNPVertexIndices(sClipPlaneAABBVertexIndices);
372
373        // calc near plane
374        sNearPlane = Plane3(mCamera->GetDirection(),
375                                -mCamera->GetDirection() * mCamera->GetPosition());
376}
377
378
379float Bvh::CalcDistance(BvhNode *node) const
380{
381        return node->GetBox().GetMinVisibleDistance(sNearPlane);
382}
383
384
385void Bvh::RenderBoundingBox(BvhNode *node)
386{
387        static BvhNodeContainer dummy(1);
388        dummy[0] = node;
389        RenderBoundingBoxes(dummy);
390}
391
392
393
394int Bvh::RenderBoundingBoxes(const BvhNodeContainer &nodes)
395{
396        int renderedBoxes = PrepareBoundingBoxesWithDrawArrays(nodes);
397        RenderBoundingBoxesWithDrawArrays(renderedBoxes);
398
399        return renderedBoxes;
400}
401
402
403int Bvh::PrepareBoundingBoxesWithDrawArrays(const BvhNodeContainer &nodes)
404{
405        //////
406        //-- for the first time we come here ...
407
408        if (!mIndices)
409        {       // create list of indices
410                CreateIndices();
411        }
412
413        if (sCurrentVboId == -1)
414        {       
415                // prepare the vbo
416                PrepareVertices();
417        }
418
419        ///////////////
420
421        int numNodes = 0;
422
423        BvhNodeContainer::const_iterator nit, nit_end = nodes.end();
424
425        for (nit = nodes.begin(); nit != nit_end; ++ nit)
426        {
427                BvhNode *node = *nit;
428
429        const int numIndices = node->mNumTestNodes * sNumIndicesPerBox;
430               
431                // copy indices
432                memcpy(mIndices + numNodes * sNumIndicesPerBox,
433                           mTestIndices + node->mIndicesPtr,
434#if 0 //ALIGN_INDICES
435                           ((numIndices / 32) * 32 + 32) * sizeof(unsigned int));
436#else
437                           numIndices * sizeof(unsigned int));
438#endif
439                numNodes += node->mNumTestNodes;
440        }
441
442        return numNodes;
443}
444
445
446void Bvh::RenderBoundingBoxesWithDrawArrays(int numNodes)
447{
448        //////
449        //-- Rendering the vbo
450        if (useVbos)
451                // set the vertex pointer to the vertex buffer
452                glVertexPointer(3, GL_FLOAT, 0, (char *)NULL);         
453        else
454                glVertexPointer(3, GL_FLOAT, 0, mVertices);
455
456        // we do use the last or the first index (they are generate and only used to connect strips)
457        const int numElements = numNodes * sNumIndicesPerBox - 1;
458
459        // don't render first degenerate index
460        glDrawElements(GL_TRIANGLE_STRIP, numElements, GL_UNSIGNED_INT, mIndices + 1);
461}
462
463
464#define ALIGN_INDICES 1
465
466void Bvh::CreateIndices()
467{
468        // collect bvh nodes
469        BvhNodeContainer nodes;
470        CollectNodes(mRoot, nodes);
471
472        cout << "creating new indices" << endl;
473
474        int numMaxIndices = 0;
475
476        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
477
478        for (lit = nodes.begin(); lit != lit_end; ++ lit)
479        {
480                int offset = (*lit)->mNumTestNodes * sNumIndicesPerBox;
481#if ALIGN_INDICES
482                // align with 32
483                offset = (offset / 32) * 32 + 32;
484#endif
485                numMaxIndices += offset;
486        }
487
488
489        cout << "creating global indices buffer" << endl;
490
491        if (mIndices) delete [] mIndices;
492        if (mTestIndices) delete [] mTestIndices;
493
494        // global buffer: create it once so we don't have
495        // to allocate memory from the chunks of the node
496        mIndices = new unsigned int[numMaxIndices];
497
498        // create new index buffer for the individual nodes
499        mTestIndices = new unsigned int[numMaxIndices];
500       
501        mCurrentIndicesPtr = 0;
502
503        for (lit = nodes.begin(); lit != lit_end; ++ lit)
504        {
505                BvhNode *node = *lit;
506               
507                // resize array
508                node->mIndicesPtr = mCurrentIndicesPtr;
509
510                int numIndices = 0;
511
512                // the bounding box of the test nodes are rendered instead of the root node
513                // => store their indices
514                for (int i = 0; i < node->mNumTestNodes; ++ i, numIndices += sNumIndicesPerBox)
515                {
516                        BvhNode *testNode = mTestNodes[node->mTestNodesIdx + i];
517
518                        // add indices to root node
519                        for (int j = 0; j < sNumIndicesPerBox; ++ j)
520                        {
521                                mTestIndices[mCurrentIndicesPtr + numIndices + j] = sIndices[j] + testNode->GetId() * 8;
522                        }
523                }
524
525                // align with 32
526#if ALIGN_INDICES
527                const int offset = (numIndices / 32) * 32 + 32;
528#else
529                const int offset = numIndices;
530#endif
531                mCurrentIndicesPtr += offset;
532        }
533}
534
535
536void Bvh::ComputeIds()
537{
538        // collect all nodes
539        BvhNodeContainer nodes;
540        CollectNodes(mRoot, nodes);
541
542        // assign ids to all nodes of the "regular" hierarchy
543        int i = 0;
544        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
545
546        for (lit = nodes.begin(); lit != lit_end; ++ lit, ++ i)
547        {
548                (*lit)->SetId(i);
549        }
550}
551
552
553void Bvh::PrepareVertices()
554{
555        // collect all nodes
556        BvhNodeContainer nodes;
557
558        nodes.reserve(GetNumNodes());
559        CollectNodes(mRoot, nodes);
560
561        const unsigned int bufferSize = 8 * (int)nodes.size();
562        mVertices = new Vector3[bufferSize];
563       
564        int i = 0;
565       
566        // store bounding box vertices
567        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
568
569        for (lit = nodes.begin(); lit != lit_end; ++ lit, i += 8)
570        {
571                BvhNode *node = *lit;
572
573                for (int j = 0; j < 8; ++ j)
574                        ((Vector3 *)mVertices)[node->GetId() * 8 + j] = node->GetBox().GetVertex(j);
575        }
576
577        if (useVbos)
578        {
579                glGenBuffersARB(1, &sCurrentVboId);
580                glBindBufferARB(GL_ARRAY_BUFFER_ARB, sCurrentVboId);
581                glVertexPointer(3, GL_FLOAT, 0, (char *)NULL);
582       
583                glBufferDataARB(GL_ARRAY_BUFFER_ARB,
584                                    bufferSize * sizeof(Vector3),
585                                    mVertices,
586                                                GL_STATIC_DRAW_ARB);
587
588                //glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
589
590                // data handled by graphics driver from now on
591                DEL_PTR(mVertices);
592
593                cout << "***** created vbos *********" << endl;
594        }
595        else
596        {
597                sCurrentVboId = 0;
598        glVertexPointer(3, GL_FLOAT, 0, mVertices);
599
600                cout << "******* created vertex arrays ********" << endl;
601        }
602}
603
604
605void Bvh::SetMaxDepthForTestingChildren(int maxDepth)
606{
607        if (maxDepth != mMaxDepthForTestingChildren)
608        {
609                mMaxDepthForTestingChildren = maxDepth;
610                RecomputeBounds();
611        }
612}
613
614
615void Bvh::SetAreaRatioThresholdForTestingChildren(float ratio)
616{
617        if (ratio != mAreaRatioThreshold)
618        {
619                mAreaRatioThreshold = ratio;
620                RecomputeBounds();
621        }
622}
623
624
625void Bvh::RecomputeBounds()
626{
627        // clear old list
628        mTestNodes.clear();
629
630        // collect all nodes
631        BvhNodeContainer nodes;
632        CollectNodes(mRoot, nodes);
633
634        cout << "recomputing bounds, children will be tested in depth " << mMaxDepthForTestingChildren << endl;
635
636        int success = 0;
637        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
638
639        for (lit = nodes.begin(); lit != lit_end; ++ lit)
640        {
641                BvhNode *node = *lit;
642
643                // recreate list of nodes that will be tested as a proxy ...
644                if (CreateNodeRenderList(node))
645                        ++ success;
646        }
647
648        float p = 100.0f * (float)success / nodes.size();
649        cout << "created tighter bounds for " << p << " percent of the nodes" << endl;
650
651        // recreate indices used for indirect mode rendering
652        if (mIndices)
653                CreateIndices();
654}
655
656       
657bool Bvh::CreateNodeRenderList(BvhNode *node)
658{
659        BvhNodeContainer children;
660
661        // collect nodes that will be tested instead of the leaf node
662        // in order to get a tighter bounding box test
663        CollectNodes(node, children, mMaxDepthForTestingChildren);
664       
665
666        // using the tighter bounds is not feasable in case
667        // that the tighter bounds represent nearly the same projected area
668        // as the old bounding box. Find this out using either volume or area
669        // heuristics
670
671        float vol = 0;
672        float area = 0;
673
674        BvhNodeContainer::const_iterator cit;
675
676        for (cit = children.begin(); cit != children.end(); ++ cit)
677                area += (*cit)->GetBox().SurfaceArea();
678
679        const float areaRatio = area / node->GetBox().SurfaceArea();
680       
681        bool success;
682
683        if (areaRatio < mAreaRatioThreshold)
684                success = true;
685        else
686        {
687                // hack: only store node itself
688                children.clear();
689                children.push_back(node);
690
691                success = false;
692        }
693
694        // the new test nodes are added at the end of the vector
695        node->mTestNodesIdx = (int)mTestNodes.size();
696
697        // use the found nodes as nodes during the occlusion tests
698        for (cit = children.begin(); cit != children.end(); ++ cit)
699        {
700                BvhNode *child = *cit;
701                mTestNodes.push_back(child);
702        }
703
704        node->mNumTestNodes = (int)children.size();
705
706        return success;
707}
708
709
710void Bvh::ResetNodeClassifications()
711{
712        BvhNodeContainer nodes;
713
714        nodes.reserve(GetNumNodes());
715        CollectNodes(mRoot, nodes);
716
717        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
718
719        for (lit = nodes.begin(); lit != lit_end; ++ lit)
720        {
721                (*lit)->ResetVisibility();
722        }
723}
724
725
726int Bvh::CountTriangles(BvhLeaf *leaf) const
727{
728        int triangleCount = 0;
729       
730        for (int i = leaf->mFirst; i <= leaf->mLast; ++ i)
731                triangleCount += mGeometry[i]->GetGeometry()->CountTriangles();
732       
733        return triangleCount;
734}
735
736
737void Bvh::ComputeBvhStats()
738{
739        std::stack<BvhNode *> nodeStack;
740        nodeStack.push(mRoot);
741
742        while (!nodeStack.empty())
743        {
744                BvhNode *node = nodeStack.top();
745                nodeStack.pop();
746
747                if (node->IsLeaf())
748                {
749                        BvhLeaf *leaf = static_cast<BvhLeaf *>(node);
750
751                        mBvhStats.mLeafSA += leaf->mBox.SurfaceArea();
752                        mBvhStats.mLeafVol += leaf->mBox.GetVolume();
753                }
754                else
755                {
756                        mBvhStats.mInteriorSA += node->mBox.SurfaceArea();
757                        mBvhStats.mInteriorVol += node->mBox.GetVolume();
758
759                        BvhInterior *interior = static_cast<BvhInterior *>(node);
760                       
761                        nodeStack.push(interior->mBack);
762                        nodeStack.push(interior->mFront);
763                }
764        }
765
766        mBvhStats.mGeometryRatio = mGeometrySize / (float)GetNumLeaves();
767        mBvhStats.mTriangleRatio = mBvhStats.mTriangles / (float)GetNumLeaves();
768}
769
770
771void Bvh::PrintBvhStats() const
772{
773        cout << "bvh stats:" << endl;
774        cout << "interiorNodesSA = " << mBvhStats.mInteriorSA / mRoot->mBox.SurfaceArea() << endl;
775        cout << "leafNodesSA = " << mBvhStats.mLeafSA / mRoot->mBox.SurfaceArea() << endl;
776        cout << "interiorNodesVolume = " << mBvhStats.mInteriorVol / mRoot->mBox.GetVolume() << endl;
777        cout << "leafNodesVolume = " << mBvhStats.mLeafVol / mRoot->mBox.GetVolume() << endl;
778
779        cout << "geometry per leaf: " <<  mBvhStats.mGeometryRatio << endl;
780        cout << "triangles per leaf: " <<  mBvhStats.mTriangleRatio << endl;
781}
782
783
784int Bvh::CountTriangles(BvhNode *node) const
785{
786        int numTriangles = 0;
787
788        for (int i = node->mFirst; i <= node->mLast; ++ i)
789                numTriangles += mGeometry[i]->GetGeometry()->CountTriangles();
790       
791        return numTriangles;
792}
793
794
795float Bvh::GetArea(BvhNode *node) const
796{
797        return node->mArea;
798}
799
800
801void Bvh::UpdateNumLeaves(BvhNode *node) const
802{
803        if (node->IsLeaf())
804        {
805                node->mNumLeaves = 1;
806        }
807        else
808        {
809                BvhNode *f = static_cast<BvhInterior *>(node)->GetFront();
810                BvhNode *b = static_cast<BvhInterior *>(node)->GetBack();
811
812                UpdateNumLeaves(f);
813                UpdateNumLeaves(b);
814
815                node->mNumLeaves = f->mNumLeaves + b->mNumLeaves;
816        }
817}
818
819
820int Bvh::Render(BvhNode *node, RenderState *state)
821{
822        //cout << "r " << node->mFirst << " " <<  node->mLast << endl;
823
824        int rendered = 0;
825
826        for (int i = node->mFirst; i <= node->mLast; ++ i)
827        {
828                if (mGeometry[i]->GetLastRendered() != mFrameId)
829                {
830                        mGeometry[i]->Render(state);
831                        mGeometry[i]->SetLastRendered(mFrameId);
832                        ++ rendered;
833                }
834        }
835
836        return rendered;
837}
838
839
840}
Note: See TracBrowser for help on using the repository browser.