protected virtual void _updateFrustum()
{
Real vpTop, vpRight, vpBottom, vpLeft;
CalculateProjectionParameters( out vpLeft, out vpRight, out vpBottom, out vpTop );
if ( !_customProjectionMatrix )
{
// The code below will dealing with general projection
// parameters, similar glFrustum and glOrtho.
// Doesn't optimise manually except division operator, so the
// code more self-explaining.
float inv_w = 1.0f / ( vpRight - vpLeft );
float inv_h = 1.0f / ( vpTop - vpBottom );
float inv_d = 1.0f / ( _farDistance - _nearDistance );
// Recalc if frustum params changed
if ( _projectionType == Projection.Perspective )
{
// Calc matrix elements
float A = 2.0f * _nearDistance * inv_w;
float B = 2.0f * _nearDistance * inv_h;
float C = ( vpRight + vpLeft ) * inv_w;
float D = ( vpTop + vpBottom ) * inv_h;
float q, qn;
if ( _farDistance == 0.0f )
{
// Infinite far plane
q = Frustum.InfiniteFarPlaneAdjust - 1.0f;
qn = _nearDistance * ( Frustum.InfiniteFarPlaneAdjust - 2.0f );
}
else
{
q = -( _farDistance + _nearDistance ) * inv_d;
qn = -2.0f * ( _farDistance * _nearDistance ) * inv_d;
}
// NB: This creates 'uniform' perspective projection matrix,
// which depth range [-1,1], right-handed rules
//
// [ A 0 C 0 ]
// [ 0 B D 0 ]
// [ 0 0 q qn ]
// [ 0 0 -1 0 ]
//
// A = 2 * near / (right - left)
// B = 2 * near / (top - bottom)
// C = (right + left) / (right - left)
// D = (top + bottom) / (top - bottom)
// q = - (far + near) / (far - near)
// qn = - 2 * (far * near) / (far - near)
_projectionMatrix = Matrix4.Zero;
_projectionMatrix.m00 = A;
_projectionMatrix.m02 = C;
_projectionMatrix.m11 = B;
_projectionMatrix.m12 = D;
_projectionMatrix.m22 = q;
_projectionMatrix.m23 = qn;
_projectionMatrix.m32 = -1.0f;
if ( useObliqueDepthProjection )
{
// Translate the plane into view space
// Don't use getViewMatrix here, incase overrided by
// camera and return a cull frustum view matrix
UpdateView();
Plane plane = _viewMatrix * obliqueProjPlane;
// Thanks to Eric Lenyel for posting this calculation
// at www.terathon.com
// Calculate the clip-space corner point opposite the
// clipping plane
// as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
// transform it into camera space by multiplying it
// by the inverse of the projection matrix
/* generalised version
Vector4 q = matrix.inverse() *
Vector4(Math::Sign(plane.normal.x),
Math::Sign(plane.normal.y), 1.0f, 1.0f);
*/
Vector4 q1 = new Vector4();
q1.x = ( System.Math.Sign( plane.Normal.x ) + _projectionMatrix.m02 ) / _projectionMatrix.m00;
q1.y = ( System.Math.Sign( plane.Normal.y ) + _projectionMatrix.m12 ) / _projectionMatrix.m11;
q1.z = -1.0f;
q1.w = ( 1.0f + _projectionMatrix.m22 ) / _projectionMatrix.m23;
// Calculate the scaled plane vector
Vector4 clipPlane4d = new Vector4( plane.Normal.x, plane.Normal.y, plane.Normal.z, plane.D );
Vector4 c = clipPlane4d * ( 2.0f / ( clipPlane4d.Dot( q1 ) ) );
// Replace the third row of the projection matrix
_projectionMatrix.m20 = c.x;
_projectionMatrix.m21 = c.y;
_projectionMatrix.m22 = c.z + 1.0f;
_projectionMatrix.m23 = c.w;
}
} // perspective
else if ( _projectionType == Projection.Orthographic )
{
float A = 2.0f * inv_w;
float B = 2.0f * inv_h;
float C = -( vpRight + vpLeft ) * inv_w;
float D = -( vpTop + vpBottom ) * inv_h;
float q, qn;
if ( _farDistance == 0.0f )
{
// Can not do infinite far plane here, avoid divided zero only
q = -Frustum.InfiniteFarPlaneAdjust / _nearDistance;
qn = -Frustum.InfiniteFarPlaneAdjust - 1.0f;
}
else
{
q = -2.0f * inv_d;
qn = -( _farDistance + _nearDistance ) * inv_d;
}
// NB: This creates 'uniform' orthographic projection matrix,
// which depth range [-1,1], right-handed rules
//
// [ A 0 0 C ]
// [ 0 B 0 D ]
// [ 0 0 q qn ]
// [ 0 0 0 1 ]
//
// A = 2 * / (right - left)
// B = 2 * / (top - bottom)
// C = - (right + left) / (right - left)
// D = - (top + bottom) / (top - bottom)
// q = - 2 / (far - near)
// qn = - (far + near) / (far - near)
_projectionMatrix = Matrix4.Zero;
_projectionMatrix.m00 = A;
_projectionMatrix.m03 = C;
_projectionMatrix.m11 = B;
_projectionMatrix.m13 = D;
_projectionMatrix.m22 = q;
_projectionMatrix.m23 = qn;
_projectionMatrix.m33 = 1.0f;
} // ortho
} // if !_customProjectionMatrix
// grab a reference to the current render system
RenderSystem renderSystem = Root.Instance.RenderSystem;
// API specific
renderSystem.ConvertProjectionMatrix(_projectionMatrix, out _projectionMatrixRS);
// API specific for Gpu Programs
renderSystem.ConvertProjectionMatrix(_projectionMatrix, out _projectionMatrixRSDepth, true);
// Calculate bounding box (local)
// Box is from 0, down -Z, max dimensions as determined from far plane
// If infinite view frustum just pick a far value
float farDist = ( _farDistance == 0.0f ) ? 100000.0f : _farDistance;
// Near plane bounds
Vector3 min = new Vector3( vpLeft, vpBottom, -farDist );
Vector3 max = new Vector3( vpRight, vpTop, 0 );
if ( _customProjectionMatrix )
{
// Some custom projection matrices can have unusual inverted settings
// So make sure the AABB is the right way around to start with
Vector3 tmp = min;
min.Floor( max );
max.Ceil( tmp );
}
float radio = 1.0f;
if ( _projectionType == Projection.Perspective )
{
// Merge with far plane bounds
radio = _farDistance / _nearDistance;
min.Floor( new Vector3( vpLeft * radio, vpBottom * radio, -_farDistance ) );
max.Ceil( new Vector3( vpRight * radio, vpTop * radio, 0 ) );
}
_boundingBox.SetExtents( min, max );
_recalculateFrustum = false;
// Signal to update frustum clipping planes
_recalculateFrustumPlanes = true;
}