/* -*-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. * * ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski * Thanks to to my company http://www.ai.com.pl for allowing me free this work. */ #include #include #include #define IGNORE_OBJECTS_LARGER_THAN_HEIGHT 0 using namespace osgShadow; MinimalCullBoundsShadowMap::MinimalCullBoundsShadowMap(): BaseClass() { } MinimalCullBoundsShadowMap::MinimalCullBoundsShadowMap (const MinimalCullBoundsShadowMap& copy, const osg::CopyOp& copyop) : BaseClass(copy,copyop) { } MinimalCullBoundsShadowMap::~MinimalCullBoundsShadowMap() { } void MinimalCullBoundsShadowMap::ViewData::init ( ThisClass *st, osgUtil::CullVisitor *cv ) { BaseClass::ViewData::init( st, cv ); } void MinimalCullBoundsShadowMap::ViewData::aimShadowCastingCamera ( const osg::Light *light, const osg::Vec4 &lightPos, const osg::Vec3 &lightDir, const osg::Vec3 &lightUp ) { MinimalShadowMap::ViewData::aimShadowCastingCamera ( light, lightPos, lightDir, lightUp ); frameShadowCastingCamera ( _cv->getCurrentRenderBin()->getStage()->getCamera(), _camera.get() ); } void MinimalCullBoundsShadowMap::ViewData::cullShadowReceivingScene( ) { RenderLeafList rllOld, rllNew; GetRenderLeaves( _cv->getRenderStage(), rllOld ); MinimalShadowMap::ViewData::cullShadowReceivingScene( ); GetRenderLeaves( _cv->getRenderStage(), rllNew ); RemoveOldRenderLeaves( rllNew, rllOld ); RemoveIgnoredRenderLeaves( rllNew ); osg::Matrix projectionToModelSpace = osg::Matrix::inverse( *_modellingSpaceToWorldPtr * *_cv->getModelViewMatrix() * *_cv->getProjectionMatrix() ); osg::BoundingBox bb; if( *_cv->getProjectionMatrix() != _clampedProjection ) { osg::Polytope polytope; #if 1 polytope.setToUnitFrustum(); #else polytope.add( osg::Plane( 0.0,0.0,-1.0,1.0 ) ); // only far plane #endif polytope.transformProvidingInverse( *_modellingSpaceToWorldPtr * *_cv->getModelViewMatrix() * _clampedProjection ); bb = ComputeRenderLeavesBounds( rllNew, projectionToModelSpace, polytope ); } else { bb = ComputeRenderLeavesBounds( rllNew, projectionToModelSpace ); } cutScenePolytope( *_modellingSpaceToWorldPtr, osg::Matrix::inverse( *_modellingSpaceToWorldPtr ), bb ); } void MinimalCullBoundsShadowMap::ViewData::GetRenderLeaves ( osgUtil::RenderBin *rb, RenderLeafList & rll ) { osgUtil::RenderBin::RenderBinList& bins = rb->getRenderBinList(); osgUtil::RenderBin::RenderBinList::const_iterator rbitr; // scan pre render bins for(rbitr = bins.begin(); rbitr!=bins.end() && rbitr->first<0; ++rbitr ) GetRenderLeaves( rbitr->second.get(), rll ); // scan fine grained ordering. osgUtil::RenderBin::RenderLeafList& renderLeafList = rb->getRenderLeafList(); osgUtil::RenderBin::RenderLeafList::const_iterator rlitr; for( rlitr= renderLeafList.begin(); rlitr!= renderLeafList.end(); ++rlitr ) { rll.push_back( *rlitr ); } // scan coarse grained ordering. osgUtil::RenderBin::StateGraphList& stateGraphList = rb->getStateGraphList(); osgUtil::RenderBin::StateGraphList::const_iterator oitr; for( oitr= stateGraphList.begin(); oitr!= stateGraphList.end(); ++oitr ) { for( osgUtil::StateGraph::LeafList::const_iterator dw_itr = (*oitr)->_leaves.begin(); dw_itr != (*oitr)->_leaves.end(); ++dw_itr) { rll.push_back( dw_itr->get() ); } } // scan post render bins for(; rbitr!=bins.end(); ++rbitr ) GetRenderLeaves( rbitr->second.get(), rll ); } class CompareRenderLeavesByMatrices { public: bool operator()( const osgUtil::RenderLeaf *a, const osgUtil::RenderLeaf *b ) { if ( !a ) return false; // NULL render leaf goes last return !b || a->_projection < b->_projection || (a->_projection == b->_projection && a->_modelview < b->_modelview); } }; inline bool CheckAndMultiplyBoxIfWithinPolytope ( osg::BoundingBox & bb, osg::Matrix & m, osg::Polytope &p ) { if( !bb.valid() ) return false; osg::Vec3 o = bb._min * m, s[3]; for( int i = 0; i < 3; i ++ ) s[i] = osg::Vec3( m(i,0), m(i,1), m(i,2) ) * ( bb._max[i] - bb._min[i] ); for( osg::Polytope::PlaneList::iterator it = p.getPlaneList().begin(); it != p.getPlaneList().end(); ++it ) { float dist = it->distance( o ), dist_min = dist, dist_max = dist; for( int i = 0; i < 3; i ++ ) { dist = it->dotProductNormal( s[i] ); if( dist < 0 ) dist_min += dist; else dist_max += dist; } if( dist_max < 0 ) return false; } bb._max = bb._min = o; #if 1 for( int i = 0; i < 3; i ++ ) for( int j = 0; j < 3; j ++ ) if( s[i][j] < 0 ) bb._min[j] += s[i][j]; else bb._max[j] += s[i][j]; #else b.expandBy( o + s[0] ); b.expandBy( o + s[1] ); b.expandBy( o + s[2] ); b.expandBy( o + s[0] + s[1] ); b.expandBy( o + s[0] + s[2] ); b.expandBy( o + s[1] + s[2] ); b.expandBy( o + s[0] + s[1] + s[2] ); #endif #if ( IGNORE_OBJECTS_LARGER_THAN_HEIGHT > 0 ) if( bb._max[2] - bb._min[2] > IGNORE_OBJECTS_LARGER_THAN_HEIGHT ) // ignore huge objects return false; #endif return true; } unsigned MinimalCullBoundsShadowMap::ViewData::RemoveOldRenderLeaves ( RenderLeafList &rllNew, RenderLeafList &rllOld ) { unsigned count = 0; std::sort( rllOld.begin(), rllOld.end() ); RenderLeafList::iterator itNew, itOld; for( itNew = rllNew.begin(); itNew != rllNew.end() && rllOld.size(); ++itNew ) { itOld = std::lower_bound( rllOld.begin(), rllOld.end(), *itNew ); if( itOld == rllOld.end() || *itOld != *itNew ) continue; // found ! rllOld.erase( itOld ); //remove it from old range to speed up search *itNew = NULL; //its not new = invalidate it among new render leaves count ++; } return count; } unsigned MinimalCullBoundsShadowMap::ViewData::RemoveIgnoredRenderLeaves ( RenderLeafList &rll ) { unsigned count = 0; for( RenderLeafList::iterator it = rll.begin(); it != rll.end(); ++it ) { if( !*it ) continue; const char * name = (*it)->_drawable->className(); // Its a dirty but quick test (not very future proof) if( !name || name[0] != 'L' ) continue; if( !strcmp( name, "LightPointDrawable" ) || !strcmp( name, "LightPointSpriteDrawable" ) ) { *it = NULL; //ignored = invalidate this in new render leaves list count++; } } return count; } osg::BoundingBox MinimalCullBoundsShadowMap::ViewData::ComputeRenderLeavesBounds ( RenderLeafList &rll, osg::Matrix & projectionToWorld ) { osg::BoundingBox bbResult; if( rll.size() == 0 ) return bbResult; std::sort( rll.begin(), rll.end(), CompareRenderLeavesByMatrices() ); osg::ref_ptr< osg::RefMatrix > modelview; osg::ref_ptr< osg::RefMatrix > projection; osg::Matrix viewToWorld, modelToWorld, *ptrProjection = NULL, *ptrViewToWorld = &projectionToWorld, *ptrModelToWorld; osg::BoundingBox bb; // compute bounding boxes but skip old ones (placed at the end as NULLs) for( RenderLeafList::iterator it = rll.begin(); ; ++it ) { // we actually allow to pass one element behind end to flush bb queue osgUtil::RenderLeaf *rl = ( it != rll.end() ? *it : NULL ); // Don't trust already computed bounds for cull generated drawables // LightPointDrawable & LightPointSpriteDrawable are such examples // they store wrong recorded bounds from very first pass if( rl && rl->_modelview == NULL ) rl->_drawable->dirtyBound(); // Stay as long as possible in model space to minimize matrix ops if( rl && rl->_modelview == modelview && rl->_projection == projection ){ bb.expandBy( rl->_drawable->getBound() ); } else { if( bb.valid() ) { // Conditions to avoid matrix multiplications if( projection.valid() ) { if( projection.get() != ptrProjection ) { ptrProjection = projection.get(); viewToWorld = *ptrProjection * projectionToWorld; } ptrViewToWorld = &viewToWorld; } else { ptrViewToWorld = &projectionToWorld; } if( modelview.valid() ) { modelToWorld = *modelview.get() * *ptrViewToWorld; ptrModelToWorld = &modelToWorld; } else { ptrModelToWorld = ptrViewToWorld; } for( int i = 0; i < 8; i++ ) bbResult.expandBy( bb.corner( i ) * *ptrModelToWorld ); } if( !rl ) break; bb = rl->_drawable->getBound(); modelview = rl->_modelview; projection = rl->_projection; } } rll.clear(); return bbResult; } osg::BoundingBox MinimalCullBoundsShadowMap::ViewData::ComputeRenderLeavesBounds ( RenderLeafList &rll, osg::Matrix & projectionToWorld, osg::Polytope & p ) { osg::BoundingBox bbResult, bb; if( rll.size() == 0 ) return bbResult; std::sort( rll.begin(), rll.end(), CompareRenderLeavesByMatrices() ); osg::ref_ptr< osg::RefMatrix > modelview; osg::ref_ptr< osg::RefMatrix > projection; osg::Matrix viewToWorld, modelToWorld, *ptrProjection = NULL, *ptrViewToWorld = &projectionToWorld, *ptrModelToWorld = NULL; // compute bounding boxes but skip old ones (placed at the end as NULLs) for( RenderLeafList::iterator it = rll.begin(); it != rll.end(); ++it ) { // we actually allow to pass one element behind end to flush bb queue osgUtil::RenderLeaf *rl = *it; if( !rl ) break; // Don't trust already computed bounds for cull generated drawables // LightPointDrawable & LightPointSpriteDrawable are such examples // they store wrong recorded bounds from very first pass if( rl && rl->_modelview == NULL ) rl->_drawable->dirtyBound(); bb = rl->_drawable->getBound(); if( !bb.valid() ) continue; // Stay as long as possible in model space to minimize matrix ops if( rl->_modelview != modelview || rl->_projection != projection ) { projection = rl->_projection; if( projection.valid() ) { if( projection.get() != ptrProjection ) { ptrProjection = projection.get(); viewToWorld = *ptrProjection * projectionToWorld; } ptrViewToWorld = &viewToWorld; } else { ptrViewToWorld = &projectionToWorld; } modelview = rl->_modelview; if( modelview.valid() ) { modelToWorld = *modelview.get() * *ptrViewToWorld; ptrModelToWorld = &modelToWorld; } else { ptrModelToWorld = ptrViewToWorld; } } if( CheckAndMultiplyBoxIfWithinPolytope( bb, *ptrModelToWorld, p ) ) bbResult.expandBy( bb ); } rll.clear(); return bbResult; }