source: GTP/trunk/App/Demos/Vis/Teapots/RenderTraverser.cpp @ 937

Revision 937, 10.9 KB checked in by mattausch, 18 years ago (diff)
Line 
1#include "RenderTraverser.h"
2#include "glInterface.h"
3#include "Timers.h"
4
5extern "C"
6{
7        #include "MathStuff.h"
8}
9
10
11RenderTraverser::RenderTraverser(): mFrameID(1), mVisibilityThreshold(0),
12mHierarchyRoot(NULL), mOcclusionQueries(NULL), mCurrentQueryIdx(0), mIsQueryMode(false),
13mNumTraversedNodes(0), mNumQueryCulledNodes(0), mNumFrustumCulledNodes(0),
14mRenderTime(0), mNumRenderedGeometry(0), mUseOptimization(true),
15mUseArbQueries(false)
16{
17}
18
19RenderTraverser::~RenderTraverser()
20{
21        DelQueries();
22}
23
24void RenderTraverser::Render(int mode)
25{
26        mDistanceQueue.push(mHierarchyRoot);
27
28        long startTime = getTime();
29
30        Preprocess();
31
32        switch(mode)
33        {
34                case RENDER_CULL_FRUSTUM:
35                        RenderCullFrustum();
36            break;
37                case RENDER_STOP_AND_WAIT:
38                        RenderStopAndWait();
39                        break;
40                case RENDER_COHERENT:
41                        RenderCoherentWithQueue();
42                        break;
43                default:
44                        RenderCullFrustum();
45                        break;
46        }
47
48        mFrameID ++;
49       
50
51        long endTime = getTime();
52        mRenderTime = endTime - startTime;
53
54        finishTiming();
55}
56
57/**
58        This is the standard render traversal algorithm doing only frustum culling
59*/
60void RenderTraverser::RenderCullFrustum()
61{
62        while(!mDistanceQueue.empty())
63        {
64                HierarchyNode *node = mDistanceQueue.top();
65                mDistanceQueue.pop();
66       
67                // interesting for the visualization, so rest and set
68                node->SetVisible(false);
69                mNumTraversedNodes ++;
70
71                bool intersects;
72       
73                if(InsideViewFrustum(node, intersects))
74                {
75                        // update node's visited flag => needed for rendering
76                        // so set it also here
77                        node->SetLastVisited(mFrameID);
78                        node->SetVisible(true);
79                        TraverseNode(node);
80                }
81                else
82                {
83                        mNumFrustumCulledNodes ++;
84                }
85        }
86}
87
88void RenderTraverser::RenderStopAndWait()
89{
90        while(! mDistanceQueue.empty())
91        {
92                HierarchyNode *node = mDistanceQueue.top();
93                mDistanceQueue.pop();
94                mNumTraversedNodes ++;
95                // interesting for the visualization, so rest and set
96                node->SetVisible(false);
97
98                bool intersects;
99               
100                if(InsideViewFrustum(node, intersects))
101                {
102                        // update node's visited flag
103                        node->SetLastVisited(mFrameID);
104
105                        // for near plane intersecting abbs possible
106                        // wrong results => skip occlusion query
107                        if(intersects)
108                        {
109                                node->SetVisible(true);
110                                TraverseNode(node);
111                        }
112                        else
113                        {
114                                IssueOcclusionQuery(node, false);
115                               
116                                // wait if result not available
117                                int visiblePixels = GetOcclusionQueryResult(node);
118                               
119                                // node visible
120                                if(visiblePixels > mVisibilityThreshold)
121                                {
122                                        node->SetVisible(true);
123                                        TraverseNode(node);
124                                }
125                                else
126                                {
127                                        mNumQueryCulledNodes ++;
128                                }
129                        }                                       
130                }
131                else
132                {
133                        mNumFrustumCulledNodes ++;
134                }
135        }
136}
137
138void RenderTraverser::RenderCoherentWithQueue()
139{
140        QueryQueue queryQueue;
141
142        //-- PART 1: process finished occlusion queries
143        while (!mDistanceQueue.empty() || !queryQueue.empty())
144        {
145                while (!queryQueue.empty() &&
146                           (ResultAvailable(queryQueue.front()) || mDistanceQueue.empty()))
147                {
148                        HierarchyNode *node = queryQueue.front();
149                        queryQueue.pop();
150                       
151                        // wait until result available
152                        int visiblePixels = GetOcclusionQueryResult(node);
153
154                        if(visiblePixels > mVisibilityThreshold)
155                        {
156                                PullUpVisibility(node);
157                                TraverseNode(node);
158                        }
159                        else
160                        {
161                                mNumQueryCulledNodes ++;
162                        }
163                }       
164
165                //-- PART 2: hierarchical traversal
166                if (!mDistanceQueue.empty())
167                {
168                        HierarchyNode *node = mDistanceQueue.top();
169
170                        mDistanceQueue.pop();
171       
172                        mNumTraversedNodes ++;
173
174                        bool intersects;
175                       
176                        if (InsideViewFrustum(node, intersects))
177                        {
178                                // for near plane intersecting bounding box possible
179                                // wrong results => skip occlusion query
180                                if(intersects)
181                                {
182                                        // update node's visited flag
183                                        node->SetLastVisited(mFrameID);
184                                        node->SetVisible(true);
185                                        PullUpVisibility(node);
186                                        TraverseNode(node);
187                                }
188                                else
189                                {               
190                                        // identify previously visible nodes
191                                        bool wasVisible = node->Visible() && (node->LastVisited() == mFrameID - 1);
192                                       
193                                        // identify nodes that we cannot skip queries for
194                                        bool leafOrWasInvisible = (node->LastVisited() != mFrameID) && (!wasVisible || node->IsLeaf());
195
196                                        // reset node's visibility classification
197                                        node->SetVisible(false);
198
199                                        // update node's visited flag
200                                        node->SetLastVisited(mFrameID);
201                                       
202                                        // skip testing previously visible interior nodes
203                                        if(leafOrWasInvisible)
204                                        {
205                                                IssueOcclusionQuery(node, wasVisible);
206                                                queryQueue.push(node);
207                                        }
208                                       
209                                        // always traverse a node if it was visible
210                                        if(wasVisible)
211                                                TraverseNode(node);
212                                }
213                        }
214                        else
215                        {
216                                // for stats
217                                mNumFrustumCulledNodes ++;
218                        }
219                }
220        }
221}
222       
223void RenderTraverser::TraverseNode(HierarchyNode *node)
224{
225        if(node->IsLeaf())
226                mNumRenderedGeometry += node->Render();
227        else
228        {
229                // for non leafs this renders only the bounding volume (if the flag is set)
230                //node->Render();
231                mDistanceQueue.push(node->GetLeftChild());
232                mDistanceQueue.push(node->GetRightChild());
233        }
234}
235
236void RenderTraverser::RenderVisualization()
237{
238        mDistanceQueue.push(mHierarchyRoot);
239
240        while(! mDistanceQueue.empty())
241        {
242                HierarchyNode *node = mDistanceQueue.top();
243                mDistanceQueue.pop();
244
245                // identify previously visible nodes
246                bool wasVisible = node->Visible() && (node->LastVisited() == mFrameID - 1);
247
248                if(wasVisible)
249                        TraverseNode(node);
250                else
251                {
252                        // also render culled nodes
253                        glColor3f(1.0,0.0,0.0);
254                        node->RenderBoundingVolumeForVisualization();           
255                }
256        }
257}
258
259
260void RenderTraverser::PullUpVisibility(HierarchyNode *node)
261{
262        while(node && !node->Visible())
263        {
264                node->SetVisible(true);
265                node = node->GetParent();
266        }
267}
268
269bool RenderTraverser::ResultAvailable(HierarchyNode *node) const
270{
271        unsigned int result;
272        if (mUseArbQueries)
273        {
274                glGetQueryObjectuivARB(node->GetOcclusionQuery(),
275                                                           GL_QUERY_RESULT_AVAILABLE_ARB, &result);
276        }
277        else
278        {
279               
280                glGetOcclusionQueryuivNV(node->GetOcclusionQuery(),
281                                                                 GL_PIXEL_COUNT_AVAILABLE_NV, &result);
282
283        }
284       
285        return (result == GL_TRUE);
286}
287
288void RenderTraverser::SetHierarchy(HierarchyNode *sceneRoot)
289{
290        mHierarchyRoot = sceneRoot;
291
292        DelQueries();
293}
294
295HierarchyNode *RenderTraverser::GetHierarchy()
296{
297        return mHierarchyRoot;
298}
299
300unsigned int RenderTraverser::GetOcclusionQueryResult(HierarchyNode *node) const
301{
302        unsigned int result;
303
304        if (mUseArbQueries)
305        {
306                glGetQueryObjectuivARB(node->GetOcclusionQuery(), GL_QUERY_RESULT_ARB, &result);
307        }
308        else
309        {
310                glGetOcclusionQueryuivNV(node->GetOcclusionQuery(), GL_PIXEL_COUNT_NV, &result);
311        }
312
313        return result;
314}
315
316
317void RenderTraverser::Switch2GLQueryState()
318{       
319        // boolean used to avoid unnecessary state changes
320        if(!mIsQueryMode)
321        {
322                glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
323                glDepthMask(GL_FALSE);
324                glDisable(GL_LIGHTING);
325                mIsQueryMode = true;
326        }
327}
328
329
330void RenderTraverser::Switch2GLRenderState()
331{
332        // boolean used to avoid unnecessary state changes
333        if(mIsQueryMode)
334        {
335                // switch back to rendermode           
336                glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
337                glDepthMask(GL_TRUE);
338                glEnable(GL_LIGHTING);
339                mIsQueryMode = false;
340        }
341}
342
343void RenderTraverser::IssueOcclusionQuery(HierarchyNode *node, const bool wasVisible)
344{
345        // get next available test id
346        unsigned int occlusionQuery = mOcclusionQueries[mCurrentQueryIdx ++];
347       
348        node->SetOcclusionQuery(occlusionQuery);
349
350        // do the actual occlusion query for this node
351        if (mUseArbQueries)
352        {
353                glBeginQueryARB(GL_SAMPLES_PASSED_ARB, occlusionQuery);
354        }
355        else
356        {
357                glBeginOcclusionQueryNV(occlusionQuery);
358        }
359       
360        // if leaf and was visible => will be rendered anyway, thus we
361        // can also test with the real geometry
362        if(node->IsLeaf() && wasVisible && mUseOptimization)
363        {
364                mNumRenderedGeometry += node->Render();
365        }
366        else
367        {
368                // change state so the bounding box gets not actually rendered on the screen
369                Switch2GLQueryState();
370                node->RenderBoundingVolume();
371                Switch2GLRenderState();
372        }
373
374        if (mUseArbQueries)
375        {
376                glEndQueryARB(GL_SAMPLES_PASSED_ARB);
377        }
378        else
379        {
380                glEndOcclusionQueryNV();
381        }
382}
383
384void RenderTraverser::Preprocess()
385{
386        if (!mOcclusionQueries)
387        {
388                mOcclusionQueries = new unsigned int[mHierarchyRoot->GetNumHierarchyNodes()];
389
390                // generate ids for occlusion test
391                if (mUseArbQueries)
392                {
393                        glGenQueriesARB(mHierarchyRoot->GetNumHierarchyNodes(), mOcclusionQueries);
394                }
395                else
396                {
397                        glGenOcclusionQueriesNV(mHierarchyRoot->GetNumHierarchyNodes(), mOcclusionQueries);
398                }
399        }
400
401        // view frustum planes for view frustum culling
402        calcViewFrustumPlanes(&mClipPlanes, mProjViewMatrix);
403        calcAABNPVertexIndices(mNPVertexIndices, mClipPlanes);
404
405        mCurrentQueryIdx = 0;
406
407        // reset statistics
408        mNumTraversedNodes = 0;
409        mNumQueryCulledNodes = 0;
410        mNumFrustumCulledNodes = 0;
411        mNumRenderedGeometry = 0;
412}
413
414
415void RenderTraverser::DelQueries()
416{
417        if (!mOcclusionQueries)
418                return;
419
420        // tell the driver that the occlusion queries won't be needed any more
421        if (mUseArbQueries)
422        {
423                glDeleteQueriesARB(mHierarchyRoot->GetNumHierarchyNodes(), mOcclusionQueries);
424        }
425        else
426        {
427                glDeleteOcclusionQueriesNV(mHierarchyRoot->GetNumHierarchyNodes(), mOcclusionQueries);
428        }
429
430        delete [] mOcclusionQueries;
431
432        mOcclusionQueries = NULL;
433}
434
435
436void RenderTraverser::SetViewpoint(Vector3 const &viewpoint)
437{
438        copyVector3(mViewpoint, viewpoint);
439}
440       
441
442void RenderTraverser::SetProjViewMatrix(Matrix4x4 const &projViewMatrix)
443{
444        copyMatrix(mProjViewMatrix, projViewMatrix);
445}
446
447
448bool RenderTraverser::InsideViewFrustum(HierarchyNode *node, bool &intersects)
449{
450        Vector3x8 vertices;
451       
452        bool intersect = false;
453
454        calcAABoxPoints(vertices, node->GetBoundingVolume());
455
456        // simply test all 6 vertices
457        for (int i = 0; i < 6; i++)
458        {               
459                // test the n-vertex
460                // note: the calcAABNearestVertexId should be preprocessed
461                if(!pointBeforePlane(mClipPlanes.plane[i], vertices[mNPVertexIndices[i * 2]]))
462                {
463                        // outside
464                        return false;
465                }
466        }
467
468        // test if bounding box is intersected by nearplane (using the p-vertex)
469        intersects = (!pointBeforePlane(mClipPlanes.plane[5], vertices[mNPVertexIndices[11]]));
470
471        // -- get vector from viewpoint to center of bounding volume
472        Vector3 vec;
473        calcAABoxCenter(vec, node->GetBoundingVolume());
474        diffVector3(vec, vec, mViewpoint);
475
476        // compute distance from nearest point to viewpoint
477        diffVector3(vec, vertices[calcAABNearestVertexIdx(vec)], mViewpoint);
478        node->SetDistance(squaredLength(vec));
479       
480        return true;
481}
482
483
484void RenderTraverser::SetVisibilityThreshold(int threshold)
485{
486        mVisibilityThreshold = threshold;
487}
488
489void RenderTraverser::SetUseArbQueries(const bool useArbQueries)
490{
491        DelQueries();
492        mUseArbQueries = useArbQueries;
493}
494
495bool RenderTraverser::GetUseArbQueries() const
496{
497        return mUseArbQueries;
498}
499
500long RenderTraverser::GetRenderTime()
501{
502        return mRenderTime;
503}
504
505int RenderTraverser::GetNumTraversedNodes()
506{
507        return mNumTraversedNodes;
508}
509
510int RenderTraverser::GetNumQueryCulledNodes()
511{
512        return mNumQueryCulledNodes;
513}
514
515int RenderTraverser::GetNumFrustumCulledNodes()
516{
517        return mNumFrustumCulledNodes;
518}
519
520
521int RenderTraverser::GetNumRenderedGeometry()
522{
523        return mNumRenderedGeometry;
524}
525
526
527int RenderTraverser::GetVisibilityThreshold()
528{
529        return mVisibilityThreshold;
530}
531
532void RenderTraverser::SetUseOptimization(bool useOptimization)
533{
534        mUseOptimization = useOptimization;
535        printf("using opt %d\n", mUseOptimization);
536}
Note: See TracBrowser for help on using the repository browser.