From 9aae37e3575e3d49064aef38b1878545c5c49934 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 9 Feb 2007 16:03:29 +0000 Subject: [PATCH] Moved the basic ShadowVolume code into osgShadow --- examples/osgshadow/osgshadow.cpp | 1 - include/osgShadow/ShadowTechnique | 3 +- include/osgShadow/ShadowVolume | 19 +- src/osgShadow/ShadowTechnique.cpp | 10 +- src/osgShadow/ShadowVolume.cpp | 351 +++++++++++++++++++++++++++++- 5 files changed, 369 insertions(+), 15 deletions(-) diff --git a/examples/osgshadow/osgshadow.cpp b/examples/osgshadow/osgshadow.cpp index 7ee216ab7..f3e09021f 100644 --- a/examples/osgshadow/osgshadow.cpp +++ b/examples/osgshadow/osgshadow.cpp @@ -681,7 +681,6 @@ int main(int argc, char** argv) } - osg::ref_ptr light = new osg::Light; light->setPosition(lightpos); diff --git a/include/osgShadow/ShadowTechnique b/include/osgShadow/ShadowTechnique index acb9d469e..b37012555 100644 --- a/include/osgShadow/ShadowTechnique +++ b/include/osgShadow/ShadowTechnique @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -45,7 +46,7 @@ class OSGSHADOW_EXPORT ShadowTechnique : public osg::Object virtual void update(osg::NodeVisitor& nv); /** run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/ - virtual void cull(osg::NodeVisitor& nv); + virtual void cull(osgUtil::CullVisitor& cv); /** Clean scene graph from any shadow technique specific nodes, state and drawables.*/ virtual void cleanSceneGraph(); diff --git a/include/osgShadow/ShadowVolume b/include/osgShadow/ShadowVolume index c64b9e88f..49487f93a 100644 --- a/include/osgShadow/ShadowVolume +++ b/include/osgShadow/ShadowVolume @@ -15,8 +15,11 @@ #define OSGSHADOW_SHADOWVOLUME 1 #include +#include +#include #include +#include namespace osgShadow { @@ -37,7 +40,7 @@ class OSGSHADOW_EXPORT ShadowVolume : public ShadowTechnique virtual void update(osg::NodeVisitor& nv); /** run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/ - virtual void cull(osg::NodeVisitor& nv); + virtual void cull(osgUtil::CullVisitor& cv); /** Clean scene graph from any shadow technique specific nodes, state and drawables.*/ virtual void cleanSceneGraph(); @@ -46,6 +49,20 @@ class OSGSHADOW_EXPORT ShadowVolume : public ShadowTechnique virtual ~ShadowVolume(); + osgShadow::ShadowVolumeGeometry::DrawMode _drawMode; + + osg::ref_ptr _occluder; + osg::ref_ptr _shadowVolume; + + osg::ref_ptr _ambientLight; + osg::ref_ptr _diffuseLight; + + osg::ref_ptr _ss1; + osg::ref_ptr _mainShadowStateSet; + osg::ref_ptr _shadowVolumeStateSet; + osg::ref_ptr _shadowedSceneStateSet; + + }; } diff --git a/src/osgShadow/ShadowTechnique.cpp b/src/osgShadow/ShadowTechnique.cpp index 1155aa3ef..4beb82509 100644 --- a/src/osgShadow/ShadowTechnique.cpp +++ b/src/osgShadow/ShadowTechnique.cpp @@ -47,10 +47,10 @@ void ShadowTechnique::update(osg::NodeVisitor& nv) _shadowedScene->osg::Group::traverse(nv); } -void ShadowTechnique::cull(osg::NodeVisitor& nv) +void ShadowTechnique::cull(osgUtil::CullVisitor& cv) { - osg::notify(osg::NOTICE)<osg::Group::traverse(nv); + osg::notify(osg::NOTICE)<osg::Group::traverse(cv); } void ShadowTechnique::cleanSceneGraph() @@ -70,7 +70,9 @@ void ShadowTechnique::traverse(osg::NodeVisitor& nv) } else if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { - cull(nv); + osgUtil::CullVisitor* cv = dynamic_cast(&nv); + if (cv) cull(*cv); + else _shadowedScene->osg::Group::traverse(nv); } else { diff --git a/src/osgShadow/ShadowVolume.cpp b/src/osgShadow/ShadowVolume.cpp index 208e36239..0f83e375e 100644 --- a/src/osgShadow/ShadowVolume.cpp +++ b/src/osgShadow/ShadowVolume.cpp @@ -15,15 +15,138 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + using namespace osgShadow; -ShadowVolume::ShadowVolume() + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ComputeBoundingBoxVisitor +// +class ComputeBoundingBoxVisitor : public osg::NodeVisitor +{ +public: + ComputeBoundingBoxVisitor(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + { + } + + virtual void reset() + { + _matrixStack.clear(); + _bb.init(); + } + + osg::BoundingBox& getBoundingBox() { return _bb; } + + void getPolytope(osg::Polytope& polytope, float margin=0.1) const + { + float delta = _bb.radius()*margin; + polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) ); + polytope.add( osg::Plane(0.0, 0.0, -1.0, (_bb.zMax()+delta)) ); + + polytope.add( osg::Plane(1.0, 0.0, 0.0, -(_bb.xMin()-delta)) ); + polytope.add( osg::Plane(-1.0, 0.0, 0.0, (_bb.xMax()+delta)) ); + + polytope.add( osg::Plane(0.0, 1.0, 0.0, -(_bb.yMin()-delta)) ); + polytope.add( osg::Plane(0.0, -1.0, 0.0, (_bb.yMax()+delta)) ); + } + + void getBase(osg::Polytope& polytope, float margin=0.1) const + { + float delta = _bb.radius()*margin; + polytope.add( osg::Plane(0.0, 0.0, 1.0, -(_bb.zMin()-delta)) ); + } + + void apply(osg::Node& node) + { + traverse(node); + } + + void apply(osg::Transform& transform) + { + osg::Matrix matrix; + if (!_matrixStack.empty()) matrix = _matrixStack.back(); + + transform.computeLocalToWorldMatrix(matrix,this); + + pushMatrix(matrix); + + traverse(transform); + + popMatrix(); + } + + void apply(osg::Geode& geode) + { + for(unsigned int i=0; igetBound()); + else + { + osg::Matrix& matrix = _matrixStack.back(); + const osg::BoundingBox& dbb = drawable->getBound(); + if (dbb.valid()) + { + _bb.expandBy(dbb.corner(0) * matrix); + _bb.expandBy(dbb.corner(1) * matrix); + _bb.expandBy(dbb.corner(2) * matrix); + _bb.expandBy(dbb.corner(3) * matrix); + _bb.expandBy(dbb.corner(4) * matrix); + _bb.expandBy(dbb.corner(5) * matrix); + _bb.expandBy(dbb.corner(6) * matrix); + _bb.expandBy(dbb.corner(7) * matrix); + } + } + } + +protected: + + typedef std::vector MatrixStack; + + MatrixStack _matrixStack; + osg::BoundingBox _bb; +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// ShadowVolume +// +ShadowVolume::ShadowVolume(): + _drawMode(osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED) { osg::notify(osg::NOTICE)<<"Warning: osgShadow::ShadowVolume technique in development."<osg::Group::traverse(cbbv); + + + osg::Vec4 lightpos; + lightpos.set(0.5f,0.25f,0.8f,0.0f); + + // set up the occluder + _occluder = new osgShadow::OccluderGeometry; + _occluder->computeOccluderGeometry(_shadowedScene); +// cbbv.getPolytope(_occluder->getBoundingPolytope(),0.001); + cbbv.getBase(_occluder->getBoundingPolytope(),0.001); + + + // set up shadow volume + _shadowVolume = new osgShadow::ShadowVolumeGeometry; + _shadowVolume->setUseDisplayList(!updateLightPosition); + _occluder->computeShadowVolumeGeometry(lightpos, *_shadowVolume); + + osg::ref_ptr geode = new osg::Geode; + _shadowedScene->addChild(geode.get()); + int shadowVolumeBin = 1000; + + if (_drawMode == osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED) + { + osg::notify(osg::NOTICE)<<"STENCIL_TWO_SIDED seleteced"<getOrCreateStateSet(); + ss_sv1->setRenderBinDetails(shadowVolumeBin, "RenderBin"); + geode->addDrawable(_shadowVolume.get()); + } + else + { + osg::notify(osg::NOTICE)<<"STENCIL_TWO_PASSES seleteced"<getOrCreateStateSet(); + ss_sv1->setRenderBinDetails(shadowVolumeBin, "RenderBin"); + geode->addDrawable(_shadowVolume.get()); + } + + + + { + + // first group, render the depth buffer + ambient light contribution + { + _ss1 = new osg::StateSet; + +#if 0 + osg::LightModel* lm1 = new osg::LightModel; + lm1->setAmbientIntensity(ambient); + _ss1->setAttribute(lm1); + + osg::Light* light1 = new osg::Light; + light1->setAmbient(ambient); + light1->setDiffuse(zero_colour); + light1->setPosition(_lightpos); + _ss1->setAttributeAndModes(light1, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); +#endif + + _ss1->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } + + { + _mainShadowStateSet = new osg::StateSet; + + osg::Depth* depth = new osg::Depth; + depth->setWriteMask(false); + depth->setFunction(osg::Depth::LEQUAL); + _mainShadowStateSet->setAttribute(depth); + _mainShadowStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } + + { + _shadowVolumeStateSet = new osg::StateSet; + + osg::Depth* depth = new osg::Depth; + depth->setWriteMask(false); + depth->setFunction(osg::Depth::LEQUAL); + _shadowVolumeStateSet->setAttribute(depth); + _shadowVolumeStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + _shadowVolumeStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); + + + if (_drawMode == osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED) + { + osg::StencilTwoSided* stencil = new osg::StencilTwoSided; + stencil->setFunction(osg::StencilTwoSided::BACK, osg::StencilTwoSided::ALWAYS,0,~0u); + stencil->setOperation(osg::StencilTwoSided::BACK, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::DECR_WRAP); + stencil->setFunction(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::ALWAYS,0,~0u); + stencil->setOperation(osg::StencilTwoSided::FRONT, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::KEEP, osg::StencilTwoSided::INCR_WRAP); + + osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false); + + _shadowVolumeStateSet->setAttributeAndModes(stencil,osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + _shadowVolumeStateSet->setAttribute(colourMask, osg::StateAttribute::OVERRIDE); + _shadowVolumeStateSet->setMode(GL_CULL_FACE,osg::StateAttribute::OFF); + + + } + else + { + osg::Stencil* stencil = new osg::Stencil; + stencil->setFunction(osg::Stencil::ALWAYS,0,~0u); + stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP); + + osg::ColorMask* colourMask = new osg::ColorMask(false, false, false, false); + + _shadowVolumeStateSet->setAttributeAndModes(stencil,osg::StateAttribute::ON); + _shadowVolumeStateSet->setAttribute(colourMask); + _shadowVolumeStateSet->setMode(GL_CULL_FACE,osg::StateAttribute::ON); + } + } + + + { + _shadowedSceneStateSet = new osg::StateSet; + + osg::Depth* depth = new osg::Depth; + depth->setWriteMask(false); + depth->setFunction(osg::Depth::LEQUAL); + _shadowedSceneStateSet->setAttribute(depth); + _shadowedSceneStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON); + // _shadowedSceneStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + +#if 0 + osg::LightModel* lm1 = new osg::LightModel; + lm1->setAmbientIntensity(zero_colour); + _shadowedSceneStateSet->setAttribute(lm1); + + osg::Light* light = new osg::Light; + light->setAmbient(zero_colour); + light->setDiffuse(diffuse); + light->setPosition(_lightpos); + + _shadowedSceneStateSet->setMode(GL_LIGHT0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + _shadowedSceneStateSet->setAttribute(light); +#endif + + // set up the stencil ops so that only operator on this mirrors stencil value. + osg::Stencil* stencil = new osg::Stencil; + stencil->setFunction(osg::Stencil::EQUAL,0,~0u); + stencil->setOperation(osg::Stencil::KEEP, osg::Stencil::KEEP, osg::Stencil::KEEP); + _shadowedSceneStateSet->setAttributeAndModes(stencil,osg::StateAttribute::ON); + + osg::BlendFunc* blend = new osg::BlendFunc; + blend->setFunction(osg::BlendFunc::ONE, osg::BlendFunc::ONE); + _shadowedSceneStateSet->setAttributeAndModes(blend, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + // _shadowedSceneStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + } + + _ss1->setThreadSafeRefUnref(true); + _mainShadowStateSet->setThreadSafeRefUnref(true); + _shadowVolumeStateSet->setThreadSafeRefUnref(true); + _shadowedSceneStateSet->setThreadSafeRefUnref(true); + + } _dirty = false; } void ShadowVolume::update(osg::NodeVisitor& nv) { - osg::notify(osg::NOTICE)<osg::Group::traverse(nv); } -void ShadowVolume::cull(osg::NodeVisitor& nv) +void ShadowVolume::cull(osgUtil::CullVisitor& cv) { - osg::notify(osg::NOTICE)<osg::Group::traverse(nv); + + osg::ref_ptr original_bin = cv.getCurrentRenderBin(); + + osg::ref_ptr new_bin = original_bin->find_or_insert(0,"RenderBin"); + + cv.setCurrentRenderBin(new_bin.get()); + + _shadowedScene->osg::Group::traverse(cv); + + cv.setCurrentRenderBin(original_bin.get()); + + osgUtil::RenderBin::RenderBinList::iterator itr = new_bin->getRenderBinList().find(1000); + osg::ref_ptr shadowVolumeBin; + if (itr != new_bin->getRenderBinList().end()) + { + shadowVolumeBin = itr->second; + + if (shadowVolumeBin.valid()) + { + // osg::notify(osg::NOTICE)<<"Found shadow volume bin, now removing it"<getRenderBinList().erase(itr); + } + } + + if (shadowVolumeBin.valid()) + { + original_bin->setStateSet(_ss1.get()); + + osgUtil::RenderStage* orig_rs = cv.getRenderStage(); + osgUtil::RenderStage* new_rs = new osgUtil::RenderStage; + orig_rs->addPostRenderStage(new_rs); + + new_rs->setViewport(orig_rs->getViewport()); + new_rs->setClearColor(orig_rs->getClearColor()); + new_rs->setClearMask(GL_STENCIL_BUFFER_BIT); + new_rs->setDrawBuffer(orig_rs->getDrawBuffer()); + new_rs->setReadBuffer(orig_rs->getReadBuffer()); + new_rs->setColorMask(orig_rs->getColorMask()); + + new_rs->setPositionalStateContainer(orig_rs->getPositionalStateContainer()); + + if (shadowVolumeBin.valid()) + { + // new_rs->setStateSet(_mainShadowStateSet.get()); + new_rs->getRenderBinList()[0] = shadowVolumeBin; + shadowVolumeBin->setStateSet(_shadowVolumeStateSet.get()); + + osg::ref_ptr nested_bin = new_rs->find_or_insert(1,"RenderBin"); + nested_bin->getRenderBinList()[0] = new_bin; + nested_bin->setStateSet(_shadowedSceneStateSet.get()); + } + } + + } void ShadowVolume::cleanSceneGraph()