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

Revision 3201, 35.0 KB checked in by mattausch, 16 years ago (diff)

still searching for error

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