From Paul Martz, Introduced osg::OcclusionQueryNode with support for OpenGL occlusion query extension
This commit is contained in:
686
src/osg/OcclusionQueryNode.cpp
Normal file
686
src/osg/OcclusionQueryNode.cpp
Normal file
@@ -0,0 +1,686 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
|
||||
|
||||
//
|
||||
// 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 ), _active( false ), _numPixels( 0 ) {}
|
||||
~TestResult() {}
|
||||
|
||||
bool _init;
|
||||
|
||||
// Query ID for this context.
|
||||
GLuint _id;
|
||||
|
||||
// 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() {}
|
||||
|
||||
// TBD implement copy constructor
|
||||
|
||||
virtual void drawImplementation( osg::RenderInfo& renderInfo ) const;
|
||||
|
||||
unsigned int getNumPixels( const osg::Camera* cam );
|
||||
|
||||
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_extensions[ contextID ] && createIfNotInitalized)
|
||||
s_extensions[ contextID ] = new osg::Drawable::Extensions( contextID );
|
||||
return s_extensions[ contextID ].get();
|
||||
}
|
||||
|
||||
typedef osg::buffered_value< osg::ref_ptr< osg::Drawable::Extensions > > BufferedExtensions;
|
||||
static BufferedExtensions s_extensions;
|
||||
|
||||
osg::Drawable::Extensions* _extensionsFallback;
|
||||
};
|
||||
|
||||
RetrieveQueriesCallback::BufferedExtensions RetrieveQueriesCallback::s_extensions;
|
||||
|
||||
|
||||
|
||||
// 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 ) << "oagOQ: CQCB: Invalid RQCB." << std::endl;
|
||||
return;
|
||||
}
|
||||
_rqcb->reset();
|
||||
}
|
||||
|
||||
RetrieveQueriesCallback* _rqcb;
|
||||
};
|
||||
|
||||
|
||||
QueryGeometry::QueryGeometry( const std::string& oqnName )
|
||||
: _oqnName( oqnName )
|
||||
{
|
||||
// TBD check to see if we can have this on.
|
||||
setUseDisplayList( false );
|
||||
}
|
||||
|
||||
// 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 ) << "oagOQ: QG: Invalid RQCB." << std::endl;
|
||||
return;
|
||||
}
|
||||
rqcb->add( tr );
|
||||
|
||||
|
||||
// Issue query
|
||||
if (!tr->_init)
|
||||
{
|
||||
ext->glGenQueries( 1, &(tr->_id) );
|
||||
tr->_init = true;
|
||||
}
|
||||
|
||||
osg::notify( osg::DEBUG_INFO ) <<
|
||||
"oagOQ: QG: Querying for: " << _oqnName << std::endl;
|
||||
|
||||
ext->glBeginQuery( GL_SAMPLES_PASSED_ARB, tr->_id );
|
||||
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;
|
||||
}
|
||||
|
||||
// End support classes
|
||||
//
|
||||
|
||||
|
||||
|
||||
namespace osg
|
||||
{
|
||||
|
||||
|
||||
OcclusionQueryNode::OcclusionQueryNode()
|
||||
: _enabled( true ),
|
||||
_visThreshold( 500 ),
|
||||
_queryFrameCount( 5 ),
|
||||
_debugBB( false )
|
||||
{
|
||||
setDataVariance( osg::Object::DYNAMIC );
|
||||
|
||||
#if 0
|
||||
// Before integration into core OSG, OcclusionQueryNode supported a config
|
||||
// file to override default values. Can't include this in core osg as it
|
||||
// would introduce a dependency onto osgDB for finding the config file.
|
||||
|
||||
// Override defaults if specified in the config file using the OptionLoader singleton.
|
||||
int clampMe;
|
||||
if ( OptionLoader::instance()->getOption( "VisibilityThreshold", clampMe ) )
|
||||
_visThreshold = (clampMe < 0 ) ? 0 : static_cast<unsigned int>( clampMe );
|
||||
if ( OptionLoader::instance()->getOption( "QueryFrameCount", clampMe ) )
|
||||
_queryFrameCount = (clampMe < 1) ? 1 : static_cast<unsigned int>( clampMe );
|
||||
OptionLoader::instance()->getOption( "DebugBoundingVolume", _debugBB );
|
||||
#endif
|
||||
|
||||
// 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 )
|
||||
{
|
||||
_enabled = oqn._enabled;
|
||||
_debugBB = oqn._debugBB;
|
||||
|
||||
// Regardless of shallow or deep, create unique support nodes.
|
||||
createSupportNodes();
|
||||
}
|
||||
|
||||
//PORT-TBD
|
||||
#if 0
|
||||
// Currently, retrieves last frame's query results during current frame's cull traversal.
|
||||
// In the future, should retrive last frame's results immediately after last
|
||||
// frame's buffer swap.
|
||||
void
|
||||
OcclusionQueryNode::traverse( osg::NodeVisitor& nv )
|
||||
{
|
||||
if ( !_enabled || (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) )
|
||||
{
|
||||
osg::Group::traverse( nv );
|
||||
return;
|
||||
}
|
||||
|
||||
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
|
||||
osg::Camera* camera = cv->getRenderStage()->getCamera();
|
||||
|
||||
|
||||
// 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;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 = cv->getDistanceToEyePoint( bs._center, false ) - bs._radius;
|
||||
_passed = ( distance <= 0.f );
|
||||
if (!_passed)
|
||||
{
|
||||
int result = qg->getNumPixels( camera );
|
||||
_passed = ( (unsigned int)(result) > _visThreshold );
|
||||
}
|
||||
|
||||
if (_passed)
|
||||
// We're visible. Traverse our children
|
||||
osg::Group::traverse( nv );
|
||||
|
||||
// Submit a new query only if sufficient frames have elapsed.
|
||||
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 );
|
||||
|
||||
if (_debugBB)
|
||||
// If requested, display the debug geometry
|
||||
_debugGeode->accept( nv );
|
||||
|
||||
osg::notify( osg::DEBUG_INFO ) <<
|
||||
"oagOQ: OQN::traverse: OQN: " << getName() <<
|
||||
", Cam: " << camera <<
|
||||
", Passed: " << _passed << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
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::setQueryStateSets( osg::StateSet* ss, osg::StateSet* ssDebug )
|
||||
{
|
||||
if (!_queryGeode.valid() || !_debugGeode.valid())
|
||||
{
|
||||
osg::notify( osg::WARN ) << "osgOQ: OcclusionQueryNode:: Invalid support node(s)." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
_queryGeode->setStateSet( ss );
|
||||
_debugGeode->setStateSet( ssDebug );
|
||||
}
|
||||
|
||||
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.
|
||||
setQueryStateSets( initOQState(), initOQDebugState() );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user