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