source: GTP/trunk/App/Demos/Vis/FriendlyCulling/src/Bvh.cpp @ 3074

Revision 3074, 34.3 KB checked in by mattausch, 16 years ago (diff)

included vbo support for dynamic objects

Line 
1#include "Bvh.h"
2#include "Camera.h"
3#include "Plane3.h"
4#include "glInterface.h"
5#include "Triangle3.h"
6#include "SceneEntity.h"
7#include "Geometry.h"
8#include "RenderState.h"
9#include "Material.h"
10#include "gzstream.h"
11
12#include <queue>
13#include <stack>
14#include <fstream>
15#include <iostream>
16#include <iomanip>
17
18
19#ifdef _CRT_SET
20        #define _CRTDBG_MAP_ALLOC
21        #include <stdlib.h>
22        #include <crtdbg.h>
23
24        // redefine new operator
25        #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
26        #define new DEBUG_NEW
27#endif
28
29#define INVALID_TEST ((unsigned int)-1)
30
31using namespace std;
32
33
34namespace CHCDemoEngine
35{
36
37int BvhNode::sCurrentState = 0;
38
39
40/*
413 x---------x 2
42  |\         \
43  | \         \
44  |  \         \
45  |     4 x---------x 5
46  |   |         |
470 x   |     x 1 |
48   \  |         |
49    \ |         |
50     \|         |
51        7 x---------x 6             
52*/
53
54static unsigned int sIndices[] =       
55{7, // degenerated
56 7, 6, 4, 5, 3, 2, 0, 1,
57 1, 4, // degenerated
58 4, 3, 7, 0, 6, 1, 5, 2,
59 2 // degenerated
60};
61
62
63const static int sNumIndicesPerBox = 20;
64
65/*      Order of vertices
66        0 = (0, 0, 0)
67        1 = (1, 0, 0)
68        2 = (1, 1, 0)
69        3 = (0, 1, 0)
70        4 = (0, 1, 1)
71        5 = (1, 1, 1)
72        6 = (1, 0, 1)
73        7 = (0, 0, 1)
74*/
75
76static Plane3 sNearPlane;
77static float sNear;
78static Frustum sFrustum;
79
80/// these values are valid for all nodes
81static int sClipPlaneAABBVertexIndices[12];
82
83#define ALIGN_INDICES
84
85
86BvhNode::BvhNode(BvhNode *parent):
87mParent(parent),
88mAxis(-1),
89mDepth(0),
90mFirst(-1),
91mLast(-1),
92mNumTestNodes(1),
93mTestNodesIdx(-1),
94mIndicesPtr(-1),
95mId(0),
96mIsMaxDepthForVirtualLeaf(false),
97mIsVirtualLeaf(false)
98{
99        for (int i = 0; i < NUM_STATES; ++ i)
100        {
101                mPlaneMask[i] = 0;
102                mPreferredPlane[i]= 0;
103                mLastRenderedFrame[i] = -1;
104        }
105}
106
107
108BvhNode::~BvhNode()
109{
110}
111
112
113void BvhNode::ResetVisibility()
114{
115        for (int i = 0; i < NUM_STATES; ++ i)
116        {
117                mVisibility[i].Reset();
118                mLastRenderedFrame[i] = -1;
119                mPlaneMask[i] = 0;
120                mPreferredPlane[i]= 0;
121        }
122}
123
124
125void BvhNode::VisibilityInfo::Reset()
126{
127        mIsVisible = false;
128        mAssumedVisibleFrameId = 0;
129        mLastVisitedFrame = -1;
130        mTimesInvisible = 0;
131        mIsFrustumCulled = false;
132        mIsNew = true;
133        mLastQueriedFrame = -1;
134}
135
136
137BvhInterior::~BvhInterior()
138{
139        DEL_PTR(mBack);
140        DEL_PTR(mFront);
141}
142
143
144BvhLeaf::~BvhLeaf()
145{
146}
147
148
149/**********************************************************/
150/*                class Bvh implementation                */
151/**********************************************************/
152
153
154
155inline AxisAlignedBox3 ComputeBoundingBox(SceneEntity **entities, int numEntities)
156{
157        AxisAlignedBox3 box;
158       
159        if (!numEntities)
160        {
161                // no box=> just initialize
162                box.Initialize();
163        }
164        else
165        {
166                box = entities[0]->GetWorldBoundingBox();
167
168                for (int i = 1; i < numEntities; ++ i)
169                {
170                        box.Include(entities[i]->GetWorldBoundingBox());
171                }
172        }
173
174        return box;
175}
176
177
178Bvh::Bvh()
179{
180        Init();
181}
182
183
184Bvh::Bvh(const SceneEntityContainer &staticEntities,
185                 const SceneEntityContainer &dynamicEntities)
186{
187        Init();
188
189        mGeometrySize = staticEntities.size() + dynamicEntities.size();
190        mGeometry = new SceneEntity*[mGeometrySize];
191
192        mStaticGeometrySize = staticEntities.size();
193        mDynamicGeometrySize = dynamicEntities.size();
194
195        for (size_t i = 0; i < mStaticGeometrySize; ++ i)
196        {
197                mGeometry[i] = staticEntities[i];
198        }
199
200        for (size_t i = 0; i < mDynamicGeometrySize; ++ i)
201        {
202                mGeometry[mStaticGeometrySize + i] = dynamicEntities[i];
203        }
204}
205
206
207Bvh::~Bvh()
208{
209        DEL_ARRAY_PTR(mVertices);
210        DEL_ARRAY_PTR(mIndices);
211        DEL_ARRAY_PTR(mTestIndices);
212        DEL_ARRAY_PTR(mGeometry);
213
214        if (mRoot) delete mRoot;
215        // delete vbo
216        glDeleteBuffersARB(1, &mVboId);
217}
218
219
220void Bvh::Init()
221{
222        mStaticRoot = NULL;
223        mDynamicRoot = NULL;
224        mRoot = NULL;
225
226        mVertices = NULL;
227        mIndices = NULL;
228        mTestIndices = NULL;
229        mCurrentIndicesPtr = 0;
230        mNumNodes = 0;
231       
232        // nodes are tested using the subnodes from 3 levels below
233        mMaxDepthForTestingChildren = 3;
234        //mMaxDepthForTestingChildren = 4;
235
236        // the ratio of area between node and subnodes where
237        // testing the subnodes as proxy is still considered feasable
238        mAreaRatioThreshold = 2.0f;
239        //mAreaRatioThreshold = 1.4f;
240
241        mVboId = -1;
242
243        mMaxDepthForDynamicBranch = 10;
244}
245
246
247
248
249//////////////////////
250//-- functions that are used during the main traversal
251
252void Bvh::PullUpLastVisited(BvhNode *node, int frameId) const
253{               
254        BvhNode *parent = node->GetParent();
255
256        while (parent && (parent->GetLastVisitedFrame() != frameId))
257        {
258                parent->SetLastVisitedFrame(frameId);
259                parent = parent->GetParent();
260        }
261}
262
263
264void Bvh::MakeParentsVisible(BvhNode *node)
265{
266        BvhNode *parent = node->GetParent();
267
268        while (parent && (!parent->IsVisible()))
269        {
270                parent->SetVisible(true);
271                parent = parent->GetParent();
272        }
273}
274
275
276////////////////////////////////
277
278void Bvh::CollectLeaves(BvhNode *node, BvhLeafContainer &leaves)
279{
280        stack<BvhNode *> tStack;
281        tStack.push(node);
282
283        while (!tStack.empty())
284        {
285                BvhNode *node = tStack.top();
286
287                tStack.pop();
288
289                if (!node->IsLeaf())
290                {
291                        BvhInterior *interior = static_cast<BvhInterior *>(node);
292
293                        tStack.push(interior->mFront);
294                        tStack.push(interior->mBack);
295                }
296                else
297                {
298                        leaves.push_back(static_cast<BvhLeaf *>(node));
299                }
300        }
301}
302
303
304void Bvh::CollectNodes(BvhNode *node, BvhNodeContainer &nodes)
305{
306        stack<BvhNode *> tStack;
307
308        tStack.push(node);
309
310        while (!tStack.empty())
311        {
312                BvhNode *node = tStack.top();
313                tStack.pop();
314
315                nodes.push_back(node);
316
317                if (!node->IsLeaf())
318                {
319                        BvhInterior *interior = static_cast<BvhInterior *>(node);
320
321                        tStack.push(interior->mFront);
322                        tStack.push(interior->mBack);
323                }
324        }
325}
326
327
328typedef pair<BvhNode *, int> tPair;
329
330void Bvh::CollectNodes(BvhNode *root, BvhNodeContainer &nodes, int depth)
331{
332        stack<tPair> tStack;
333        tStack.push(tPair(root, 0));
334
335        while (!tStack.empty())
336        {
337                BvhNode *node = tStack.top().first;
338                const int d = tStack.top().second;
339
340                tStack.pop();
341
342                // found node in specified depth => take this node
343                if ((d == depth) || (node->IsLeaf()))
344                {
345                        nodes.push_back(node);
346                }
347                else
348                {
349                        BvhInterior *interior = static_cast<BvhInterior *>(node);
350
351                        tStack.push(tPair(interior->mFront, d + 1));
352                        tStack.push(tPair(interior->mBack, d + 1));
353                }
354        }
355}
356
357
358SceneEntity **Bvh::GetGeometry(BvhNode *node, int &geometrySize) const
359{
360        geometrySize = node->CountPrimitives();
361        return mGeometry + node->mFirst;
362}
363
364
365bool Bvh::TestPlane(BvhNode *node, int i, bool &bIntersect)
366{
367        // do the test only if necessary
368        if (!(node->mPlaneMask[BvhNode::sCurrentState] & (1 << i)))
369        {
370                return true;
371        }
372
373
374        ////////
375        //-- test the n-vertex
376               
377        if ((node->mBox.GetDistance(sClipPlaneAABBVertexIndices[i * 2 + 0], sFrustum.mClipPlanes[i]) > 0.0f))
378        {
379                // outside
380                node->mPreferredPlane[BvhNode::sCurrentState] = i;
381                return false;
382        }
383
384        ////////////
385        //-- test the p-vertex
386
387        if (node->mBox.GetDistance(sClipPlaneAABBVertexIndices[i * 2 + 1], sFrustum.mClipPlanes[i]) <= 0.0f)
388        {
389                // completely inside: children don't need to check against this plane no more
390                node->mPlaneMask[BvhNode::sCurrentState] ^= 1 << i;
391        }
392        else
393        {
394                bIntersect = true;
395        }
396       
397        return true;
398}
399
400
401int     Bvh::IsWithinViewFrustum(BvhNode *node)
402{
403        bool bIntersect = false;
404
405        if (node->GetParent())
406                node->mPlaneMask[BvhNode::sCurrentState] = node->GetParent()->mPlaneMask[BvhNode::sCurrentState];
407
408        ////////
409        //-- apply frustum culling for the planes with index mPreferredPlane to 6
410
411        for (int i = node->mPreferredPlane[BvhNode::sCurrentState]; i < 6; ++ i)
412                if (!TestPlane(node, i, bIntersect)) return 0;
413       
414
415        //////////
416        //-- apply frustum culling for the planes with index 0 to mPreferredPlane
417
418        for (int i = 0; i < node->mPreferredPlane[BvhNode::sCurrentState]; ++ i)
419                if (!TestPlane(node, i, bIntersect)) return 0;
420       
421        return bIntersect ? -1 : 1;
422}
423
424
425static void CalcNPVertexIndices(const Frustum &frustum, int *indices)
426{
427        for (int i = 0; i < 6; ++ i)
428        {
429                // n-vertex
430                indices[i * 2 + 0] = AxisAlignedBox3::GetIndexNearestVertex(frustum.mClipPlanes[i].mNormal);
431                // p-vertex
432                indices[i * 2 + 1] = AxisAlignedBox3::GetIndexFarthestVertex(frustum.mClipPlanes[i].mNormal);   
433        }
434}
435
436
437void Bvh::InitFrame(Camera *cam, RenderState *state)
438{
439        // = 0011 1111 which means that at the beginning, all six planes have to frustum culled
440        mRoot->mPlaneMask[BvhNode::sCurrentState] = 0x3f;
441
442        cam->CalcFrustum(sFrustum);
443        CalcNPVertexIndices(sFrustum, sClipPlaneAABBVertexIndices);
444
445        // store near plane
446        sNearPlane = Plane3(cam->GetDirection(), cam->GetPosition());
447        sNear = cam->GetNear();
448
449        // update dynamic part of the hierarchy
450        //if (!mDynamicEntities.empty())
451        if (mDynamicGeometrySize)
452        {
453                UpdateDynamicBranch(mDynamicRoot);
454                UpdateDynamicBounds(state);
455        }
456}
457
458
459void Bvh::UpdateDistance(BvhNode *node) const
460{
461        // q: should we use distance to center rather than the distance to the near plane?
462        // distance to near plane can also be used for checking near plane intersection
463        //node->mDistance = sNearPlane.Distance(node->GetBox().Center());
464        node->mDistance = node->GetBox().GetMinDistance(sNearPlane);
465}
466
467
468float Bvh::CalcMaxDistance(BvhNode *node) const
469{
470#if 1
471        return node->GetBox().GetMaxDistance(sNearPlane);
472
473#else
474        // use bounding boxes of geometry to determine max dist
475        float maxDist = .0f;
476
477        int geometrySize;
478        SceneEntity **entities = GetGeometry(node, geometrySize);
479
480        for (int i = 0; i < geometrySize; ++ i)
481        {
482                SceneEntity *ent = entities[i];
483                float dist = ent->GetWorldBoundingBox().GetMaxDistance(sNearPlane);
484
485                if (dist > maxDist) maxDist = dist;
486        }
487
488        return maxDist;
489#endif
490}
491
492
493void Bvh::RenderBounds(BvhNode *node, RenderState *state, bool useTightBounds)
494{
495        // hack: use dummy contayiner as wrapper in order to use multibox function
496        static BvhNodeContainer dummy(1);
497        dummy[0] = node;
498        RenderBounds(dummy, state, useTightBounds);
499}
500
501
502int Bvh::RenderBounds(const BvhNodeContainer &nodes,
503                                          RenderState *state,
504                                          bool useTightBounds)
505{
506        int renderedBoxes;
507
508        if (!useTightBounds)
509        {
510                // if not using tight bounds, rendering boxes in immediate mode
511                // is preferable to vertex arrays (less setup time)
512                BvhNodeContainer::const_iterator nit, nit_end = nodes.end();
513                       
514                for (nit = nodes.begin(); nit != nit_end; ++ nit)
515                {
516                        RenderBoundingBoxImmediate((*nit)->GetBox());
517                }
518
519                renderedBoxes = (int)nodes.size();
520        }
521        else
522        {
523                renderedBoxes = PrepareBoundsWithDrawArrays(nodes);
524                RenderBoundsWithDrawArrays(renderedBoxes, state);
525        }
526
527        return renderedBoxes;
528}
529
530
531int Bvh::PrepareBoundsWithDrawArrays(const BvhNodeContainer &nodes)
532{
533        ///////////////////
534        //-- for the first time we come here => create vbo and indices
535
536        if (!mIndices)
537        {       
538                // create list of indices
539                CreateIndices();
540        }
541
542        if (mVboId == -1)
543        {       
544                // prepare the vbo
545                PrepareVertices();
546        }
547
548        ///////////////
549
550        int numNodes = 0;
551
552        BvhNodeContainer::const_iterator nit, nit_end = nodes.end();
553
554        for (nit = nodes.begin(); nit != nit_end; ++ nit)
555        {
556                BvhNode *node = *nit;
557        const int numIndices = node->mNumTestNodes * sNumIndicesPerBox;
558               
559                // copy indices
560                memcpy(mIndices + numNodes * sNumIndicesPerBox,
561                           mTestIndices + node->mIndicesPtr,
562                           numIndices * sizeof(unsigned int));
563
564                numNodes += node->mNumTestNodes;
565        }
566
567        return numNodes;
568}
569
570
571void Bvh::RenderBoundsWithDrawArrays(int numNodes, RenderState *state)
572{
573        /////////
574        //-- Render the vbo
575
576        if (state->GetCurrentVboId() != mVboId)
577        {
578                glBindBufferARB(GL_ARRAY_BUFFER_ARB, mVboId);
579                // set the vertex pointer to the vertex buffer
580                glVertexPointer(3, GL_FLOAT, 0, (char *)NULL); 
581
582                state->SetCurrentVboId(mVboId);
583        }
584
585        // we do use the last or the first index (they are generate and only used to connect strips)
586        int numElements = numNodes * sNumIndicesPerBox - 1;
587        // don't render first degenerate index
588        glDrawElements(GL_TRIANGLE_STRIP, numElements, GL_UNSIGNED_INT, mIndices + 1);
589}
590
591
592void Bvh::CreateIndices()
593{
594        // collect bvh nodes
595        BvhNodeContainer nodes;
596        CollectNodes(mRoot, nodes);
597
598        cout << "creating new indices" << endl;
599
600        int numMaxIndices = 0;
601
602        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
603
604        for (lit = nodes.begin(); lit != lit_end; ++ lit)
605        {
606                int offset = (*lit)->mNumTestNodes * sNumIndicesPerBox;
607#ifdef ALIGN_INDICES
608                // align with 32 in order to speed up memcopy
609                offset = (offset / 32) * 32 + 32;
610#endif
611                numMaxIndices += offset;
612        }
613
614        cout << "creating global indices buffer" << endl;
615
616        if (mIndices) delete [] mIndices;
617        if (mTestIndices) delete [] mTestIndices;
618
619        // global buffer: create it once so we don't have
620        // to allocate memory from the chunks of the node
621        mIndices = new unsigned int[numMaxIndices];
622        // create new index buffer for the individual nodes
623        mTestIndices = new unsigned int[numMaxIndices];
624       
625        mCurrentIndicesPtr = 0;
626
627        for (lit = nodes.begin(); lit != lit_end; ++ lit)
628        {
629                BvhNode *node = *lit;
630               
631                // resize array
632                node->mIndicesPtr = mCurrentIndicesPtr;
633                int numIndices = 0;
634
635                // the bounding boxes of the test nodes are rendered instead of the node itself
636                // => store their indices
637                for (int i = 0; i < node->mNumTestNodes; ++ i, numIndices += sNumIndicesPerBox)
638                {
639                        BvhNode *testNode = mTestNodes[node->mTestNodesIdx + i];
640
641                        // add indices to root node
642                        for (int j = 0; j < sNumIndicesPerBox; ++ j)
643                        {
644                                mTestIndices[mCurrentIndicesPtr + numIndices + j] = sIndices[j] + testNode->GetId() * 8;
645                        }
646                }
647
648                // align with 32
649#ifdef ALIGN_INDICES
650                const int offset = (numIndices / 32) * 32 + 32;
651#else
652                const int offset = numIndices;
653#endif
654                mCurrentIndicesPtr += offset;
655        }
656}
657
658
659void Bvh::ComputeIds()
660{
661        // collect all nodes
662        BvhNodeContainer nodes;
663        //CollectNodes(mRoot, nodes);
664        // first collect dynamic nodes so we make sure that they are in the beginning
665        CollectNodes(mDynamicRoot, nodes);
666        // then collect static nodes
667        CollectNodes(mStaticRoot, nodes);
668        // also add root
669        nodes.push_back(mRoot);
670
671        // assign ids to all nodes of the hierarchy
672        int i = 0;
673        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
674
675        for (lit = nodes.begin(); lit != lit_end; ++ lit, ++ i)
676        {
677                (*lit)->SetId(i);
678        }
679}
680
681
682void Bvh::PrepareVertices()
683{
684        // collect all nodes
685        BvhNodeContainer nodes;
686
687        nodes.reserve(GetNumNodes());
688        // first collect dynamic nodes so we make sure that they are in the beginning
689        CollectNodes(mDynamicRoot, nodes);
690        // then collect static nodes
691        CollectNodes(mStaticRoot, nodes);
692        // also add root
693        nodes.push_back(mRoot);
694
695        const unsigned int bufferSize = 8 * (int)nodes.size();
696        mVertices = new Vector3[bufferSize];
697       
698        int i = 0;
699       
700        // store bounding box vertices
701        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
702
703        for (lit = nodes.begin(); lit != lit_end; ++ lit, i += 8)
704        {
705                BvhNode *node = *lit;
706
707                for (int j = 0; j < 8; ++ j)
708                        (static_cast<Vector3 *>(mVertices))[node->GetId() * 8 + j] = node->GetBox().GetVertex(j);
709        }
710
711        glGenBuffersARB(1, &mVboId);
712        glBindBufferARB(GL_ARRAY_BUFFER_ARB, mVboId);
713        glVertexPointer(3, GL_FLOAT, 0, (char *)NULL);
714       
715        glBufferDataARB(GL_ARRAY_BUFFER_ARB,
716                            bufferSize * sizeof(Vector3),
717                            mVertices,
718                                        //GL_STATIC_DRAW_ARB);
719                                        GL_DYNAMIC_DRAW_ARB);
720
721        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
722
723        // data handled by graphics driver from now on
724        DEL_ARRAY_PTR(mVertices);
725
726        cout << "******** created vbos for tighter bounds *********" << endl;
727}
728
729
730void Bvh::UpdateDynamicBounds(RenderState *state)
731{
732        // vbos not created yet
733        if (mVboId == -1) return;
734
735        // collect all nodes
736        static BvhNodeContainer nodes;
737        nodes.clear();
738        //nodes.reserve(GetNumNodes());
739        CollectNodes(mDynamicRoot, nodes);
740
741        const unsigned int bufferSize = 8 * (int)nodes.size();
742       
743        if (!mVertices) mVertices = new Vector3[bufferSize];
744       
745        int i = 0;
746       
747        // store bounding box vertices
748        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
749
750        for (lit = nodes.begin(); lit != lit_end; ++ lit, i += 8)
751        {
752                BvhNode *node = *lit;
753
754                for (int j = 0; j < 8; ++ j)
755                        (static_cast<Vector3 *>(mVertices))[node->GetId() * 8 + j] = node->GetBox().GetVertex(j);
756        }
757       
758        if (state->GetCurrentVboId() != mVboId)
759        {
760                glBindBufferARB(GL_ARRAY_BUFFER_ARB, mVboId);
761                // set the vertex pointer to the vertex buffer
762                glVertexPointer(3, GL_FLOAT, 0, (char *)NULL); 
763
764                state->SetCurrentVboId(mVboId);
765        }
766
767        glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0,
768                                           bufferSize * sizeof(Vector3),
769                                           mVertices);
770
771        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
772}
773
774
775void Bvh::SetMaxDepthForTestingChildren(int maxDepth)
776{
777        if (maxDepth != mMaxDepthForTestingChildren)
778        {
779                mMaxDepthForTestingChildren = maxDepth;
780                RecomputeBounds();
781        }
782}
783
784
785void Bvh::SetAreaRatioThresholdForTestingChildren(float ratio)
786{
787        if (ratio != mAreaRatioThreshold)
788        {
789                mAreaRatioThreshold = ratio;
790                RecomputeBounds();
791        }
792}
793
794
795void Bvh::RecomputeBounds()
796{
797        // collect all nodes
798        BvhNodeContainer nodes;
799        CollectNodes(mRoot, nodes);
800
801        cout << "recomputing bounds, children will be tested in depth " << mMaxDepthForTestingChildren << endl;
802
803        int success = 0;
804        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
805
806        for (lit = nodes.begin(); lit != lit_end; ++ lit)
807        {
808                BvhNode *node = *lit;
809
810                // recreate list of nodes that will be queried as a proxy
811                if (CreateNodeRenderList(node))
812                        ++ success;
813        }
814
815        float p = 100.0f * (float)success / nodes.size();
816        cout << "created tighter bounds for " << p << " percent of the nodes" << endl;
817
818        // recreate indices used for indirect mode rendering
819        if (mIndices) CreateIndices();
820}
821
822       
823bool Bvh::CreateNodeRenderList(BvhNode *node)
824{
825        BvhNodeContainer children;
826
827        // collect nodes that will be tested instead of the leaf node
828        // in order to get a tighter bounding box test
829        CollectNodes(node, children, mMaxDepthForTestingChildren);
830       
831
832        // using the tighter bounds is not feasable in case
833        // that the tighter bounds represent nearly the same projected area
834        // as the old bounding box. Test this property using either
835        // volume or area heuristics
836
837        float vol = 0;
838        float area = 0;
839
840        BvhNodeContainer::const_iterator cit;
841
842        for (cit = children.begin(); cit != children.end(); ++ cit)
843                area += (*cit)->GetBox().SurfaceArea();
844
845        const float areaRatio = area / node->GetBox().SurfaceArea();
846       
847        bool success;
848
849        if (areaRatio < mAreaRatioThreshold)
850                success = true;
851        else
852        {
853                // hack: only store node itself
854                children.clear();
855                children.push_back(node);
856
857                success = false;
858        }
859
860        // the new test nodes are added at the end of the vector
861        node->mTestNodesIdx = (int)mTestNodes.size();
862
863        // use the collected nodes as proxy for the occlusion tests
864        for (cit = children.begin(); cit != children.end(); ++ cit)
865        {
866                BvhNode *child = *cit;
867                mTestNodes.push_back(child);
868        }
869
870        node->mNumTestNodes = (int)children.size();
871
872        return success;
873}
874
875
876void Bvh::ResetNodeClassifications()
877{
878        BvhNodeContainer nodes;
879
880        nodes.reserve(GetNumNodes());
881        CollectNodes(mRoot, nodes);
882
883        BvhNodeContainer::const_iterator lit, lit_end = nodes.end();
884
885        for (lit = nodes.begin(); lit != lit_end; ++ lit)
886        {
887                (*lit)->ResetVisibility();
888        }
889}
890
891
892void Bvh::ComputeBvhStats(BvhNode *root, BvhStats &bvhStats)
893{
894        bvhStats.Reset();
895        std::stack<BvhNode *> nodeStack;
896        nodeStack.push(root);
897
898        int numVirtualLeaves = 0;
899        int numGeometry = 0;
900
901        while (!nodeStack.empty())
902        {
903                BvhNode *node = nodeStack.top();
904                nodeStack.pop();
905
906                if (node->IsVirtualLeaf())
907                {
908                        ++ numVirtualLeaves;
909                        numGeometry += node->CountPrimitives();
910
911                        BvhLeaf *leaf = static_cast<BvhLeaf *>(node);
912
913                        bvhStats.mTriangles += CountTriangles(leaf);
914                        bvhStats.mLeafSA += leaf->mBox.SurfaceArea();
915                        bvhStats.mLeafVol += leaf->mBox.GetVolume();
916                }
917                else
918                {
919                        bvhStats.mInteriorSA += node->mBox.SurfaceArea();
920                        bvhStats.mInteriorVol += node->mBox.GetVolume();
921
922                        BvhInterior *interior = static_cast<BvhInterior *>(node);
923                       
924                        nodeStack.push(interior->mBack);
925                        nodeStack.push(interior->mFront);
926                }
927        }
928
929        bvhStats.mGeometryRatio = (float)numGeometry / numVirtualLeaves;
930        bvhStats.mTriangleRatio = (float)bvhStats.mTriangles / numVirtualLeaves;
931        bvhStats.mLeaves = numVirtualLeaves;
932}
933
934
935void Bvh::PrintBvhStats(const BvhStats &bvhStats) const
936{
937        cout << "\n============ BVH statistics =============" << endl;
938        cout << "interiorNodesSA = " << bvhStats.mInteriorSA / mRoot->mBox.SurfaceArea() << endl;
939        cout << "leafNodesSA = " << bvhStats.mLeafSA / mRoot->mBox.SurfaceArea() << endl;
940        cout << "interiorNodesVolume = " << bvhStats.mInteriorVol / mRoot->mBox.GetVolume() << endl;
941        cout << "leafNodesVolume = " << bvhStats.mLeafVol / mRoot->mBox.GetVolume() << endl;
942
943        cout << "geometry per leaf: " << bvhStats.mGeometryRatio << endl;
944        cout << "triangles per leaf: " << bvhStats.mTriangleRatio << endl;
945        cout << "===========================================" << endl << endl;
946}
947
948
949int Bvh::CountTriangles(BvhNode *node) const
950{
951        int numTriangles = 0;
952
953        for (int i = node->mFirst; i <= node->mLast; ++ i)
954        {
955                numTriangles += mGeometry[i]->CountNumTriangles(0);
956        }
957
958        return numTriangles;
959}
960
961
962float Bvh::GetArea(BvhNode *node) const
963{
964        return node->mArea;
965}
966
967
968void Bvh::UpdateNumLeaves(BvhNode *node) const
969{
970        if (node->IsLeaf())
971        {
972                node->mNumLeaves = 1;
973        }
974        else
975        {
976                BvhNode *f = static_cast<BvhInterior *>(node)->GetFront();
977                BvhNode *b = static_cast<BvhInterior *>(node)->GetBack();
978
979                UpdateNumLeaves(f);
980                UpdateNumLeaves(b);
981
982                node->mNumLeaves = f->mNumLeaves + b->mNumLeaves;
983        }
984}
985
986
987void Bvh::CollectVirtualLeaves(BvhNode *node, BvhNodeContainer &leaves)
988{
989        stack<BvhNode *> tStack;
990        tStack.push(node);
991
992        while (!tStack.empty())
993        {
994                BvhNode *node = tStack.top();
995                tStack.pop();
996
997                if (node->mIsVirtualLeaf)
998                {
999                        leaves.push_back(node);
1000                }
1001                else if (!node->IsLeaf())
1002                {
1003                        BvhInterior *interior = static_cast<BvhInterior *>(node);
1004
1005                        tStack.push(interior->mFront);
1006                        tStack.push(interior->mBack);
1007                }
1008        }
1009}
1010
1011
1012void Bvh::SetVirtualLeaves(int numTriangles)
1013{
1014        // first invalidate old virtual leaves
1015        BvhNodeContainer leaves;
1016        CollectVirtualLeaves(mRoot, leaves);
1017
1018        BvhNodeContainer::const_iterator bit, bit_end = leaves.end();
1019
1020        for (bit = leaves.begin(); bit != bit_end; ++ bit)
1021        {
1022                (*bit)->mIsVirtualLeaf = false;
1023        }
1024
1025        mNumVirtualNodes = 0;
1026        // assign new virtual leaves based on specified #triangles per leaf
1027        std::stack<BvhNode *> nodeStack;
1028        nodeStack.push(mRoot);
1029
1030        while (!nodeStack.empty())
1031        {
1032                BvhNode *node = nodeStack.top();
1033                nodeStack.pop();
1034
1035                ++ mNumVirtualNodes;
1036
1037                if (node->IsLeaf())
1038                {
1039                        BvhLeaf *leaf = static_cast<BvhLeaf *>(node);
1040                        leaf->mIsVirtualLeaf = true;
1041                }
1042                else
1043                {
1044                        BvhInterior *interior = static_cast<BvhInterior *>(node);
1045
1046                        BvhNode *f = interior->mFront;
1047                        BvhNode *b = interior->mBack;
1048
1049                        if (node->mIsMaxDepthForVirtualLeaf ||
1050                                (CountTriangles(node) <= numTriangles))
1051                        {
1052                                 node->mIsVirtualLeaf = true;
1053                        }
1054                        else
1055                        {
1056                                nodeStack.push(interior->mBack);
1057                                nodeStack.push(interior->mFront);
1058                        }
1059                }
1060        }
1061
1062        /// Reset the node states
1063        ResetNodeClassifications();
1064}
1065
1066
1067void Bvh::ComputeMaxDepthForVirtualLeaves()
1068{
1069        // We initialize the maximal depth for virtual leaves
1070        // of this bvh, i.e., the nodes that are used as
1071        // leaves of the hierarchy during traversal.
1072
1073        // Initially they are set either
1074        // a) to the real leaves of the hierarchy or
1075        // b) the point where the subdivision on object level ends
1076        // and the subsequent nodes are just used to provide tighter bounds
1077        // (see article for the notations)
1078
1079        std::stack<BvhNode *> nodeStack;
1080        nodeStack.push(mRoot);
1081
1082        while (!nodeStack.empty())
1083        {
1084                BvhNode *node = nodeStack.top();
1085                nodeStack.pop();
1086
1087                if (node->IsLeaf())
1088                {
1089                        node->mIsMaxDepthForVirtualLeaf = true;
1090                }
1091                else
1092                {
1093                        BvhInterior *interior = static_cast<BvhInterior *>(node);
1094
1095                        BvhNode *f = interior->mFront;
1096                        BvhNode *b = interior->mBack;
1097
1098                        if ((f->mFirst == b->mFirst) && (f->mLast == b->mLast))
1099                        {
1100                                // point reached where beyond there would be no further reduction
1101                                // as both subtrees contain the same objects => stop here
1102                                // The tree beyond the current node is used to describe
1103                                // tighter bounds on the geometry contained  in it
1104                                node->mIsMaxDepthForVirtualLeaf = true;
1105                        }
1106                        else
1107                        {
1108                                nodeStack.push(f);
1109                                nodeStack.push(b);
1110                        }
1111                }
1112        }
1113}
1114
1115
1116// this function must be called once after hierarchy creation
1117void Bvh::PostProcess()
1118{
1119        CreateRoot();
1120
1121        ComputeMaxDepthForVirtualLeaves();
1122        // set virtual leaves for specified number of triangles
1123        SetVirtualLeaves(INITIAL_TRIANGLES_PER_VIRTUAL_LEAVES);
1124        /// for each node compute the number of leaves under this node
1125        UpdateNumLeaves(mRoot);
1126        // compute new ids
1127        ComputeIds();
1128        // specify bounds for occlusion tests
1129        RecomputeBounds();
1130
1131        mBox = mRoot->GetBox();
1132
1133        // compute and print stats
1134        ComputeBvhStats(mRoot, mBvhStats);
1135        PrintBvhStats(mBvhStats);
1136
1137        if (mDynamicGeometrySize)
1138        {
1139                BvhStats bvhStats;
1140                ComputeBvhStats(mDynamicRoot, bvhStats);
1141
1142                cout << "\n=========== Dynamic BVH statistics: =========" << endl;
1143                cout << "leaves = " << bvhStats.mLeaves << endl;
1144                cout << "interiorNodesSA = " << bvhStats.mInteriorSA << endl;
1145                cout << "leafNodesSA = " << bvhStats.mLeafSA << endl;
1146                cout << "interiorNodesVolume = " << bvhStats.mInteriorVol  << endl;
1147                cout << "leafNodesVolume = " << bvhStats.mLeafVol << endl;
1148
1149                cout << "geometry per leaf: " << bvhStats.mGeometryRatio << endl;
1150                cout << "triangles per leaf: " << bvhStats.mTriangleRatio << endl;
1151                cout << "=============================================" << endl << endl;
1152        }
1153}
1154
1155
1156void Bvh::RenderBoundingBoxImmediate(const AxisAlignedBox3 &box)
1157{
1158        const Vector3 l = box.Min();
1159        const Vector3 u = box.Max();
1160
1161        ///////////
1162        //-- render AABB as triangle strips
1163
1164        glBegin(GL_TRIANGLE_STRIP);
1165
1166        // render first half of AABB
1167        glVertex3f(l.x, l.y, u.z);
1168        glVertex3f(u.x, l.y, u.z);
1169        glVertex3f(l.x, u.y, u.z);
1170        glVertex3f(u.x, u.y, u.z);
1171        glVertex3f(l.x, u.y, l.z);
1172        glVertex3f(u.x, u.y, l.z);
1173        glVertex3f(l.x, l.y, l.z);
1174        glVertex3f(u.x, l.y, l.z);
1175
1176        glPrimitiveRestartNV();
1177
1178        // render second half of AABB
1179        glVertex3f(l.x, u.y, u.z);
1180        glVertex3f(l.x, u.y, l.z);
1181        glVertex3f(l.x, l.y, u.z);
1182        glVertex3f(l.x, l.y, l.z);
1183        glVertex3f(u.x, l.y, u.z);
1184        glVertex3f(u.x, l.y, l.z);
1185        glVertex3f(u.x, u.y, u.z);
1186        glVertex3f(u.x, u.y, l.z);
1187
1188        glEnd();
1189}
1190
1191
1192static void RenderBoxForViz(const AxisAlignedBox3 &box)
1193{
1194        glBegin(GL_LINE_LOOP);
1195        glVertex3d(box.Min().x, box.Max().y, box.Min().z);
1196        glVertex3d(box.Max().x, box.Max().y, box.Min().z);
1197        glVertex3d(box.Max().x, box.Min().y, box.Min().z);
1198        glVertex3d(box.Min().x, box.Min().y, box.Min().z);
1199        glEnd();
1200
1201        glBegin(GL_LINE_LOOP);
1202        glVertex3d(box.Min().x, box.Min().y, box.Max().z);
1203        glVertex3d(box.Max().x, box.Min().y, box.Max().z);
1204        glVertex3d(box.Max().x, box.Max().y, box.Max().z);
1205        glVertex3d(box.Min().x, box.Max().y, box.Max().z);
1206        glEnd();
1207
1208        glBegin(GL_LINE_LOOP);
1209        glVertex3d(box.Max().x, box.Min().y, box.Min().z);
1210        glVertex3d(box.Max().x, box.Min().y, box.Max().z);
1211        glVertex3d(box.Max().x, box.Max().y, box.Max().z);
1212        glVertex3d(box.Max().x, box.Max().y, box.Min().z);
1213        glEnd();
1214
1215        glBegin(GL_LINE_LOOP);
1216        glVertex3d(box.Min().x, box.Min().y, box.Min().z);
1217        glVertex3d(box.Min().x, box.Min().y, box.Max().z);
1218        glVertex3d(box.Min().x, box.Max().y, box.Max().z);
1219        glVertex3d(box.Min().x, box.Max().y, box.Min().z);
1220        glEnd();
1221
1222        glBegin(GL_LINE_LOOP);
1223        glVertex3d(box.Min().x, box.Min().y, box.Min().z);
1224        glVertex3d(box.Max().x, box.Min().y, box.Min().z);
1225        glVertex3d(box.Max().x, box.Min().y, box.Max().z);
1226        glVertex3d(box.Min().x, box.Min().y, box.Max().z);
1227        glEnd();
1228
1229        glBegin(GL_LINE_LOOP);
1230        glVertex3d(box.Min().x, box.Max().y, box.Min().z);
1231        glVertex3d(box.Max().x, box.Max().y, box.Min().z);
1232        glVertex3d(box.Max().x, box.Max().y, box.Max().z);
1233        glVertex3d(box.Min().x, box.Max().y, box.Max().z);
1234
1235        glEnd();
1236}
1237
1238
1239static Technique GetVizTechnique()
1240{
1241        Technique tech;
1242        tech.Init();
1243
1244        //tech.SetLightingEnabled(false);
1245        //tech.SetDepthWriteEnabled(false);
1246
1247        tech.SetEmmisive(RgbaColor(1.0f, 1.0f, 1.0f, 1.0f));
1248        tech.SetDiffuse(RgbaColor(1.0f, 1.0f, 1.0f, 1.0f));
1249        tech.SetAmbient(RgbaColor(1.0f, 1.0f, 1.0f, 1.0f));
1250
1251        return tech;
1252}
1253
1254
1255void Bvh::RenderBoundsForViz(BvhNode *node,
1256                                                         RenderState *state,
1257                                                         bool useTightBounds)
1258{
1259        Technique *oldTech = state->GetState();
1260        // we set a simple material
1261        static Technique boxMat = GetVizTechnique();
1262        boxMat.Render(state);
1263
1264        if (!useTightBounds)
1265        {
1266                RenderBoxForViz(node->GetBox());
1267                /*glPolygonMode(GL_FRONT, GL_LINE);
1268                RenderBoundingBoxImmediate(node->GetBox());
1269                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);*/
1270        }
1271        else
1272        {
1273                for (int i = 0; i < node->mNumTestNodes; ++ i)
1274                {
1275                        RenderBoxForViz(mTestNodes[node->mTestNodesIdx + i]->GetBox());
1276                }
1277        }
1278
1279        if (oldTech) oldTech->Render(state);
1280}
1281
1282
1283
1284////////////////////////
1285//-- functions for construction of the dynamic hierarchy
1286
1287int Bvh::SortTriangles(BvhLeaf *leaf,
1288                                           int axis,
1289                                           float position
1290                                           )
1291{
1292        int i = leaf->mFirst;
1293        int j = leaf->mLast;
1294
1295    while (1)
1296        {
1297                while (mGeometry[i]->GetWorldCenter()[axis] < position) ++ i;
1298                while (position < mGeometry[j]->GetWorldCenter()[axis]) -- j;
1299
1300                // sorting finished
1301                if (i >= j) break;
1302
1303                // swap entities
1304                swap(mGeometry[i], mGeometry[j]);
1305                       
1306                ++ i;
1307                -- j;
1308        }
1309
1310        return j;
1311}
1312
1313
1314int Bvh::SortTrianglesSpatialMedian(BvhLeaf *leaf,
1315                                                                        int axis
1316                                                                        )
1317{
1318        // spatial median
1319        float m = leaf->mBox.Center()[axis];
1320        return SortTriangles(leaf, axis, m);
1321}
1322
1323
1324BvhNode *Bvh::SubdivideLeaf(BvhLeaf *leaf,
1325                                                        int parentAxis
1326                                                        )
1327{
1328        if (TerminationCriteriaMet(leaf))
1329        {
1330                //cout << "leaf constructed:" << leaf->mBox << " " << leaf->mFirst << " " << leaf->mLast << endl;
1331                return leaf;
1332        }
1333
1334        //const int axis = (parentAxis + 1) % 3;
1335        const int axis = leaf->mBox.MajorAxis();
1336
1337        const int scale = 20;
1338
1339        // position of the split in the partailly sorted array of triangles
1340        // corresponding to this leaf
1341        int split = -1;
1342        float pos = -1.0f;
1343       
1344        // Spatial median subdivision
1345        split = SortTrianglesSpatialMedian(leaf, axis);
1346        pos = leaf->mBox.Center()[axis];
1347       
1348        if (split == leaf->mLast)
1349        {
1350                // no split could be achieved => just halve number of objects
1351                split = (leaf->mLast - leaf->mFirst) / 2;
1352                cerr << "no reduction " << leaf->CountPrimitives() << " " << leaf->mFirst << " " << leaf->mLast << endl;
1353        }
1354
1355        // create two more nodes
1356        mNumNodes += 2;
1357        BvhInterior *parent = new BvhInterior(leaf->GetParent());
1358        parent->mFirst = leaf->mFirst;
1359        parent->mLast = leaf->mLast;
1360       
1361        parent->mAxis = axis;
1362        parent->mBox = leaf->mBox;
1363        parent->mDepth = leaf->mDepth;
1364
1365        BvhLeaf *front = new BvhLeaf(parent);
1366
1367        parent->mBack = leaf;
1368        parent->mFront = front;
1369               
1370        // now assign the triangles to the subnodes
1371        front->mFirst = split + 1;
1372        front->mLast = leaf->mLast;
1373        front->mDepth = leaf->mDepth + 1;
1374
1375        leaf->mLast = split;
1376        leaf->mDepth = front->mDepth;
1377        leaf->mParent = parent;
1378       
1379        front->mBox = ComputeBoundingBox(mGeometry + front->mFirst, front->CountPrimitives());
1380        leaf->mBox = ComputeBoundingBox(mGeometry + leaf->mFirst, leaf->CountPrimitives());
1381
1382        // recursively continue subdivision
1383        parent->mBack = SubdivideLeaf(static_cast<BvhLeaf *>(parent->mBack), axis);
1384        parent->mFront = SubdivideLeaf(static_cast<BvhLeaf *>(parent->mFront), axis);
1385       
1386        return parent;
1387}
1388
1389
1390bool Bvh::TerminationCriteriaMet(BvhLeaf *leaf) const
1391{
1392        const bool criteriaMet =
1393                (leaf->mDepth > mMaxDepthForDynamicBranch) ||
1394                (leaf->CountPrimitives() == 1);
1395
1396        return criteriaMet;
1397}
1398
1399
1400void Bvh::UpdateDynamicBranch(BvhNode *node)
1401{
1402        if (node->IsLeaf())
1403        {
1404                int numEntities;
1405                SceneEntity **entities = GetGeometry(node, numEntities);
1406
1407                node->mBox = ComputeBoundingBox(entities, numEntities);
1408                //cout << "box: " << node->mBox << endl;
1409        }
1410        else
1411        {
1412                BvhNode *f = static_cast<BvhInterior *>(node)->GetFront();
1413                BvhNode *b = static_cast<BvhInterior *>(node)->GetBack();
1414
1415                UpdateDynamicBranch(f);
1416                UpdateDynamicBranch(b);
1417
1418                node->mBox = f->mBox;
1419                node->mBox.Include(b->mBox);
1420        }
1421}
1422
1423
1424void Bvh::CreateDynamicBranch()
1425{
1426        // the bvh has two main branches
1427        // a static branch (the old root), and adynamic branch
1428        // we create a 'dynamic' leaf which basically is a container
1429        // for all dynamic objects underneath
1430
1431        // the bounding boxes of the dynamic tree must be updated
1432        // once each frame in order to be able to incorporate
1433        // the movements of the objects within
1434
1435        DEL_PTR(mDynamicRoot);
1436
1437        BvhLeaf *l = new BvhLeaf(mRoot);
1438        mDynamicRoot = l;
1439
1440        l->mBox = ComputeBoundingBox(mGeometry + mStaticGeometrySize, (int)mDynamicGeometrySize);
1441
1442        l->mFirst = (int)mStaticGeometrySize;
1443        l->mLast = (int)mGeometrySize - 1;
1444        l->mArea = l->mBox.SurfaceArea();
1445       
1446        cout << "updating dynamic branch " << l->mFirst << " " << l->mLast << endl;
1447
1448        if (mDynamicGeometrySize)
1449                mDynamicRoot = SubdivideLeaf(l, 0);
1450}
1451
1452
1453bool Bvh::IntersectsNearPlane(BvhNode *node) const
1454{
1455        // note: we have problems with large scale object penetrating the near plane
1456        // (e.g., objects in the distance which are always handled to be visible)
1457        // especially annoying is this problem when using the frustum
1458        // fitting on the visible objects for shadow mapping
1459        // but don't see how to solve this issue without using costly calculations
1460       
1461        // we stored the near plane distance => we can use it also here
1462        float distanceToNearPlane = node->GetDistance();
1463        //float distanceToNearPlane = node->GetBox().GetMinDistance(sNearPlane);
1464       
1465        return (distanceToNearPlane < sNear);
1466}
1467
1468
1469void Bvh::CreateRoot()
1470{
1471        // create new root
1472        mRoot = new BvhInterior(NULL);
1473
1474        // the separation is a purely logical one
1475        // the bounding boxes of the child nodes are
1476        // identical to those of the root node
1477
1478        mRoot->mBox = mStaticRoot->mBox;
1479        mRoot->mBox.Include(mDynamicRoot->mBox);
1480
1481        mRoot->mArea = mRoot->mBox.SurfaceArea();
1482
1483        mRoot->mFirst = 0;
1484        mRoot->mLast = (int)mGeometrySize - 1;
1485
1486        cout<<"f: " << mRoot->mFirst<< " l: " <<mRoot->mLast << endl;
1487        // add static root on left subtree
1488        mRoot->mFront = mStaticRoot;
1489        mStaticRoot->mParent = mRoot;
1490
1491        // add dynamic root on left subtree
1492        mRoot->mBack = mDynamicRoot;
1493        mDynamicRoot->mParent = mRoot;
1494}
1495
1496
1497}
Note: See TracBrowser for help on using the repository browser.