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

Revision 13041, 24.3 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

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