~ubuntu-branches/ubuntu/trusty/openscenegraph/trusty

« back to all changes in this revision

Viewing changes to OpenSceneGraph/src/osg/OcclusionQueryNode.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cyril Brulebois
  • Date: 2008-07-29 04:34:38 UTC
  • mfrom: (1.1.6 upstream) (2.1.3 lenny)
  • Revision ID: james.westby@ubuntu.com-20080729043438-no1h9h0dpsrlzp1y
* Non-maintainer upload.
* No longer try to detect (using /proc/cpuinfo when available) how many
  CPUs are available, fixing the FTBFS (due to -j0) on various platforms
  (Closes: #477353). The right way to do it is to support parallel=n in
  DEB_BUILD_OPTIONS (see Debian Policy §4.9.1), and adequate support has
  been implemented.
* Add patch to fix FTBFS due to the build system now refusing to handle
  whitespaces (Policy CMP0004 say the logs), thanks to Andreas Putzo who
  provided it (Closes: #482239):
   - debian/patches/fix-cmp0004-build-failure.dpatch
* Remove myself from Uploaders, as requested a while ago, done by Luk in
  his 2.2.0-2.1 NMU, which was never acknowledged.

Show diffs side-by-side

added added

removed removed

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