2003-03-15 04:35:45 +08:00
|
|
|
#include <osg/Texture2D>
|
|
|
|
#include <osg/TexGen>
|
|
|
|
#include <osg/Material>
|
|
|
|
#include <osg/LightSource>
|
|
|
|
#include <osg/Geode>
|
|
|
|
#include <osg/ShapeDrawable>
|
|
|
|
|
|
|
|
#include <osgUtil/CullVisitor>
|
|
|
|
#include <osgUtil/RenderToTextureStage>
|
|
|
|
|
|
|
|
#include "CreateShadowedScene.h"
|
|
|
|
|
|
|
|
using namespace osg;
|
|
|
|
|
|
|
|
class CreateShadowTextureCullCallback : public osg::NodeCallback
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
CreateShadowTextureCullCallback(osg::Node* shadower,const osg::Vec3& position, const osg::Vec4& ambientLightColor, unsigned int textureUnit):
|
|
|
|
_shadower(shadower),
|
|
|
|
_position(position),
|
|
|
|
_ambientLightColor(ambientLightColor),
|
|
|
|
_unit(textureUnit),
|
2003-09-16 19:54:56 +08:00
|
|
|
_shadowState(new osg::StateSet),
|
|
|
|
_shadowedState(new osg::StateSet)
|
2003-03-15 04:35:45 +08:00
|
|
|
{
|
|
|
|
_texture = new osg::Texture2D;
|
|
|
|
_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
|
|
|
|
_texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
|
2003-03-20 16:58:51 +08:00
|
|
|
_texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
|
|
|
|
_texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
|
|
|
|
_texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
2003-03-15 04:35:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
|
|
|
|
{
|
|
|
|
|
|
|
|
osgUtil::CullVisitor* cullVisitor = dynamic_cast<osgUtil::CullVisitor*>(nv);
|
|
|
|
if (cullVisitor && (_texture.valid() && _shadower.valid()))
|
|
|
|
{
|
|
|
|
doPreRender(*node,*cullVisitor);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// must traverse the shadower
|
|
|
|
traverse(node,nv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
void doPreRender(osg::Node& node, osgUtil::CullVisitor& cv);
|
|
|
|
|
|
|
|
osg::ref_ptr<osg::Node> _shadower;
|
|
|
|
osg::ref_ptr<osg::Texture2D> _texture;
|
|
|
|
osg::Vec3 _position;
|
|
|
|
osg::Vec4 _ambientLightColor;
|
|
|
|
unsigned int _unit;
|
|
|
|
osg::ref_ptr<osg::StateSet> _shadowState;
|
2003-09-16 19:54:56 +08:00
|
|
|
osg::ref_ptr<osg::StateSet> _shadowedState;
|
2003-03-15 04:35:45 +08:00
|
|
|
|
|
|
|
// we need this to get round the order dependance
|
|
|
|
// of eye linear tex gen...
|
|
|
|
class MyTexGen : public TexGen
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
void setMatrix(const osg::Matrix& matrix)
|
|
|
|
{
|
|
|
|
_matrix = matrix;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void apply(osg::State& state) const
|
|
|
|
{
|
|
|
|
glPushMatrix();
|
2003-09-06 04:48:42 +08:00
|
|
|
glLoadMatrix(_matrix.ptr());
|
2003-03-15 04:35:45 +08:00
|
|
|
TexGen::apply(state);
|
|
|
|
glPopMatrix();
|
|
|
|
}
|
|
|
|
|
|
|
|
osg::Matrix _matrix;
|
|
|
|
};
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
void CreateShadowTextureCullCallback::doPreRender(osg::Node& node, osgUtil::CullVisitor& cv)
|
|
|
|
{
|
|
|
|
|
|
|
|
const osg::BoundingSphere& bs = _shadower->getBound();
|
|
|
|
if (!bs.valid())
|
|
|
|
{
|
|
|
|
osg::notify(osg::WARN) << "bb invalid"<<_shadower.get()<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// create the render to texture stage.
|
|
|
|
osg::ref_ptr<osgUtil::RenderToTextureStage> rtts = new osgUtil::RenderToTextureStage;
|
|
|
|
|
|
|
|
// set up lighting.
|
|
|
|
// currently ignore lights in the scene graph itself..
|
|
|
|
// will do later.
|
|
|
|
osgUtil::RenderStage* previous_stage = cv.getCurrentRenderBin()->_stage;
|
|
|
|
|
|
|
|
// set up the background color and clear mask.
|
2003-03-20 22:07:03 +08:00
|
|
|
rtts->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
2003-03-15 04:35:45 +08:00
|
|
|
rtts->setClearMask(previous_stage->getClearMask());
|
|
|
|
|
|
|
|
// set up to charge the same RenderStageLighting is the parent previous stage.
|
|
|
|
rtts->setRenderStageLighting(previous_stage->getRenderStageLighting());
|
|
|
|
|
|
|
|
|
|
|
|
// record the render bin, to be restored after creation
|
|
|
|
// of the render to text
|
|
|
|
osgUtil::RenderBin* previousRenderBin = cv.getCurrentRenderBin();
|
|
|
|
|
|
|
|
// set the current renderbin to be the newly created stage.
|
|
|
|
cv.setCurrentRenderBin(rtts.get());
|
|
|
|
|
|
|
|
|
|
|
|
float centerDistance = (_position-bs.center()).length();
|
|
|
|
|
2003-04-15 17:33:56 +08:00
|
|
|
float znear = centerDistance-bs.radius();
|
|
|
|
float zfar = centerDistance+bs.radius();
|
2003-03-15 04:35:45 +08:00
|
|
|
float zNearRatio = 0.001f;
|
|
|
|
if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
|
|
|
|
|
|
|
|
|
|
|
|
// 2:1 aspect ratio as per flag geomtry below.
|
|
|
|
float top = (bs.radius()/centerDistance)*znear;
|
|
|
|
float right = top;
|
|
|
|
|
|
|
|
// set up projection.
|
|
|
|
osg::RefMatrix* projection = new osg::RefMatrix;
|
|
|
|
projection->makeFrustum(-right,right,-top,top,znear,zfar);
|
|
|
|
|
|
|
|
cv.pushProjectionMatrix(projection);
|
|
|
|
|
|
|
|
osg::RefMatrix* matrix = new osg::RefMatrix;
|
|
|
|
matrix->makeLookAt(_position,bs.center(),osg::Vec3(0.0f,1.0f,0.0f));
|
|
|
|
|
|
|
|
|
|
|
|
osg::Matrix MV = cv.getModelViewMatrix();
|
|
|
|
|
|
|
|
// compute the matrix which takes a vertex from local coords into tex coords
|
|
|
|
// will use this later to specify osg::TexGen..
|
|
|
|
osg::Matrix MVPT =
|
|
|
|
*matrix *
|
|
|
|
*projection *
|
|
|
|
osg::Matrix::translate(1.0,1.0,1.0) *
|
|
|
|
osg::Matrix::scale(0.5f,0.5f,0.5f);
|
|
|
|
|
|
|
|
cv.pushModelViewMatrix(matrix);
|
|
|
|
|
|
|
|
// make the material black for a shadow.
|
|
|
|
osg::Material* material = new osg::Material;
|
2003-11-24 18:01:02 +08:00
|
|
|
material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
|
2003-03-15 04:35:45 +08:00
|
|
|
material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
|
2003-11-24 18:01:02 +08:00
|
|
|
material->setEmission(osg::Material::FRONT_AND_BACK,_ambientLightColor);
|
2003-03-15 04:35:45 +08:00
|
|
|
material->setShininess(osg::Material::FRONT_AND_BACK,0.0f);
|
|
|
|
_shadowState->setAttribute(material,osg::StateAttribute::OVERRIDE);
|
|
|
|
|
|
|
|
cv.pushStateSet(_shadowState.get());
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// traverse the shadower
|
|
|
|
_shadower->accept(cv);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv.popStateSet();
|
|
|
|
|
|
|
|
// restore the previous model view matrix.
|
|
|
|
cv.popModelViewMatrix();
|
|
|
|
|
|
|
|
|
|
|
|
// restore the previous model view matrix.
|
|
|
|
cv.popProjectionMatrix();
|
|
|
|
|
|
|
|
// restore the previous renderbin.
|
|
|
|
cv.setCurrentRenderBin(previousRenderBin);
|
|
|
|
|
|
|
|
if (rtts->_renderGraphList.size()==0 && rtts->_bins.size()==0)
|
|
|
|
{
|
|
|
|
// getting to this point means that all the shadower has been
|
|
|
|
// culled by small feature culling or is beyond LOD ranges.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int height = 256;
|
|
|
|
int width = 256;
|
|
|
|
|
|
|
|
|
|
|
|
const osg::Viewport& viewport = *cv.getViewport();
|
|
|
|
|
|
|
|
// offset the impostor viewport from the center of the main window
|
|
|
|
// viewport as often the edges of the viewport might be obscured by
|
|
|
|
// other windows, which can cause image/reading writing problems.
|
|
|
|
int center_x = viewport.x()+viewport.width()/2;
|
|
|
|
int center_y = viewport.y()+viewport.height()/2;
|
|
|
|
|
|
|
|
osg::Viewport* new_viewport = new osg::Viewport;
|
|
|
|
new_viewport->setViewport(center_x-width/2,center_y-height/2,width,height);
|
|
|
|
rtts->setViewport(new_viewport);
|
|
|
|
|
|
|
|
_shadowState->setAttribute(new_viewport);
|
|
|
|
|
|
|
|
// and the render to texture stage to the current stages
|
|
|
|
// dependancy list.
|
|
|
|
cv.getCurrentRenderBin()->_stage->addToDependencyList(rtts.get());
|
|
|
|
|
|
|
|
// if one exist attach texture to the RenderToTextureStage.
|
|
|
|
if (_texture.valid()) rtts->setTexture(_texture.get());
|
|
|
|
|
|
|
|
|
|
|
|
// set up the stateset to decorate the shadower with the shadow texture
|
|
|
|
// with the appropriate tex gen coords.
|
|
|
|
MyTexGen* texgen = new MyTexGen;
|
|
|
|
texgen->setMatrix(MV);
|
|
|
|
texgen->setMode(osg::TexGen::EYE_LINEAR);
|
|
|
|
texgen->setPlane(osg::TexGen::S,osg::Plane(MVPT(0,0),MVPT(1,0),MVPT(2,0),MVPT(3,0)));
|
|
|
|
texgen->setPlane(osg::TexGen::T,osg::Plane(MVPT(0,1),MVPT(1,1),MVPT(2,1),MVPT(3,1)));
|
|
|
|
texgen->setPlane(osg::TexGen::R,osg::Plane(MVPT(0,2),MVPT(1,2),MVPT(2,2),MVPT(3,2)));
|
|
|
|
texgen->setPlane(osg::TexGen::Q,osg::Plane(MVPT(0,3),MVPT(1,3),MVPT(2,3),MVPT(3,3)));
|
|
|
|
|
2003-09-16 19:54:56 +08:00
|
|
|
_shadowedState->setTextureAttributeAndModes(_unit,_texture.get(),osg::StateAttribute::ON);
|
|
|
|
_shadowedState->setTextureAttribute(_unit,texgen);
|
|
|
|
_shadowedState->setTextureMode(_unit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
|
|
|
_shadowedState->setTextureMode(_unit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
|
|
|
_shadowedState->setTextureMode(_unit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
|
|
|
|
_shadowedState->setTextureMode(_unit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
|
2003-03-15 04:35:45 +08:00
|
|
|
|
2003-09-16 19:54:56 +08:00
|
|
|
cv.pushStateSet(_shadowedState.get());
|
2003-03-15 04:35:45 +08:00
|
|
|
|
|
|
|
// must traverse the shadower
|
|
|
|
traverse(&node,&cv);
|
|
|
|
|
|
|
|
cv.popStateSet();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// set up a light source with the shadower and shodower subgraphs below it
|
|
|
|
// with the appropriate callbacks set up.
|
|
|
|
osg::Group* createShadowedScene(osg::Node* shadower,osg::Node* shadowed,const osg::Vec3& lightPosition,float radius,unsigned int textureUnit)
|
|
|
|
{
|
|
|
|
osg::LightSource* lightgroup = new osg::LightSource;
|
|
|
|
|
|
|
|
osg::Light* light = new osg::Light;
|
|
|
|
light->setPosition(osg::Vec4(lightPosition,1.0f));
|
|
|
|
light->setLightNum(0);
|
|
|
|
|
|
|
|
lightgroup->setLight(light);
|
|
|
|
|
2003-11-24 18:01:02 +08:00
|
|
|
osg::Vec4 ambientLightColor(0.2,0.2f,0.2f,1.0f);
|
2003-03-15 04:35:45 +08:00
|
|
|
|
|
|
|
// add the shadower
|
|
|
|
lightgroup->addChild(shadower);
|
|
|
|
|
|
|
|
// add the shadowed with the callback to generate the shadow texture.
|
|
|
|
shadowed->setCullCallback(new CreateShadowTextureCullCallback(shadower,lightPosition,ambientLightColor,textureUnit));
|
|
|
|
lightgroup->addChild(shadowed);
|
|
|
|
|
|
|
|
osg::Geode* lightgeode = new osg::Geode;
|
|
|
|
lightgeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
|
|
|
|
lightgeode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(lightPosition,radius)));
|
|
|
|
|
|
|
|
lightgroup->addChild(lightgeode);
|
|
|
|
|
|
|
|
return lightgroup;
|
|
|
|
}
|
|
|
|
|