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

Revision 3123, 35.4 KB checked in by mattausch, 16 years ago (diff)

working on ssao for dynamic objects, found error with tight bounds

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