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

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