372 lines
12 KiB
C++
372 lines
12 KiB
C++
/* -*-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 <osgShadow/MinimalCullBoundsShadowMap>
|
|
#include <osgUtil/RenderLeaf>
|
|
#include <string.h>
|
|
|
|
#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 );
|
|
_frameShadowCastingCameraPasses = 2;
|
|
}
|
|
|
|
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->_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;
|
|
}
|
|
|