734 lines
25 KiB
C++
734 lines
25 KiB
C++
//====== Copyright © 1996-2004, Valve Corporation, All rights reserved. =======
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
#include "mathlib/camera.h"
|
|
#include "tier0/dbg.h"
|
|
#include "mathlib/vector.h"
|
|
#include "mathlib/vmatrix.h"
|
|
#include "tier2/tier2.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#define FORWARD_AXIS 0
|
|
#define LEFT_AXIS 1
|
|
#define UP_AXIS 2
|
|
|
|
/// matrix to align our Valve coordinate system camera space with a standard viewing coordinate system
|
|
/// x-axis goes from left to right in view space (right in camera space)
|
|
/// y-axis goes from bottom to top in view space (up in camera space)
|
|
/// z-axis goes from far to near in view space (-forward in camera space)
|
|
/// The constructor takes sequential matrix rows, which are basis vectors in view space (so transposed from the init)
|
|
#ifdef YUP_ACTIVE
|
|
#error YUP_ACTIVE is not supported on this branch.
|
|
#endif
|
|
|
|
/// g_ViewAlignMatrix.Init( Vector(0,-1,0), Vector(0,0,1), Vector(-1,0,0), vec3_origin );
|
|
static matrix3x4_t g_ViewAlignMatrix( 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0 );
|
|
VMatrix g_matViewToCameraMatrix( 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1 );
|
|
VMatrix g_matCameraToViewMatrix( 0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1 );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Extract the direction vectors from the a matrix
|
|
//--------------------------------------------------------------------------------------------------
|
|
void ExtractDirectionVectors( Vector *pForward, Vector *pLeft, Vector *pUp, const matrix3x4_t &mMatrix )
|
|
{
|
|
MatrixGetColumn( mMatrix, FORWARD_AXIS, *pForward );
|
|
MatrixGetColumn( mMatrix, LEFT_AXIS, *pLeft );
|
|
MatrixGetColumn( mMatrix, UP_AXIS, *pUp );
|
|
}
|
|
|
|
|
|
// Returns points in this order:
|
|
// 2--3
|
|
// | |
|
|
// 0--1
|
|
void CalcFarPlaneCameraRelativePoints( Vector *p4PointsOut, Vector &vForward, Vector &vUp, Vector &vLeft, float flFarPlane,
|
|
float flFovX, float flAspect,
|
|
float flClipSpaceBottomLeftX /*= -1.0f*/, float flClipSpaceBottomLeftY /*= -1.0f*/,
|
|
float flClipSpaceTopRightX /*= 1.0f*/, float flClipSpaceTopRightY /*= 1.0f*/ )
|
|
{
|
|
Vector vFowardShift = flFarPlane * vForward;
|
|
Vector vUpShift;
|
|
Vector vRightShift;
|
|
if ( flFovX == -1 )
|
|
{
|
|
vUpShift = vUp;
|
|
vRightShift = -vLeft;
|
|
}
|
|
else
|
|
{
|
|
float flTanX = tanf( DEG2RAD( flFovX * 0.5f ) );
|
|
float flTanY = flTanX / flAspect;
|
|
|
|
vUpShift = flFarPlane * flTanY * vUp;
|
|
vRightShift = flFarPlane * flTanX * -vLeft;
|
|
}
|
|
|
|
p4PointsOut[0] = vFowardShift + flClipSpaceBottomLeftX * vRightShift + flClipSpaceBottomLeftY * vUpShift;
|
|
p4PointsOut[1] = vFowardShift + flClipSpaceTopRightX * vRightShift + flClipSpaceBottomLeftY * vUpShift;
|
|
p4PointsOut[2] = vFowardShift + flClipSpaceBottomLeftX * vRightShift + flClipSpaceTopRightY * vUpShift;
|
|
p4PointsOut[3] = vFowardShift + flClipSpaceTopRightX * vRightShift + flClipSpaceTopRightY * vUpShift;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// accessors for generated matrices
|
|
//-----------------------------------------------------------------------------
|
|
void ComputeViewMatrix( matrix3x4_t *pWorldToView, matrix3x4_t *pCameraToWorld, const Camera_t &camera )
|
|
{
|
|
AngleMatrix( camera.m_angles, camera.m_origin, *pCameraToWorld );
|
|
matrix3x4_t tmp;
|
|
ConcatTransforms( *pCameraToWorld, g_ViewAlignMatrix, tmp );
|
|
MatrixInvert( tmp, *pWorldToView );
|
|
}
|
|
|
|
void ComputeViewMatrix( matrix3x4_t *pWorldToView, matrix3x4_t *pCameraToWorld,
|
|
Vector const &vecOrigin,
|
|
Vector const &vecForward, Vector const &vecLeft, Vector const &vecUp )
|
|
{
|
|
MatrixSetColumn( vecForward, FORWARD_AXIS, *pCameraToWorld );
|
|
MatrixSetColumn( vecLeft, LEFT_AXIS, *pCameraToWorld );
|
|
MatrixSetColumn( vecUp, UP_AXIS, *pCameraToWorld );
|
|
MatrixSetColumn( vecOrigin, ORIGIN, *pCameraToWorld );
|
|
matrix3x4_t tmp;
|
|
ConcatTransforms( *pCameraToWorld, g_ViewAlignMatrix, tmp );
|
|
MatrixInvert( tmp, *pWorldToView );
|
|
}
|
|
|
|
|
|
void ComputeViewMatrix( matrix3x4_t *pWorldToView, const Camera_t &camera )
|
|
{
|
|
matrix3x4_t cameraToWorld;
|
|
ComputeViewMatrix( pWorldToView, &cameraToWorld, camera );
|
|
}
|
|
|
|
void ComputeViewMatrix( VMatrix *pWorldToView, const Camera_t &camera )
|
|
{
|
|
matrix3x4_t transform, invTransform;
|
|
AngleMatrix( camera.m_angles, camera.m_origin, transform );
|
|
|
|
VMatrix matRotate( transform );
|
|
|
|
#ifndef YUP_ACTIVE
|
|
VMatrix matRotateZ;
|
|
MatrixBuildRotationAboutAxis( matRotateZ, Vector(0,0,1), -90 );
|
|
MatrixMultiply( matRotate, matRotateZ, matRotate );
|
|
|
|
VMatrix matRotateX;
|
|
MatrixBuildRotationAboutAxis( matRotateX, Vector(1,0,0), 90 );
|
|
MatrixMultiply( matRotate, matRotateX, matRotate );
|
|
transform = matRotate.As3x4();
|
|
#else
|
|
VMatrix matRotateUp;
|
|
MatrixBuildRotationAboutAxis( matRotateUp, Vector(0,1,0), -180 );
|
|
transform = matRotate.As3x4();
|
|
#endif
|
|
|
|
MatrixInvert( transform, invTransform );
|
|
|
|
pWorldToView->Init( invTransform );
|
|
}
|
|
|
|
void ComputeViewMatrix( VMatrix *pViewMatrix, const Vector &origin, const QAngle &angles )
|
|
{
|
|
static VMatrix baseRotation;
|
|
static bool bDidInit;
|
|
|
|
if ( !bDidInit )
|
|
{
|
|
MatrixBuildRotationAboutAxis( baseRotation, Vector( 1, 0, 0 ), -90 );
|
|
MatrixRotate( baseRotation, Vector( 0, 0, 1 ), 90 );
|
|
bDidInit = true;
|
|
}
|
|
|
|
*pViewMatrix = baseRotation;
|
|
MatrixRotate( *pViewMatrix, Vector( 1, 0, 0 ), -angles[2] );
|
|
MatrixRotate( *pViewMatrix, Vector( 0, 1, 0 ), -angles[0] );
|
|
MatrixRotate( *pViewMatrix, Vector( 0, 0, 1 ), -angles[1] );
|
|
|
|
MatrixTranslate( *pViewMatrix, -origin );
|
|
}
|
|
|
|
void ComputeViewMatrix( VMatrix *pViewMatrix, const matrix3x4_t &matGameCustom )
|
|
{
|
|
//translate game coordinates to rendering coordinates. Basically does the same as baseRotation in the other version of ComputeViewMatrix()
|
|
pViewMatrix->m[0][0] = -matGameCustom.m_flMatVal[1][0];
|
|
pViewMatrix->m[0][1] = -matGameCustom.m_flMatVal[1][1];
|
|
pViewMatrix->m[0][2] = -matGameCustom.m_flMatVal[1][2];
|
|
pViewMatrix->m[0][3] = -matGameCustom.m_flMatVal[1][3];
|
|
|
|
pViewMatrix->m[1][0] = matGameCustom.m_flMatVal[2][0];
|
|
pViewMatrix->m[1][1] = matGameCustom.m_flMatVal[2][1];
|
|
pViewMatrix->m[1][2] = matGameCustom.m_flMatVal[2][2];
|
|
pViewMatrix->m[1][3] = matGameCustom.m_flMatVal[2][3];
|
|
|
|
pViewMatrix->m[2][0] = -matGameCustom.m_flMatVal[0][0];
|
|
pViewMatrix->m[2][1] = -matGameCustom.m_flMatVal[0][1];
|
|
pViewMatrix->m[2][2] = -matGameCustom.m_flMatVal[0][2];
|
|
pViewMatrix->m[2][3] = -matGameCustom.m_flMatVal[0][3];
|
|
|
|
//standard 4th row
|
|
pViewMatrix->m[3][0] = pViewMatrix->m[3][1] = pViewMatrix->m[3][2] = 0.0f;
|
|
pViewMatrix->m[3][3] = 1.0f;
|
|
}
|
|
|
|
void ComputeProjectionMatrix( VMatrix *pCameraToProjection, const Camera_t &camera, int width, int height )
|
|
{
|
|
float flApsectRatio = (float)width / (float)height;
|
|
ComputeProjectionMatrix( pCameraToProjection, camera.m_flZNear, camera.m_flZFar, camera.m_flFOVX, flApsectRatio );
|
|
}
|
|
|
|
void ComputeProjectionMatrix( VMatrix *pCameraToProjection, float flZNear, float flZFar, float flFOVX, float flAspectRatio )
|
|
{
|
|
float halfWidth = tan( flFOVX * M_PI / 360.0 );
|
|
float halfHeight = halfWidth / flAspectRatio;
|
|
memset( pCameraToProjection, 0, sizeof( VMatrix ) );
|
|
pCameraToProjection->m[0][0] = 1.0f / halfWidth;
|
|
pCameraToProjection->m[1][1] = 1.0f / halfHeight;
|
|
pCameraToProjection->m[2][2] = flZFar / ( flZNear - flZFar );
|
|
pCameraToProjection->m[3][2] = -1.0f;
|
|
pCameraToProjection->m[2][3] = flZNear * flZFar / ( flZNear - flZFar );
|
|
}
|
|
|
|
void ComputeProjectionMatrix( VMatrix *pCameraToProjection, float flZNear, float flZFar, float flFOVX, float flAspectRatio,
|
|
float flClipSpaceBottomLeftX, float flClipSpaceBottomLeftY,
|
|
float flClipSpaceTopRightX, float flClipSpaceTopRightY )
|
|
{
|
|
Vector pNearPoints[ 4 ];
|
|
Vector vForward( 0, 0, 1 );
|
|
Vector vUp( 0, 1, 0 );
|
|
Vector vLeft( -1, 0, 0 );
|
|
CalcFarPlaneCameraRelativePoints( pNearPoints, vForward, vUp, vLeft, flZNear,
|
|
flFOVX, flAspectRatio,
|
|
flClipSpaceBottomLeftX, flClipSpaceBottomLeftY,
|
|
flClipSpaceTopRightX, flClipSpaceTopRightY );
|
|
|
|
float l = pNearPoints[ 0 ].x;
|
|
float r = pNearPoints[ 1 ].x;
|
|
float b = pNearPoints[ 0 ].y;
|
|
float t = pNearPoints[ 2 ].y;
|
|
float zn = flZNear;
|
|
float zf = flZFar;
|
|
|
|
float flWidth = r - l;
|
|
float flHeight = t - b;
|
|
float flReverseDepth = zn - zf;
|
|
|
|
memset( pCameraToProjection, 0, sizeof( VMatrix ) );
|
|
|
|
pCameraToProjection->m[0][0] = ( 2.0f * zn ) / flWidth;
|
|
pCameraToProjection->m[1][1] = ( 2.0f * zn ) / flHeight;
|
|
pCameraToProjection->m[2][2] = zf / flReverseDepth;
|
|
pCameraToProjection->m[0][2] = ( l + r ) / flWidth;
|
|
pCameraToProjection->m[1][2] = ( t + b ) / flHeight;
|
|
pCameraToProjection->m[2][3] = ( zn * zf ) / flReverseDepth;
|
|
pCameraToProjection->m[3][2] = -1.0f;
|
|
/*
|
|
// this is the matrix we're going for here
|
|
2*zn/(r-l) 0 0 0
|
|
0 2*zn/(t-b) 0 0
|
|
(l+r)/(r-l) (t+b)/(t-b) zf/(zn-zf) -1
|
|
0 0 zn*zf/(zn-zf) 0
|
|
*/
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the screen space position given a screen size
|
|
//-----------------------------------------------------------------------------
|
|
void ComputeScreenSpacePosition( Vector2D *pScreenPosition, const Vector &vecWorldPosition,
|
|
const Camera_t &camera, int width, int height )
|
|
{
|
|
VMatrix view, proj, viewproj;
|
|
ComputeViewMatrix( &view, camera );
|
|
ComputeProjectionMatrix( &proj, camera, width, height );
|
|
MatrixMultiply( proj, view, viewproj );
|
|
|
|
Vector vecScreenPos;
|
|
Vector3DMultiplyPositionProjective( viewproj, vecWorldPosition, vecScreenPos );
|
|
|
|
pScreenPosition->x = ( vecScreenPos.x + 1.0f ) * width / 2.0f;
|
|
pScreenPosition->y = ( -vecScreenPos.y + 1.0f ) * height / 2.0f;
|
|
}
|
|
|
|
VMatrix ViewMatrixRH( Vector &vEye, Vector &vAt, Vector &vUp )
|
|
{
|
|
Vector xAxis, yAxis;
|
|
Vector zAxis = vEye - vAt;
|
|
xAxis = CrossProduct( vUp, zAxis );
|
|
yAxis = CrossProduct( zAxis, xAxis );
|
|
xAxis.NormalizeInPlace();
|
|
yAxis.NormalizeInPlace();
|
|
zAxis.NormalizeInPlace();
|
|
float flDotX = -DotProduct( xAxis, vEye );
|
|
float flDotY = -DotProduct( yAxis, vEye );
|
|
float flDotZ = -DotProduct( zAxis, vEye );
|
|
|
|
// YUP_ACTIVE: This is ok
|
|
VMatrix mRet(
|
|
xAxis.x, yAxis.x, zAxis.x, 0,
|
|
xAxis.y, yAxis.y, zAxis.y, 0,
|
|
xAxis.z, yAxis.z, zAxis.z, 0,
|
|
flDotX, flDotY, flDotZ, 1 );
|
|
return mRet.Transpose();
|
|
}
|
|
|
|
|
|
// Given populated camera params, generate view and proj matrices.
|
|
void MatricesFromCamera( VMatrix &mWorldToView, VMatrix &mProjection, const Camera_t &camera,
|
|
float flClipSpaceBottomLeftX, float flClipSpaceBottomLeftY,
|
|
float flClipSpaceTopRightX, float flClipSpaceTopRightY )
|
|
{
|
|
matrix3x4_t cameraToWorld;
|
|
ComputeViewMatrix( &mWorldToView.As3x4(), &cameraToWorld, camera );
|
|
|
|
if ( camera.IsOrthographic() )
|
|
{
|
|
mProjection = OrthoMatrixRH( camera.m_flWidth, camera.m_flHeight, camera.m_flZNear, camera.m_flZFar );
|
|
}
|
|
else
|
|
{
|
|
ComputeProjectionMatrix( &mProjection, camera.m_flZNear, camera.m_flZFar, camera.m_flFOVX, camera.m_flAspect,
|
|
flClipSpaceBottomLeftX, flClipSpaceBottomLeftY, flClipSpaceTopRightX, flClipSpaceTopRightY );
|
|
}
|
|
}
|
|
|
|
// Generate frustum planes from viewproj matrix
|
|
void FrustumFromViewProj( Frustum_t *pFrustum, const VMatrix &mViewProj, const Vector &origin, bool bD3DClippingRange )
|
|
{
|
|
VPlane planes[FRUSTUM_NUMPLANES];
|
|
ExtractClipPlanesFromNonTransposedMatrix( mViewProj, planes, bD3DClippingRange );
|
|
|
|
// Subtract the origin.
|
|
for ( int i = 0; i < FRUSTUM_NUMPLANES; ++i )
|
|
{
|
|
planes[i].m_Dist = planes[i].m_Dist + DotProduct( planes[i].m_Normal, -origin );
|
|
}
|
|
|
|
pFrustum->SetPlanes( planes );
|
|
}
|
|
|
|
// Generate frustum planes given view and proj matrices
|
|
void FrustumFromMatrices( Frustum_t *pFrustum, const VMatrix &mWorldToView, const VMatrix &mProjection, const Vector &origin, bool bD3DClippingRange )
|
|
{
|
|
VMatrix viewProj;
|
|
viewProj = ( mProjection * mWorldToView );
|
|
|
|
FrustumFromViewProj( pFrustum, viewProj, origin, bD3DClippingRange );
|
|
}
|
|
|
|
VMatrix ViewProjFromVectors( const Vector &origin, float flNear, float flFar, float flFOV, float flAspect,
|
|
Vector const &vecForward, Vector const &vecLeft, Vector const &vecUp )
|
|
{
|
|
|
|
matrix3x4_t mCameraToWorld;
|
|
matrix3x4_t mWorldToView;
|
|
ComputeViewMatrix( &mWorldToView, &mCameraToWorld, origin, vecForward, vecLeft, vecUp );
|
|
VMatrix mProjection;
|
|
ComputeProjectionMatrix( &mProjection, flNear, flFar, flFOV, flAspect );
|
|
VMatrix mViewProj;
|
|
mViewProj = (mProjection * VMatrix(mWorldToView));
|
|
return mViewProj;
|
|
}
|
|
|
|
|
|
|
|
int CFrustum::CheckBoxAgainstNearAndFarPlanes( const VectorAligned &minBounds, const VectorAligned &maxBounds ) const
|
|
{
|
|
// !!speed!! not super fast. change to use simd, inlining
|
|
float flNear = 0;
|
|
float flFar = 0;
|
|
AABB_t aabb;
|
|
aabb.m_vMinBounds = minBounds;
|
|
aabb.m_vMaxBounds = maxBounds;
|
|
Vector vZero( 0, 0, 0 );
|
|
GetNearAndFarPlanesAroundBox( &flNear, &flFar, aabb, vZero );
|
|
int nRet = 0;
|
|
if ( flNear <= m_camera.m_flZNear )
|
|
{
|
|
nRet |= BOXCHECK_FLAGS_OVERLAPS_NEAR;
|
|
}
|
|
if ( flFar >= m_camera.m_flZFar )
|
|
{
|
|
nRet |= BOXCHECK_FLAGS_OVERLAPS_FAR;
|
|
}
|
|
return nRet;
|
|
|
|
}
|
|
|
|
|
|
void CFrustum::GetNearAndFarPlanesAroundBox( float *pNear, float *pFar, AABB_t const &inBox, Vector &vOriginShift ) const
|
|
{
|
|
AABB_t box = inBox;
|
|
box.m_vMinBounds -= vOriginShift;
|
|
box.m_vMaxBounds -= vOriginShift;
|
|
|
|
Vector vCorners[8];
|
|
vCorners[0] = box.m_vMinBounds;
|
|
vCorners[1] = Vector( box.m_vMinBounds.x, box.m_vMinBounds.y, box.m_vMaxBounds.z );
|
|
vCorners[2] = Vector( box.m_vMinBounds.x, box.m_vMaxBounds.y, box.m_vMinBounds.z );
|
|
vCorners[3] = Vector( box.m_vMinBounds.x, box.m_vMaxBounds.y, box.m_vMaxBounds.z );
|
|
|
|
vCorners[4] = Vector( box.m_vMaxBounds.x, box.m_vMinBounds.y, box.m_vMinBounds.z );
|
|
vCorners[5] = Vector( box.m_vMaxBounds.x, box.m_vMinBounds.y, box.m_vMaxBounds.z );
|
|
vCorners[6] = Vector( box.m_vMaxBounds.x, box.m_vMaxBounds.y, box.m_vMinBounds.z );
|
|
vCorners[7] = box.m_vMaxBounds;
|
|
|
|
float flNear = FLT_MAX;//m_camera.m_flZNear;
|
|
float flFar = -FLT_MAX;//m_camera.m_flZFar;
|
|
for ( int i=0; i<8; ++i )
|
|
{
|
|
Vector vDelta = vCorners[i] - m_camera.m_origin;
|
|
float flDist = DotProduct( m_forward, vDelta );
|
|
flNear = MIN( flNear, flDist );
|
|
flFar = MAX( flFar, flDist );
|
|
}
|
|
|
|
*pNear = flNear;
|
|
*pFar = flFar;
|
|
}
|
|
|
|
|
|
void CFrustum::UpdateFrustumFromCamera()
|
|
{
|
|
if ( !m_bDirty )
|
|
return;
|
|
|
|
ComputeViewMatrix( &m_worldToView, &m_cameraToWorld, m_camera );
|
|
|
|
if ( m_camera.IsOrthographic() )
|
|
{
|
|
MatrixBuildOrtho( m_projection,
|
|
m_camera.m_flWidth * m_flClipSpaceBottomLeftX * 0.5f,
|
|
m_camera.m_flHeight * m_flClipSpaceTopRightY * 0.5f,
|
|
m_camera.m_flWidth * m_flClipSpaceTopRightX * 0.5f,
|
|
m_camera.m_flHeight * m_flClipSpaceBottomLeftY * 0.5f,
|
|
m_camera.m_flZNear, m_camera.m_flZFar );
|
|
}
|
|
else
|
|
{
|
|
// Determine the extents
|
|
ComputeProjectionMatrix( &m_projection, m_camera.m_flZNear, m_camera.m_flZFar, m_camera.m_flFOVX, m_camera.m_flAspect,
|
|
m_flClipSpaceBottomLeftX, m_flClipSpaceBottomLeftY, m_flClipSpaceTopRightX, m_flClipSpaceTopRightY );
|
|
}
|
|
|
|
CalcViewProj();
|
|
ExtractDirectionVectors( &m_forward, &m_left, &m_up, m_cameraToWorld );
|
|
|
|
FrustumFromViewProj( &m_frustumStruct, m_viewProj, m_camera.m_origin, true );
|
|
|
|
m_bDirty = false;
|
|
}
|
|
|
|
|
|
void CFrustum::BuildFrustumFromVectors( const Vector &origin, float flNear, float flFar, float flFOV, float flAspect,
|
|
Vector const &vecForward, Vector const &vecLeft, Vector const &vecUp )
|
|
{
|
|
InitCamera( origin, QAngle( 0, 0, 0 ), flNear, flFar, flFOV, flAspect );
|
|
ComputeViewMatrix( &m_worldToView, &m_cameraToWorld, origin, vecForward, vecLeft, vecUp );
|
|
ComputeProjectionMatrix( &m_projection, flNear, flFar, flFOV, flAspect );
|
|
m_viewProj = (m_projection * VMatrix(m_worldToView));
|
|
ExtractDirectionVectors( &m_forward, &m_left, &m_up, m_cameraToWorld );
|
|
|
|
m_frustumStruct.CreatePerspectiveFrustum( vec3_origin, m_forward, -m_left, m_up,
|
|
flNear, flFar, flFOV, flAspect );
|
|
|
|
MatrixInverseGeneral( m_viewProj, m_invViewProj );
|
|
MatrixInverseGeneral( m_projection, m_invProjection );
|
|
|
|
VMatrix worldToView( m_worldToView );
|
|
|
|
VMatrix viewToWorld;
|
|
worldToView.InverseGeneral( viewToWorld );
|
|
m_cameraToWorld = viewToWorld.As3x4();
|
|
|
|
viewToWorld.GetTranslation( m_camera.m_origin );
|
|
|
|
}
|
|
|
|
|
|
/// Given only the world->view and an ortho view->proj matrices, this helper method computes
|
|
/// the implied frustum values needed for orthographic shadow buffer rendering (but
|
|
/// should work with perspective projections too). This is slow and general, but
|
|
/// it should guarantee a frustum in a consistent/sane state given any world->view and
|
|
/// view->proj matrices.
|
|
void CFrustum::BuildShadowFrustum( VMatrix &newWorldToView, VMatrix &newProj )
|
|
{
|
|
SetView( newWorldToView );
|
|
SetProj( newProj );
|
|
CalcViewProj();
|
|
|
|
VMatrix &viewToProj = m_projection;
|
|
Assert( ( viewToProj.m[3][0] == 0.0f ) && ( viewToProj.m[3][1] == 0.0f ) && ( viewToProj.m[3][2] == 0.0f ) && ( viewToProj[3][3] == 1.0f ) );
|
|
|
|
VMatrix worldToView( m_worldToView );
|
|
|
|
VMatrix worldToCamera;
|
|
MatrixMultiply( g_matViewToCameraMatrix, worldToView, worldToCamera );
|
|
VMatrix cameraToWorld;
|
|
MatrixInverseGeneral( worldToCamera, cameraToWorld );
|
|
m_cameraToWorld = cameraToWorld.As3x4();
|
|
|
|
// Compute camera location in world space.
|
|
|
|
VMatrix viewToWorld;
|
|
MatrixInverseGeneral( worldToView, viewToWorld );
|
|
viewToWorld.GetTranslation( m_camera.m_origin );
|
|
|
|
cameraToWorld.GetTranslation( m_camera.m_origin );
|
|
|
|
MatrixToAngles( cameraToWorld, m_camera.m_angles );
|
|
|
|
// forward/left/up - world relative coordinates, assuming an FPS camera sitting on an XY plane, Z is up
|
|
MatrixGetRow( worldToCamera, FORWARD_AXIS, &m_forward );
|
|
MatrixGetRow( worldToCamera, LEFT_AXIS, &m_left );
|
|
MatrixGetRow( worldToCamera, UP_AXIS, &m_up );
|
|
|
|
// Compute near/far planes, assuming D3D-style clipping range of [0,1]
|
|
VMatrix projToView;
|
|
viewToProj.InverseGeneral( projToView );
|
|
|
|
Vector vNearPoint, vFarPoint;
|
|
projToView.V3Mul( Vector( 0.0f, 0.0f, 0.0f ), vNearPoint );
|
|
projToView.V3Mul( Vector( 0.0f, 0.0f, 1.0f ), vFarPoint );
|
|
m_camera.m_flZNear = fabs( vNearPoint.z );
|
|
m_camera.m_flZFar = fabs( vFarPoint.z );
|
|
m_camera.m_flAspect = 1.0f;
|
|
m_camera.m_flFOVX = -1.0f;
|
|
|
|
Vector vCornerPoints[2];
|
|
|
|
// Y's are negated here because MatrixBuildOrtho() flips top/bottom!
|
|
projToView.V3Mul( Vector( -1.0f, 1.0f, 0.0f ), vCornerPoints[0] ); // left/bottom
|
|
projToView.V3Mul( Vector( 1.0f, -1.0f, 0.0f ), vCornerPoints[1] ); // right/top
|
|
|
|
m_flClipSpaceBottomLeftX = vCornerPoints[0].x;
|
|
m_flClipSpaceBottomLeftY = vCornerPoints[0].y;
|
|
m_flClipSpaceTopRightX = vCornerPoints[1].x;
|
|
m_flClipSpaceTopRightY = vCornerPoints[1].y;
|
|
|
|
m_camera.m_flWidth = 2.0f;
|
|
m_camera.m_flHeight = 2.0f;
|
|
|
|
// Now compute the frustum planes used for culling purposes. These planes are computed assuming the camera is already at the origin.
|
|
VMatrix worldToCamLocalWorld;
|
|
// This is confusing - vShadowCamPos is not negated here, because we need to compensate for the fact that the
|
|
// frustum culling code makes the cam pos the origin before culling by subtracting the camera's origin - so undo it.
|
|
// Calc a viewproj matrix that has no g_ViewAlignMatrix matrix in it.
|
|
MatrixBuildTranslation( worldToCamLocalWorld, m_camera.m_origin.x, m_camera.m_origin.y, m_camera.m_origin.z );
|
|
VMatrix worldToCamLocalWorldToView( worldToView * worldToCamLocalWorld );
|
|
VMatrix shadowCamLocalWorldToViewProj( viewToProj * worldToCamLocalWorldToView );
|
|
|
|
VPlane pSixPlanes[FRUSTUM_NUMPLANES];
|
|
|
|
#if 0
|
|
Vector actualOriginProjSpace( 0.0f, 0.0f, .5f );
|
|
Vector actualOriginWorldSpace;
|
|
VMatrix projToWorld;
|
|
m_viewProj.InverseGeneral( projToWorld );
|
|
projToWorld.V3Mul( actualOriginProjSpace, actualOriginWorldSpace );
|
|
|
|
ExtractClipPlanesFromNonTransposedMatrix( m_viewProj, pSixPlanes, true );
|
|
// Testing
|
|
float flDots[6];
|
|
for (uint i = 0; i < 6; i++)
|
|
{
|
|
flDots[i] = pSixPlanes[i].DistTo( actualOriginWorldSpace );
|
|
}
|
|
#endif
|
|
|
|
ExtractClipPlanesFromNonTransposedMatrix( shadowCamLocalWorldToViewProj, pSixPlanes, true );
|
|
|
|
m_frustumStruct.SetPlanes( pSixPlanes );
|
|
|
|
MatrixInverseGeneral( m_viewProj, m_invViewProj );
|
|
MatrixInverseGeneral( m_projection, m_invProjection );
|
|
|
|
m_bDirty = false;
|
|
|
|
// This should be a no-op (ignoring FP precision) if all the above stuff was done right.
|
|
//m_bDirty = true;
|
|
//UpdateFrustumFromCamera();
|
|
}
|
|
|
|
void CFrustum::CalcFarPlaneCameraRelativePoints( Vector *p4PointsOut, float flFarPlane,
|
|
float flClipSpaceBottomLeftX, float flClipSpaceBottomLeftY,
|
|
float flClipSpaceTopRightX, float flClipSpaceTopRightY ) const
|
|
{
|
|
Vector vForward = CameraForward();
|
|
Vector vUp = CameraUp();
|
|
Vector vLeft = CameraLeft();
|
|
|
|
float flFovX = GetCameraFOV();
|
|
::CalcFarPlaneCameraRelativePoints( p4PointsOut, vForward, vUp, vLeft, flFarPlane,
|
|
flFovX, GetCameraAspect(),
|
|
flClipSpaceBottomLeftX, flClipSpaceBottomLeftY,
|
|
flClipSpaceTopRightX, flClipSpaceTopRightY );
|
|
}
|
|
|
|
|
|
// generates 8 vertices of the frustum
|
|
void Camera_t::ComputeGeometry( Vector *pVertsOut8, const Vector &vForward, const Vector &vLeft, const Vector &vUp ) const
|
|
{
|
|
Vector vNearLeft, vFarLeft;
|
|
Vector vNearUp, vFarUp;
|
|
Vector vNear = m_origin + m_flZNear * vForward;
|
|
Vector vFar = m_origin + m_flZFar * vForward;
|
|
|
|
if ( IsOrthographic() )
|
|
{
|
|
vNearLeft = vLeft * m_flWidth;
|
|
vNearUp = vUp * m_flHeight;
|
|
vFarLeft = vNearLeft;
|
|
vFarUp = vNearUp;
|
|
}
|
|
else
|
|
{
|
|
float flTanX = tan( DEG2RAD(m_flFOVX) * 0.5f );
|
|
float flooAspect = 1.0f / m_flAspect;
|
|
float flWidth = m_flZNear * flTanX;
|
|
float flHeight = flWidth * flooAspect;
|
|
|
|
vNearLeft = vLeft * flWidth;
|
|
vNearUp = vUp * flHeight;
|
|
|
|
float flFarWidth = m_flZFar * flTanX;
|
|
float flFarHeight = flFarWidth * flooAspect;
|
|
vFarLeft = vLeft * flFarWidth;
|
|
vFarUp = vUp * flFarHeight;
|
|
}
|
|
|
|
pVertsOut8[0] = vNear + vNearLeft - vNearUp;
|
|
pVertsOut8[1] = vNear - vNearLeft - vNearUp;
|
|
pVertsOut8[2] = vNear + vNearLeft + vNearUp;
|
|
pVertsOut8[3] = vNear - vNearLeft + vNearUp;
|
|
|
|
pVertsOut8[4] = vFar + vFarLeft - vFarUp;
|
|
pVertsOut8[5] = vFar - vFarLeft - vFarUp;
|
|
pVertsOut8[6] = vFar + vFarLeft + vFarUp;
|
|
pVertsOut8[7] = vFar - vFarLeft + vFarUp;
|
|
}
|
|
|
|
void Camera_t::ComputeGeometry( Vector *pVertsOut8 ) const
|
|
{
|
|
Vector vForward, vLeft, vUp;
|
|
AngleVectorsFLU( m_angles, &vForward, &vLeft, &vUp );
|
|
ComputeGeometry( pVertsOut8, vForward, vLeft, vUp );
|
|
}
|
|
|
|
// generates 8 vertices of the frustum as bounds
|
|
void CFrustum::ComputeBounds( Vector *pMins, Vector *pMaxs ) const
|
|
{
|
|
ClearBounds( *pMins, *pMaxs );
|
|
Vector vPts[8];
|
|
m_camera.ComputeGeometry( vPts, m_forward, m_left, m_up );
|
|
|
|
for ( int i = 0; i < 8; i++ )
|
|
{
|
|
AddPointToBounds( vPts[i], *pMins, *pMaxs );
|
|
}
|
|
}
|
|
|
|
static inline void InvertVMatrix( const VMatrix &src, VMatrix &dst )
|
|
{
|
|
src.InverseGeneral( dst );
|
|
}
|
|
|
|
void CFrustum::CalcViewProj()
|
|
{
|
|
m_viewProj = ( m_projection * VMatrix( m_worldToView ) );
|
|
InvertVMatrix( m_viewProj, m_invViewProj );
|
|
InvertVMatrix( m_projection, m_invProjection );
|
|
}
|
|
|
|
float CFrustum::ComputeScreenSize( Vector vecOrigin, float flRadius ) const
|
|
{
|
|
vecOrigin -= GetCameraPosition();
|
|
float flDist = vecOrigin.Length();
|
|
if ( flDist < flRadius )
|
|
{
|
|
return 1.0; // eye inside sphere
|
|
}
|
|
float flSin = sin( DEG2RAD( MIN( GetCameraFOV(), 90.0 ) ) );
|
|
return MIN( 1.0, flSin * ( flRadius / flDist ) );
|
|
|
|
}
|
|
|
|
void CFrustum::ViewToWorld( const Vector2D &vViewMinusOneToOne, Vector *pOutWorld )
|
|
{
|
|
Vector vView3D;
|
|
vView3D.x = vViewMinusOneToOne.x;
|
|
vView3D.y = vViewMinusOneToOne.y;
|
|
vView3D.z = 0;
|
|
|
|
const VMatrix &invViewProjMatrix = GetInvViewProj();
|
|
Vector3DMultiplyPositionProjective( invViewProjMatrix, vView3D, *pOutWorld );
|
|
}
|
|
|
|
void CFrustum::BuildRay( const Vector2D &vViewMinusOneToOne, Vector *pOutRayStart, Vector *pOutRayDirection )
|
|
{
|
|
Vector vClickPoint;
|
|
ViewToWorld( vViewMinusOneToOne, &vClickPoint );
|
|
|
|
if ( !IsOrthographic() )
|
|
{
|
|
Camera_t camera = GetCameraStruct();
|
|
|
|
Vector vRay = vClickPoint - camera.m_origin;
|
|
VectorNormalize( vRay );
|
|
|
|
*pOutRayStart = camera.m_origin;
|
|
*pOutRayDirection = vRay;
|
|
}
|
|
else
|
|
{
|
|
*pOutRayStart = vClickPoint;
|
|
ViewForward( *pOutRayDirection );
|
|
}
|
|
}
|
|
|
|
void CFrustum::BuildFrustumFromParameters(
|
|
const Vector &origin, const QAngle &angles,
|
|
float flNear, float flFar, float flFOV, float flAspect,
|
|
const VMatrix &worldToView, const VMatrix &viewToProj )
|
|
{
|
|
InitCamera( origin, angles, flNear, flFar, flFOV, flAspect );
|
|
|
|
m_worldToView = worldToView.As3x4();
|
|
|
|
VMatrix worldToCamera;
|
|
MatrixMultiply( g_matViewToCameraMatrix, worldToView, worldToCamera );
|
|
VMatrix cameraToWorld;
|
|
MatrixInverseGeneral( worldToCamera, cameraToWorld );
|
|
m_cameraToWorld = cameraToWorld.As3x4();
|
|
|
|
m_projection = viewToProj;
|
|
|
|
CalcViewProj();
|
|
|
|
// forward/left/up - world relative coordinates, assuming an FPS camera sitting on an XY plane, Z is up
|
|
MatrixGetRow( worldToCamera, FORWARD_AXIS, &m_forward );
|
|
MatrixGetRow( worldToCamera, LEFT_AXIS, &m_left );
|
|
MatrixGetRow( worldToCamera, UP_AXIS, &m_up );
|
|
|
|
// Now compute the frustum planes used for culling purposes. These planes are computed assuming the camera is already at the origin.
|
|
VMatrix worldToCamLocalWorld;
|
|
// This is confusing - vShadowCamPos is not negated here, because we need to compensate for the fact that the
|
|
// frustum culling code makes the cam pos the origin before culling by subtracting the camera's origin - so undo it.
|
|
MatrixBuildTranslation( worldToCamLocalWorld, m_camera.m_origin.x, m_camera.m_origin.y, m_camera.m_origin.z );
|
|
|
|
VMatrix worldToCamLocalWorldToView( worldToView * worldToCamLocalWorld );
|
|
VMatrix shadowCamLocalWorldToViewProj( viewToProj * worldToCamLocalWorldToView );
|
|
VPlane pSixPlanes[FRUSTUM_NUMPLANES];
|
|
ExtractClipPlanesFromNonTransposedMatrix( shadowCamLocalWorldToViewProj, pSixPlanes, true );
|
|
|
|
m_frustumStruct.SetPlanes( pSixPlanes );
|
|
|
|
m_bDirty = false;
|
|
|
|
// This should be a no-op (ignoring FP precision) if all the above stuff was done right.
|
|
//m_bDirty = true;
|
|
//UpdateFrustumFromCamera();
|
|
}
|