root/OpenSceneGraph/trunk/src/osg/OcclusionQueryNode.cpp @ 10933

Revision 10933, 25.2 kB (checked in by robert, 5 years ago)

From Paul Martz, "OcclusionQueryNode? was incorrectly marking itself as DYNAMIC, so I removed that code. And I also found some uses of dynamic_cast that could be changed to static_cast.
"

RevLine 
[7731]1//
2// Copyright (C) 2007 Skew Matrix Software LLC (http://www.skew-matrix.com)
3//
4// This library is open source and may be redistributed and/or modified under 
5// the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
6// (at your option) any later version.  The full license is in LICENSE file
7// included with this distribution, and on the openscenegraph.org website.
8//
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// OpenSceneGraph Public License for more details.
13//
14
15#include <osg/OcclusionQueryNode>
16#include <OpenThreads/ScopedLock>
17#include <osg/Timer>
18#include <osg/Notify>
19#include <osg/CopyOp>
20#include <osg/Vec3>
21#include <osg/MatrixTransform>
22#include <osg/Group>
23#include <osg/Geode>
24#include <osg/Geometry>
25#include <osg/BoundingBox>
26#include <osg/BoundingSphere>
27#include <osg/Referenced>
28#include <osg/ComputeBoundsVisitor>
29#include <osg/StateSet>
30#include <osg/StateAttribute>
31#include <osg/PolygonMode>
32#include <osg/ColorMask>
33#include <osg/PolygonOffset>
34#include <osg/Depth>
35#include <map>
36#include <vector>
37
[8974]38#include <OpenThreads/Thread>
[7731]39
[8974]40
[7851]41typedef osg::buffered_value< osg::ref_ptr< osg::Drawable::Extensions > > OcclusionQueryBufferedExtensions;
42static OcclusionQueryBufferedExtensions s_OQ_bufferedExtensions;
[7731]43
44//
45// Support classes, used by (and private to) OcclusionQueryNode.
46//   (Note a lot of this is historical. OcclusionQueryNode formaerly
47//   existed as a NodeKit outside the core OSG distribution. Many
48//   of these classes existed in their own separate header and
49//   source files.)
50
51
52// Create and return a StateSet appropriate for performing an occlusion
53//   query test (disable lighting, texture mapping, etc). Probably some
54//   room for improvement here. Could disable shaders, for example.
55osg::StateSet*
56initOQState()
57{
58    osg::StateSet* state = new osg::StateSet;
59    // TBD Possible bug, need to allow user to set render bin number.
60    state->setRenderBinDetails( 9, "RenderBin" );
61
62    state->setMode( GL_LIGHTING, osg::StateAttribute::OFF |
63        osg::StateAttribute::PROTECTED);
64    state->setTextureMode( 0, GL_TEXTURE_2D, osg::StateAttribute::OFF |
65        osg::StateAttribute::PROTECTED);
66    state->setMode( GL_CULL_FACE, osg::StateAttribute::ON |
67        osg::StateAttribute::PROTECTED);
68
69    osg::ColorMask* cm = new osg::ColorMask( false, false, false, false );
70    state->setAttributeAndModes( cm, osg::StateAttribute::ON |
71        osg::StateAttribute::PROTECTED);
72    osg::Depth* d = new osg::Depth( osg::Depth::LEQUAL, 0.f, 1.f, false );
73    state->setAttributeAndModes( d, osg::StateAttribute::ON |
74        osg::StateAttribute::PROTECTED);
75    osg::PolygonMode* pm = new osg::PolygonMode(
76        osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL );
77    state->setAttributeAndModes( pm, osg::StateAttribute::ON |
78        osg::StateAttribute::PROTECTED);
79
80    osg::PolygonOffset* po = new osg::PolygonOffset( -1., -1. );
81    state->setAttributeAndModes( po, osg::StateAttribute::ON |
82        osg::StateAttribute::PROTECTED);
83
84    return state;
85}
86
87// Create and return a StateSet for rendering a debug representation of query geometry.
88osg::StateSet*
89initOQDebugState()
90{
91    osg::StateSet* debugState = new osg::StateSet;
92
93    debugState->setMode( GL_LIGHTING, osg::StateAttribute::OFF |
94        osg::StateAttribute::PROTECTED);
95    debugState->setTextureMode( 0, GL_TEXTURE_2D, osg::StateAttribute::OFF |
96        osg::StateAttribute::PROTECTED);
97    debugState->setMode( GL_CULL_FACE, osg::StateAttribute::ON |
98        osg::StateAttribute::PROTECTED);
99
100    osg::PolygonMode* pm = new osg::PolygonMode(
101        osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
102    debugState->setAttributeAndModes( pm, osg::StateAttribute::ON |
103        osg::StateAttribute::PROTECTED);
104
105    osg::PolygonOffset* po = new osg::PolygonOffset( -1., -1. );
106    debugState->setAttributeAndModes( po, osg::StateAttribute::ON |
107        osg::StateAttribute::PROTECTED);
108
109    return debugState;
110}
111
112
113// TestResult -- stores (per context) results of an occlusion query
114//   test performed by QueryGeometry. An OcclusionQueryNode has a
115//   Geode owning a single QueryGeometry that
116//   draws the occlusion query geometry. QueryGeometry keeps a
117//   TestResult per context to store the result/status of each query.
118// Accessed during the cull and draw traversals.
119class TestResult : public osg::Referenced
120{
121public:
[7790]122    TestResult() : _init( false ), _id( 0 ), _contextID( 0 ), _active( false ), _numPixels( 0 ) {}
[7731]123    ~TestResult() {}
124
125    bool _init;
126
127    // Query ID for this context.
128    GLuint _id;
[7790]129    // Context ID owning this query ID.
130    unsigned int _contextID;
[7731]131
132    // Set to true when a query gets issued and set to
133    //   false when the result is retrieved.
134    mutable bool _active;
135
136    // Result of last query.
137    GLint _numPixels;
138};
139
140// QueryGeometry -- A Drawable that performs an occlusion query,
141//   using its geometric data as the query geometry.
142class QueryGeometry : public osg::Geometry
143{
144public:
145    QueryGeometry( const std::string& oqnName=std::string("") );
[7790]146    ~QueryGeometry();
[7731]147
[7790]148    void reset();
149
[7731]150    // TBD implement copy constructor
151
152    virtual void drawImplementation( osg::RenderInfo& renderInfo ) const;
153
154    unsigned int getNumPixels( const osg::Camera* cam );
155
[7790]156
157    void releaseGLObjects( osg::State* state = 0 );
158    static void deleteQueryObject( unsigned int contextID, GLuint handle );
159    static void flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime );
160    static void discardDeletedQueryObjects( unsigned int contextID );
161   
[7731]162protected:
163    typedef std::map< const osg::Camera*, TestResult > ResultMap;
164    mutable ResultMap _results;
165    mutable OpenThreads::Mutex _mapMutex;
166
167    // Needed for debug only
168    std::string _oqnName;
169};
170
171struct RetrieveQueriesCallback : public osg::Camera::DrawCallback
172{
173    typedef std::vector<TestResult*> ResultsVector;
174    ResultsVector _results;
175
176    RetrieveQueriesCallback( osg::Drawable::Extensions* ext=NULL )
177      : _extensionsFallback( ext )
178    {
179    }
180
181    RetrieveQueriesCallback( const RetrieveQueriesCallback&, const osg::CopyOp& ) {}
182    META_Object( osgOQ, RetrieveQueriesCallback )
183
184    virtual void operator() (const osg::Camera& camera) const
185    {
186        if (_results.empty())
187            return;
188
189        const osg::Timer& timer = *osg::Timer::instance();
190        osg::Timer_t start_tick = timer.tick();
191        double elapsedTime( 0. );
192        int count( 0 );
193
194        osg::Drawable::Extensions* ext;
195        if (camera.getGraphicsContext())
196        {
197            // The typical path, for osgViewer-based applications or any
198            //   app that has set up a valid GraphicsCOntext for the Camera.
199            unsigned int contextID = camera.getGraphicsContext()->getState()->getContextID();
200            RetrieveQueriesCallback* const_this = const_cast<RetrieveQueriesCallback*>( this );
201            ext = const_this->getExtensions( contextID, true );
202        }
203        else
204        {
205            // No valid GraphicsContext in the Camera. This might happen in
206            //   SceneView-based apps. Rely on the creating code to have passed
207            //   in a valid Extensions pointer, and hope it's valid for any
208            //   context that might be current.
209            osg::notify( osg::DEBUG_INFO ) << "osgOQ: RQCB: Using fallback path to obtain Extensions pointer." << std::endl;
210            ext = _extensionsFallback;
211            if (!ext)
212            {
213                osg::notify( osg::FATAL ) << "osgOQ: RQCB: Extensions pointer fallback is NULL." << std::endl;
214                return;
215            }
216        }
217
218        ResultsVector::const_iterator it = _results.begin();
219        while (it != _results.end())
220        {
221            TestResult* tr = const_cast<TestResult*>( *it );
222
223            if (!tr->_active || !tr->_init)
224            {
225                // This test wasn't executed last frame. This is probably because
226                //   a parent node failed the OQ test, this node is outside the
227                //   view volume, or we didn't run the test because we had not
228                //   exceeded visibleQueryFrameCount.
229                // Do not obtain results from OpenGL.
230                it++;
231                continue;
232            }
233
234            osg::notify( osg::DEBUG_INFO ) <<
235                "osgOQ: RQCB: Retrieving..." << std::endl;
236
[8795]237#ifdef FORCE_QUERY_RESULT_AVAILABLE_BEFORE_RETRIEVAL
238
239            // Should not need to do this, but is required on some platforms to
240            // work aroung issues in the device driver. For example, without this
241            // code, we've seen crashes on 64-bit Mac/Linux NVIDIA systems doing
242            // multithreaded, multipipe rendering (as in a CAVE).
[8974]243            // Tried with ATI and verified this workaround is not needed; the
244            //   problem is specific to NVIDIA.
[8795]245            GLint ready( 0 );
246            while( !ready )
247            {
[8974]248                // Apparently, must actually sleep here to avoid issues w/ NVIDIA Quadro.
249                OpenThreads::Thread::microSleep( 5 );
[8795]250                ext->glGetQueryObjectiv( tr->_id, GL_QUERY_RESULT_AVAILABLE, &ready );
251            };
252#endif
253
[7731]254            ext->glGetQueryObjectiv( tr->_id, GL_QUERY_RESULT, &(tr->_numPixels) );
255            if (tr->_numPixels < 0)
256                osg::notify( osg::WARN ) << "osgOQ: RQCB: " <<
257                "glGetQueryObjectiv returned negative value (" << tr->_numPixels << ")." << std::endl;
258
259            // Either retrieve last frame's results, or ignore it because the
260            //   camera is inside the view. In either case, _active is now false.
261            tr->_active = false;
262
263            it++;
264            count++;
265        }
266
267        elapsedTime = timer.delta_s(start_tick,timer.tick());
268        osg::notify( osg::INFO ) << "osgOQ: RQCB: " << "Retrieved " << count <<
269            " queries in " << elapsedTime << " seconds." << std::endl;
270    }
271
272    void reset()
273    {
274        _results.clear();
275    }
276
277    void add( TestResult* tr )
278    {
279        _results.push_back( tr );
280    }
281
282    osg::Drawable::Extensions* getExtensions( unsigned int contextID, bool createIfNotInitalized )
283    {
[7851]284        if (!s_OQ_bufferedExtensions[ contextID ] && createIfNotInitalized)
285            s_OQ_bufferedExtensions[ contextID ] = new osg::Drawable::Extensions( contextID );
286        return s_OQ_bufferedExtensions[ contextID ].get();
[7731]287    }
288
289
290    osg::Drawable::Extensions* _extensionsFallback;
291};
292
293
294
295// PreDraw callback; clears the list of Results from the PostDrawCallback (above).
296struct ClearQueriesCallback : public osg::Camera::DrawCallback
297{
298    ClearQueriesCallback() : _rqcb( NULL ) {}
299    ClearQueriesCallback( const ClearQueriesCallback&, const osg::CopyOp& ) {}
300    META_Object( osgOQ, ClearQueriesCallback )
301
[9460]302    virtual void operator() (const osg::Camera&) const
[7731]303    {
304        if (!_rqcb)
305        {
[8665]306            osg::notify( osg::FATAL ) << "osgOQ: CQCB: Invalid RQCB." << std::endl;
[7731]307            return;
308        }
309        _rqcb->reset();
310    }
311
312    RetrieveQueriesCallback* _rqcb;
313};
314
315
[7790]316// static cache of deleted query objects which can only
317// be completely deleted once the appropriate OpenGL context
318// is set.
319typedef std::list< GLuint > QueryObjectList;
320typedef osg::buffered_object< QueryObjectList > DeletedQueryObjectCache;
321
322static OpenThreads::Mutex s_mutex_deletedQueryObjectCache;
323static DeletedQueryObjectCache s_deletedQueryObjectCache;
324
[7731]325QueryGeometry::QueryGeometry( const std::string& oqnName )
326  : _oqnName( oqnName )
327{
328    // TBD check to see if we can have this on.
329    setUseDisplayList( false );
330}
331
[7790]332QueryGeometry::~QueryGeometry()
333{
334    reset();
335}
336
337
338void
339QueryGeometry::reset()
340{
341    OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _mapMutex );
342
343    ResultMap::iterator it = _results.begin();
344    while (it != _results.end())
345    {
346        TestResult& tr = it->second;
347        if (tr._init)
348            QueryGeometry::deleteQueryObject( tr._contextID, tr._id );
349        it++;
350    }
351    _results.clear();
352}
353
[7731]354// After 1.2, param 1 changed from State to RenderInfo.
355// Warning: Version was still 1.2 on dev branch long after the 1.2 release,
356//   and finally got bumped to 1.9 in April 2007.
357void
358QueryGeometry::drawImplementation( osg::RenderInfo& renderInfo ) const
359{
360    unsigned int contextID = renderInfo.getState()->getContextID();
361    osg::Drawable::Extensions* ext = getExtensions( contextID, true );
362    osg::Camera* cam = renderInfo.getCurrentCamera();
363
364    // Add callbacks if necessary.
365    if (!cam->getPostDrawCallback())
366    {
367        RetrieveQueriesCallback* rqcb = new RetrieveQueriesCallback( ext );
368        cam->setPostDrawCallback( rqcb );
369
370        ClearQueriesCallback* cqcb = new ClearQueriesCallback;
371        cqcb->_rqcb = rqcb;
372        cam->setPreDrawCallback( cqcb );
373    }
374
375    // Get TestResult from Camera map
376    TestResult* tr;
377    {
378        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _mapMutex );
379        tr = &( _results[ cam ] );
380    }
381
382    // Add TestResult to RQCB.
383    RetrieveQueriesCallback* rqcb = dynamic_cast<
384        RetrieveQueriesCallback* >( cam->getPostDrawCallback() );
385    if (!rqcb)
386    {
[8665]387        osg::notify( osg::FATAL ) << "osgOQ: QG: Invalid RQCB." << std::endl;
[7731]388        return;
389    }
390    rqcb->add( tr );
391
392
393    // Issue query
394    if (!tr->_init)
395    {
396        ext->glGenQueries( 1, &(tr->_id) );
[7790]397        tr->_contextID = contextID;
[7731]398        tr->_init = true;
399    }
400
401    osg::notify( osg::DEBUG_INFO ) <<
[8665]402        "osgOQ: QG: Querying for: " << _oqnName << std::endl;
[7731]403
404    ext->glBeginQuery( GL_SAMPLES_PASSED_ARB, tr->_id );
[8495]405    osg::Geometry::drawImplementation( renderInfo );
[7731]406    ext->glEndQuery( GL_SAMPLES_PASSED_ARB );
407    tr->_active = true;
408
409
410    osg::notify( osg::DEBUG_INFO ) <<
411        "osgOQ: QG. OQNName: " << _oqnName <<
412        ", Ctx: " << contextID <<
413        ", ID: " << tr->_id << std::endl;
414#ifdef _DEBUG
415    {
416        GLenum err;
417        if ((err = glGetError()) != GL_NO_ERROR)
418            osg::notify( osg::FATAL ) <<
419            "osgOQ: QG: OpenGL error: " << err << "." << std::endl;
420    }
421#endif
422
423
424}
425
426
427unsigned int
428QueryGeometry::getNumPixels( const osg::Camera* cam )
429{
430    TestResult tr;
431    {
432        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _mapMutex );
433        tr =  _results[ cam ];
434    }
435    return tr._numPixels;
436}
437
[7790]438
439void
440QueryGeometry::releaseGLObjects( osg::State* state )
441{
442    if (!state)
443        // delete all query IDs for all contexts.
444        reset();
445
446    else
447    {
448        // Delete all query IDs for the specified context.
449        unsigned int contextID = state->getContextID();
450        ResultMap::iterator it = _results.begin();
451        while (it != _results.end())
452        {
453            TestResult& tr = it->second;
454            if (tr._contextID == contextID)
455            {
456                QueryGeometry::deleteQueryObject( contextID, tr._id );
457                tr._init = false;
458            }
459            it++;
460        }
461    }
462}
463
464void
465QueryGeometry::deleteQueryObject( unsigned int contextID, GLuint handle )
466{
467    if (handle!=0)
468    {
469        OpenThreads::ScopedLock< OpenThreads::Mutex > lock( s_mutex_deletedQueryObjectCache );
470
471        // insert the handle into the cache for the appropriate context.
472        s_deletedQueryObjectCache[contextID].push_back( handle );
473    }
474}
475
476
477void
478QueryGeometry::flushDeletedQueryObjects( unsigned int contextID, double /*currentTime*/, double& availableTime )
479{
480    // if no time available don't try to flush objects.
481    if (availableTime<=0.0) return;
482
483    const osg::Timer& timer = *osg::Timer::instance();
484    osg::Timer_t start_tick = timer.tick();
485    double elapsedTime = 0.0;
486
487    {
488        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedQueryObjectCache);
489
490        const osg::Drawable::Extensions* extensions = getExtensions( contextID, true );
491
492        QueryObjectList& qol = s_deletedQueryObjectCache[contextID];
493
494        for(QueryObjectList::iterator titr=qol.begin();
495            titr!=qol.end() && elapsedTime<availableTime;
496            )
497        {
498            extensions->glDeleteQueries( 1L, &(*titr ) );
499            titr = qol.erase(titr);
500            elapsedTime = timer.delta_s(start_tick,timer.tick());
501        }
502    }
503       
504    availableTime -= elapsedTime;
505}
506
507void
508QueryGeometry::discardDeletedQueryObjects( unsigned int contextID )
509{
510    OpenThreads::ScopedLock< OpenThreads::Mutex > lock( s_mutex_deletedQueryObjectCache );
511    QueryObjectList& qol = s_deletedQueryObjectCache[ contextID ];
512    qol.clear();
513}
514
[7731]515// End support classes
516//
517
518
519
520namespace osg
521{
522
523
524OcclusionQueryNode::OcclusionQueryNode()
525  : _enabled( true ),
526    _visThreshold( 500 ),
527    _queryFrameCount( 5 ),
528    _debugBB( false )
529{
530    // OQN has two Geode member variables, one for doing the
531    //   query and one for rendering the debug geometry.
532    //   Create and initialize them.
533    createSupportNodes();
534}
535
536OcclusionQueryNode::~OcclusionQueryNode()
537{
538}
539
540OcclusionQueryNode::OcclusionQueryNode( const OcclusionQueryNode& oqn, const osg::CopyOp& copyop )
[7857]541  : Group( oqn, copyop ),
542    _passed( false )
[7731]543{
544    _enabled = oqn._enabled;
[7857]545    _visThreshold = oqn._visThreshold;
546    _queryFrameCount = oqn._queryFrameCount;
[7731]547    _debugBB = oqn._debugBB;
548
549    // Regardless of shallow or deep, create unique support nodes.
550    createSupportNodes();
551}
552
553
554bool
555OcclusionQueryNode::getPassed( const osg::Camera* camera, float distanceToEyePoint )
556{
557    if ( !_enabled )
558        // Queries are not enabled. The caller should be osgUtil::CullVisitor,
559        //   return true to traverse the subgraphs.
560        return true;
561
[10933]562    if (_queryGeode->getDrawable( 0 ) == NULL)
[7731]563    {
564        osg::notify( osg::FATAL ) <<
565            "osgOQ: OcclusionQueryNode: No QueryGeometry." << std::endl;
566        // Something's broke. Return true so we at least render correctly.
567        return true;
568    }
[10933]569    QueryGeometry* qg = static_cast< QueryGeometry* >( _queryGeode->getDrawable( 0 ) );
[7731]570
571    // If the distance to the bounding sphere shell is positive, retrieve
572    //   the results. Others (we're inside the BS shell) we are considered
573    //   to have passed and don't need to retrieve the query.
574    const osg::BoundingSphere& bs = getBound();
575    float distance = distanceToEyePoint  - bs._radius;
576    _passed = ( distance <= 0.f );
577    if (!_passed)
578    {
579        int result = qg->getNumPixels( camera );
580        _passed = ( (unsigned int)(result) > _visThreshold );
581    }
582
583    return _passed;
584}
585
586void
587OcclusionQueryNode::traverseQuery( const osg::Camera* camera, osg::NodeVisitor& nv )
588{
589    bool issueQuery;
590    {
591        const int curFrame = nv.getTraversalNumber();
592
593        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _frameCountMutex );
594        int& lastQueryFrame = _frameCountMap[ camera ];
[9376]595        issueQuery = (curFrame - lastQueryFrame >= _queryFrameCount);
596        if (issueQuery)
[7731]597            lastQueryFrame = curFrame;
598    }
599    if (issueQuery)
600        _queryGeode->accept( nv );
601}
602
603void
604OcclusionQueryNode::traverseDebug( osg::NodeVisitor& nv )
605{
606    if (_debugBB)
607        // If requested, display the debug geometry
608        _debugGeode->accept( nv );
609}
610
611osg::BoundingSphere
612OcclusionQueryNode::computeBound() const
613{
614    {
615        // Need to make this routine thread-safe. Typically called by the update
616        //   Visitor, or just after the update traversal, but could be called by
617        //   an application thread or by a non-osgViewer application.
618        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _computeBoundMutex )  ;
619
620        // This is the logical place to put this code, but the method is const. Cast
621        //   away constness to compute the bounding box and modify the query geometry.
622        osg::OcclusionQueryNode* nonConstThis = const_cast<osg::OcclusionQueryNode*>( this );
623
624
625        osg::ComputeBoundsVisitor cbv;
626        nonConstThis->accept( cbv );
627        osg::BoundingBox bb = cbv.getBoundingBox();
628
629        osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
630        v->resize( 8 );
631        (*v)[0] = osg::Vec3( bb._min.x(), bb._min.y(), bb._min.z() );
632        (*v)[1] = osg::Vec3( bb._max.x(), bb._min.y(), bb._min.z() );
633        (*v)[2] = osg::Vec3( bb._max.x(), bb._min.y(), bb._max.z() );
634        (*v)[3] = osg::Vec3( bb._min.x(), bb._min.y(), bb._max.z() );
635        (*v)[4] = osg::Vec3( bb._max.x(), bb._max.y(), bb._min.z() );
636        (*v)[5] = osg::Vec3( bb._min.x(), bb._max.y(), bb._min.z() );
637        (*v)[6] = osg::Vec3( bb._min.x(), bb._max.y(), bb._max.z() );
638        (*v)[7] = osg::Vec3( bb._max.x(), bb._max.y(), bb._max.z() );
639
[10933]640        osg::Geometry* geom = static_cast< osg::Geometry* >( nonConstThis->_queryGeode->getDrawable( 0 ) );
[7731]641        geom->setVertexArray( v.get() );
642
[10933]643        geom = static_cast< osg::Geometry* >( nonConstThis->_debugGeode->getDrawable( 0 ) );
[7731]644        geom->setVertexArray( v.get() );
645    }
646
647    return Group::computeBound();
648}
649
650
651// Should only be called outside of cull/draw. No thread issues.
652void
653OcclusionQueryNode::setQueriesEnabled( bool enable )
654{
655    _enabled = enable;
656}
657
658// Should only be called outside of cull/draw. No thread issues.
659void
660OcclusionQueryNode::setDebugDisplay( bool debug )
661{
662    _debugBB = debug;
663}
664bool
665OcclusionQueryNode::getDebugDisplay() const
666{
667    return _debugBB;
668}
669
670
671
672void
[7889]673OcclusionQueryNode::setQueryStateSet( osg::StateSet* ss )
[7731]674{
[7889]675    if (!_queryGeode)
[7731]676    {
[7889]677        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid query support node." << std::endl;
[7731]678        return;
679    }
680
681    _queryGeode->setStateSet( ss );
682}
[7889]683osg::StateSet*
684OcclusionQueryNode::getQueryStateSet()
685{
686    if (!_queryGeode)
687    {
688        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid query support node." << std::endl;
689        return NULL;
690    }
691    return _queryGeode->getStateSet();
692}
693
694const osg::StateSet*
695OcclusionQueryNode::getQueryStateSet() const
696{
697    if (!_queryGeode)
698    {
699        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid query support node." << std::endl;
700        return NULL;
701    }
702    return _queryGeode->getStateSet();
703}
704
[7754]705void
[7889]706OcclusionQueryNode::setDebugStateSet( osg::StateSet* ss )
[7754]707{
[7889]708    if (!_debugGeode)
709    {
710        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid debug support node." << std::endl;
711        return;
712    }
713    _debugGeode->setStateSet( ss );
[7754]714}
[7731]715
[7889]716osg::StateSet*
717OcclusionQueryNode::getDebugStateSet()
718{
719    if (!_debugGeode.valid())
720    {
721        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid debug support node." << std::endl;
722        return NULL;
723    }
724    return _debugGeode->getStateSet();
725}
726const osg::StateSet*
727OcclusionQueryNode::getDebugStateSet() const
728{
729    if (!_debugGeode.valid())
730    {
731        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid debug support node." << std::endl;
732        return NULL;
733    }
734    return _debugGeode->getStateSet();
735}
736
[7731]737bool
738OcclusionQueryNode::getPassed() const
739{
740    return _passed;
741}
742
743
744void
745OcclusionQueryNode::createSupportNodes()
746{
747    GLushort indices[] = { 0, 1, 2, 3,  4, 5, 6, 7,
748        0, 3, 6, 5,  2, 1, 4, 7,
749        5, 4, 1, 0,  2, 7, 6, 3 };
750
751    {
752        // Add the test geometry Geode
753        _queryGeode = new osg::Geode;
754        _queryGeode->setName( "OQTest" );
755        _queryGeode->setDataVariance( osg::Object::DYNAMIC );
756
757        osg::ref_ptr< QueryGeometry > geom = new QueryGeometry( getName() );
758        geom->setDataVariance( osg::Object::DYNAMIC );
759        geom->addPrimitiveSet( new osg::DrawElementsUShort(
760                    osg::PrimitiveSet::QUADS, 24, indices ) );
761
762        _queryGeode->addDrawable( geom.get() );
763    }
764
765    {
766        // Add a Geode that is a visual representation of the
767        //   test geometry for debugging purposes
768        _debugGeode = new osg::Geode;
769        _debugGeode->setName( "Debug" );
770        _debugGeode->setDataVariance( osg::Object::DYNAMIC );
771
772        osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
773        geom->setDataVariance( osg::Object::DYNAMIC );
774
775        osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array;
776        ca->push_back( osg::Vec4( 1.f, 1.f, 1.f, 1.f ) );
777        geom->setColorArray( ca.get() );
778        geom->setColorBinding( osg::Geometry::BIND_OVERALL );
779
780        geom->addPrimitiveSet( new osg::DrawElementsUShort(
781                    osg::PrimitiveSet::QUADS, 24, indices ) );
782
783        _debugGeode->addDrawable( geom.get() );
784    }
785
786    // Creste state sets. Note that the osgOQ visitors (which place OQNs throughout
787    //   the scene graph) create a single instance of these StateSets shared
788    //   between all OQNs for efficiency.
[7889]789    setQueryStateSet( initOQState() );
790    setDebugStateSet( initOQDebugState() );
[7731]791}
792
793
[7790]794void
795OcclusionQueryNode::releaseGLObjects( osg::State* state ) const
796{
[10933]797    if(_queryGeode->getDrawable( 0 ) != NULL)
798    {
799        // Query object discard and deletion is handled by QueryGeometry support class.
800        OcclusionQueryNode* nonConstThis = const_cast< OcclusionQueryNode* >( this );
801        QueryGeometry* qg = static_cast< QueryGeometry* >( nonConstThis->_queryGeode->getDrawable( 0 ) );
802        qg->releaseGLObjects( state );
803    }
[7731]804}
[7790]805
806void
807OcclusionQueryNode::flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime )
808{
809    // Query object discard and deletion is handled by QueryGeometry support class.
810    QueryGeometry::flushDeletedQueryObjects( contextID, currentTime, availableTime );
811}
812
813void
814OcclusionQueryNode::discardDeletedQueryObjects( unsigned int contextID )
815{
816    // Query object discard and deletion is handled by QueryGeometry support class.
817    QueryGeometry::discardDeletedQueryObjects( contextID );
818}
819
820
821}
Note: See TracBrowser for help on using the browser.