OcclusionQueryNode 7.1 KB
Newer Older
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
//
// 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 <osg/Export>
#include <osg/CopyOp>
#include <osg/Group>
#include <osg/Geometry>


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 ) {}
    ~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 );

    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*, TestResult > 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; }

    // 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 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:
    virtual ~OcclusionQueryNode();

    virtual void createSupportNodes();

    osg::ref_ptr< osg::Geode > _queryGeode;
    osg::ref_ptr< osg::Geode > _debugGeode;

    bool _enabled;

    // 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