// // 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. // #ifndef OSG_OCCLUSION_QUERY_NODE #define OSG_OCCLUSION_QUERY_NODE 1 #include #include #include #include namespace osg { // 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(); // Create and return a StateSet for rendering a debug representation of query geometry. osg::StateSet* initOQDebugState(); // 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 ) {setThreadSafeRefUnref(true);} ~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 OSG_EXPORT 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; struct QueryResult { QueryResult() : valid(false), numPixels(0) {} QueryResult(bool v, unsigned int p) : valid(v), numPixels(p) {} bool valid; unsigned int numPixels; }; /** 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 ) const; unsigned int getNumPixels( const osg::Camera* cam ) const; virtual void releaseGLObjects( osg::State* state = 0 ) const; 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*, osg::ref_ptr > ResultMap; mutable ResultMap _results; mutable OpenThreads::Mutex _mapMutex; // Needed for debug only std::string _oqnName; }; // This Node performs occlusion query testing on its children. // You can use it directly to occlusion query test a portion // of your scene graph, or you can use it implicitly with an // OcclusionQueryRoot, which places OcclusionQueryNodes where // needed and acts as a master control. class OSG_EXPORT OcclusionQueryNode : public osg::Group { public: OcclusionQueryNode(); // Copy constructor using CopyOp to manage deep vs shallow copy. OcclusionQueryNode( const OcclusionQueryNode& oqn, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY ); META_Node( osg, OcclusionQueryNode ); virtual osg::BoundingSphere computeBound() const; virtual void releaseGLObjects( osg::State* state = 0 ) const; // When disabled, OQN doesn't perform occlusion queries, and simply // renders its children. void setQueriesEnabled( bool enable=true ); bool getQueriesEnabled() const { return _enabled; } // Sets/gets the visibility threshold. If the test indicates that // the number of visible pixels is less than the specified // threshold, don't draw the actual geometry. void setVisibilityThreshold( unsigned int pixels ) { _visThreshold = pixels; } unsigned int getVisibilityThreshold() const { return _visThreshold; } // Specifies how many frames to wait before issuing another query. void setQueryFrameCount( unsigned int frames ) { _queryFrameCount = frames; } unsigned int getQueryFrameCount() const { return _queryFrameCount; } // Resets the queries. The next frame will issue a new query. // This is useful for big view changes, if it shouldn't be waited for // '_queryFrameCount' till the frame contents change. void resetQueries(); // Indicate whether or not the bounding box used in the occlusion query test // should be rendered. Handy for debugging and development. // Should only be called outside of cull/draw. No thread issues. void setDebugDisplay( bool enable ); bool getDebugDisplay() const; // Set and get the StateSet used by the OcclusionQueryNode // when rendering the query geometry. OQN creates its own by // default, but if you use many OQNs you might want to use // this method to set all OQNs to use the same StateSet // for more efficient processing. void setQueryStateSet( osg::StateSet* ss ); osg::StateSet* getQueryStateSet(); const osg::StateSet* getQueryStateSet() const; // 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 // when rendering the debug query geometry (see setDebugDisplay). void setDebugStateSet( osg::StateSet* ss ); osg::StateSet* getDebugStateSet(); const osg::StateSet* getDebugStateSet() const; // For statistics gathering, e.g., by a NodeVisitor. bool getPassed() const; // These methods are public so that osgUtil::CullVisitor can access them. // Not intended for application use. virtual bool getPassed( const osg::Camera* camera, osg::NodeVisitor& nv ); void traverseQuery( const osg::Camera* camera, osg::NodeVisitor& nv ); void traverseDebug( osg::NodeVisitor& nv ); // Delete unused query IDs for this contextID. static void flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime ); // discard all the cached query objects which need to be deleted // in the OpenGL context related to contextID. // Note, unlike flush no OpenGL calls are made, instead the handles are all removed. // this call is useful for when an OpenGL context has been destroyed. 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; mutable QueryGeometryState _queryGeometryState; // Tracks the last frame number that we performed a query. // User can set how many times (See setQueryFrameCount). typedef std::map< const osg::Camera*, unsigned int > FrameCountMap; FrameCountMap _frameCountMap; mutable OpenThreads::Mutex _frameCountMutex; // For statistics gathering bool _passed; // User-settable variables unsigned int _visThreshold; unsigned int _queryFrameCount; bool _debugBB; // Required to ensure that computeBound() is thread-safe. mutable OpenThreads::Mutex _computeBoundMutex; }; } #endif