OpenSceneGraph/include/osgUtil/CullVisitor
Robert Osfield b4774099d6 Fixed the clampProjectionMatrix method so it doesn't modify the input
znar and zfar, by avoid the use of float/double&.
2004-01-29 20:14:20 +00:00

411 lines
17 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; }
void updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable) { updateCalculatedNearFar(matrix,drawable.getBound()); }
void updateCalculatedNearFar(const osg::Matrix& matrix,const osg::BoundingBox& bb);
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;
};
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