2: minor tweak for a DebugHUD drawn improperly case when multiple slave views shared one window. It now uses slave view viewport to correctly position DebugHUD. 3: deactivated ConvexPolyhedron notifications (they were accidentaly activated when you replaced osg::notify calls with OSG_NOTIFY macro). These warnings are useful only for shadow map developer working on shadow volume optimizations. So there is no sense in having them active all the time."
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 && 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;
|
|
}
|
|
|