Ported osgshadow across to using new osg::CameraNode
This commit is contained in:
@@ -1,254 +1,208 @@
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/TexGen>
|
||||
#include <osg/Material>
|
||||
#include <osg/LightSource>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/ShapeDrawable>
|
||||
|
||||
#include <osgUtil/CullVisitor>
|
||||
#include <osgUtil/RenderToTextureStage>
|
||||
#include <osg/CameraNode>
|
||||
#include <osg/TexGenNode>
|
||||
#include <osg/Notify>
|
||||
#include <osg/io_utils>
|
||||
|
||||
#include "CreateShadowedScene.h"
|
||||
|
||||
using namespace osg;
|
||||
|
||||
class CreateShadowTextureCullCallback : public osg::NodeCallback
|
||||
class UpdateCameraAndTexGenCallback : public osg::NodeCallback
|
||||
{
|
||||
public:
|
||||
|
||||
CreateShadowTextureCullCallback(osg::Node* shadower,const osg::Vec3& position, const osg::Vec4& ambientLightColor, unsigned int textureUnit):
|
||||
_shadower(shadower),
|
||||
UpdateCameraAndTexGenCallback(const osg::Vec3& position, osg::CameraNode* cameraNode, osg::TexGenNode* texgenNode):
|
||||
_position(position),
|
||||
_ambientLightColor(ambientLightColor),
|
||||
_unit(textureUnit),
|
||||
_shadowState(new osg::StateSet),
|
||||
_shadowedState(new osg::StateSet)
|
||||
_cameraNode(cameraNode),
|
||||
_texgenNode(texgenNode)
|
||||
{
|
||||
_texture = new osg::Texture2D;
|
||||
_texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
|
||||
_texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
|
||||
_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));
|
||||
}
|
||||
|
||||
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
|
||||
// first update subgraph to make sure objects are all moved into postion
|
||||
traverse(node,nv);
|
||||
|
||||
// now compute the camera's view and projection matrix to point at the shadower (the camera's children)
|
||||
osg::BoundingSphere bs;
|
||||
for(unsigned int i=0; i<_cameraNode->getNumChildren(); ++i)
|
||||
{
|
||||
// must traverse the shadower
|
||||
traverse(node,nv);
|
||||
bs.expandBy(_cameraNode->getChild(i)->getBound());
|
||||
}
|
||||
|
||||
if (!bs.valid())
|
||||
{
|
||||
osg::notify(osg::WARN) << "bb invalid"<<_cameraNode.get()<<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
float centerDistance = (_position-bs.center()).length();
|
||||
|
||||
float znear = centerDistance-bs.radius();
|
||||
float zfar = centerDistance+bs.radius();
|
||||
float zNearRatio = 0.001f;
|
||||
if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
|
||||
|
||||
float top = (bs.radius()/centerDistance)*znear;
|
||||
float right = top;
|
||||
|
||||
_cameraNode->setReferenceFrame(osg::CameraNode::ABSOLUTE_RF);
|
||||
_cameraNode->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
|
||||
_cameraNode->setViewMatrixAsLookAt(_position,bs.center(),osg::Vec3(0.0f,1.0f,0.0f));
|
||||
|
||||
// compute the matrix which takes a vertex from local coords into tex coords
|
||||
// will use this later to specify osg::TexGen..
|
||||
osg::Matrix MVPT = _cameraNode->getViewMatrix() *
|
||||
_cameraNode->getProjectionMatrix() *
|
||||
osg::Matrix::translate(1.0,1.0,1.0) *
|
||||
osg::Matrix::scale(0.5f,0.5f,0.5f);
|
||||
|
||||
_texgenNode->getTexGen()->setMode(osg::TexGen::EYE_LINEAR);
|
||||
_texgenNode->getTexGen()->setPlanesFromMatrix(MVPT);
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~UpdateCameraAndTexGenCallback() {}
|
||||
|
||||
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;
|
||||
osg::ref_ptr<osg::StateSet> _shadowedState;
|
||||
osg::Vec3 _position;
|
||||
osg::ref_ptr<osg::CameraNode> _cameraNode;
|
||||
osg::ref_ptr<osg::TexGenNode> _texgenNode;
|
||||
|
||||
};
|
||||
|
||||
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()->getStage();
|
||||
|
||||
// set up the background color and clear mask.
|
||||
rtts->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
||||
//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();
|
||||
|
||||
float znear = centerDistance-bs.radius();
|
||||
float zfar = centerDistance+bs.radius();
|
||||
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;
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
|
||||
material->setEmission(osg::Material::FRONT_AND_BACK,_ambientLightColor);
|
||||
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->getRenderGraphList().size()==0 && rtts->getRenderBinList().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()->getStage()->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.
|
||||
TexGen* texgen = new TexGen;
|
||||
//texgen->setMatrix(MV);
|
||||
texgen->setMode(osg::TexGen::EYE_LINEAR);
|
||||
texgen->setPlanesFromMatrix(MVPT);
|
||||
|
||||
cv.getRenderStage()->addPositionedTextureAttribute(0,new osg::RefMatrix(MV),texgen);
|
||||
|
||||
|
||||
_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);
|
||||
|
||||
cv.pushStateSet(_shadowedState.get());
|
||||
|
||||
// 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::Group* createShadowedScene(osg::Node* shadower,osg::Node* shadowed,const osg::Vec3& lightPosition,float radius,unsigned int unit)
|
||||
{
|
||||
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);
|
||||
osg::Group* group = new osg::Group;
|
||||
|
||||
// add light source
|
||||
{
|
||||
osg::LightSource* lightSource = new osg::LightSource;
|
||||
lightSource->getLight()->setPosition(osg::Vec4(lightPosition,1.0f));
|
||||
lightSource->getLight()->setLightNum(0);
|
||||
|
||||
group->addChild(lightSource);
|
||||
|
||||
osg::Geode* lightgeode = new osg::Geode;
|
||||
lightgeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
|
||||
lightgeode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(lightPosition,radius)));
|
||||
group->addChild(lightgeode);
|
||||
}
|
||||
|
||||
osg::Vec4 ambientLightColor(0.2,0.2f,0.2f,1.0f);
|
||||
|
||||
// add the shadower
|
||||
lightgroup->addChild(shadower);
|
||||
// add the shadower and shadowed.
|
||||
group->addChild(shadower);
|
||||
group->addChild(shadowed);
|
||||
|
||||
// add the shadowed with the callback to generate the shadow texture.
|
||||
shadowed->setCullCallback(new CreateShadowTextureCullCallback(shadower,lightPosition,ambientLightColor,textureUnit));
|
||||
lightgroup->addChild(shadowed);
|
||||
|
||||
unsigned int tex_width = 512;
|
||||
unsigned int tex_height = 512;
|
||||
|
||||
osg::Geode* lightgeode = new osg::Geode;
|
||||
lightgeode->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
|
||||
lightgeode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(lightPosition,radius)));
|
||||
osg::Texture2D* texture = new osg::Texture2D;
|
||||
texture->setTextureSize(tex_width, tex_height);
|
||||
texture->setInternalFormat(GL_RGB);
|
||||
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
|
||||
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
|
||||
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));
|
||||
|
||||
// set up the render to texture camera.
|
||||
{
|
||||
|
||||
// create the camera
|
||||
osg::CameraNode* camera = new osg::CameraNode;
|
||||
|
||||
camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
||||
|
||||
// set viewport
|
||||
camera->setViewport(0,0,tex_width,tex_height);
|
||||
camera->getOrCreateStateSet()->setAttribute(camera->getViewport());
|
||||
|
||||
// set the camera to render before the main camera.
|
||||
camera->setRenderOrder(osg::CameraNode::PRE_RENDER);
|
||||
|
||||
// tell the camera to use OpenGL frame buffer object where supported.
|
||||
camera->setRenderTargetImplmentation(osg::CameraNode::FRAME_BUFFER_OBJECT);
|
||||
|
||||
// attach the texture and use it as the color buffer.
|
||||
camera->attach(osg::CameraNode::COLOR_BUFFER, texture);
|
||||
|
||||
// add subgraph to render
|
||||
camera->addChild(shadower);
|
||||
|
||||
osg::StateSet* stateset = camera->getOrCreateStateSet();
|
||||
|
||||
// make the material black for a shadow.
|
||||
osg::Material* material = new osg::Material;
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f));
|
||||
material->setEmission(osg::Material::FRONT_AND_BACK,ambientLightColor);
|
||||
material->setShininess(osg::Material::FRONT_AND_BACK,0.0f);
|
||||
stateset->setAttribute(material,osg::StateAttribute::OVERRIDE);
|
||||
|
||||
group->addChild(camera);
|
||||
|
||||
// create the texgen node to project the tex coords onto the subgraph
|
||||
osg::TexGenNode* texgenNode = new osg::TexGenNode;
|
||||
texgenNode->setTextureUnit(unit);
|
||||
group->addChild(texgenNode);
|
||||
|
||||
// set an update callback to keep moving the camera and tex gen in the right direction.
|
||||
group->setUpdateCallback(new UpdateCameraAndTexGenCallback(lightPosition, camera, texgenNode));
|
||||
}
|
||||
|
||||
// set the shadowed subgraph so that it uses the texture and tex gen settings.
|
||||
{
|
||||
osg::StateSet* stateset = shadowed->getOrCreateStateSet();
|
||||
stateset->setTextureAttributeAndModes(unit,texture,osg::StateAttribute::ON);
|
||||
stateset->setTextureMode(unit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
||||
stateset->setTextureMode(unit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
||||
stateset->setTextureMode(unit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
|
||||
stateset->setTextureMode(unit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
|
||||
}
|
||||
|
||||
lightgroup->addChild(lightgeode);
|
||||
|
||||
// set hud to render shadow texture, just for interest
|
||||
{
|
||||
osg::Geode* geode = new osg::Geode;
|
||||
osg::Geometry* geom = osg::createTexturedQuadGeometry(osg::Vec3(0,0,0),osg::Vec3(100.0,0.0,0.0),osg::Vec3(0.0,100.0,0.0));
|
||||
geom->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
|
||||
geom->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
|
||||
geode->addDrawable(geom);
|
||||
|
||||
osg::CameraNode* camera = new osg::CameraNode;
|
||||
|
||||
// set the projection matrix
|
||||
camera->setProjectionMatrix(osg::Matrix::ortho2D(0,100,0,100));
|
||||
|
||||
// set the view matrix
|
||||
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
|
||||
camera->setViewMatrix(osg::Matrix::identity());
|
||||
|
||||
camera->setViewport(50,50,100,100);
|
||||
camera->getOrCreateStateSet()->setAttribute(camera->getViewport());
|
||||
|
||||
// only clear the depth buffer
|
||||
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// draw subgraph after main camera view.
|
||||
camera->setRenderOrder(osg::CameraNode::POST_RENDER);
|
||||
|
||||
camera->addChild(geode);
|
||||
|
||||
group->addChild(camera);
|
||||
|
||||
}
|
||||
|
||||
return lightgroup;
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user