culling to use the pixelSize method instead of a ratio of radius to distance from eye point. setSmallFeatureCullingPixelSize() method has also been added to provide the user with finer control of small featyre culling.
519 lines
18 KiB
Plaintext
519 lines
18 KiB
Plaintext
//C++ header - Open Scene Graph - Copyright (C) 1998-2001 Robert Osfield
|
|
//Distributed under the terms of the GNU Library General Public License (LGPL)
|
|
//as published by the Free Software Foundation.
|
|
|
|
#ifndef OSGUTIL_CULLVISITOR
|
|
#define OSGUTIL_CULLVISITOR 1
|
|
|
|
#include <osg/NodeVisitor>
|
|
#include <osg/BoundingSphere>
|
|
#include <osg/BoundingBox>
|
|
#include <osg/Matrix>
|
|
#include <osg/Drawable>
|
|
#include <osg/StateSet>
|
|
#include <osg/State>
|
|
#include <osg/Impostor>
|
|
#include <osg/EarthSky>
|
|
#include <osg/Notify>
|
|
|
|
#include <osgUtil/RenderGraph>
|
|
#include <osgUtil/RenderStage>
|
|
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
#include <osg/Vec3>
|
|
|
|
namespace osgUtil {
|
|
|
|
/**
|
|
* Basic NodeVisitor implementation for rendering a scene.
|
|
* This visitor traverses the scene graph, collecting transparent and
|
|
* opaque osg::Drawables into a depth sorted transparent bin and a state
|
|
* sorted opaque bin. The opaque bin is rendered first, and then the
|
|
* transparent bin in rendered in order from the furthest osg::Drawable
|
|
* from the eye to the one nearest the eye.
|
|
*/
|
|
class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor
|
|
{
|
|
public:
|
|
|
|
enum
|
|
{
|
|
NO_CULLING = 0x00,
|
|
FRUSTUM_LEFT_CULLING = 0x01,
|
|
FRUSTUM_RIGHT_CULLING = 0x02,
|
|
FRUSTUM_BOTTOM_CULLING = 0x04,
|
|
FRUSTUM_TOP_CULLING = 0x08,
|
|
FRUSTUM_NEAR_CULLING = 0x10,
|
|
FRUSTUM_FAR_CULLING = 0x20,
|
|
VIEW_FRUSTUM_CULLING = 0x3F,
|
|
SMALL_FEATURE_CULLING = 0x40,
|
|
ENABLE_ALL_CULLING = 0x7F
|
|
};
|
|
|
|
typedef unsigned int CullingMode;
|
|
|
|
|
|
CullVisitor();
|
|
virtual ~CullVisitor();
|
|
|
|
virtual CullVisitor* cloneType() const { return new CullVisitor(); }
|
|
|
|
virtual void reset();
|
|
|
|
virtual void apply(osg::Node&);
|
|
virtual void apply(osg::Geode& node);
|
|
virtual void apply(osg::Billboard& node);
|
|
virtual void apply(osg::LightSource& node);
|
|
virtual void apply(osg::ClipNode& node);
|
|
|
|
virtual void apply(osg::Group& node);
|
|
virtual void apply(osg::Transform& node);
|
|
virtual void apply(osg::Projection& node);
|
|
virtual void apply(osg::Switch& node);
|
|
virtual void apply(osg::LOD& node);
|
|
virtual void apply(osg::EarthSky& node);
|
|
virtual void apply(osg::Impostor& node);
|
|
|
|
void setEarthSky(const osg::EarthSky* earthSky) { _earthSky = earthSky; }
|
|
const osg::EarthSky* getEarthSky() const { return _earthSky.get(); }
|
|
|
|
|
|
void setLODBias(const float bias) { _LODBias = bias; }
|
|
const float getLODBias() const { return _LODBias; }
|
|
|
|
/** Switch the creation of Impostors on or off.
|
|
* Setting active to false forces the CullVisitor to use the Impostor
|
|
* LOD children for rendering. Setting active to true forces the
|
|
* CullVisitor to create the appropriate pre-rendering stages which
|
|
* render to the ImpostorSprite's texture.*/
|
|
void setImpostorsActive(const bool active) { _impostorActive = active; }
|
|
|
|
/** Get whether impostors are active or not. */
|
|
const bool getImpostorsActive() const { return _impostorActive; }
|
|
|
|
/** Set the impostor error threshold.
|
|
* Used in calculation of whether impostors remain valid.*/
|
|
void setImpostorPixelErrorThreshold(const float numPixels) { _impostorPixelErrorThreshold=numPixels; }
|
|
|
|
/** Get the impostor error threshold.*/
|
|
const float getImpostorPixelErrorThreshold() const { return _impostorPixelErrorThreshold; }
|
|
|
|
/** Set whether ImpsotorSprite's should be placed in a depth sorted bin for rendering.*/
|
|
void setDepthSortImpostorSprites(const bool doDepthSort) { _depthSortImpostorSprites = doDepthSort; }
|
|
|
|
/** Get whether ImpsotorSprite's are depth sorted bin for rendering.*/
|
|
const bool setDepthSortImpostorSprites() const { return _depthSortImpostorSprites; }
|
|
|
|
/** Set the number of frames that an ImpsotorSprite's is kept whilst not being beyond,
|
|
* before being recycled.*/
|
|
void setNumberOfFrameToKeepImpostorSprites(const int numFrames) { _numFramesToKeepImpostorSprites = numFrames; }
|
|
|
|
/** Get the number of frames that an ImpsotorSprite's is kept whilst not being beyond,
|
|
* before being recycled.*/
|
|
const int getNumberOfFrameToKeepImpostorSprites() const { return _numFramesToKeepImpostorSprites; }
|
|
|
|
enum TransparencySortMode {
|
|
LOOK_VECTOR_DISTANCE,
|
|
OBJECT_EYE_POINT_DISTANCE
|
|
};
|
|
|
|
void setTransparencySortMode(TransparencySortMode tsm) { _tsm = tsm; }
|
|
|
|
/** Sets the current CullingMode.*/
|
|
void setCullingMode(CullingMode mode);
|
|
|
|
/** Returns the current CullingMode.*/
|
|
CullingMode getCullingMode() const;
|
|
|
|
|
|
void pushViewport(osg::Viewport* viewport);
|
|
void popViewport();
|
|
|
|
void pushProjectionMatrix(osg::Matrix* matrix);
|
|
void popProjectionMatrix();
|
|
|
|
void pushModelViewMatrix(osg::Matrix* matrix);
|
|
void popModelViewMatrix();
|
|
|
|
/** Push state set on the current state group.
|
|
* If the state exists in a child state group of the current
|
|
* state group then move the current state group to that child.
|
|
* Otherwise, create a new state group for the state set, add
|
|
* it to the current state group then move the current state
|
|
* group pointer to the new state group.
|
|
*/
|
|
inline void pushStateSet(const osg::StateSet* ss)
|
|
{
|
|
_currentRenderGraph = _currentRenderGraph->find_or_insert(ss);
|
|
if (ss->useRenderBinDetails())
|
|
{
|
|
_currentRenderBin = _currentRenderBin->find_or_insert(ss->getBinNumber(),ss->getBinName());
|
|
}
|
|
}
|
|
|
|
/** Pop the top state set and hence associated state group.
|
|
* Move the current state group to the parent of the popped
|
|
* state group.
|
|
*/
|
|
inline void popStateSet()
|
|
{
|
|
if (_currentRenderGraph->_stateset->useRenderBinDetails())
|
|
{
|
|
_currentRenderBin = _currentRenderBin->_parent;
|
|
}
|
|
_currentRenderGraph = _currentRenderGraph->_parent;
|
|
}
|
|
|
|
void setRenderGraph(RenderGraph* rg)
|
|
{
|
|
_rootRenderGraph = rg;
|
|
_currentRenderGraph = rg;
|
|
}
|
|
|
|
RenderGraph* getRenderGraph()
|
|
{
|
|
return _rootRenderGraph.get();
|
|
}
|
|
|
|
void setRenderStage(RenderStage* rg)
|
|
{
|
|
_rootRenderStage = rg;
|
|
_currentRenderBin = rg;
|
|
}
|
|
|
|
RenderStage* getRenderStage()
|
|
{
|
|
return _rootRenderStage.get();
|
|
}
|
|
|
|
const float getCalculatedNearPlane() const { return _computed_znear; }
|
|
|
|
const float getCalculatedFarPlane() const { return _computed_zfar; }
|
|
|
|
inline const osg::Vec3& getEyeLocal() const
|
|
{
|
|
return _eyePointStack.back();
|
|
}
|
|
|
|
|
|
inline float pixelSize(const osg::Vec3& v, float const radius)
|
|
{
|
|
const osg::Matrix& mvpw = getMVPW();
|
|
if (_windowToModelFactorDirty)
|
|
{
|
|
_windowToModelFactorDirty=false;
|
|
_windowToModelFactor = osg::Vec3(mvpw(0,0),mvpw(1,0),mvpw(2,0)).length();
|
|
}
|
|
float W = v.x()*mvpw(0,3)+
|
|
v.y()*mvpw(1,3)+
|
|
v.z()*mvpw(2,3)+
|
|
mvpw(3,3);
|
|
|
|
return fabs(radius*_windowToModelFactor/W);
|
|
}
|
|
|
|
|
|
inline bool isCulled(const osg::BoundingSphere& sp,CullingMode& mode)
|
|
{
|
|
if (!sp.isValid()) return true;
|
|
|
|
if (!(_modelviewClippingVolumeStack.back().contains(sp,mode))) return true;
|
|
|
|
if (mode&SMALL_FEATURE_CULLING)
|
|
{
|
|
if (pixelSize(sp.center(),sp.radius())<_smallFeatureCullingPixelSize) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline const bool isCulled(const osg::BoundingBox& bb,CullingMode mode)
|
|
{
|
|
if (!bb.isValid()) return true;
|
|
|
|
return !_modelviewClippingVolumeStack.back().contains(bb,mode);
|
|
}
|
|
|
|
void setSmallFeatureCullingPixelSize(float s) { _smallFeatureCullingPixelSize=s; }
|
|
float getSmallFeatureCullingPixelSize() const { return _smallFeatureCullingPixelSize; }
|
|
|
|
protected:
|
|
|
|
/** prevent unwanted copy construction.*/
|
|
CullVisitor(const CullVisitor&):osg::NodeVisitor() {}
|
|
|
|
/** prevent unwanted copy operator.*/
|
|
CullVisitor& operator = (const CullVisitor&) { return *this; }
|
|
|
|
inline void handle_cull_callbacks_and_traverse(osg::Node& node)
|
|
{
|
|
osg::NodeCallback* callback = node.getCullCallback();
|
|
if (callback) (*callback)(&node,this);
|
|
else traverse(node);
|
|
}
|
|
|
|
inline void handle_cull_callbacks_and_accept(osg::Node& node,osg::Node* acceptNode)
|
|
{
|
|
osg::NodeCallback* callback = node.getCullCallback();
|
|
if (callback) (*callback)(&node,this);
|
|
else acceptNode->accept(*this);
|
|
}
|
|
|
|
|
|
inline const osg::Vec3 getUpLocal() const
|
|
{
|
|
const osg::Matrix& matrix = *_modelviewStack.back();
|
|
return osg::Vec3(matrix(0,1),matrix(1,1),matrix(2,1));
|
|
}
|
|
|
|
inline const osg::Vec3 getLookVectorLocal() const
|
|
{
|
|
const osg::Matrix& matrix = *_modelviewStack.back();
|
|
return osg::Vec3(-matrix(0,2),-matrix(1,2),-matrix(2,2));
|
|
}
|
|
|
|
|
|
|
|
void updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable);
|
|
void updateCalculatedNearFar(const osg::Vec3& pos);
|
|
|
|
/** Add a drawable to current render graph.*/
|
|
inline void addDrawable(osg::Drawable* drawable,osg::Matrix* matrix)
|
|
{
|
|
if (_currentRenderGraph->leaves_empty())
|
|
{
|
|
// this is first leaf to be added to RenderGraph
|
|
// and therefore should not already know to current render bin,
|
|
// so need to add it.
|
|
_currentRenderBin->addRenderGraph(_currentRenderGraph);
|
|
}
|
|
//_currentRenderGraph->addLeaf(new RenderLeaf(drawable,matrix));
|
|
_currentRenderGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix));
|
|
}
|
|
|
|
/** Add a drawable and depth to current render graph.*/
|
|
inline void addDrawableAndDepth(osg::Drawable* drawable,osg::Matrix* matrix,const float depth)
|
|
{
|
|
if (_currentRenderGraph->leaves_empty())
|
|
{
|
|
// this is first leaf to be added to RenderGraph
|
|
// and therefore should not already know to current render bin,
|
|
// so need to add it.
|
|
_currentRenderBin->addRenderGraph(_currentRenderGraph);
|
|
}
|
|
//_currentRenderGraph->addLeaf(new RenderLeaf(drawable,matrix,depth));
|
|
_currentRenderGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix,depth));
|
|
}
|
|
|
|
/** Add an attribute which is positioned related to the modelview matrix.*/
|
|
inline void addPositionedAttribute(osg::Matrix* matrix,const osg::StateAttribute* attr)
|
|
{
|
|
_currentRenderBin->_stage->addPositionedAttribute(matrix,attr);
|
|
}
|
|
|
|
|
|
/** create an impostor sprite by setting up a pre-rendering stage
|
|
* to generate the impostor texture. */
|
|
osg::ImpostorSprite* createImpostorSprite(osg::Impostor& node);
|
|
|
|
|
|
osg::Viewport* getViewport()
|
|
{
|
|
if (!_viewportStack.empty())
|
|
{
|
|
return _viewportStack.back().get();
|
|
}
|
|
else
|
|
{
|
|
return 0L;
|
|
}
|
|
}
|
|
|
|
osg::Matrix& getModelViewMatrix()
|
|
{
|
|
if (!_modelviewStack.empty())
|
|
{
|
|
return *_modelviewStack.back();
|
|
}
|
|
else
|
|
{
|
|
return _identity;
|
|
}
|
|
}
|
|
|
|
osg::Matrix& getProjectionMatrix()
|
|
{
|
|
if (!_projectionStack.empty())
|
|
{
|
|
return *_projectionStack.back();
|
|
}
|
|
else
|
|
{
|
|
return _identity;
|
|
}
|
|
}
|
|
|
|
const osg::Matrix getWindowMatrix()
|
|
{
|
|
if (!_viewportStack.empty())
|
|
{
|
|
osg::Viewport* viewport = _viewportStack.back().get();
|
|
return viewport->computeWindowMatrix();
|
|
}
|
|
else
|
|
{
|
|
return _identity;
|
|
}
|
|
}
|
|
|
|
const osg::Matrix& getMVPW()
|
|
{
|
|
if (!_MVPW_Stack.empty())
|
|
{
|
|
if (!_MVPW_Stack.back())
|
|
{
|
|
_MVPW_Stack.back() = createOrReuseMatrix(getModelViewMatrix());
|
|
(*_MVPW_Stack.back()) *= getProjectionMatrix();
|
|
(*_MVPW_Stack.back()) *= getWindowMatrix();
|
|
}
|
|
return *_MVPW_Stack.back();
|
|
}
|
|
else
|
|
{
|
|
return _identity;
|
|
}
|
|
}
|
|
|
|
|
|
void pushClippingVolume();
|
|
void popClippingVolume();
|
|
|
|
|
|
typedef std::vector<osg::ClippingVolume> ClippingVolumeStack;
|
|
typedef std::vector<osg::ref_ptr<osg::Matrix> > MatrixStack;
|
|
|
|
MatrixStack _projectionStack;
|
|
MatrixStack _PW_Stack;
|
|
ClippingVolumeStack _projectionClippingVolumeStack;
|
|
|
|
MatrixStack _modelviewStack;
|
|
MatrixStack _MVPW_Stack;
|
|
ClippingVolumeStack _modelviewClippingVolumeStack;
|
|
|
|
bool _windowToModelFactorDirty;
|
|
float _windowToModelFactor;
|
|
|
|
typedef std::vector<osg::ref_ptr<osg::Viewport> > ViewportStack;
|
|
ViewportStack _viewportStack;
|
|
|
|
typedef std::vector<osg::Vec3> EyePointStack;
|
|
EyePointStack _eyePointStack;
|
|
|
|
typedef std::vector<CullingMode> CullingModeStack;
|
|
CullingModeStack _cullingModeStack;
|
|
|
|
unsigned int _bbCornerNear;
|
|
unsigned int _bbCornerFar;
|
|
|
|
osg::Matrix _identity;
|
|
|
|
osg::ref_ptr<RenderGraph> _rootRenderGraph;
|
|
RenderGraph* _currentRenderGraph;
|
|
|
|
osg::ref_ptr<RenderStage> _rootRenderStage;
|
|
RenderBin* _currentRenderBin;
|
|
|
|
|
|
float _LODBias;
|
|
float _smallFeatureCullingPixelSize;
|
|
|
|
enum ComputeNearFarMode
|
|
{
|
|
DO_NOT_COMPUTE_NEAR_FAR = 0,
|
|
COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES,
|
|
COMPUTE_NEAR_FAR_USING_PRIMITIVES
|
|
};
|
|
|
|
ComputeNearFarMode _computeNearFar;
|
|
float _computed_znear;
|
|
float _computed_zfar;
|
|
|
|
osg::ref_ptr<const osg::EarthSky> _earthSky;
|
|
|
|
TransparencySortMode _tsm;
|
|
|
|
bool _impostorActive;
|
|
bool _depthSortImpostorSprites;
|
|
float _impostorPixelErrorThreshold;
|
|
int _numFramesToKeepImpostorSprites;
|
|
|
|
typedef std::vector< osg::ref_ptr<osg::Matrix> > MatrixList;
|
|
MatrixList _reuseMatrixList;
|
|
unsigned int _currentReuseMatrixIndex;
|
|
|
|
inline osg::Matrix* createOrReuseMatrix(const osg::Matrix& value)
|
|
{
|
|
// skip of any already reused matrix.
|
|
while (_currentReuseMatrixIndex<_reuseMatrixList.size() &&
|
|
_reuseMatrixList[_currentReuseMatrixIndex]->referenceCount()>1)
|
|
{
|
|
osg::notify(osg::NOTICE)<<"Warning:createOrReuseMatrix() skipping multiply refrenced entry."<< std::endl;
|
|
++_currentReuseMatrixIndex;
|
|
}
|
|
|
|
// if still within list, element must be singularly referenced
|
|
// there return it to be reused.
|
|
if (_currentReuseMatrixIndex<_reuseMatrixList.size())
|
|
{
|
|
osg::Matrix* matrix = _reuseMatrixList[_currentReuseMatrixIndex++].get();
|
|
matrix->set(value);
|
|
return matrix;
|
|
}
|
|
|
|
// otherwise need to create new matrix.
|
|
osg::Matrix* matrix = new osg::Matrix(value);
|
|
_reuseMatrixList.push_back(matrix);
|
|
++_currentReuseMatrixIndex;
|
|
return matrix;
|
|
}
|
|
|
|
typedef std::vector< osg::ref_ptr<RenderLeaf> > RenderLeafList;
|
|
RenderLeafList _reuseRenderLeafList;
|
|
unsigned int _currentReuseRenderLeafIndex;
|
|
|
|
inline RenderLeaf* createOrReuseRenderLeaf(osg::Drawable* drawable,osg::Matrix* projection,osg::Matrix* matrix, float depth=0.0f)
|
|
{
|
|
// skip of any already reused renderleaf.
|
|
while (_currentReuseRenderLeafIndex<_reuseRenderLeafList.size() &&
|
|
_reuseRenderLeafList[_currentReuseRenderLeafIndex]->referenceCount()>1)
|
|
{
|
|
osg::notify(osg::NOTICE)<<"Warning:createOrReuseRenderLeaf() skipping multiply refrenced entry."<< std::endl;
|
|
++_currentReuseRenderLeafIndex;
|
|
}
|
|
|
|
// if still within list, element must be singularly referenced
|
|
// there return it to be reused.
|
|
if (_currentReuseRenderLeafIndex<_reuseRenderLeafList.size())
|
|
{
|
|
RenderLeaf* renderleaf = _reuseRenderLeafList[_currentReuseRenderLeafIndex++].get();
|
|
renderleaf->set(drawable,projection,matrix,depth);
|
|
return renderleaf;
|
|
}
|
|
|
|
// otherwise need to create new renderleaf.
|
|
RenderLeaf* renderleaf = new RenderLeaf(drawable,projection,matrix,depth);
|
|
_reuseRenderLeafList.push_back(renderleaf);
|
|
++_currentReuseRenderLeafIndex;
|
|
return renderleaf;
|
|
}
|
|
|
|
osg::ref_ptr<osg::ImpostorSpriteManager> _impostorSpriteManager;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|
|
|