diff --git a/examples/osgshadowtexture/CreateShadowedScene.cpp b/examples/osgshadowtexture/CreateShadowedScene.cpp index 96cb3ab8c..f2c827c1b 100644 --- a/examples/osgshadowtexture/CreateShadowedScene.cpp +++ b/examples/osgshadowtexture/CreateShadowedScene.cpp @@ -1,254 +1,208 @@ #include -#include #include #include #include +#include #include - -#include -#include +#include +#include +#include +#include #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(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()<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 _shadower; - osg::ref_ptr _texture; - osg::Vec3 _position; - osg::Vec4 _ambientLightColor; - unsigned int _unit; - osg::ref_ptr _shadowState; - osg::ref_ptr _shadowedState; + osg::Vec3 _position; + osg::ref_ptr _cameraNode; + osg::ref_ptr _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()< 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 (znearmakeFrustum(-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; } -