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

Revision 11127, 26.3 kB (checked in by robert, 5 years ago)

From Paul Martz, "The changes are very similar to Magne's, except they now take the near plane into account. The changes are:

  • Change OcclusionQueryNode::getPassed to take a NodeVisitor? rather than the distance from BS center to the eye point. Change where CullVisitor? calls this method to use the new parameters.
  • getPassed now exits early and returns true to avoid blinking / blink-in of geometry for the first frame or for out-of-range LOD children coming back into view.
  • getPassed now considers the distance from the near plane to the bounding sphere (rather than eye point to bounding sphere) when determining if the viewer is "inside" the bounding sphere or not."
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
[11127]555OcclusionQueryNode::getPassed( const osg::Camera* camera, osg::NodeVisitor& nv )
[7731]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
[11127]562    {
563        // Two situations where we want to simply do a regular traversal:
564        //  1) it's the first frame for this camers
565        //  2) we haven't rendered for an abnormally long time (probably because we're an out-of-range LOD child)
566        // In these cases, assume we're visible to avoid blinking.
567        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _frameCountMutex );
568        const int& lastQueryFrame( _frameCountMap[ camera ] );
569        if( ( lastQueryFrame == 0 ) ||
570            ( (nv.getTraversalNumber() - lastQueryFrame) >  (_queryFrameCount + 1) ) )
571            return true;
572    }
573
[10933]574    if (_queryGeode->getDrawable( 0 ) == NULL)
[7731]575    {
576        osg::notify( osg::FATAL ) <<
577            "osgOQ: OcclusionQueryNode: No QueryGeometry." << std::endl;
578        // Something's broke. Return true so we at least render correctly.
579        return true;
580    }
[10933]581    QueryGeometry* qg = static_cast< QueryGeometry* >( _queryGeode->getDrawable( 0 ) );
[7731]582
[11127]583    // Get the near plane for the upcoming distance calculation.
584    float nearPlane;
585    const osg::Matrix& proj( camera->getProjectionMatrix() );
586    if( ( proj(3,3) != 1. ) || ( proj(2,3) != 0. ) || ( proj(1,3) != 0. ) || ( proj(0,3) != 0.) )
587        nearPlane = proj(3,2) / (proj(2,2)-1.);  // frustum / perspective
588    else
589        nearPlane = (proj(3,2)+1.) / proj(2,2);  // ortho
590
591    // If the distance from the near plane to the bounding sphere shell is positive, retrieve
592    //   the results. Otherwise (near plane inside the BS shell) we are considered
[7731]593    //   to have passed and don't need to retrieve the query.
594    const osg::BoundingSphere& bs = getBound();
[11127]595    float distanceToEyePoint = nv.getDistanceToEyePoint( bs._center, false );
596
597    float distance = distanceToEyePoint - nearPlane - bs._radius;
[7731]598    _passed = ( distance <= 0.f );
599    if (!_passed)
600    {
601        int result = qg->getNumPixels( camera );
602        _passed = ( (unsigned int)(result) > _visThreshold );
603    }
604
605    return _passed;
606}
607
608void
609OcclusionQueryNode::traverseQuery( const osg::Camera* camera, osg::NodeVisitor& nv )
610{
611    bool issueQuery;
612    {
613        const int curFrame = nv.getTraversalNumber();
614
615        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _frameCountMutex );
616        int& lastQueryFrame = _frameCountMap[ camera ];
[9376]617        issueQuery = (curFrame - lastQueryFrame >= _queryFrameCount);
618        if (issueQuery)
[7731]619            lastQueryFrame = curFrame;
620    }
621    if (issueQuery)
622        _queryGeode->accept( nv );
623}
624
625void
626OcclusionQueryNode::traverseDebug( osg::NodeVisitor& nv )
627{
628    if (_debugBB)
629        // If requested, display the debug geometry
630        _debugGeode->accept( nv );
631}
632
633osg::BoundingSphere
634OcclusionQueryNode::computeBound() const
635{
636    {
637        // Need to make this routine thread-safe. Typically called by the update
638        //   Visitor, or just after the update traversal, but could be called by
639        //   an application thread or by a non-osgViewer application.
640        OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _computeBoundMutex )  ;
641
642        // This is the logical place to put this code, but the method is const. Cast
643        //   away constness to compute the bounding box and modify the query geometry.
644        osg::OcclusionQueryNode* nonConstThis = const_cast<osg::OcclusionQueryNode*>( this );
645
646
647        osg::ComputeBoundsVisitor cbv;
648        nonConstThis->accept( cbv );
649        osg::BoundingBox bb = cbv.getBoundingBox();
650
651        osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
652        v->resize( 8 );
653        (*v)[0] = osg::Vec3( bb._min.x(), bb._min.y(), bb._min.z() );
654        (*v)[1] = osg::Vec3( bb._max.x(), bb._min.y(), bb._min.z() );
655        (*v)[2] = osg::Vec3( bb._max.x(), bb._min.y(), bb._max.z() );
656        (*v)[3] = osg::Vec3( bb._min.x(), bb._min.y(), bb._max.z() );
657        (*v)[4] = osg::Vec3( bb._max.x(), bb._max.y(), bb._min.z() );
658        (*v)[5] = osg::Vec3( bb._min.x(), bb._max.y(), bb._min.z() );
659        (*v)[6] = osg::Vec3( bb._min.x(), bb._max.y(), bb._max.z() );
660        (*v)[7] = osg::Vec3( bb._max.x(), bb._max.y(), bb._max.z() );
661
[10933]662        osg::Geometry* geom = static_cast< osg::Geometry* >( nonConstThis->_queryGeode->getDrawable( 0 ) );
[7731]663        geom->setVertexArray( v.get() );
664
[10933]665        geom = static_cast< osg::Geometry* >( nonConstThis->_debugGeode->getDrawable( 0 ) );
[7731]666        geom->setVertexArray( v.get() );
667    }
668
669    return Group::computeBound();
670}
671
672
673// Should only be called outside of cull/draw. No thread issues.
674void
675OcclusionQueryNode::setQueriesEnabled( bool enable )
676{
677    _enabled = enable;
678}
679
680// Should only be called outside of cull/draw. No thread issues.
681void
682OcclusionQueryNode::setDebugDisplay( bool debug )
683{
684    _debugBB = debug;
685}
686bool
687OcclusionQueryNode::getDebugDisplay() const
688{
689    return _debugBB;
690}
691
692
693
694void
[7889]695OcclusionQueryNode::setQueryStateSet( osg::StateSet* ss )
[7731]696{
[7889]697    if (!_queryGeode)
[7731]698    {
[7889]699        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid query support node." << std::endl;
[7731]700        return;
701    }
702
703    _queryGeode->setStateSet( ss );
704}
[7889]705osg::StateSet*
706OcclusionQueryNode::getQueryStateSet()
707{
708    if (!_queryGeode)
709    {
710        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid query support node." << std::endl;
711        return NULL;
712    }
713    return _queryGeode->getStateSet();
714}
715
716const osg::StateSet*
717OcclusionQueryNode::getQueryStateSet() const
718{
719    if (!_queryGeode)
720    {
721        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid query support node." << std::endl;
722        return NULL;
723    }
724    return _queryGeode->getStateSet();
725}
726
[7754]727void
[7889]728OcclusionQueryNode::setDebugStateSet( osg::StateSet* ss )
[7754]729{
[7889]730    if (!_debugGeode)
731    {
732        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid debug support node." << std::endl;
733        return;
734    }
735    _debugGeode->setStateSet( ss );
[7754]736}
[7731]737
[7889]738osg::StateSet*
739OcclusionQueryNode::getDebugStateSet()
740{
741    if (!_debugGeode.valid())
742    {
743        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid debug support node." << std::endl;
744        return NULL;
745    }
746    return _debugGeode->getStateSet();
747}
748const osg::StateSet*
749OcclusionQueryNode::getDebugStateSet() const
750{
751    if (!_debugGeode.valid())
752    {
753        osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid debug support node." << std::endl;
754        return NULL;
755    }
756    return _debugGeode->getStateSet();
757}
758
[7731]759bool
760OcclusionQueryNode::getPassed() const
761{
762    return _passed;
763}
764
765
766void
767OcclusionQueryNode::createSupportNodes()
768{
769    GLushort indices[] = { 0, 1, 2, 3,  4, 5, 6, 7,
770        0, 3, 6, 5,  2, 1, 4, 7,
771        5, 4, 1, 0,  2, 7, 6, 3 };
772
773    {
774        // Add the test geometry Geode
775        _queryGeode = new osg::Geode;
776        _queryGeode->setName( "OQTest" );
777        _queryGeode->setDataVariance( osg::Object::DYNAMIC );
778
779        osg::ref_ptr< QueryGeometry > geom = new QueryGeometry( getName() );
780        geom->setDataVariance( osg::Object::DYNAMIC );
781        geom->addPrimitiveSet( new osg::DrawElementsUShort(
782                    osg::PrimitiveSet::QUADS, 24, indices ) );
783
784        _queryGeode->addDrawable( geom.get() );
785    }
786
787    {
788        // Add a Geode that is a visual representation of the
789        //   test geometry for debugging purposes
790        _debugGeode = new osg::Geode;
791        _debugGeode->setName( "Debug" );
792        _debugGeode->setDataVariance( osg::Object::DYNAMIC );
793
794        osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
795        geom->setDataVariance( osg::Object::DYNAMIC );
796
797        osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array;
798        ca->push_back( osg::Vec4( 1.f, 1.f, 1.f, 1.f ) );
799        geom->setColorArray( ca.get() );
800        geom->setColorBinding( osg::Geometry::BIND_OVERALL );
801
802        geom->addPrimitiveSet( new osg::DrawElementsUShort(
803                    osg::PrimitiveSet::QUADS, 24, indices ) );
804
805        _debugGeode->addDrawable( geom.get() );
806    }
807
808    // Creste state sets. Note that the osgOQ visitors (which place OQNs throughout
809    //   the scene graph) create a single instance of these StateSets shared
810    //   between all OQNs for efficiency.
[7889]811    setQueryStateSet( initOQState() );
812    setDebugStateSet( initOQDebugState() );
[7731]813}
814
815
[7790]816void
817OcclusionQueryNode::releaseGLObjects( osg::State* state ) const
818{
[10933]819    if(_queryGeode->getDrawable( 0 ) != NULL)
820    {
821        // Query object discard and deletion is handled by QueryGeometry support class.
822        OcclusionQueryNode* nonConstThis = const_cast< OcclusionQueryNode* >( this );
823        QueryGeometry* qg = static_cast< QueryGeometry* >( nonConstThis->_queryGeode->getDrawable( 0 ) );
824        qg->releaseGLObjects( state );
825    }
[7731]826}
[7790]827
828void
829OcclusionQueryNode::flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime )
830{
831    // Query object discard and deletion is handled by QueryGeometry support class.
832    QueryGeometry::flushDeletedQueryObjects( contextID, currentTime, availableTime );
833}
834
835void
836OcclusionQueryNode::discardDeletedQueryObjects( unsigned int contextID )
837{
838    // Query object discard and deletion is handled by QueryGeometry support class.
839    QueryGeometry::discardDeletedQueryObjects( contextID );
840}
841
842
843}
Note: See TracBrowser for help on using the browser.