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