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