/* -*-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 #include #include using namespace osgShadow; class VDSMCameraCullCallback : public osg::NodeCallback { public: VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope); virtual void operator()(osg::Node*, osg::NodeVisitor* nv); protected: ViewDependentShadowMap* _vdsm; osg::Polytope _polytope; }; VDSMCameraCullCallback::VDSMCameraCullCallback(ViewDependentShadowMap* vdsm, osg::Polytope& polytope): _vdsm(vdsm), _polytope(polytope) { } void VDSMCameraCullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) { osgUtil::CullVisitor* cv = dynamic_cast(nv); osg::Camera* camera = dynamic_cast(node); OSG_NOTICE<<"VDSMCameraCullCallback::operator()(osg::Node* "<getProjectionCullingStack().back(); cs.setFrustum(_polytope); cv->pushCullingSet(); } if (_vdsm->getShadowedScene()) { _vdsm->getShadowedScene()->osg::Group::traverse(*nv); } if (!_polytope.empty()) { OSG_NOTICE<<"Popping custom Polytope"<popCullingSet(); } if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR) { osg::Matrixd projection = *(cv->getProjectionMatrix()); OSG_NOTICE<<"RTT Projection matrix "<setProjectionMatrix(projection); } } /////////////////////////////////////////////////////////////////////////////////////////////// // // ViewDependentShadowMap // ViewDependentShadowMap::ViewDependentShadowMap(): ShadowTechnique() { _shadowRecievingPlaceholderStateSet = new osg::StateSet; } ViewDependentShadowMap::ViewDependentShadowMap(const ViewDependentShadowMap& vdsm, const osg::CopyOp& copyop): ShadowTechnique(vdsm,copyop) { _shadowRecievingPlaceholderStateSet = new osg::StateSet; } ViewDependentShadowMap::~ViewDependentShadowMap() { } void ViewDependentShadowMap::init() { if (!_shadowedScene) return; OSG_NOTICE<<"ViewDependentShadowMap::init()"< lock(_viewDependentDataMapMutex); ViewDependentDataMap::iterator itr = _viewDependentDataMap.find(cv); if (itr!=_viewDependentDataMap.end()) return itr->second.get(); osg::ref_ptr vdd = createViewDependentData(cv); _viewDependentDataMap[cv] = vdd; return vdd.release(); } void ViewDependentShadowMap::update(osg::NodeVisitor& nv) { OSG_NOTICE<<"ViewDependentShadowMap::update(osg::NodeVisitor& "<<&nv<<")"<osg::Group::traverse(nv); } void ViewDependentShadowMap::cull(osgUtil::CullVisitor& cv) { OSG_NOTICE< decoratorStateGraph = cv.getCurrentStateGraph(); cullShadowReceivingScene(&cv); cv.popStateSet(); // 2. select active light sources // create a list of light sources + their matrices to place them PositionedLightList pll; selectActiveLights(&cv, pll); unsigned int pos_x = 0; unsigned int baseTextureUnit = 1; unsigned int textureUnit = baseTextureUnit; unsigned int numValidShadows = 0; Frustum frustum(&cv); for(PositionedLightList::iterator itr = pll.begin(); itr != pll.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures PositionedLight& pl = *itr; // 4. For each light/shadow map { osg::ref_ptr camera; { camera = new osg::Camera; camera->ref(); camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT); camera->setClearMask(GL_DEPTH_BUFFER_BIT); //_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); //camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); // set viewport //camera->setViewport(0,0,_textureSize.x(),_textureSize.y()); camera->setViewport(pos_x,0,400,400); pos_x += 440; // set the camera to render before the main camera. //camera->setRenderOrder(osg::Camera::PRE_RENDER); camera->setRenderOrder(osg::Camera::POST_RENDER); // tell the camera to use OpenGL frame buffer object where supported. //camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); //camera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW); // attach the texture and use it as the color buffer. // camera->attach(osg::Camera::DEPTH_BUFFER, texture.get()); } osg::ref_ptr texgen = new osg::TexGen; // 4.1 compute light space polytope // osg::Polytope polytope = computeLightViewFrustumPolytope(frustum, pl); // if polytope is empty then no rendering. if (polytope.empty()) { OSG_NOTICE<<"Polytope empty no shadow to render"<getViewMatrix()); polytope.transformProvidingInverse(invertModelView); camera->setCullCallback(new VDSMCameraCullCallback(this, polytope)); // 4.3 traverse RTT camera // cullShadowCastingScene(&cv, camera.get()); // 4.4 compute main scene graph TexGen + uniform settings + setup state // assignTexGenSettings(&cv, camera.get(), textureUnit, texgen); // mark the light as one that has active shadows and requires shaders pl.textureUnits.push_back(textureUnit); // increment counters. ++textureUnit; ++numValidShadows ; } } if (numValidShadows>0) { OSG_NOTICE<<"Need to assign "<setStateSet(selectStateSetForRenderingShadow(pll)); } } bool ViewDependentShadowMap::selectActiveLights(osgUtil::CullVisitor* cv, PositionedLightList& pll) const { OSG_NOTICE<<"selectActiveLights"<getRenderStage(); osg::Matrixd modelViewMatrix = *(cv->getModelViewMatrix()); osgUtil::PositionalStateContainer::AttrMatrixList& aml = rs->getPositionalStateContainer()->getAttrMatrixList(); for(osgUtil::PositionalStateContainer::AttrMatrixList::reverse_iterator itr = aml.rbegin(); itr != aml.rend(); ++itr) { const osg::Light* light = dynamic_cast(itr->first.get()); if (light) { PositionedLightList::iterator pll_itr = pll.begin(); for(; pll_itr != pll.end(); ++pll_itr) { if (pll_itr->light->getLightNum()==light->getLightNum()) break; } if (pll_itr==pll.end()) { OSG_NOTICE<<"Light num "<getLightNum()<second, light, modelViewMatrix)); } else { OSG_NOTICE<<"Light num "<getLightNum()<<" already used, ignore light"<getPosition(); directionalLight = (light->getPosition().w()== 0.0); if (directionalLight) { lightPos3.set(0.0, 0.0, 0.0); // directional light has no destinct position lightDir.set(-lightPos.x(), -lightPos.y(), -lightPos.z()); lightDir.normalize(); OSG_NOTICE<<" Directional light, lightPos="<getCalculatedNearPlane(); osgUtil::CullVisitor::value_type zFar = cv->getCalculatedFarPlane(); cv->clampProjectionMatrix(projectionMatrix,zNear,zFar); OSG_NOTICE<<"zNear = "< lightSide_z.length()) ? lightSide_y : lightSide_z; lightSide.normalize(); //osg::Vec3d lightSide = positionedLight.lightDir ^ frustum.frustumCenterLine; lightSide.normalize(); osg::Vec3d lightUp = lightSide ^ positionedLight.lightDir; if (positionedLight.directionalLight) { double xMin=0.0, xMax=0.0; double yMin=0.0, yMax=0.0; double zMin=0.0, zMax=0.0; for(Frustum::Vertices::iterator itr = frustum.corners.begin(); itr != frustum.corners.end(); ++itr) { osg::Vec3d cornerDelta(*itr - frustum.center); osg::Vec3d cornerInLightCoords(cornerDelta*lightSide, cornerDelta*lightUp, cornerDelta*positionedLight.lightDir); xMin = osg::minimum( xMin, cornerInLightCoords.x()); xMax = osg::maximum( xMax, cornerInLightCoords.x()); yMin = osg::minimum( yMin, cornerInLightCoords.y()); yMax = osg::maximum( yMax, cornerInLightCoords.y()); zMin = osg::minimum( zMin, cornerInLightCoords.z()); zMax = osg::maximum( zMax, cornerInLightCoords.z()); } OSG_NOTICE<<"before bs xMin="<setProjectionMatrixAsOrtho(xMin,xMax, yMin, yMax,0.0,zMax-zMin); camera->setViewMatrixAsLookAt(frustum.center+positionedLight.lightDir*zMin, frustum.center, lightUp); } } else { double zMax=-DBL_MAX; OSG_NOTICE<<"lightDir = "<setMode(osg::TexGen::EYE_LINEAR); // compute the matrix which takes a vertex from local coords into tex coords // We actually use two matrices one used to define texgen // and second that will be used as modelview when appling to OpenGL texgen->setPlanesFromMatrix( camera->getProjectionMatrix() * osg::Matrix::translate(1.0,1.0,1.0) * osg::Matrix::scale(0.5f,0.5f,0.5f) ); // Place texgen with modelview which removes big offsets (making it float friendly) osg::ref_ptr refMatrix = new osg::RefMatrix( camera->getInverseViewMatrix() * (*(cv->getModelViewMatrix())) ); cv->getRenderStage()->getPositionalStateContainer()->addPositionedTextureAttribute( textureUnit, refMatrix.get(), texgen ); return true; } void ViewDependentShadowMap::cullShadowReceivingScene(osgUtil::CullVisitor* cv) const { OSG_NOTICE<<"cullShadowReceivingScene()"<getTraversalMask(); cv->setTraversalMask( traversalMask & _shadowedScene->getReceivesShadowTraversalMask() ); _shadowedScene->osg::Group::traverse(*cv); cv->setTraversalMask( traversalMask ); return; } void ViewDependentShadowMap::cullShadowCastingScene(osgUtil::CullVisitor* cv, osg::Camera* camera) const { OSG_NOTICE<<"cullShadowCastingScene()"<getTraversalMask(); cv->setTraversalMask( traversalMask & _shadowedScene->getCastsShadowTraversalMask() ); if (camera) camera->accept(*cv); cv->setTraversalMask( traversalMask ); return; } osg::StateSet* ViewDependentShadowMap::selectStateSetForRenderingShadow(PositionedLightList& pll) const { osg::ref_ptr newStateSet = new osg::StateSet; newStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); for(PositionedLightList::iterator itr = pll.begin(); itr != pll.end(); ++itr) { // 3. create per light/per shadow map division of lightspace/frustum // create a list of light/shadow map data structures PositionedLight& pl = *itr; // if no texture units have been activated for this light then no shadow state required. if (pl.textureUnits.empty()) continue; for(PositionedLight::ActiveTextureUnits::iterator atu_itr = pl.textureUnits.begin(); atu_itr != pl.textureUnits.end(); ++atu_itr) { OSG_NOTICE<<" Need to assign state for "<<*atu_itr<