OpenSceneGraph/src/osgShadow/ShadowVolume.cpp

357 lines
13 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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.
*/
#include <osgShadow/ShadowVolume>
#include <osgShadow/ShadowedScene>
#include <osg/Notify>
#include <osg/LightModel>
#include <osg/Depth>
#include <osg/BlendFunc>
#include <osg/Camera>
#include <osg/Stencil>
#include <osg/StencilTwoSided>
#include <osg/CullFace>
#include <osg/Geometry>
#include <osg/ComputeBoundsVisitor>
#include <osg/io_utils>
using namespace osgShadow;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ShadowVolume
//
ShadowVolume::ShadowVolume():
_drawMode(osgShadow::ShadowVolumeGeometry::STENCIL_TWO_SIDED),
_dynamicShadowVolumes(false)
{
// _drawMode = osgShadow::ShadowVolumeGeometry::GEOMETRY;
osg::notify(osg::NOTICE)<<"Warning: osgShadow::ShadowVolume technique is still in development, with current limitations that make it unsuitable for deployment. Please contact the osg-users for an update of developements."<<std::endl;
}
ShadowVolume::ShadowVolume(const ShadowVolume& sv, const osg::CopyOp& copyop):
ShadowTechnique(sv,copyop),
_drawMode(sv._drawMode),
_dynamicShadowVolumes(sv._dynamicShadowVolumes)
{
}
ShadowVolume::~ShadowVolume()
{
}
void ShadowVolume::setDrawMode(osgShadow::ShadowVolumeGeometry::DrawMode drawMode)
{
if (_drawMode == drawMode) return;
_drawMode = drawMode;
dirty();
}
void ShadowVolume::setDynamicShadowVolumes(bool dynamicShadowVolumes)
{
_dynamicShadowVolumes = dynamicShadowVolumes;
}
void ShadowVolume::init()
{
if (!_shadowedScene) return;
// get the bounds of the model.
osg::ComputeBoundsVisitor cbbv;
_shadowedScene->osg::Group::traverse(cbbv);
osg::Vec4 ambient(0.2,0.2,0.2,1.0);
osg::Vec4 diffuse(0.8,0.8,0.8,1.0);
osg::Vec4 zero_colour(0.0,0.0,0.0,1.0);
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(!_dynamicShadowVolumes);
_shadowVolume->setDrawMode(_drawMode);
_occluder->computeShadowVolumeGeometry(lightpos, *_shadowVolume);
osg::ref_ptr<osg::Geode> 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 selected"<<std::endl;
osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
ss_sv1->setRenderBinDetails(shadowVolumeBin, "RenderBin");
geode->addDrawable(_shadowVolume.get());
}
else
{
osg::notify(osg::NOTICE)<<"STENCIL_TWO_PASSES selecteted"<<std::endl;
osg::StateSet* ss_sv1 = geode->getOrCreateStateSet();
ss_sv1->setRenderBinDetails(shadowVolumeBin, "RenderBin");
geode->addDrawable(_shadowVolume.get());
}
{
// first group, render the depth buffer + ambient light contribution
{
_ss1 = new osg::StateSet;
osg::LightModel* lm1 = new osg::LightModel;
lm1->setAmbientIntensity(ambient);
_ss1->setAttribute(lm1);
_ambientLight = new osg::Light;
_ambientLight->setAmbient(ambient);
_ambientLight->setDiffuse(zero_colour);
_ss1->setAttributeAndModes(_ambientLight.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
_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);
osg::LightModel* lm1 = new osg::LightModel;
lm1->setAmbientIntensity(zero_colour);
_shadowedSceneStateSet->setAttribute(lm1);
_diffuseLight = new osg::Light;
_diffuseLight->setAmbient(zero_colour);
_diffuseLight->setDiffuse(diffuse);
_shadowedSceneStateSet->setMode(GL_LIGHT0, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
_shadowedSceneStateSet->setAttribute(_diffuseLight.get());
// 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)
{
_shadowedScene->osg::Group::traverse(nv);
}
void ShadowVolume::cull(osgUtil::CullVisitor& cv)
{
osg::ref_ptr<osgUtil::RenderBin> original_bin = cv.getCurrentRenderBin();
osg::ref_ptr<osgUtil::RenderBin> 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<osgUtil::RenderBin> shadowVolumeBin;
if (itr != new_bin->getRenderBinList().end())
{
shadowVolumeBin = itr->second;
if (shadowVolumeBin.valid())
{
//osg::notify(osg::NOTICE)<<"Found shadow volume bin, now removing it"<<std::endl;
new_bin->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());
osg::Vec4 lightpos;
osg::ref_ptr<osgUtil::PositionalStateContainer> ps = new osgUtil::PositionalStateContainer;
new_rs->setPositionalStateContainer(ps.get());
const osg::Light* selectLight = 0;
osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
itr != aml.end();
++itr)
{
const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
if (light)
{
osg::RefMatrix* matrix = itr->second.get();
if (matrix) lightpos = light->getPosition() * (*matrix);
else lightpos = light->getPosition();
selectLight = light;
}
else
{
ps->addPositionedAttribute(itr->second.get(), itr->first.get());
}
}
_ambientLight->setPosition(lightpos);
orig_rs->addPositionedAttribute(0,_ambientLight.get());
_diffuseLight->setPosition(lightpos);
if (selectLight)
{
_ambientLight->setAmbient(selectLight->getAmbient());
_diffuseLight->setDiffuse(selectLight->getDiffuse());
_diffuseLight->setSpecular(selectLight->getSpecular());
_diffuseLight->setDirection(selectLight->getDirection());
_diffuseLight->setConstantAttenuation(selectLight->getConstantAttenuation());
_diffuseLight->setLinearAttenuation(selectLight->getLinearAttenuation());
_diffuseLight->setQuadraticAttenuation(selectLight->getQuadraticAttenuation());
_diffuseLight->setSpotExponent(selectLight->getSpotExponent());
_diffuseLight->setSpotCutoff(selectLight->getSpotCutoff());
}
ps->addPositionedAttribute(0, _diffuseLight.get());
if (_lightpos != lightpos && _dynamicShadowVolumes)
{
_lightpos = lightpos;
osg::Matrix eyeToWorld;
eyeToWorld.invert(*cv.getModelViewMatrix());
_occluder->computeShadowVolumeGeometry(lightpos * eyeToWorld, *_shadowVolume);
}
if (shadowVolumeBin.valid())
{
// new_rs->setStateSet(_mainShadowStateSet.get());
new_rs->getRenderBinList()[0] = shadowVolumeBin;
shadowVolumeBin->setStateSet(_shadowVolumeStateSet.get());
osg::ref_ptr<osgUtil::RenderBin> nested_bin = new_rs->find_or_insert(1,"RenderBin");
nested_bin->getRenderBinList()[0] = new_bin;
nested_bin->setStateSet(_shadowedSceneStateSet.get());
}
}
}
void ShadowVolume::cleanSceneGraph()
{
osg::notify(osg::NOTICE)<<className()<<"::cleanSceneGraph()) not implemened yet, but almost."<<std::endl;
}