1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2008 Robert Osfield
3 * This library is open source and may be redistributed and/or modified under
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version. The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * OpenSceneGraph Public License for more details.
14#ifndef OSGUTIL_CULLVISITOR
15#define OSGUTIL_CULLVISITOR 1
20#include <osg/NodeVisitor>
21#include <osg/BoundingSphere>
22#include <osg/BoundingBox>
24#include <osg/Drawable>
25#include <osg/StateSet>
27#include <osg/ClearNode>
31#include <osg/CullStack>
33#include <osgUtil/StateGraph>
34#include <osgUtil/RenderStage>
41 * Basic NodeVisitor implementation for rendering a scene.
42 * This visitor traverses the scene graph, collecting transparent and
43 * opaque osg::Drawables into a depth sorted transparent bin and a state
44 * sorted opaque bin. The opaque bin is rendered first, and then the
45 * transparent bin is rendered in order from the furthest osg::Drawable
46 * from the eye to the one nearest the eye.
48class OSGUTIL_EXPORT CullVisitor : public osg::NodeVisitor, public osg::CullStack
52 typedef osg::Matrix::value_type value_type;
57 /// Copy constructor that does a shallow copy.
58 CullVisitor(const CullVisitor&);
60 META_NodeVisitor(osgUtil, CullVisitor)
62 /** Convert 'this' into a osgUtil::CullVisitor pointer if Object is a osgUtil::CullVisitor, otherwise return 0.
63 * Equivalent to dynamic_cast<osgUtil::CullVisitor*>(this).*/
64 virtual osgUtil::CullVisitor* asCullVisitor() { return this; }
66 /** convert 'const this' into a const osgUtil::CullVisitor pointer if Object is a osgUtil::CullVisitor, otherwise return 0.
67 * Equivalent to dynamic_cast<const osgUtil::CullVisitor*>(this).*/
68 virtual const osgUtil::CullVisitor* asCullVisitor() const { return this; }
70 /** Convert 'this' into a osg::CullStack pointer if Object is a osg::CullStack, otherwise return 0.
71 * Equivalent to dynamic_cast<osg::CullStack*>(this).*/
72 virtual osg::CullStack* asCullStack() { return static_cast<osg::CullStack*>(this); }
74 /** convert 'const this' into a const osg::CullStack pointer if Object is a osg::CullStack, otherwise return 0.
75 * Equivalent to dynamic_cast<const osg::CullStack*>(this).*/
76 virtual const osg::CullStack* asCullStack() const { return static_cast<const osg::CullStack*>(this); }
79 using osg::NodeVisitor::clone;
81 /** Create a shallow copy of the CullVisitor, used by CullVisitor::create() to clone the prototype. */
82 virtual CullVisitor* clone() const { return new CullVisitor(*this); }
84 /** get the prototype singleton used by CullVisitor::create().*/
85 static osg::ref_ptr<CullVisitor>& prototype();
87 /** create a CullVisitor by cloning CullVisitor::prototype().*/
88 static CullVisitor* create();
92 struct Identifier : public osg::Referenced
95 virtual ~Identifier() {}
98 void setIdentifier(Identifier* identifier) { _identifier = identifier; }
99 Identifier* getIdentifier() { return _identifier.get(); }
100 const Identifier* getIdentifier() const { return _identifier.get(); }
102 virtual osg::Vec3 getEyePoint() const { return getEyeLocal(); }
103 virtual osg::Vec3 getViewPoint() const { return getViewPointLocal(); }
105 virtual float getDistanceToEyePoint(const osg::Vec3& pos, bool withLODScale) const;
106 virtual float getDistanceFromEyePoint(const osg::Vec3& pos, bool withLODScale) const;
108 virtual float getDistanceToViewPoint(const osg::Vec3& pos, bool withLODScale) const;
110 virtual void apply(osg::Node&);
111 virtual void apply(osg::Geode& node);
112 virtual void apply(osg::Drawable& drawable);
113 virtual void apply(osg::Billboard& node);
114 virtual void apply(osg::LightSource& node);
115 virtual void apply(osg::ClipNode& node);
116 virtual void apply(osg::TexGenNode& node);
118 virtual void apply(osg::Group& node);
119 virtual void apply(osg::Transform& node);
120 virtual void apply(osg::Projection& node);
121 virtual void apply(osg::Switch& node);
122 virtual void apply(osg::LOD& node);
123 virtual void apply(osg::ClearNode& node);
124 virtual void apply(osg::Camera& node);
125 virtual void apply(osg::OccluderNode& node);
126 virtual void apply(osg::OcclusionQueryNode& node);
128 /** Push state set on the current state group.
129 * If the state exists in a child state group of the current
130 * state group then move the current state group to that child.
131 * Otherwise, create a new state group for the state set, add
132 * it to the current state group then move the current state
133 * group pointer to the new state group.
135 inline void pushStateSet(const osg::StateSet* ss)
137 _currentStateGraph = _currentStateGraph->find_or_insert(ss);
139 bool useRenderBinDetails = (ss->useRenderBinDetails() && !ss->getBinName().empty()) &&
140 (_numberOfEncloseOverrideRenderBinDetails==0 || (ss->getRenderBinMode()&osg::StateSet::PROTECTED_RENDERBIN_DETAILS)!=0);
142 if (useRenderBinDetails)
144 _renderBinStack.push_back(_currentRenderBin);
146 _currentRenderBin = ss->getNestRenderBins() ?
147 _currentRenderBin->find_or_insert(ss->getBinNumber(),ss->getBinName()) :
148 _currentRenderBin->getStage()->find_or_insert(ss->getBinNumber(),ss->getBinName());
151 if ((ss->getRenderBinMode()&osg::StateSet::OVERRIDE_RENDERBIN_DETAILS)!=0)
153 ++_numberOfEncloseOverrideRenderBinDetails;
157 /** Pop the top state set and hence associated state group.
158 * Move the current state group to the parent of the popped
161 inline void popStateSet()
163 const osg::StateSet* ss = _currentStateGraph->getStateSet();
164 if ((ss->getRenderBinMode()&osg::StateSet::OVERRIDE_RENDERBIN_DETAILS)!=0)
166 --_numberOfEncloseOverrideRenderBinDetails;
169 bool useRenderBinDetails = (ss->useRenderBinDetails() && !ss->getBinName().empty()) &&
170 (_numberOfEncloseOverrideRenderBinDetails==0 || (ss->getRenderBinMode()&osg::StateSet::PROTECTED_RENDERBIN_DETAILS)!=0);
172 if (useRenderBinDetails)
174 if (_renderBinStack.empty())
176 _currentRenderBin = _currentRenderBin->getStage();
180 _currentRenderBin = _renderBinStack.back();
181 _renderBinStack.pop_back();
184 _currentStateGraph = _currentStateGraph->_parent;
187 inline void setStateGraph(StateGraph* rg)
189 _rootStateGraph = rg;
190 _currentStateGraph = rg;
193 inline StateGraph* getRootStateGraph()
195 return _rootStateGraph.get();
198 inline StateGraph* getCurrentStateGraph()
200 return _currentStateGraph;
203 inline void setRenderStage(RenderStage* rg)
205 _rootRenderStage = rg;
206 _currentRenderBin = rg;
209 inline RenderStage* getRenderStage()
211 return _rootRenderStage.get();
214 inline RenderStage* getCurrentRenderStage()
216 return _currentRenderBin->getStage();
219 inline osg::Camera* getCurrentCamera()
221 return getCurrentRenderStage()->getCamera();
224 inline RenderBin* getCurrentRenderBin()
226 return _currentRenderBin;
229 inline void setCurrentRenderBin(RenderBin* rb)
231 _currentRenderBin = rb;
234 void setCalculatedNearPlane(value_type value) { _computed_znear = value; }
235 inline value_type getCalculatedNearPlane() const { return _computed_znear; }
237 void setCalculatedFarPlane(value_type value) { _computed_zfar = value; }
238 inline value_type getCalculatedFarPlane() const { return _computed_zfar; }
240 value_type computeNearestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable);
241 value_type computeFurthestPointInFrustum(const osg::Matrix& matrix, const osg::Polytope::PlaneList& planes,const osg::Drawable& drawable);
243 bool updateCalculatedNearFar(const osg::Matrix& matrix,const osg::BoundingBox& bb);
245 bool updateCalculatedNearFar(const osg::Matrix& matrix,const osg::Drawable& drawable, bool isBillboard=false);
247 void updateCalculatedNearFar(const osg::Vec3& pos);
249 /** Add a drawable to current render graph.*/
250 inline void addDrawable(osg::Drawable* drawable,osg::RefMatrix* matrix);
252 /** Add a drawable and depth to current render graph.*/
253 inline void addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth);
255 /** Add an attribute which is positioned relative to the modelview matrix.*/
256 inline void addPositionedAttribute(osg::RefMatrix* matrix,const osg::StateAttribute* attr);
258 /** Add an attribute which is positioned relative to the modelview matrix.*/
259 inline void addPositionedTextureAttribute(unsigned int textureUnit, osg::RefMatrix* matrix,const osg::StateAttribute* attr);
262 /** compute near plane based on the polgon intersection of primtives in near plane candidate list of drawables.
263 * Note, you have to set ComputeNearFarMode to COMPUTE_NEAR_FAR_USING_PRIMITIVES to be able to near plane candidate drawables to be recorded by the cull traversal. */
264 void computeNearPlane();
266 /** Re-implement CullStack's popProjectionMatrix() adding clamping of the projection matrix to
267 * the computed near and far.*/
268 virtual void popProjectionMatrix();
271 /** CullVisitor's default clamping of the projection float matrix to computed near and far values.
272 * Note, do not call this method directly, use clampProjectionMatrix(..) instead, unless you want to bypass the callback.*/
273 virtual bool clampProjectionMatrixImplementation(osg::Matrixf& projection, double& znear, double& zfar) const;
275 /** CullVisitor's default clamping of the projection double matrix to computed near and far values.
276 * Note, do not call this method directly, use clampProjectionMatrix(..) instead, unless you want to bypass the callback.*/
277 virtual bool clampProjectionMatrixImplementation(osg::Matrixd& projection, double& znear, double& zfar) const;
279 /** Clamp the projection float matrix to computed near and far values, use callback if it exists,
280 * otherwise use default CullVisitor implementation.*/
281 inline bool clampProjectionMatrix(osg::Matrixf& projection, value_type& znear, value_type& zfar) const
286 if (_clampProjectionMatrixCallback.valid()) result = _clampProjectionMatrixCallback->clampProjectionMatrixImplementation(projection, zn, zf);
287 else result = clampProjectionMatrixImplementation(projection, zn, zf);
299 /** Clamp the projection double matrix to computed near and far values, use callback if it exists,
300 * otherwise use default CullVisitor implementation.*/
301 inline bool clampProjectionMatrix(osg::Matrixd& projection, value_type& znear, value_type& zfar) const
307 if (_clampProjectionMatrixCallback.valid()) result = _clampProjectionMatrixCallback->clampProjectionMatrixImplementation(projection, zn, zf);
308 else result = clampProjectionMatrixImplementation(projection, zn, zf);
321 void setState(osg::State* state) { _renderInfo.setState(state); }
322 osg::State* getState() { return _renderInfo.getState(); }
323 const osg::State* getState() const { return _renderInfo.getState(); }
325 void setRenderInfo(osg::RenderInfo& renderInfo) { _renderInfo = renderInfo; }
326 osg::RenderInfo& getRenderInfo() { return _renderInfo; }
327 const osg::RenderInfo& getRenderInfo() const { return _renderInfo; }
331 virtual ~CullVisitor();
333 /** Prevent unwanted copy operator.*/
334 CullVisitor& operator = (const CullVisitor&) { return *this; }
336 inline void handle_cull_callbacks_and_traverse(osg::Node& node)
338 osg::Callback* callback = node.getCullCallback();
339 if (callback) callback->run(&node,this);
343 inline void handle_cull_callbacks_and_accept(osg::Node& node,osg::Node* acceptNode)
345 osg::Callback* callback = node.getCullCallback();
346 if (callback) callback->run(&node,this);
347 else acceptNode->accept(*this);
350 osg::ref_ptr<StateGraph> _rootStateGraph;
351 StateGraph* _currentStateGraph;
353 osg::ref_ptr<RenderStage> _rootRenderStage;
354 RenderBin* _currentRenderBin;
355 std::vector<RenderBin*> _renderBinStack;
357 value_type _computed_znear;
358 value_type _computed_zfar;
360 unsigned int _traversalOrderNumber;
363 typedef std::vector< osg::ref_ptr<RenderLeaf> > RenderLeafList;
364 RenderLeafList _reuseRenderLeafList;
365 unsigned int _currentReuseRenderLeafIndex;
367 inline RenderLeaf* createOrReuseRenderLeaf(osg::Drawable* drawable,osg::RefMatrix* projection,osg::RefMatrix* matrix, float depth=0.0f);
369 unsigned int _numberOfEncloseOverrideRenderBinDetails;
371 osg::RenderInfo _renderInfo;
374 struct MatrixPlanesDrawables
376 MatrixPlanesDrawables():
381 void set(const osg::Matrix& matrix, const osg::Drawable* drawable, const osg::Polytope& frustum)
384 _drawable = drawable;
385 if (!_planes.empty()) _planes.clear();
387 // create a new list of planes from the active walls of the frustum.
388 osg::Polytope::ClippingMask result_mask = frustum.getResultMask();
389 osg::Polytope::ClippingMask selector_mask = 0x1;
390 for(osg::Polytope::PlaneList::const_iterator itr=frustum.getPlaneList().begin();
391 itr!=frustum.getPlaneList().end();
394 if (result_mask&selector_mask) _planes.push_back(*itr);
399 MatrixPlanesDrawables(const MatrixPlanesDrawables& mpd):
400 _matrix(mpd._matrix),
401 _drawable(mpd._drawable),
402 _planes(mpd._planes) {}
404 MatrixPlanesDrawables& operator = (const MatrixPlanesDrawables& mpd)
406 _matrix = mpd._matrix;
407 _drawable = mpd._drawable;
408 _planes = mpd._planes;
413 const osg::Drawable* _drawable;
414 osg::Polytope::PlaneList _planes;
417 typedef std::multimap<value_type, MatrixPlanesDrawables> DistanceMatrixDrawableMap;
418 DistanceMatrixDrawableMap _nearPlaneCandidateMap;
419 DistanceMatrixDrawableMap _farPlaneCandidateMap;
421 osg::ref_ptr<Identifier> _identifier;
424inline void CullVisitor::addDrawable(osg::Drawable* drawable,osg::RefMatrix* matrix)
426 if (_currentStateGraph->leaves_empty())
428 // this is first leaf to be added to StateGraph
429 // and therefore should not already know to current render bin,
430 // so need to add it.
431 _currentRenderBin->addStateGraph(_currentStateGraph);
433 //_currentStateGraph->addLeaf(new RenderLeaf(drawable,matrix));
434 _currentStateGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix));
437/** Add a drawable and depth to current render graph.*/
438inline void CullVisitor::addDrawableAndDepth(osg::Drawable* drawable,osg::RefMatrix* matrix,float depth)
440 if (_currentStateGraph->leaves_empty())
442 // this is first leaf to be added to StateGraph
443 // and therefore should not already know to current render bin,
444 // so need to add it.
445 _currentRenderBin->addStateGraph(_currentStateGraph);
447 //_currentStateGraph->addLeaf(new RenderLeaf(drawable,matrix,depth));
448 _currentStateGraph->addLeaf(createOrReuseRenderLeaf(drawable,_projectionStack.back().get(),matrix,depth));
451/** Add an attribute which is positioned relative to the modelview matrix.*/
452inline void CullVisitor::addPositionedAttribute(osg::RefMatrix* matrix,const osg::StateAttribute* attr)
454 _currentRenderBin->getStage()->addPositionedAttribute(matrix,attr);
457/** Add an attribute which is positioned relative to the modelview matrix.*/
458inline void CullVisitor::addPositionedTextureAttribute(unsigned int textureUnit, osg::RefMatrix* matrix,const osg::StateAttribute* attr)
460 _currentRenderBin->getStage()->addPositionedTextureAttribute(textureUnit,matrix,attr);
463inline RenderLeaf* CullVisitor::createOrReuseRenderLeaf(osg::Drawable* drawable,osg::RefMatrix* projection,osg::RefMatrix* matrix, float depth)
465 // Skips any already reused renderleaf.
466 while (_currentReuseRenderLeafIndex<_reuseRenderLeafList.size() &&
467 _reuseRenderLeafList[_currentReuseRenderLeafIndex]->referenceCount()>1)
469 osg::notify(osg::INFO)<<"CullVisitor:createOrReuseRenderLeaf() skipping multiply referenced entry. _reuseRenderLeafList.size()="<< _reuseRenderLeafList.size()<<" _reuseRenderLeafList["<<_currentReuseRenderLeafIndex<<"]->referenceCount()="<<_reuseRenderLeafList[_currentReuseRenderLeafIndex]->referenceCount()<<std::endl;
470 ++_currentReuseRenderLeafIndex;
473 // If still within list, element must be singularly referenced then return it to be reused.
474 if (_currentReuseRenderLeafIndex<_reuseRenderLeafList.size())
476 RenderLeaf* renderleaf = _reuseRenderLeafList[_currentReuseRenderLeafIndex++].get();
477 renderleaf->set(drawable,projection,matrix,depth,_traversalOrderNumber++);
482 // Otherwise need to create new renderleaf.
483 RenderLeaf* renderleaf = new RenderLeaf(drawable,projection,matrix,depth,_traversalOrderNumber++);
484 _reuseRenderLeafList.push_back(renderleaf);
486 ++_currentReuseRenderLeafIndex;