Files
OpenSceneGraph/include/osgUtil/CullVisitor
Robert Osfield 5d79eb1c9d Added support for fine grained computation of the near plane, by taking
into account individual primitives culled against the view frustum.

Added better support for computing the near far for billboards.
2004-04-29 22:21:06 +00:00

455 lines
18 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
*
* 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 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/ClearNode>
#include <osg/Notify>
#include <osg/Notify>
#include <osg/CullStack>
#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 osg::CullStack
{
public:
typedef osg::Matrix::value_type value_type;
CullVisitor();
virtual ~CullVisitor();
virtual CullVisitor* cloneType() const { return new CullVisitor(); }
virtual void reset();
virtual osg::Vec3 getEyePoint() const { return getEyeLocal(); }
virtual float getDistanceToEyePoint(const osg::Vec3& pos, bool withLODScale) const;
virtual float getDistanceFromEyePoint(const osg::Vec3& pos, bool withLODScale) const;
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::ClearNode& node);
virtual void apply(osg::OccluderNode& node);
virtual void apply(osg::Impostor& node);
void setClearNode(const osg::ClearNode* earthSky) { _clearNode = earthSky; }
const osg::ClearNode* getClearNode() const { return _clearNode.get(); }
/** 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(bool active) { _impostorActive = active; }
/** Get whether impostors are active or not. */
bool getImpostorsActive() const { return _impostorActive; }
/** Set the impostor error threshold.
* Used in calculation of whether impostors remain valid.*/
void setImpostorPixelErrorThreshold(float numPixels) { _impostorPixelErrorThreshold=numPixels; }
/** Get the impostor error threshold.*/
float getImpostorPixelErrorThreshold() const { return _impostorPixelErrorThreshold; }
/** Set whether ImpostorSprite's should be placed in a depth sorted bin for rendering.*/
void setDepthSortImpostorSprites(bool doDepthSort) { _depthSortImpostorSprites = doDepthSort; }
/** Get whether ImpostorSprite's are depth sorted bin for rendering.*/
bool setDepthSortImpostorSprites() const { return _depthSortImpostorSprites; }
/** Set the number of frames that an ImpostorSprite's is kept whilst not being beyond,
* before being recycled.*/
void setNumberOfFrameToKeepImpostorSprites(int numFrames) { _numFramesToKeepImpostorSprites = numFrames; }
/** Get the number of frames that an ImpostorSprite's is kept whilst not being beyond,
* before being recycled.*/
int getNumberOfFrameToKeepImpostorSprites() const { return _numFramesToKeepImpostorSprites; }
enum ComputeNearFarMode
{
DO_NOT_COMPUTE_NEAR_FAR = 0,
COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES,
COMPUTE_NEAR_FAR_USING_PRIMITIVES
};
void setComputeNearFarMode(ComputeNearFarMode cnfm) { _computeNearFar=cnfm; }
ComputeNearFarMode getComputeNearFarMode() const { return _computeNearFar;}
void setNearFarRatio(float ratio) { _nearFarRatio = ratio; }
float getNearFarRatio() const { return _nearFarRatio; }
/** 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;
}
inline void setRenderGraph(RenderGraph* rg)
{
_rootRenderGraph = rg;
_currentRenderGraph = rg;
}
inline RenderGraph* getRootRenderGraph()
{
return _rootRenderGraph.get();
}
inline RenderGraph* getCurrentRenderGraph()
{
return _currentRenderGraph;
}
inline void setRenderStage(RenderStage* rg)
{
_rootRenderStage = rg;
_currentRenderBin = rg;
}
inline RenderStage* getRenderStage()
{
return _rootRenderStage.get();
}
inline RenderBin* getCurrentRenderBin()
{
return _currentRenderBin;
}
inline void setCurrentRenderBin(RenderBin* rb)
{
_currentRenderBin = rb;
}
inline value_type getCalculatedNearPlane() const { return _computed_znear; }
inline value_type getCalculatedFarPlane() const { return _computed_zfar; }
value_type computeNearestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable);
bool updateCalculatedNearFar(const osg::Matrix& matrix,const osg::BoundingBox& bb);
bool updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable, bool isBillboard=false);
void updateCalculatedNearFar(const osg::Vec3& pos);
/** Add a drawable to current render graph.*/
inline void addDrawable(osg::Drawable* drawable,osg::RefMatrix* matrix);
/** Add a drawable and depth to current render graph.*/
inline void addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth);
/** Add an attribute which is positioned related to the modelview matrix.*/
inline void addPositionedAttribute(osg::RefMatrix* matrix,const osg::StateAttribute* attr);
/** reimplement CullStack's popProjectionMatrix() adding clamping of the projection matrix to the computed near and far.*/
virtual void popProjectionMatrix();
/** Callback for overriding the CullVisitor's default clamping of the projection matrix to computed near and far values.
* Note, both Matrixf and Matrixd versions of clampProjectionMatrixImplementation must be implemented as the CullVisitor
* can target either Matrix data type, configured at compile time.*/
struct ClampProjectionMatrixCallback : public osg::Referenced
{
virtual bool clampProjectionMatrixImplementation(osg::Matrixf& projection, double znear, double zfar) const = 0;
virtual bool clampProjectionMatrixImplementation(osg::Matrixd& projection, double znear, double zfar) const = 0;
};
/** set the ClampProjectionMatrixCallback.*/
void setClampProjectionMatrixCallback(ClampProjectionMatrixCallback* cpmc) { _clampProjectionMatrixCallback = cpmc; }
/** get the non const ClampProjectionMatrixCallback.*/
ClampProjectionMatrixCallback* getClampProjectionMatrixCallback() { return _clampProjectionMatrixCallback.get(); }
/** get the const ClampProjectionMatrixCallback.*/
const ClampProjectionMatrixCallback* getClampProjectionMatrixCallback() const { return _clampProjectionMatrixCallback.get(); }
/** CullVisitor's default clamping of the projection float matrix to computed near and far values.
* Note, do not call this method directly, use clampProjectionMatrix(..) instead, unless you want to bypass the callback.*/
virtual bool clampProjectionMatrixImplementation(osg::Matrixf& projection, double znear, double zfar) const;
/** CullVisitor's default clamping of the projection double matrix to computed near and far values.
* Note, do not call this method directly, use clampProjectionMatrix(..) instead, unless you want to bypass the callback.*/
virtual bool clampProjectionMatrixImplementation(osg::Matrixd& projection, double znear, double zfar) const;
/** clamp the projection float matrix to computed near and far values, use callback if it exists, otherwise use default CullVisitro implemntation.*/
inline bool clampProjectionMatrix(osg::Matrixf& projection, value_type znear, value_type zfar) const
{
double zn = znear;
double zf = zfar;
bool result = false;
if (_clampProjectionMatrixCallback.valid()) result = _clampProjectionMatrixCallback->clampProjectionMatrixImplementation(projection, zn, zf);
else result = clampProjectionMatrixImplementation(projection, zn, zf);
if (result)
{
znear = zn;
zfar = zn;
return true;
}
else
return false;
}
/** clamp the projection double matrix to computed near and far values, use callback if it exists, otherwise use default CullVisitro implemntation.*/
inline bool clampProjectionMatrix(osg::Matrixd& projection, value_type znear, value_type zfar) const
{
double zn = znear;
double zf = zfar;
bool result = false;
if (_clampProjectionMatrixCallback.valid()) result = _clampProjectionMatrixCallback->clampProjectionMatrixImplementation(projection, zn, zf);
else result = clampProjectionMatrixImplementation(projection, zn, zf);
if (result)
{
znear = zn;
zfar = zn;
return true;
}
else
return false;
}
void setState(osg::State* state) { _state = state; }
osg::State* getState() { return _state.get(); }
const osg::State* getState() const { return _state.get(); }
protected:
// /** prevent unwanted copy construction.*/
// CullVisitor(const CullVisitor&): osg::NodeVisitor(), osg::CullStack() {}
/** 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);
}
/** create an impostor sprite by setting up a pre-rendering stage
* to generate the impostor texture. */
osg::ImpostorSprite* createImpostorSprite(osg::Impostor& node);
osg::ref_ptr<RenderGraph> _rootRenderGraph;
RenderGraph* _currentRenderGraph;
osg::ref_ptr<RenderStage> _rootRenderStage;
RenderBin* _currentRenderBin;
ComputeNearFarMode _computeNearFar;
value_type _nearFarRatio;
value_type _computed_znear;
value_type _computed_zfar;
osg::ref_ptr<ClampProjectionMatrixCallback> _clampProjectionMatrixCallback;
osg::ref_ptr<const osg::ClearNode> _clearNode;
bool _impostorActive;
bool _depthSortImpostorSprites;
float _impostorPixelErrorThreshold;
int _numFramesToKeepImpostorSprites;
typedef std::vector< osg::ref_ptr<RenderLeaf> > RenderLeafList;
RenderLeafList _reuseRenderLeafList;
unsigned int _currentReuseRenderLeafIndex;
inline RenderLeaf* createOrReuseRenderLeaf(osg::Drawable* drawable,osg::RefMatrix* projection,osg::RefMatrix* matrix, float depth=0.0f);
osg::ref_ptr<osg::ImpostorSpriteManager> _impostorSpriteManager;
osg::ref_ptr<osg::State> _state;
struct MatrixPlanesDrawables
{
MatrixPlanesDrawables(const osg::Matrix& matrix, const osg::Drawable* drawable, const osg::Polytope& frustum):
_matrix(matrix),
_drawable(drawable)
{
// create a new list of planes from the active walls of the frustum.
osg::Polytope::ClippingMask result_mask = frustum.getResultMask();
osg::Polytope::ClippingMask selector_mask = 0x1;
for(osg::Polytope::PlaneList::const_iterator itr=frustum.getPlaneList().begin();
itr!=frustum.getPlaneList().end();
++itr)
{
if (result_mask&selector_mask) _planes.push_back(*itr);
selector_mask <<= 1;
}
}
MatrixPlanesDrawables(const MatrixPlanesDrawables& mpd):
_matrix(mpd._matrix),
_drawable(mpd._drawable),
_planes(mpd._planes) {}
MatrixPlanesDrawables& operator = (const MatrixPlanesDrawables& mpd)
{
_matrix = mpd._matrix;
_drawable = mpd._drawable;
_planes = mpd._planes;
return *this;
}
osg::Matrix _matrix;
const osg::Drawable* _drawable;
osg::Polytope::PlaneList _planes;
};
typedef std::multimap<value_type, MatrixPlanesDrawables> DistanceMatrixDrawableMap;
DistanceMatrixDrawableMap _nearPlaneCandidateMap;
};
inline void CullVisitor::addDrawable(osg::Drawable* drawable,osg::RefMatrix* 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 CullVisitor::addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,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 CullVisitor::addPositionedAttribute(osg::RefMatrix* matrix,const osg::StateAttribute* attr)
{
_currentRenderBin->_stage->addPositionedAttribute(matrix,attr);
}
inline RenderLeaf* CullVisitor::createOrReuseRenderLeaf(osg::Drawable* drawable,osg::RefMatrix* projection,osg::RefMatrix* matrix, float depth)
{
// 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;
}
}
#endif