#include #include #include #include #include #include #include #include #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), _shadowState(new osg::StateSet) { _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 { // must traverse the shadower traverse(node,nv); } } protected: 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; // 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(); glLoadMatrix(_matrix.ptr()); 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()< 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. 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,_ambientLightColor); material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f)); material->setEmission(osg::Material::FRONT_AND_BACK,osg::Vec4(0.0f,0.0f,0.0f,1.0f)); 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. osg::StateSet* stateset = new osg::StateSet; 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))); stateset->setTextureAttributeAndModes(_unit,_texture.get(),osg::StateAttribute::ON); stateset->setTextureAttribute(_unit,texgen); 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); cv.pushStateSet(stateset); // 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); osg::Vec4 ambientLightColor(0.1f,0.1f,0.1f,1.0f); // 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; }