diff --git a/include/osg/OcclusionQueryNode b/include/osg/OcclusionQueryNode index f2fb2c229..9d8ebabc3 100644 --- a/include/osg/OcclusionQueryNode +++ b/include/osg/OcclusionQueryNode @@ -82,9 +82,9 @@ public: }; /** return a QueryResult for specified Camera, where the QueryResult.valid is true when query results are available, and in which case the QueryResult.numPixels provides the num of pixels in the query result.*/ - QueryResult getQueryResult( const osg::Camera* cam ); + QueryResult getQueryResult( const osg::Camera* cam ) const; - unsigned int getNumPixels( const osg::Camera* cam ); + unsigned int getNumPixels( const osg::Camera* cam ) const; virtual void releaseGLObjects( osg::State* state = 0 ) const; @@ -158,8 +158,11 @@ public: osg::StateSet* getQueryStateSet(); const osg::StateSet* getQueryStateSet() const; - // Get the QueryGeometry object used for occlusion query. Returns 0 if no QueryGeometry is created. - osg::QueryGeometry* getQueryGeometry(); + // Set and get the QueryGeometry object used for the occlusion query. + // By default an axis aligned box is used as the query geometry. + // Resetting to the default query geometry is done by setting it to 0. + // Returns 0 if no QueryGeometry is created. + void setQueryGeometry( osg::QueryGeometry* geom ); const osg::QueryGeometry* getQueryGeometry() const; // Set and get the StateSet used by the OcclusionQueryNode @@ -189,17 +192,30 @@ public: static void discardDeletedQueryObjects( unsigned int contextID ); protected: + enum QueryGeometryState { + INVALID, + VALID, + USER_DEFINED + }; + virtual ~OcclusionQueryNode(); virtual void createSupportNodes(); + bool isQueryGeometryValid() const { return _queryGeometryState != INVALID; } + + void setQueryGeometryInternal( osg::QueryGeometry* queryGeom, + osg::Geometry* debugQueryGeom, + QueryGeometryState state ); + + void updateDefaultQueryGeometry(); + osg::ref_ptr< osg::Geode > _queryGeode; osg::ref_ptr< osg::Geode > _debugGeode; bool _enabled; - // If the box of the query geometry is valid. - mutable bool _validQueryGeometry; + mutable QueryGeometryState _queryGeometryState; // Tracks the last frame number that we performed a query. // User can set how many times (See setQueryFrameCount). diff --git a/src/osg/OcclusionQueryNode.cpp b/src/osg/OcclusionQueryNode.cpp index b6222acb7..6be23a5f5 100644 --- a/src/osg/OcclusionQueryNode.cpp +++ b/src/osg/OcclusionQueryNode.cpp @@ -48,6 +48,36 @@ namespace osg { +QueryGeometry* createDefaultQueryGeometry( const std::string& name ) +{ + 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 }; + + ref_ptr geom = new QueryGeometry( name ); + geom->setDataVariance( Object::DYNAMIC ); + geom->addPrimitiveSet( new DrawElementsUShort( PrimitiveSet::QUADS, 24, indices ) ); + + return geom.release(); +} + +Geometry* createDefaultDebugQueryGeometry() +{ + 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 }; + + ref_ptr ca = new Vec4Array; + ca->push_back( Vec4( 1.f, 1.f, 1.f, 1.f ) ); + + ref_ptr geom = new Geometry; + geom->setDataVariance( Object::DYNAMIC ); + geom->setColorArray( ca.get(), Array::BIND_OVERALL ); + geom->addPrimitiveSet( new DrawElementsUShort( PrimitiveSet::QUADS, 24, indices ) ); + + return geom.release(); +} + // 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. @@ -364,7 +394,7 @@ QueryGeometry::drawImplementation( osg::RenderInfo& renderInfo ) const } -QueryGeometry::QueryResult QueryGeometry::getQueryResult( const osg::Camera* cam ) +QueryGeometry::QueryResult QueryGeometry::getQueryResult( const osg::Camera* cam ) const { osg::ref_ptr tr; { @@ -380,7 +410,7 @@ QueryGeometry::QueryResult QueryGeometry::getQueryResult( const osg::Camera* cam } unsigned int -QueryGeometry::getNumPixels( const osg::Camera* cam ) +QueryGeometry::getNumPixels( const osg::Camera* cam ) const { return getQueryResult(cam).numPixels; } @@ -442,7 +472,7 @@ QueryGeometry::discardDeletedQueryObjects( unsigned int contextID ) OcclusionQueryNode::OcclusionQueryNode() : _enabled( true ), - _validQueryGeometry( false ), + _queryGeometryState( INVALID ), _passed(false), _visThreshold( 500 ), _queryFrameCount( 5 ), @@ -460,7 +490,7 @@ OcclusionQueryNode::~OcclusionQueryNode() OcclusionQueryNode::OcclusionQueryNode( const OcclusionQueryNode& oqn, const CopyOp& copyop ) : Group( oqn, copyop ), - _validQueryGeometry( false ), + _queryGeometryState( INVALID ), _passed( false ) { _enabled = oqn._enabled; @@ -485,7 +515,7 @@ bool OcclusionQueryNode::getPassed( const Camera* camera, NodeVisitor& nv ) QueryGeometry* qg = static_cast< QueryGeometry* >( _queryGeode->getDrawable( 0 ) ); - if ( !_validQueryGeometry ) + if ( !isQueryGeometryValid() ) { // There're cases that the occlusion test result has been retrieved // after the query geometry has been changed, it's the result of the @@ -556,7 +586,7 @@ bool OcclusionQueryNode::getPassed( const Camera* camera, NodeVisitor& nv ) void OcclusionQueryNode::traverseQuery( const Camera* camera, NodeVisitor& nv ) { - if (!_validQueryGeometry || ! _enabled) + if (!isQueryGeometryValid() || ! _enabled) return; bool issueQuery; @@ -590,42 +620,13 @@ BoundingSphere OcclusionQueryNode::computeBound() const // an application thread or by a non-osgViewer application. OpenThreads::ScopedLock 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( this ); - - - ComputeBoundsVisitor cbv; - nonConstThis->accept( cbv ); - BoundingBox bb = cbv.getBoundingBox(); - const bool bbValid = bb.valid(); - _validQueryGeometry = bbValid; - - osg::ref_ptr v = new Vec3Array; - v->resize( 8 ); - - // Having (0,0,0) as vertices for the case of the invalid query geometry - // still isn't quite the right thing. But the query geometry is public - // accessible and therefore a user might expect eight vertices, so - // it seems safer to keep eight vertices in the geometry. - - if (bbValid) + if (_queryGeometryState != USER_DEFINED) { - (*v)[0] = Vec3( bb._min.x(), bb._min.y(), bb._min.z() ); - (*v)[1] = Vec3( bb._max.x(), bb._min.y(), bb._min.z() ); - (*v)[2] = Vec3( bb._max.x(), bb._min.y(), bb._max.z() ); - (*v)[3] = Vec3( bb._min.x(), bb._min.y(), bb._max.z() ); - (*v)[4] = Vec3( bb._max.x(), bb._max.y(), bb._min.z() ); - (*v)[5] = Vec3( bb._min.x(), bb._max.y(), bb._min.z() ); - (*v)[6] = Vec3( bb._min.x(), bb._max.y(), bb._max.z() ); - (*v)[7] = Vec3( bb._max.x(), bb._max.y(), bb._max.z() ); + // 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( this ); + nonConstThis->updateDefaultQueryGeometry(); } - - Geometry* geom = static_cast< Geometry* >( nonConstThis->_queryGeode->getDrawable( 0 ) ); - geom->setVertexArray( v.get() ); - - geom = static_cast< osg::Geometry* >( nonConstThis->_debugGeode->getDrawable( 0 ) ); - geom->setVertexArray( v.get() ); } return Group::computeBound(); @@ -723,21 +724,12 @@ bool OcclusionQueryNode::getPassed() const 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 Geode; _queryGeode->setName( "OQTest" ); _queryGeode->setDataVariance( Object::DYNAMIC ); - - ref_ptr< QueryGeometry > geom = new QueryGeometry( getName() ); - geom->setDataVariance( Object::DYNAMIC ); - geom->addPrimitiveSet( new DrawElementsUShort( PrimitiveSet::QUADS, 24, indices ) ); - - _queryGeode->addDrawable( geom.get() ); + _queryGeode->addDrawable( createDefaultQueryGeometry( getName() ) ); } { @@ -746,17 +738,7 @@ void OcclusionQueryNode::createSupportNodes() _debugGeode = new Geode; _debugGeode->setName( "Debug" ); _debugGeode->setDataVariance( Object::DYNAMIC ); - - ref_ptr geom = new Geometry; - geom->setDataVariance( Object::DYNAMIC ); - - ref_ptr ca = new Vec4Array; - ca->push_back( Vec4( 1.f, 1.f, 1.f, 1.f ) ); - geom->setColorArray( ca.get(), Array::BIND_OVERALL ); - - geom->addPrimitiveSet( new DrawElementsUShort( PrimitiveSet::QUADS, 24, indices ) ); - - _debugGeode->addDrawable( geom.get() ); + _debugGeode->addDrawable( createDefaultDebugQueryGeometry() ); } // Creste state sets. Note that the osgOQ visitors (which place OQNs throughout @@ -767,6 +749,69 @@ void OcclusionQueryNode::createSupportNodes() } +void OcclusionQueryNode::setQueryGeometryInternal( QueryGeometry* queryGeom, + Geometry* debugQueryGeom, + QueryGeometryState state ) +{ + if (!queryGeom || !debugQueryGeom) + { + OSG_FATAL << "osgOQ: OcclusionQueryNode: No QueryGeometry." << std::endl; + return; + } + + _queryGeometryState = state; + + _queryGeode->removeDrawables(0, _queryGeode->getNumDrawables()); + _queryGeode->addDrawable(queryGeom); + + _debugGeode->removeDrawables(0, _debugGeode->getNumDrawables()); + _debugGeode->addDrawable(debugQueryGeom); +} + + +void OcclusionQueryNode::updateDefaultQueryGeometry() +{ + if (_queryGeometryState == USER_DEFINED) + { + OSG_FATAL << "osgOQ: OcclusionQueryNode: Unexpected QueryGeometryState=USER_DEFINED." << std::endl; + return; + } + + ComputeBoundsVisitor cbv; + accept( cbv ); + + BoundingBox bb = cbv.getBoundingBox(); + const bool bbValid = bb.valid(); + _queryGeometryState = bbValid ? VALID : INVALID; + + osg::ref_ptr v = new Vec3Array; + v->resize( 8 ); + + // Having (0,0,0) as vertices for the case of the invalid query geometry + // still isn't quite the right thing. But the query geometry is public + // accessible and therefore a user might expect eight vertices, so + // it seems safer to keep eight vertices in the geometry. + + if (bbValid) + { + (*v)[0] = Vec3( bb._min.x(), bb._min.y(), bb._min.z() ); + (*v)[1] = Vec3( bb._max.x(), bb._min.y(), bb._min.z() ); + (*v)[2] = Vec3( bb._max.x(), bb._min.y(), bb._max.z() ); + (*v)[3] = Vec3( bb._min.x(), bb._min.y(), bb._max.z() ); + (*v)[4] = Vec3( bb._max.x(), bb._max.y(), bb._min.z() ); + (*v)[5] = Vec3( bb._min.x(), bb._max.y(), bb._min.z() ); + (*v)[6] = Vec3( bb._min.x(), bb._max.y(), bb._max.z() ); + (*v)[7] = Vec3( bb._max.x(), bb._max.y(), bb._max.z() ); + } + + Geometry* geom = static_cast< Geometry* >( _queryGeode->getDrawable( 0 ) ); + geom->setVertexArray( v.get() ); + + geom = static_cast< osg::Geometry* >( _debugGeode->getDrawable( 0 ) ); + geom->setVertexArray( v.get() ); +} + + void OcclusionQueryNode::releaseGLObjects( State* state ) const { if (_queryGeode.valid()) _queryGeode->releaseGLObjects(state); @@ -787,14 +832,22 @@ void OcclusionQueryNode::discardDeletedQueryObjects( unsigned int contextID ) QueryGeometry::discardDeletedQueryObjects( contextID ); } -osg::QueryGeometry* OcclusionQueryNode::getQueryGeometry() +void OcclusionQueryNode::setQueryGeometry( QueryGeometry* geom ) { - if (_queryGeode && _queryGeode->getDrawable( 0 )) + OpenThreads::ScopedLock lock( _computeBoundMutex ) ; + + if (geom) { - QueryGeometry* qg = static_cast< QueryGeometry* >( _queryGeode->getDrawable( 0 ) ); - return qg; + setQueryGeometryInternal( geom, geom, USER_DEFINED ); + } + else + { + setQueryGeometryInternal( createDefaultQueryGeometry( getName() ), + createDefaultDebugQueryGeometry(), + INVALID); + + updateDefaultQueryGeometry(); } - return 0; } const osg::QueryGeometry* OcclusionQueryNode::getQueryGeometry() const