void MinimalShadowMap::ViewData::clampProjection
                    ( osg::Matrixd & projection, float new_near, float new_far )
{
    double r, l, t, b, n, f;
    bool perspective = projection.getFrustum( l, r, b, t, n, f );
    if( !perspective && !projection.getOrtho( l, r, b, t, n, f ) )
    {
        // What to do here ?
        osg::notify( osg::WARN ) 
            << "MinimalShadowMap::clampProjectionFarPlane failed - non standard matrix"
            << std::endl;

    } else if( n < new_near || new_far < f ) {

        if( n < new_near && new_near < f ) {
            if( perspective ) {
                l *= new_near / n;
                r *= new_near / n;
                b *= new_near / n;
                t *= new_near / n;
            }
            n = new_near;
        } 

        if( n < new_far && new_far < f ) {
            f = new_far;             
        }

        if( perspective )
            projection.makeFrustum( l, r, b, t, n, f );
        else
            projection.makeOrtho( l, r, b, t, n, f );                     
    }
}
void MinimalShadowMap::ViewData::extendProjection
    ( osg::Matrixd & projection, osg::Viewport * viewport, const osg::Vec2& margin )
{
  double l,r,b,t,n,f;

  //osg::Matrix projection = camera.getProjectionMatrix();

  bool frustum = projection.getFrustum( l,r,b,t,n,f );

  if( !frustum && !projection.getOrtho( l,r,b,t,n,f ) ) {
    osg::notify( osg::WARN )
        << " Awkward projection matrix. ComputeExtendedProjection failed"
        << std::endl;
    return;
  }

  osg::Matrix window = viewport->computeWindowMatrix();
 
  osg::Vec3 vMin( viewport->x() - margin.x(), 
                 viewport->y() - margin.y(), 
                 0.0 );

  osg::Vec3 vMax( viewport->width() + margin.x() * 2  + vMin.x(), 
                  viewport->height() + margin.y() * 2  + vMin.y(), 
                  0.0 );
  
  osg::Matrix inversePW = osg::Matrix::inverse( projection * window );

  vMin = vMin * inversePW;
  vMax = vMax * inversePW;
  
  l = vMin.x(); 
  r = vMax.x();
  b = vMin.y(); 
  t = vMax.y();

  if( frustum )
    projection.makeFrustum( l,r,b,t,n,f );
  else 
    projection.makeOrtho( l,r,b,t,n,f );
}
void MinimalShadowMap::ViewData::trimProjection
    ( osg::Matrixd & projectionMatrix, osg::BoundingBox bb, unsigned int trimMask )
{
#if 1
    if( !bb.valid() || !( trimMask & (1|2|4|8|16|32) ) ) return;
    double l = -1, r = 1, b = -1, t = 1, n = 1, f = -1;

#if 0
    // make sure bounding box does not extend beyond unit frustum clip range
    for( int i = 0; i < 3; i ++ ) {
        if( bb._min[i] < -1 ) bb._min[i] = -1;
        if( bb._max[i] >  1 ) bb._max[i] =  1;
    }
#endif

    if( trimMask & 1 ) l = bb._min[0];
    if( trimMask & 2 ) r = bb._max[0];
    if( trimMask & 4 ) b = bb._min[1];
    if( trimMask & 8 ) t = bb._max[1];
    if( trimMask & 16 ) n = -bb._min[2];
    if( trimMask & 32 ) f = -bb._max[2];

    projectionMatrix.postMult( osg::Matrix::ortho( l,r,b,t,n,f ) );
#else
    if( !bb.valid() || !( trimMask & (1|2|4|8|16|32) ) ) return;
    double l, r, t, b, n, f;
    bool ortho = projectionMatrix.getOrtho( l, r, b, t, n, f ); 
    if( !ortho && !projectionMatrix.getFrustum( l, r, b, t, n, f ) )
        return; // rotated or skewed or other crooked projection - give up

    // make sure bounding box does not extend beyond unit frustum clip range
    for( int i = 0; i < 3; i ++ ) {
        if( bb._min[i] < -1 ) bb._min[i] = -1;
        if( bb._max[i] >  1 ) bb._max[i] =  1;
    }

    osg::Matrix projectionToView = osg::Matrix::inverse( projectionMatrix );
    
    osg::Vec3 min =
        osg::Vec3( bb._min[0], bb._min[1], bb._min[2] ) * projectionToView;

    osg::Vec3 max =
        osg::Vec3( bb._max[0], bb._max[1], bb._max[2] ) * projectionToView;

    if( trimMask & 16 ) { // trim near
        if( !ortho ) { // recalc frustum corners on new near plane
            l *= -min[2] / n;
            r *= -min[2] / n;
            b *= -min[2] / n;
            t *= -min[2] / n;
        }
        n = -min[2];
    }

    if( trimMask & 32 ) // trim far
        f = -max[2];

    if( !ortho ) {
        min[0] *=  -n / min[2];
        min[1] *=  -n / min[2];
        max[0] *=  -n / max[2];
        max[1] *=  -n / max[2];
    }

    if( l < r ) { // check for inverted X range
        if( l < min[0] && ( trimMask & 1 ) ) l = min[0];
        if( r > max[0] && ( trimMask & 2 ) ) r = max[0];
    } else {
        if( l > min[0] && ( trimMask & 1 ) ) l = min[0];
        if( r < max[0] && ( trimMask & 2 ) ) r = max[0];
    }

    if( b < t ) { // check for inverted Y range
        if( b < min[1] && ( trimMask & 4 ) ) b = min[1];
        if( t > max[1] && ( trimMask & 8 ) ) t = max[1];
    } else {
        if( b > min[1] && ( trimMask & 4 ) ) b = min[1];
        if( t < max[1] && ( trimMask & 8 ) ) t = max[1];
    }

    if( ortho ) 
        projectionMatrix.makeOrtho( l, r, b, t, n, f );
    else 
        projectionMatrix.makeFrustum( l, r, b, t, n, f );
#endif
}