You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1777 lines
66 KiB
1777 lines
66 KiB
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Directional lighting with cascaded shadow mapping entity.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
|
|
#include "c_env_cascade_light.h"
|
|
|
|
#include "engine/ivdebugoverlay.h"
|
|
#include "materialsystem/imaterialvar.h"
|
|
#include "view_shared.h"
|
|
#include "iviewrender.h"
|
|
#include "c_world.h"
|
|
#include "materialsystem/materialsystem_config.h"
|
|
|
|
#if defined (_PS3 )
|
|
#include <algorithm>
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
//#define CsmDbgMsg Msg
|
|
#define CsmDbgMsg(x)
|
|
|
|
#define MAX_CSM_CASCADES 3
|
|
|
|
ConVar cl_csm_enabled( "cl_csm_enabled", "1", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_max_shadow_dist("cl_csm_max_shadow_dist", ( IsX360() ) ? "350" : IsPS3() ? "250" : "-1", FCVAR_DEVELOPMENTONLY, "" );
|
|
|
|
ConVar cl_csm_capture_state( "cl_csm_capture_state", "0", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_clear_captured_state( "cl_csm_clear_captured_state", "0", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_debug_render_ztest( "cl_csm_debug_render_ztest", "1", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_max_visible_dist("cl_csm_max_visible_dist", "5000", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_debug_vis_lo_range("cl_csm_debug_vis_lo_range", ".35", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_debug_vis_hi_range("cl_csm_debug_vis_hi_range", "1.0", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_use_forced_view_matrices("cl_csm_use_forced_view_matrices", "1", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_debug_2d( "cl_csm_debug_2d", "0", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_debug_3d( "cl_csm_debug_3d", "0", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_debug_culling( "cl_csm_debug_culling", "0", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_debug_culling_cascade( "cl_csm_debug_culling_cascade", "-1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_print_culling_planes( "cl_csm_print_culling_planes", "0", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_viz_numplanes( "cl_csm_viz_numplanes", "-1", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar cl_csm_viz_polyhedron_quad_size( "cl_csm_viz_polyhedron_quad_size", "131072", FCVAR_DEVELOPMENTONLY, "" );
|
|
|
|
ConVar cl_csm_use_env_light_direction( "cl_csm_use_env_light_direction", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_rot_override( "cl_csm_rot_override", "0", FCVAR_DEVELOPMENTONLY );
|
|
|
|
// dust2's angles
|
|
ConVar cl_csm_rot_x( "cl_csm_rot_x", "50", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_rot_y( "cl_csm_rot_y", "43", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_rot_z( "cl_csm_rot_z", "0", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_disable_culling( "cl_csm_disable_culling", "0", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_shadows( "cl_csm_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_entity_shadows( "cl_csm_entity_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_static_prop_shadows( "cl_csm_static_prop_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_world_shadows( "cl_csm_world_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_world_shadows_in_viewmodelcascade( "cl_csm_world_shadows_in_viewmodelcascade", ( IsGameConsole() || IsPlatformOSX() ) ? "0" : "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_sprite_shadows( "cl_csm_sprite_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_rope_shadows( "cl_csm_rope_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_translucent_shadows( "cl_csm_translucent_shadows", ( IsGameConsole() || IsPlatformOSX() )? "0" : "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_translucent_shadows_using_opaque_path( "cl_csm_translucent_shadows_using_opaque_path", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_ignore_disable_shadow_depth_rendering( "cl_csm_ignore_disable_shadow_depth_rendering", "0", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_optimize_static_props( "cl_csm_optimize_static_props", "1", FCVAR_DEVELOPMENTONLY, "Enable/Disable optimal static prop rendering into CSM's (cull static props that make no visual contribution to shadows)" );
|
|
|
|
ConVar cl_csm_viewmodel_shadows( "cl_csm_viewmodel_shadows", "1", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_viewmodel_max_shadow_dist( "cl_csm_viewmodel_max_shadow_dist", "21", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_viewmodel_farz( "cl_csm_viewmodel_farz", ( IsGameConsole() || IsPlatformOSX() ) ? "15" : "30", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_viewmodel_max_visible_dist( "cl_csm_viewmodel_max_visible_dist", "1000", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_slopescaledepthbias_c0( "cl_csm_slopescaledepthbias_c0", ( IsGameConsole() || IsPlatformOSX() ) ? "2" : "1.3", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_slopescaledepthbias_c1( "cl_csm_slopescaledepthbias_c1", IsPlatformOSX() ? "4" : "2", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_slopescaledepthbias_c2( "cl_csm_slopescaledepthbias_c2", IsPlatformOSX() ? "4" : "2", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_slopescaledepthbias_c3( "cl_csm_slopescaledepthbias_c3", "2", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_depthbias_c0( "cl_csm_depthbias_c0", ( IsGameConsole() || IsPlatformOSX() ) ? ".000005" : ".000025", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_depthbias_c1( "cl_csm_depthbias_c1", IsPlatformOSX() ? "2" : ".000025", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_depthbias_c2( "cl_csm_depthbias_c2", IsPlatformOSX() ? "2" : ".000025", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_depthbias_c3( "cl_csm_depthbias_c3", ".000025", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_viewmodel_slopescaledepthbias( "cl_csm_viewmodel_slopescaledepthbias", ( IsGameConsole() || IsPlatformOSX() ) ? "2" : "1.5", FCVAR_DEVELOPMENTONLY );
|
|
ConVar cl_csm_viewmodel_depthbias( "cl_csm_viewmodel_depthbias", ( IsGameConsole() || IsPlatformOSX() ) ? ".000005" : ".00005", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_hack_proj_matrices_for_cull_debugging( "cl_csm_hack_proj_matrices_for_cull_debugging", "0", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_xlat_continuity( "cl_csm_xlat_continuity", "1", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_force_no_csm_in_reflections( "cl_csm_force_no_csm_in_reflections", "0", FCVAR_DEVELOPMENTONLY );
|
|
|
|
ConVar cl_csm_cull_small_prop_threshold_volume( "cl_csm_cull_small_prop_threshold_volume", "2000.0f ", FCVAR_DEVELOPMENTONLY );
|
|
|
|
void CC_CSM_Status( const CCommand& args );
|
|
static ConCommand cl_csm_status("cl_csm_status", CC_CSM_Status, "Usage:\n cl_csm_status\n", 0);
|
|
|
|
CCascadeLightManager g_CascadeLightManager;
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT(C_CascadeLight, DT_CascadeLight, CCascadeLight)
|
|
RecvPropVector(RECVINFO(m_shadowDirection)),
|
|
RecvPropVector(RECVINFO(m_envLightShadowDirection)),
|
|
RecvPropBool(RECVINFO(m_bEnabled)),
|
|
RecvPropBool(RECVINFO(m_bUseLightEnvAngles)),
|
|
RecvPropInt(RECVINFO(m_LightColor), 0, RecvProxy_Int32ToColor32),
|
|
RecvPropInt(RECVINFO(m_LightColorScale), 0, RecvProxy_Int32ToInt32 ),
|
|
RecvPropFloat(RECVINFO(m_flMaxShadowDist) )
|
|
END_RECV_TABLE()
|
|
|
|
LINK_ENTITY_TO_CLASS(env_cascade_light, C_CascadeLight);
|
|
|
|
C_CascadeLight *C_CascadeLight::m_pCascadeLight;
|
|
|
|
C_CascadeLight::C_CascadeLight() :
|
|
C_BaseEntity(),
|
|
m_shadowDirection( 0, 0, -1 ),
|
|
m_envLightShadowDirection( 0, 0, -1 ),
|
|
m_bEnabled( false ),
|
|
m_bUseLightEnvAngles( true ),
|
|
m_flMaxShadowDist( 400.0f )
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::C_CascadeLight\n" );
|
|
|
|
m_LightColor.r = 255;
|
|
m_LightColor.g = 255;
|
|
m_LightColor.b = 255;
|
|
m_LightColor.a = 255;
|
|
m_LightColorScale = 255;
|
|
}
|
|
|
|
C_CascadeLight::~C_CascadeLight()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::~C_CascadeLight\n" );
|
|
}
|
|
|
|
void C_CascadeLight::Spawn()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::Spawn\n" );
|
|
|
|
BaseClass::Spawn();
|
|
|
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
|
|
|
m_pCascadeLight = this;
|
|
}
|
|
|
|
void C_CascadeLight::Release()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::Release\n" );
|
|
|
|
m_pCascadeLight = NULL;
|
|
|
|
BaseClass::Release();
|
|
}
|
|
|
|
bool C_CascadeLight::ShouldDraw()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void C_CascadeLight::ClientThink()
|
|
{
|
|
VPROF("C_CascadeLight::ClientThink");
|
|
|
|
BaseClass::ClientThink();
|
|
}
|
|
|
|
// 4 cascades plus 1 for the scene frustum itself
|
|
static Vector g_vCascadeFrustumColors[ 4 + 1 ] =
|
|
{
|
|
Vector( 0, 1, 0 ),
|
|
Vector( 0, 0, 1 ),
|
|
Vector( 0, 1, 1 ),
|
|
Vector( 1, 0, 0 ),
|
|
Vector( .85f, .85f, .2f )
|
|
};
|
|
|
|
void CCascadeLightManager::RotXPlusDown( const CCommand &args ) { g_CascadeLightManager.m_flRotX[0] = 1.0f; }
|
|
void CCascadeLightManager::RotXPlusUp( const CCommand &args ) { g_CascadeLightManager.m_flRotX[0] = 0.0f; }
|
|
void CCascadeLightManager::RotXNegDown( const CCommand &args ) { g_CascadeLightManager.m_flRotX[1] = -1.0f; }
|
|
void CCascadeLightManager::RotXNegUp( const CCommand &args ) { g_CascadeLightManager.m_flRotX[1] = 0.0f; }
|
|
void CCascadeLightManager::RotYPlusDown( const CCommand &args ) { g_CascadeLightManager.m_flRotY[0] = 1.0f; }
|
|
void CCascadeLightManager::RotYPlusUp( const CCommand &args ) { g_CascadeLightManager.m_flRotY[0] = 0.0f; }
|
|
void CCascadeLightManager::RotYNegDown( const CCommand &args ) { g_CascadeLightManager.m_flRotY[1] = -1.0f; }
|
|
void CCascadeLightManager::RotYNegUp( const CCommand &args ) { g_CascadeLightManager.m_flRotY[1] = 0.0f; }
|
|
|
|
static ConCommand start_csm_rot_x_plus( "+csm_rot_x_plus", CCascadeLightManager::RotXPlusDown);
|
|
static ConCommand end_csm_rot_x_plus( "-csm_rot_x_plus", CCascadeLightManager::RotXPlusUp);
|
|
static ConCommand start_csm_rot_x_neg( "+csm_rot_x_neg", CCascadeLightManager::RotXNegDown);
|
|
static ConCommand end_csm_rot_x_neg( "-csm_rot_x_neg", CCascadeLightManager::RotXNegUp);
|
|
static ConCommand start_csm_rot_y_plus( "+csm_rot_y_plus", CCascadeLightManager::RotYPlusDown);
|
|
static ConCommand end_csm_rot_y_plus( "-csm_rot_y_plus", CCascadeLightManager::RotYPlusUp);
|
|
static ConCommand start_csm_rot_y_neg( "+csm_rot_y_neg", CCascadeLightManager::RotYNegDown);
|
|
static ConCommand end_csm_rot_y_neg( "-csm_rot_y_neg", CCascadeLightManager::RotYNegUp);
|
|
|
|
static void DebugRenderWireframeFrustum3D( const VMatrix &xform, const Vector &color, bool bDepthTest = false )
|
|
{
|
|
for ( uint i = 0; i < CCSMFrustumDefinition::NUM_FRUSTUM_LINES; ++i )
|
|
{
|
|
Vector4D s, e;
|
|
|
|
xform.V4Mul( CCSMFrustumDefinition::g_vProjFrustumVerts[ CCSMFrustumDefinition::g_nFrustumLineVertIndices[ i * 2 + 0 ] ], s );
|
|
xform.V4Mul( CCSMFrustumDefinition::g_vProjFrustumVerts[ CCSMFrustumDefinition::g_nFrustumLineVertIndices[ i * 2 + 1 ] ], e );
|
|
|
|
s *= ( 1.0f / s.w );
|
|
e *= ( 1.0f / e.w );
|
|
|
|
NDebugOverlay::Line( Vector( s.x, s.y, s.z ), Vector( e.x, e.y, e.z ), (int)(color.x * 255.0f), (int)(color.y * 255.0f), (int)(color.z * 255.0f), !bDepthTest, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
|
}
|
|
}
|
|
|
|
static void Add3DLineOverlay( const Vector &s, const Vector &e, const Vector &color, bool bDepthTest )
|
|
{
|
|
NDebugOverlay::Line( s, e, (int)(color.x * 255.0f), (int)(color.y * 255.0f), (int)(color.z * 255.0f), !bDepthTest, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
|
}
|
|
|
|
#if defined(_X360)
|
|
template <class T> void _swap ( T& a, T& b )
|
|
{
|
|
T c(a); a=b; b=c;
|
|
}
|
|
#endif
|
|
|
|
static void DebugRenderConvexPolyhedron( const Vector4D *pPlanes, uint nNumPlanes, const Vector &color, bool zTest )
|
|
{
|
|
#ifdef _DEBUG
|
|
for ( uint nPlaneIndex = 0; nPlaneIndex < nNumPlanes; ++nPlaneIndex )
|
|
{
|
|
float l = pPlanes[nPlaneIndex].x*pPlanes[nPlaneIndex].x + pPlanes[nPlaneIndex].y*pPlanes[nPlaneIndex].y + pPlanes[nPlaneIndex].z*pPlanes[nPlaneIndex].z;
|
|
l = sqrt( l );
|
|
Assert( (fabs( l ) - 1.0f) < .00125f );
|
|
}
|
|
#endif
|
|
|
|
uint nLastPlane = cl_csm_viz_numplanes.GetInt();
|
|
if ( nLastPlane < 0 )
|
|
nLastPlane = nNumPlanes;
|
|
nLastPlane = MIN( nLastPlane, nNumPlanes );
|
|
|
|
const float Q = cl_csm_viz_polyhedron_quad_size.GetFloat();
|
|
|
|
for ( uint nPlaneIndex = 0; nPlaneIndex < nLastPlane; ++nPlaneIndex )
|
|
{
|
|
Vector4D plane( pPlanes[nPlaneIndex] );
|
|
|
|
Vector vNormal( plane.x, plane.y, plane.z );
|
|
float flDist = -plane.w;
|
|
|
|
Vector vOrigin( vNormal * flDist );
|
|
|
|
uint nMinorAxis = 0;
|
|
float flMag = fabs( vNormal.x );
|
|
if ( fabs( vNormal.y ) < flMag ) { flMag = fabs( vNormal.y ); nMinorAxis = 1; }
|
|
if ( fabs( vNormal.z ) < flMag ) { flMag = fabs( vNormal.z ); nMinorAxis = 2; }
|
|
|
|
Vector vU( 0.0f, 0.0f, 0.0f );
|
|
vU.Base()[nMinorAxis] = 1.0f;
|
|
|
|
float flDirAlong = vU.Dot( vNormal );
|
|
|
|
vU -= flDirAlong * vNormal;
|
|
vU.NormalizeInPlace();
|
|
|
|
Vector vV( vNormal.Cross( vU ) );
|
|
vV.NormalizeInPlace();
|
|
|
|
double verts[2][64][3];
|
|
|
|
verts[0][0][0] = vOrigin.x - vU.x * Q - vV.x * Q;
|
|
verts[0][0][1] = vOrigin.y - vU.y * Q - vV.y * Q;
|
|
verts[0][0][2] = vOrigin.z - vU.z * Q - vV.z * Q;
|
|
|
|
verts[0][1][0] = vOrigin.x + vU.x * Q - vV.x * Q;
|
|
verts[0][1][1] = vOrigin.y + vU.y * Q - vV.y * Q;
|
|
verts[0][1][2] = vOrigin.z + vU.z * Q - vV.z * Q;
|
|
|
|
verts[0][2][0] = vOrigin.x + vU.x * Q + vV.x * Q;
|
|
verts[0][2][1] = vOrigin.y + vU.y * Q + vV.y * Q;
|
|
verts[0][2][2] = vOrigin.z + vU.z * Q + vV.z * Q;
|
|
|
|
verts[0][3][0] = vOrigin.x - vU.x * Q + vV.x * Q;
|
|
verts[0][3][1] = vOrigin.y - vU.y * Q + vV.y * Q;
|
|
verts[0][3][2] = vOrigin.z - vU.z * Q + vV.z * Q;
|
|
|
|
uint nVerts = 4;
|
|
double *pSrcVerts = &verts[0][0][0];
|
|
double *pDstVerts = &verts[1][0][0];
|
|
|
|
for ( uint nClipPlaneIndex = 0; nClipPlaneIndex < nLastPlane; ++nClipPlaneIndex )
|
|
{
|
|
if ( nPlaneIndex == nClipPlaneIndex )
|
|
continue;
|
|
|
|
Vector4D clipPlane( pPlanes[nClipPlaneIndex] );
|
|
|
|
double vClipNormal[3] = { clipPlane.x, clipPlane.y, clipPlane.z };
|
|
double flClipDist = -clipPlane.w;
|
|
|
|
int nClipped = ClipPolyToPlane_Precise( pSrcVerts, nVerts, pDstVerts, vClipNormal, flClipDist, .000000125f );
|
|
|
|
nVerts = nClipped;
|
|
#if defined(_X360)
|
|
_swap( pSrcVerts, pDstVerts );
|
|
#else
|
|
std::swap( pSrcVerts, pDstVerts );
|
|
#endif
|
|
if ( nVerts < 3 )
|
|
break;
|
|
}
|
|
|
|
if ( nVerts >= 3 )
|
|
{
|
|
Vector vAvg( 0.0f, 0.0f, 0.0f );
|
|
for ( uint i = 0; i < nVerts; ++i )
|
|
{
|
|
const uint j = ( i + 1 ) % nVerts;
|
|
Vector s( pSrcVerts[i*3+0], pSrcVerts[i*3+1], pSrcVerts[i*3+2] );
|
|
Vector e( pSrcVerts[j*3+0], pSrcVerts[j*3+1], pSrcVerts[j*3+2] );
|
|
|
|
Add3DLineOverlay( s, e, color, zTest );
|
|
vAvg += s;
|
|
}
|
|
|
|
vAvg /= nVerts;
|
|
|
|
Vector2D vLo( 1e+10f, 1e+10f );
|
|
Vector2D vHi( -1e+10f, -1e+10f );
|
|
for ( uint i = 0; i < nVerts; ++i )
|
|
{
|
|
Vector p( pSrcVerts[i*3+0], pSrcVerts[i*3+1], pSrcVerts[i*3+2] );
|
|
|
|
p -= vAvg;
|
|
Vector2D p2D( p.Dot( vU ), p.Dot( vV ) );
|
|
|
|
vLo = vLo.Min( p2D );
|
|
vHi = vHi.Max( p2D );
|
|
}
|
|
|
|
float L = 75.0f;
|
|
Add3DLineOverlay( vAvg, vAvg + vNormal * L, Vector( .1f, .1f, 1.0f ), true );
|
|
Add3DLineOverlay( vAvg, vAvg + vU * L, Vector( 1.0f, .1f, .1f ), true );
|
|
Add3DLineOverlay( vAvg, vAvg + vV * L, Vector( 0.1f, 1.0f, .1f ), true );
|
|
|
|
float flXD = ( vHi.x - vLo.x ) / 24.0f;
|
|
float flYD = ( vHi.y - vLo.y ) / 24.0f;
|
|
|
|
for ( float flX = vLo.x; flX < vHi.x; flX += flXD )
|
|
{
|
|
for ( float flY = vLo.y; flY < vHi.y; flY += flYD )
|
|
{
|
|
Vector p( vAvg + vU * flX + vV * flY );
|
|
|
|
uint i;
|
|
for ( i = 0; i < nLastPlane; ++i )
|
|
{
|
|
if ( i == nPlaneIndex )
|
|
continue;
|
|
float flDist = pPlanes[i].x * p.x + pPlanes[i].y * p.y + pPlanes[i].z * p.z + pPlanes[i].w;
|
|
if ( flDist < -.25f )
|
|
break;
|
|
}
|
|
if ( i == nLastPlane )
|
|
{
|
|
Add3DLineOverlay( p, p + vNormal * 4.0f, Vector( .1f, .1f, 1.0f ), true );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static void ScreenText( int x, int y, int r, int g, int b, int a, const char *text, ...)
|
|
{
|
|
char buf[256];
|
|
va_list args;
|
|
va_start( args, text );
|
|
V_vsnprintf( buf, sizeof( buf ), text, args );
|
|
va_end( args );
|
|
|
|
const float flTextColWidth = 0.0044f; // 227
|
|
const float flTextRowHeight = 0.0083f; // 120
|
|
NDebugOverlay::ScreenText( x * flTextColWidth, y * flTextRowHeight, buf, r, g, b, a, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
|
}
|
|
|
|
CDebugPrimRenderer2D::CDebugPrimRenderer2D()
|
|
{
|
|
m_debugLines.EnsureCapacity( 128 );
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::Clear()
|
|
{
|
|
m_debugLines.SetCount( 0 );
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::AddNormalizedLine2D( float sx, float sy, float ex, float ey, uint r, uint g, uint b )
|
|
{
|
|
int nIndex = m_debugLines.AddToTail();
|
|
m_debugLines[nIndex].m_EndPoints[0].Init( sx, sy );
|
|
m_debugLines[nIndex].m_EndPoints[1].Init( ex, ey );
|
|
m_debugLines[nIndex].m_nColor[0] = r;
|
|
m_debugLines[nIndex].m_nColor[1] = g;
|
|
m_debugLines[nIndex].m_nColor[2] = b;
|
|
m_debugLines[nIndex].m_nColor[3] = 255;
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::AddScreenspaceLine2D( float sx, float sy, float ex, float ey, uint r, uint g, uint b )
|
|
{
|
|
int nBackBufWidth, nBackBufHeight;
|
|
materials->GetBackBufferDimensions( nBackBufWidth, nBackBufHeight );
|
|
float flInvBackBufWidth = 1.0f / nBackBufWidth, flInvBackBufHeight = 1.0f / nBackBufHeight;
|
|
|
|
int nIndex = m_debugLines.AddToTail();
|
|
m_debugLines[nIndex].m_EndPoints[0].Init( sx * flInvBackBufWidth, sy * flInvBackBufHeight );
|
|
m_debugLines[nIndex].m_EndPoints[1].Init( ex * flInvBackBufWidth, ey * flInvBackBufHeight );
|
|
m_debugLines[nIndex].m_nColor[0] = r;
|
|
m_debugLines[nIndex].m_nColor[1] = g;
|
|
m_debugLines[nIndex].m_nColor[2] = b;
|
|
m_debugLines[nIndex].m_nColor[3] = 255;
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::AddScreenspaceRect2D( float sx, float sy, float ex, float ey, uint r, uint g, uint b )
|
|
{
|
|
AddScreenspaceLine2D( sx, sy, ex, sy, r, g, b );
|
|
AddScreenspaceLine2D( ex, sy, ex, ey, r, g, b );
|
|
AddScreenspaceLine2D( ex, ey, sx, ey, r, g, b );
|
|
AddScreenspaceLine2D( sx, ey, sx, sy, r, g, b );
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::AddScreenspaceLineList2D( uint nCount, const Vector2D *pVerts, const VertexColor_t &color )
|
|
{
|
|
int nBackBufWidth, nBackBufHeight;
|
|
materials->GetBackBufferDimensions( nBackBufWidth, nBackBufHeight );
|
|
float flInvBackBufWidth = 1.0f / nBackBufWidth, flInvBackBufHeight = 1.0f / nBackBufHeight;
|
|
|
|
for ( uint i = 0; i < nCount; ++i )
|
|
{
|
|
int nIndex = m_debugLines.AddToTail();
|
|
m_debugLines[nIndex].m_EndPoints[0].Init( pVerts[0].x * flInvBackBufWidth, pVerts[0].y * flInvBackBufHeight );
|
|
m_debugLines[nIndex].m_EndPoints[1].Init( pVerts[1].x * flInvBackBufWidth, pVerts[1].y * flInvBackBufHeight );
|
|
m_debugLines[nIndex].m_nColor[0] = color.r;
|
|
m_debugLines[nIndex].m_nColor[1] = color.g;
|
|
m_debugLines[nIndex].m_nColor[2] = color.b;
|
|
m_debugLines[nIndex].m_nColor[3] = 255;
|
|
|
|
pVerts += 2;
|
|
}
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::RenderScreenspaceDepthTexture( float sx, float sy, float ex, float ey, float su, float sv, float eu, float ev, CTextureReference &depthTex, float zLo, float zHi )
|
|
{
|
|
int nBackBufWidth, nBackBufHeight;
|
|
materials->GetBackBufferDimensions( nBackBufWidth, nBackBufHeight );
|
|
float flInvBackBufWidth = 1.0f / nBackBufWidth, flInvBackBufHeight = 1.0f / nBackBufHeight;
|
|
|
|
sx *= flInvBackBufWidth;
|
|
sy *= flInvBackBufHeight;
|
|
ex *= flInvBackBufWidth;
|
|
ey *= flInvBackBufHeight;
|
|
|
|
const float xl = -1.0f;
|
|
const float yl = 1.0f;
|
|
const float xh = 1.0f;
|
|
const float yh = -1.0f;
|
|
|
|
IMaterial *pMaterial = materials->FindMaterial( "debug/debugshadowbuffer", TEXTURE_GROUP_OTHER, true );
|
|
if ( !pMaterial )
|
|
return;
|
|
|
|
float flInvZRange = 1.0f / ( zHi - zLo );
|
|
|
|
IMaterialVar *c0_x = pMaterial->FindVar( "$c0_x", NULL, false );
|
|
c0_x->SetFloatValue( flInvZRange );
|
|
|
|
IMaterialVar *c0_y = pMaterial->FindVar( "$c0_y", NULL, false );
|
|
c0_y->SetFloatValue( -zLo * flInvZRange );
|
|
|
|
IMaterialVar *c0_z = pMaterial->FindVar( "$c0_z", NULL, false );
|
|
c0_z->SetFloatValue( 0.0f );
|
|
|
|
bool bFound = false;
|
|
IMaterialVar *pMatVar = pMaterial->FindVar( "$basetexture", &bFound, false );
|
|
if ( bFound && pMatVar )
|
|
{
|
|
if ( pMatVar->GetTextureValue() != depthTex )
|
|
{
|
|
pMatVar->SetTextureValue( depthTex );
|
|
}
|
|
}
|
|
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
pRenderContext->Bind( pMaterial );
|
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
|
|
|
|
CMeshBuilder meshBuilder;
|
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
|
|
|
|
meshBuilder.Position3f( Lerp( sx, xl, xh ), Lerp( sy, yl, yh ), 0.0f );
|
|
meshBuilder.TexCoord2f( 0, su, sv );
|
|
meshBuilder.Color4ub( 255, 255, 255, 255);
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Position3f( Lerp( ex, xl, xh ), Lerp( sy, yl, yh ), 0.0f );
|
|
meshBuilder.TexCoord2f( 0, eu, sv );
|
|
meshBuilder.Color4ub( 255, 255, 255, 255);
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Position3f( Lerp( ex, xl, xh ), Lerp( ey, yl, yh ), 0.0f );
|
|
meshBuilder.TexCoord2f( 0, eu, ev );
|
|
meshBuilder.Color4ub( 255, 255, 255, 255);
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Position3f( Lerp( sx, xl, xh ), Lerp( ey, yl, yh ), 0.0f );
|
|
meshBuilder.TexCoord2f( 0, su, ev );
|
|
meshBuilder.Color4ub( 255, 255, 255, 255);
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.End();
|
|
pMesh->Draw();
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::Render2D( )
|
|
{
|
|
if ( m_debugLines.Count() )
|
|
{
|
|
RenderDebugLines2D( m_debugLines.Count(), &m_debugLines[0] );
|
|
}
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::RenderDebugLines2D( uint nNumLines, const CDebugLine *pLines )
|
|
{
|
|
if ( !nNumLines )
|
|
return;
|
|
|
|
const float xl = -1.0f;
|
|
const float yl = 1.0f;
|
|
const float xh = 1.0f;
|
|
const float yh = -1.0f;
|
|
|
|
IMaterial *pMaterial = materials->FindMaterial( "debug/debugscreenspacewireframe", TEXTURE_GROUP_OTHER, true );
|
|
if ( !pMaterial )
|
|
return;
|
|
|
|
IMaterialVar *c0_x = pMaterial->FindVar( "$c0_x", NULL, false );
|
|
c0_x->SetFloatValue( 1.0f );
|
|
|
|
IMaterialVar *c0_y = pMaterial->FindVar( "$c0_y", NULL, false );
|
|
c0_y->SetFloatValue( 1.0f );
|
|
|
|
IMaterialVar *c0_z = pMaterial->FindVar( "$c0_z", NULL, false );
|
|
c0_z->SetFloatValue( 1.0f );
|
|
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
pRenderContext->Bind( pMaterial );
|
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
|
|
|
|
CMeshBuilder meshBuilder;
|
|
meshBuilder.Begin( pMesh, MATERIAL_LINES, nNumLines );
|
|
|
|
for ( uint i = 0; i < nNumLines; ++i )
|
|
{
|
|
const CDebugLine &debugLine = pLines[i];
|
|
|
|
meshBuilder.Position3f( Lerp( debugLine.m_EndPoints[0].x, xl, xh ), Lerp( debugLine.m_EndPoints[0].y, yl, yh ), 0.0f );
|
|
meshBuilder.Color4ub( debugLine.m_nColor[0], debugLine.m_nColor[1], debugLine.m_nColor[2], 255 );
|
|
meshBuilder.AdvanceVertex();
|
|
|
|
meshBuilder.Position3f( Lerp( debugLine.m_EndPoints[1].x, xl, xh ), Lerp( debugLine.m_EndPoints[1].y, yl, yh ), 0.0f );
|
|
meshBuilder.Color4ub( debugLine.m_nColor[0], debugLine.m_nColor[1], debugLine.m_nColor[2], 255 );
|
|
meshBuilder.AdvanceVertex();
|
|
}
|
|
|
|
meshBuilder.End();
|
|
pMesh->Draw();
|
|
}
|
|
|
|
void CDebugPrimRenderer2D::AddScreenspaceWireframeFrustum2D( const VMatrix &xform, const VertexColor_t &color, bool bShowAxes )
|
|
{
|
|
Vector2D points[ CCSMFrustumDefinition::NUM_FRUSTUM_LINES * 2 ];
|
|
Vector2D *pDstPoint = points;
|
|
|
|
for ( uint i = 0; i < CCSMFrustumDefinition::NUM_FRUSTUM_LINES; ++i )
|
|
{
|
|
Vector4D s, e;
|
|
|
|
xform.V4Mul( CCSMFrustumDefinition::g_vProjFrustumVerts[ CCSMFrustumDefinition::g_nFrustumLineVertIndices[ i * 2 + 0 ] ], s );
|
|
xform.V4Mul( CCSMFrustumDefinition::g_vProjFrustumVerts[ CCSMFrustumDefinition::g_nFrustumLineVertIndices[ i * 2 + 1 ] ], e );
|
|
|
|
s *= ( 1.0f / s.w );
|
|
e *= ( 1.0f / e.w );
|
|
|
|
pDstPoint[0].Init( s.x, s.y );
|
|
pDstPoint[1].Init( e.x, e.y );
|
|
pDstPoint += 2;
|
|
}
|
|
|
|
AddScreenspaceLineList2D( CCSMFrustumDefinition::NUM_FRUSTUM_LINES, points, color );
|
|
|
|
if ( bShowAxes )
|
|
{
|
|
Vector4D s, e;
|
|
xform.V4Mul( Vector4D( 0.0f, 0.0f, 0.0f, 1.0f ), s );
|
|
xform.V4Mul( Vector4D( 0.0f, 0.0f, 0.95f, 1.0f ), e );
|
|
s *= ( 1.0f / s.w );
|
|
e *= ( 1.0f / e.w );
|
|
points[0].Init( s.x, s.y );
|
|
points[1].Init( e.x, e.y );
|
|
AddScreenspaceLineList2D( 1, points, VertexColor_t( 20, 20, 255, 255 ) );
|
|
|
|
xform.V4Mul( Vector4D( 0.0f, 0.0f, 0.0f, 1.0f ), s );
|
|
xform.V4Mul( Vector4D( 0.0f, 15.0f, 0.0f, 1.0f ), e );
|
|
s *= ( 1.0f / s.w );
|
|
e *= ( 1.0f / e.w );
|
|
points[0].Init( s.x, s.y );
|
|
points[1].Init( e.x, e.y );
|
|
AddScreenspaceLineList2D( 1, points, VertexColor_t( 20, 255, 20, 255 ) );
|
|
|
|
xform.V4Mul( Vector4D( 0.0f, 0.0f, 0.0f, 1.0f ), s );
|
|
xform.V4Mul( Vector4D( 15.0f, 0.0f, 0.0f, 1.0f ), e );
|
|
s *= ( 1.0f / s.w );
|
|
e *= ( 1.0f / e.w );
|
|
points[0].Init( s.x, s.y );
|
|
points[1].Init( e.x, e.y );
|
|
AddScreenspaceLineList2D( 1, points, VertexColor_t( 255, 20, 20, 255 ) );
|
|
}
|
|
}
|
|
|
|
// TODO: Break CCascadeLightManager out to a separate file?
|
|
|
|
#if defined( _X360 )
|
|
#define CSM_DEFAULT_DEPTH_TEXTURE_RESOLUTION 704*2
|
|
#elif defined( _PS3 )
|
|
#define CSM_DEFAULT_DEPTH_TEXTURE_RESOLUTION 640*2
|
|
#else
|
|
// Important note: On PC, this depth texture resolution (or the inverse of it) is effectively hardcoded into the filter kernels sample offsets. Don't change it unless you fix this dependency.
|
|
#define CSM_DEFAULT_DEPTH_TEXTURE_RESOLUTION 1024*2
|
|
#define CSM_FALLBACK_DEPTH_TEXTURE_RESOLUTION 768*2
|
|
#define CSM_FALLBACK2_DEPTH_TEXTURE_RESOLUTION 640*2
|
|
#endif
|
|
|
|
CCascadeLightManager::CCascadeLightManager() :
|
|
m_bRenderTargetsAllocated( false ),
|
|
m_nDepthTextureResolution( CSM_DEFAULT_DEPTH_TEXTURE_RESOLUTION ),
|
|
m_bCSMIsActive( false ),
|
|
m_bStateIsValid( false ),
|
|
m_nCurRenderTargetQualityMode( CSMQUALITY_HIGH )
|
|
{
|
|
V_memset( &m_flRotX, 0, sizeof( m_flRotX ) );
|
|
V_memset( &m_flRotY, 0, sizeof( m_flRotY ) );
|
|
|
|
m_curState.m_CSMParallelSplit.Init( m_nDepthTextureResolution / 2, MAX_SUN_LIGHT_SHADOW_CASCADE_SIZE );
|
|
m_curViewModelState.m_CSMParallelSplit.Init( m_nDepthTextureResolution / 2, MAX_SUN_LIGHT_SHADOW_CASCADE_SIZE );
|
|
}
|
|
|
|
CCascadeLightManager::~CCascadeLightManager()
|
|
{
|
|
}
|
|
|
|
#ifdef OSX
|
|
|
|
static ConVar mat_osx_force_csm_enabled( "mat_osx_force_csm_enabled", "0", FCVAR_RELEASE );
|
|
|
|
static bool OSX_HardwareGoodEnoughForCSMs()
|
|
{
|
|
if ( IsPlatformOSX() )
|
|
{
|
|
// Historically, CS:GO did not have CSMs or multicore rendering on Mac. Both features are
|
|
// available on Mac post the Sep 2014 Linux port integration, but multicore is not enough
|
|
// to absorb the perf hit of CSMs on low end Macs. This function identifies the Macs on
|
|
// which we do not want to enable CSMs, those that satisfy the following properties:
|
|
// 1. lowend GPU identified in CShaderDeviceMgrBase::ReadHardwareCaps by setting
|
|
// the convar mat_osx_csm_enabled to false;
|
|
// 2. CPU has four or less logical processors (and less than 2.6GHz recorded clock speed)
|
|
|
|
if ( mat_osx_force_csm_enabled.GetBool() )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool bGoodEnough = true;
|
|
|
|
// Check GPU
|
|
static ConVarRef mat_osx_csm_enabled( "mat_osx_csm_enabled" );
|
|
if ( !mat_osx_csm_enabled.GetBool() )
|
|
{
|
|
// GPU not good enough
|
|
//printf("CSM: GPU matched string \"%s\", not good enough\n");
|
|
bGoodEnough = false;
|
|
}
|
|
|
|
// Check CPU
|
|
CPUInformation const& cpuInfo = GetCPUInformation();
|
|
|
|
//printf( "CSM: CPU has %d logical processors\n", cpuInfo.m_nLogicalProcessors );
|
|
|
|
if ( cpuInfo.m_nLogicalProcessors <= 4 )
|
|
{
|
|
// allow if clock speed is >= 2.6GHz, observing the list of Mac CPU's since Jan '08, this will now enable CSM's
|
|
// on most Mac Pro's and iMacs (those that have the ability for GPU's to pass the test above, but would have been excluded due to logical processor count).
|
|
if ( ( (double)cpuInfo.m_Speed / 1000000000.0 ) < 2.6 )
|
|
{
|
|
//printf("CSM: CPU cores not enough\n");
|
|
bGoodEnough = false;
|
|
}
|
|
}
|
|
|
|
return bGoodEnough;
|
|
}
|
|
else
|
|
{
|
|
// Platform other than OSX
|
|
Assert( 0 );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
bool CCascadeLightManager::InitRenderTargets()
|
|
{
|
|
VPROF_BUDGET( "CCascadeLightManager::InitRenderTargets", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
|
|
|
|
CsmDbgMsg( "C_CascadeLight::InitRenderTargets\n" );
|
|
|
|
if (
|
|
#ifdef OSX
|
|
!OSX_HardwareGoodEnoughForCSMs() ||
|
|
#endif
|
|
!cl_csm_enabled.GetBool() ||
|
|
!g_pMaterialSystemHardwareConfig->SupportsCascadedShadowMapping() ||
|
|
!g_pMaterialSystemHardwareConfig->SupportsShadowDepthTextures()
|
|
)
|
|
{
|
|
DeinitRenderTargets();
|
|
|
|
cl_csm_enabled.SetValue( 0 );
|
|
return false;
|
|
}
|
|
|
|
if ( m_bRenderTargetsAllocated )
|
|
return true;
|
|
|
|
m_bRenderTargetsAllocated = true;
|
|
|
|
ImageFormat dstFormat = g_pMaterialSystemHardwareConfig->GetShadowDepthTextureFormat(); // Vendor-dependent depth texture format
|
|
#ifndef _X360
|
|
ImageFormat nullFormat = g_pMaterialSystemHardwareConfig->GetNullTextureFormat(); // Vendor-dependent null texture format (takes as little memory as possible)
|
|
#endif
|
|
|
|
RenderTargetSizeMode_t sizeMode = RT_SIZE_OFFSCREEN;
|
|
|
|
// Don't allow the shadow buffer render target's to get resized to always be <= the size of the backbuffer on the PC.
|
|
// This allows us to use 1024x1024 or larger shadow depth buffers when 1024x768 backbuffers, for example.
|
|
sizeMode = RT_SIZE_NO_CHANGE;
|
|
|
|
m_nCurRenderTargetQualityMode = GetCSMQualityMode();
|
|
|
|
if( !( IsGameConsole() ) )
|
|
{
|
|
m_nDepthTextureResolution = CSM_DEFAULT_DEPTH_TEXTURE_RESOLUTION;
|
|
if ( GetCSMQualityMode() == CSMQUALITY_VERY_LOW )
|
|
{
|
|
m_nDepthTextureResolution = CSM_FALLBACK2_DEPTH_TEXTURE_RESOLUTION;
|
|
}
|
|
else if ( GetCSMQualityMode() == CSMQUALITY_LOW )
|
|
{
|
|
m_nDepthTextureResolution = CSM_FALLBACK_DEPTH_TEXTURE_RESOLUTION;
|
|
}
|
|
}
|
|
|
|
m_curState.m_CSMParallelSplit.Init( m_nDepthTextureResolution / 2, MAX_SUN_LIGHT_SHADOW_CASCADE_SIZE );
|
|
m_curViewModelState.m_CSMParallelSplit.Init( m_nDepthTextureResolution / 2, MAX_SUN_LIGHT_SHADOW_CASCADE_SIZE );
|
|
|
|
materials->BeginRenderTargetAllocation();
|
|
|
|
#if defined(_PS3)
|
|
|
|
m_DummyColorTexture.InitRenderTarget( 8, 8, sizeMode, nullFormat,
|
|
MATERIAL_RT_DEPTH_NONE, false, "_rt_CSMDummy" );
|
|
m_ShadowDepthTexture.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, sizeMode, dstFormat,
|
|
MATERIAL_RT_DEPTH_NONE, false, "_rt_CSMShadowDepth" );
|
|
|
|
#elif defined(_X360)
|
|
|
|
// For the 360, we'll be rendering depth directly into the dummy depth and Resolve()ing to the depth texture.
|
|
// only need the dummy surface, don't care about color results
|
|
m_DummyColorTexture.InitRenderTargetTexture( m_nDepthTextureResolution/2, m_nDepthTextureResolution/2, RT_SIZE_OFFSCREEN, IMAGE_FORMAT_BGR565, //IMAGE_FORMAT_BGRA8888,
|
|
MATERIAL_RT_DEPTH_SHARED, false, "_rt_CSMShadowDummy", CREATERENDERTARGETFLAGS_ALIASCOLORANDDEPTHSURFACES );
|
|
m_DummyColorTexture.InitRenderTargetSurface( m_nDepthTextureResolution/2, m_nDepthTextureResolution/2, IMAGE_FORMAT_BGR565, false );
|
|
|
|
m_ShadowDepthTexture.InitRenderTargetTexture( m_nDepthTextureResolution, m_nDepthTextureResolution, RT_SIZE_OFFSCREEN,
|
|
dstFormat, MATERIAL_RT_DEPTH_NONE, false, "_rt_CSMShadowDepth" );
|
|
m_ShadowDepthTexture.InitRenderTargetSurface( 1, 1, dstFormat, false );
|
|
|
|
#else
|
|
|
|
m_DummyColorTexture.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, sizeMode, nullFormat,
|
|
MATERIAL_RT_DEPTH_NONE, false, "_rt_CSMShadowDummy" );
|
|
m_ShadowDepthTexture.InitRenderTarget( m_nDepthTextureResolution, m_nDepthTextureResolution, sizeMode, dstFormat,
|
|
MATERIAL_RT_DEPTH_NONE, false, "_rt_CSMShadowDepth" );
|
|
|
|
#endif
|
|
|
|
materials->EndRenderTargetAllocation();
|
|
|
|
if ( ( !m_DummyColorTexture.IsValid() ) && ( !m_ShadowDepthTexture.IsValid() ) )
|
|
{
|
|
DeinitRenderTargets();
|
|
|
|
cl_csm_enabled.SetValue(0);
|
|
|
|
return false;
|
|
}
|
|
|
|
V_memset( &m_flRotX, 0, sizeof( m_flRotX ) );
|
|
V_memset( &m_flRotY, 0, sizeof( m_flRotY ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
void CCascadeLightManager::ShutdownRenderTargets()
|
|
{
|
|
// This purposely does NOT actually destroy the render targets here, to clone the (strange, but well tested?) behavior of the clientshadowmgr.cpp.
|
|
// Not destroying the shadow buffers when baseclientrendertargets.cpp calls this method is beneficial when loading HDR maps, which triggers a recreation of the well known
|
|
// render targets during level load.
|
|
CsmDbgMsg( "C_CascadeLight::ShutdownRenderTargets\n" );
|
|
}
|
|
|
|
void CCascadeLightManager::LevelInitPreEntity()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::LevelInitPreEntity\n" );
|
|
}
|
|
|
|
void CCascadeLightManager::LevelInitPostEntity()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::LevelInitPostEntity\n" );
|
|
}
|
|
|
|
void CCascadeLightManager::LevelShutdownPreEntity()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::LevelShutdownPreEntity\n" );
|
|
}
|
|
|
|
void CCascadeLightManager::LevelShutdownPostEntity()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::LevelShutdownPostEntity\n" );
|
|
}
|
|
|
|
void CCascadeLightManager::Shutdown()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::Shutdown\n" );
|
|
|
|
DeinitRenderTargets();
|
|
}
|
|
|
|
void CCascadeLightManager::DeinitRenderTargets()
|
|
{
|
|
CsmDbgMsg( "C_CascadeLight::DeinitRenderTargets\n" );
|
|
|
|
UnlockAllShadowDepthTextures();
|
|
|
|
if ( m_bRenderTargetsAllocated )
|
|
{
|
|
m_DummyColorTexture.Shutdown();
|
|
m_ShadowDepthTexture.Shutdown();
|
|
|
|
m_bRenderTargetsAllocated = false;
|
|
}
|
|
}
|
|
|
|
bool CCascadeLightManager::IsEnabled() const
|
|
{
|
|
if ( !C_CascadeLight::Get() || !C_CascadeLight::Get()->IsEnabled() || !cl_csm_enabled.GetBool() || !m_bRenderTargetsAllocated )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool CCascadeLightManager::IsEnabledAndActive() const
|
|
{
|
|
return IsEnabled() && ( C_CascadeLight::Get() != NULL );
|
|
}
|
|
|
|
void CCascadeLightManager::Draw3DDebugInfo()
|
|
{
|
|
if ( !IsEnabled() )
|
|
return;
|
|
|
|
CFullCSMState &state = m_capturedState.m_CSMParallelSplit.IsValid() ? m_capturedState : m_curState;
|
|
|
|
if ( !state.m_CSMParallelSplit.IsValid() )
|
|
return;
|
|
|
|
const bool zTest = cl_csm_debug_render_ztest.GetBool();
|
|
|
|
const SunLightState_t &lightState = state.m_CSMParallelSplit.GetLightState();
|
|
const CFrustum *pCascadeFrustums = lightState.m_CascadeFrustums; pCascadeFrustums;
|
|
const ShadowFrustaDebugInfo_t *pDebugInfo = lightState.m_DebugInfo; pDebugInfo;
|
|
//const SunLightShaderParamsCB_t &shadowParams = lightState.m_SunLightShaderParams;
|
|
const VMatrix *pCascadeProjToTexMatrices = lightState.m_CascadeProjToTexMatrices; pCascadeProjToTexMatrices;
|
|
const CFrustum &sceneFrustum = state.m_sceneFrustum;
|
|
|
|
const VMatrix sceneWorldToView( sceneFrustum.GetView() );
|
|
const VMatrix &sceneViewToProj = sceneFrustum.GetProj();
|
|
const VMatrix sceneWorldToProj( sceneViewToProj * sceneWorldToView );
|
|
VMatrix sceneProjToWorld;
|
|
sceneWorldToProj.InverseGeneral( sceneProjToWorld );
|
|
|
|
const Vector &vEye = sceneFrustum.GetCameraPosition();
|
|
const Vector &vForward = sceneFrustum.CameraForward();
|
|
const Vector &vLeft = sceneFrustum.CameraLeft();
|
|
const Vector &vUp = sceneFrustum.CameraUp();
|
|
|
|
const Vector vDirToLight( -state.m_shadowDir );
|
|
|
|
COMPILE_TIME_ASSERT( 4 == MAX_SUN_LIGHT_SHADOW_CASCADE_SIZE );
|
|
|
|
if ( cl_csm_debug_culling.GetBool() )
|
|
{
|
|
int s = 0, e = lightState.m_nShadowCascadeSize;
|
|
if ( cl_csm_debug_culling_cascade.GetInt() >= 0 )
|
|
{
|
|
s = clamp<int, int, int>( cl_csm_debug_culling_cascade.GetInt(), 0, lightState.m_nShadowCascadeSize - 1 );
|
|
e = s + 1;
|
|
}
|
|
for ( int i = s; i < e; ++i )
|
|
{
|
|
if ( lightState.m_CascadeVolumeCullers[i].HasBaseFrustum() )
|
|
{
|
|
VPlane basePlanes[CVolumeCuller::cNumBaseFrustumPlanes];
|
|
lightState.m_CascadeVolumeCullers[i].GetBaseFrustumPlanes( basePlanes );
|
|
int nPlaneCount = lightState.m_CascadeVolumeCullers[i].GetNumBaseFrustumPlanes();
|
|
DebugRenderConvexPolyhedron( (Vector4D*)basePlanes, nPlaneCount, Vector( 1.0f, 1.0f, .2f ), true );
|
|
}
|
|
|
|
if ( lightState.m_CascadeVolumeCullers[i].HasInclusionVolume() )
|
|
DebugRenderConvexPolyhedron( (Vector4D*)lightState.m_CascadeVolumeCullers[i].GetInclusionVolumePlanes(), lightState.m_CascadeVolumeCullers[i].GetNumInclusionVolumePlanes(), Vector( .3f, 1.0f, .3f ), true );
|
|
|
|
if ( lightState.m_CascadeVolumeCullers[i].HasExclusionFrustum() )
|
|
DebugRenderConvexPolyhedron( (Vector4D *)lightState.m_CascadeVolumeCullers[i].GetExclusionFrustumPlanes(), lightState.m_CascadeVolumeCullers[i].GetNumExclusionFrustumPlanes(), Vector( 1, .25f, .25f ), true );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
for ( uint nCascadeIndex = 0; nCascadeIndex < lightState.m_nShadowCascadeSize; ++nCascadeIndex )
|
|
{
|
|
const CFrustum &shadowFrustum = lightState.m_CascadeFrustums[ nCascadeIndex ];
|
|
//const VMatrix &shadowWorldToView = shadowFrustum.GetView();
|
|
//const VMatrix &shadowViewToProj = shadowFrustum.GetProj();
|
|
|
|
VMatrix projToWorld( shadowFrustum.GetInvViewProj() );
|
|
|
|
DebugRenderWireframeFrustum3D( projToWorld, g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
}
|
|
|
|
Add3DLineOverlay( sceneFrustum.GetCameraPosition(), sceneFrustum.GetCameraPosition() + vDirToLight * 250.0f, Vector( 1, 1, 1 ), zTest );
|
|
|
|
for ( uint nCascadeIndex = 0; nCascadeIndex <= lightState.m_nShadowCascadeSize; ++nCascadeIndex )
|
|
{
|
|
const VMatrix sceneWorldToView( sceneFrustum.GetView() );
|
|
const VMatrix &sceneViewToProj = sceneFrustum.GetProj();
|
|
|
|
VMatrix sceneProjToView;
|
|
sceneViewToProj.InverseGeneral( sceneProjToView );
|
|
|
|
VMatrix sceneViewToWorld;
|
|
sceneWorldToView.InverseGeneral( sceneViewToWorld );
|
|
|
|
Vector v[8];
|
|
for ( uint z = 0; z < 2; ++z )
|
|
{
|
|
const float flZ = z ? 1.0f : 0.0f;
|
|
|
|
for ( uint i = 0; i < 4; ++i )
|
|
{
|
|
Vector vViewspacePoint;
|
|
sceneProjToView.V3Mul( Vector( CCSMFrustumDefinition::g_vProjFrustumVerts[i].x, CCSMFrustumDefinition::g_vProjFrustumVerts[i].y, flZ ), vViewspacePoint );
|
|
|
|
// Scale the vector so its Z is -1.0f (negative viewspace Z is inside the frustum due to RH proj matrices)
|
|
|
|
if ( ( z ) && ( nCascadeIndex < lightState.m_nShadowCascadeSize ) )
|
|
{
|
|
float flOneOverZ = 1.0f / vViewspacePoint.z;
|
|
vViewspacePoint *= flOneOverZ;
|
|
vViewspacePoint.z *= -1.0f;
|
|
|
|
vViewspacePoint *= lightState.m_DebugInfo[nCascadeIndex].m_flSplitPlaneDistance;
|
|
}
|
|
else if ( ( !z ) && ( nCascadeIndex ) )
|
|
{
|
|
float flOneOverZ = 1.0f / vViewspacePoint.z;
|
|
vViewspacePoint *= flOneOverZ;
|
|
vViewspacePoint.z *= -1.0f;
|
|
|
|
vViewspacePoint *= lightState.m_DebugInfo[nCascadeIndex - 1].m_flSplitPlaneDistance;
|
|
}
|
|
else
|
|
{
|
|
// flip xy to be consistent with the other cases
|
|
vViewspacePoint.x *= -1.0f;
|
|
vViewspacePoint.y *= -1.0f;
|
|
}
|
|
|
|
sceneViewToWorld.V3Mul( vViewspacePoint, v[i + z * 4] );
|
|
}
|
|
}
|
|
|
|
if ( !nCascadeIndex )
|
|
{
|
|
Add3DLineOverlay( v[0], v[1], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[1], v[2], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[2], v[3], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[3], v[0], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
}
|
|
|
|
Add3DLineOverlay( v[4], v[5], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[5], v[6], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[6], v[7], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[7], v[4], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
|
|
Add3DLineOverlay( v[0], v[4], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[1], v[5], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[2], v[6], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
Add3DLineOverlay( v[3], v[7], g_vCascadeFrustumColors[nCascadeIndex], zTest );
|
|
}
|
|
|
|
Vector vFadeStart( vEye + vForward * lightState.m_flZLerpStartDist );
|
|
Vector vFadeEnd( vEye + vForward * lightState.m_flZLerpEndDist );
|
|
for ( uint y = 0; y < 8; y++ )
|
|
{
|
|
Add3DLineOverlay(
|
|
vFadeStart + vLeft * -300.0f + vUp * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
vFadeStart + vLeft * 300.0f + vUp * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
Vector( .1f, 0.0f, 1.0f ), true );
|
|
}
|
|
|
|
for ( uint y = 0; y < 8; y++ )
|
|
{
|
|
Add3DLineOverlay(
|
|
vFadeStart + vUp * -300.0f + vLeft * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
vFadeStart + vUp * 300.0f + vLeft * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
Vector( .1f, 0.0f, 1.0f ), true );
|
|
}
|
|
|
|
for ( uint y = 0; y < 8; y++ )
|
|
{
|
|
Add3DLineOverlay(
|
|
vFadeEnd + vLeft * -300.0f + vUp * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
vFadeEnd + vLeft * 300.0f + vUp * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
Vector( .1f, 0.5f, 1.0f ), true );
|
|
}
|
|
|
|
for ( uint y = 0; y < 8; y++ )
|
|
{
|
|
Add3DLineOverlay(
|
|
vFadeEnd + vUp * -300.0f + vLeft * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
vFadeEnd + vUp * 300.0f + vLeft * Lerp( y / 7.0f, -1.0f, 1.0f ) * 300.0f,
|
|
Vector( .1f, 0.5f, 1.0f ), true );
|
|
}
|
|
|
|
//DebugRenderWireframeFrustum3D( sceneFrustum.GetInvViewProj(), Vector( .4f, .4f, .8f ), zTest );
|
|
}
|
|
|
|
void CCascadeLightManager::Draw2DDebugInfo()
|
|
{
|
|
if ( !IsEnabled() )
|
|
return;
|
|
|
|
CFullCSMState &state = m_capturedState.m_CSMParallelSplit.IsValid() ? m_capturedState : m_curState;
|
|
|
|
if ( !state.m_CSMParallelSplit.IsValid() )
|
|
return;
|
|
|
|
const SunLightState_t &lightState = state.m_CSMParallelSplit.GetLightState();
|
|
const CFrustum *pCascadeFrustums = lightState.m_CascadeFrustums;
|
|
const ShadowFrustaDebugInfo_t *pDebugInfo = lightState.m_DebugInfo;
|
|
//const SunLightShaderParamsCB_t &shadowParams = lightState.m_SunLightShaderParams;
|
|
const VMatrix *pCascadeProjToTexMatrices = lightState.m_CascadeProjToTexMatrices;
|
|
const CFrustum &sceneFrustum = state.m_sceneFrustum;
|
|
|
|
const VMatrix sceneWorldToView( sceneFrustum.GetView() );
|
|
const VMatrix &sceneViewToProj = sceneFrustum.GetProj();
|
|
const VMatrix sceneWorldToProj( sceneViewToProj * sceneWorldToView );
|
|
VMatrix sceneProjToWorld;
|
|
sceneWorldToProj.InverseGeneral( sceneProjToWorld );
|
|
|
|
m_debugPrimRenderer.Clear();
|
|
|
|
int nFirstCascadeIndex = 0;
|
|
int nLastCascadeIndex = (int)lightState.m_nShadowCascadeSize - 1;
|
|
|
|
#if defined( _GAMECONSOLE )
|
|
const int nShadowBufferDebugVisWidth = 256;
|
|
const int nShadowBufferDebugVisHeight = 256;
|
|
#else
|
|
const int nShadowBufferDebugVisWidth = 512;
|
|
const int nShadowBufferDebugVisHeight = 512;
|
|
#endif
|
|
|
|
for ( int nCascadeIndex = nFirstCascadeIndex; nCascadeIndex <= nLastCascadeIndex; ++nCascadeIndex )
|
|
{
|
|
const Rect_t &cascadeViewport = lightState.m_CascadeViewports[nCascadeIndex];
|
|
|
|
int nPlacementX = nCascadeIndex & 1;
|
|
int nPlacementY = nCascadeIndex >> 1;
|
|
|
|
Rect_t destRect;
|
|
|
|
#if defined( _GAMECONSOLE )
|
|
destRect.x = 64 + ( nShadowBufferDebugVisWidth + 75 ) * nPlacementX;
|
|
destRect.y = 64 + ( nShadowBufferDebugVisHeight + 75 ) * nPlacementY;
|
|
#else
|
|
destRect.x = 16 + ( nShadowBufferDebugVisWidth + 75 ) * nPlacementX;
|
|
destRect.y = 16 + ( nShadowBufferDebugVisHeight + 75 ) * nPlacementY;
|
|
#endif
|
|
destRect.width = nShadowBufferDebugVisWidth;
|
|
destRect.height = nShadowBufferDebugVisHeight;
|
|
|
|
VertexColor_t color( g_vCascadeFrustumColors[nCascadeIndex].x * 255, g_vCascadeFrustumColors[nCascadeIndex].y * 255, g_vCascadeFrustumColors[nCascadeIndex].z * 255, 255 );
|
|
|
|
m_debugPrimRenderer.AddScreenspaceRect2D( destRect.x, destRect.y, destRect.x + destRect.width, destRect.y + destRect.height, color.r, color.g, color.b );
|
|
|
|
m_debugPrimRenderer.RenderScreenspaceDepthTexture(
|
|
destRect.x, destRect.y, destRect.x + destRect.width, destRect.y + destRect.height,
|
|
(float)cascadeViewport.x / m_nDepthTextureResolution,
|
|
(float)cascadeViewport.y / m_nDepthTextureResolution,
|
|
(float)( cascadeViewport.x + cascadeViewport.width ) / m_nDepthTextureResolution,
|
|
(float)( cascadeViewport.y + cascadeViewport.height ) / m_nDepthTextureResolution,
|
|
m_ShadowDepthTexture, cl_csm_debug_vis_lo_range.GetFloat(), cl_csm_debug_vis_hi_range.GetFloat() );
|
|
|
|
const CFrustum &shadowFrustum = pCascadeFrustums[ nCascadeIndex ];
|
|
const VMatrix shadowWorldToView( shadowFrustum.GetView() );
|
|
const VMatrix &shadowViewToProj = shadowFrustum.GetProj();
|
|
|
|
const VMatrix shadowWorldToProj( shadowViewToProj * shadowWorldToView );
|
|
|
|
const VMatrix shadowProjToTex( pCascadeProjToTexMatrices[nCascadeIndex] );
|
|
|
|
const VMatrix sceneProjToTex( shadowProjToTex * ( shadowWorldToProj * sceneProjToWorld ) );
|
|
|
|
VMatrix shadowTexToRect(
|
|
destRect.width, 0.0f, 0.0f, destRect.x,
|
|
0.0f, destRect.height, 0.0f, destRect.y,
|
|
0.0f, 0.0f, 0.0f, .5f,
|
|
0.0f, 0.0f, 0.0f, 1.0f );
|
|
|
|
const VMatrix sceneProjToRect( shadowTexToRect * sceneProjToTex );
|
|
|
|
m_debugPrimRenderer.AddScreenspaceWireframeFrustum2D( sceneProjToRect, color, true );
|
|
|
|
if ( m_capturedState.m_CSMParallelSplit.IsValid() )
|
|
{
|
|
CFullCSMState &actualState = m_curState;
|
|
const CFrustum &actualSceneFrustum = actualState.m_sceneFrustum;
|
|
|
|
const VMatrix actualSceneWorldToView( actualSceneFrustum.GetView() );
|
|
const VMatrix &actualSceneViewToProj = actualSceneFrustum.GetProj();
|
|
|
|
const VMatrix actualSceneWorldToProj( actualSceneViewToProj * actualSceneWorldToView );
|
|
VMatrix actualSceneProjToWorld;
|
|
actualSceneWorldToProj.InverseGeneral( actualSceneProjToWorld );
|
|
|
|
const VMatrix actualSceneProjToTex( shadowProjToTex * ( shadowWorldToProj * actualSceneProjToWorld ) );
|
|
const VMatrix actualSceneProjToRect( shadowTexToRect * actualSceneProjToTex );
|
|
|
|
VertexColor_t actualFrustumColor( 0, 255, 0, 255 );
|
|
m_debugPrimRenderer.AddScreenspaceWireframeFrustum2D( actualSceneProjToRect, actualFrustumColor, true );
|
|
}
|
|
|
|
VMatrix shadowWorldToRect( shadowTexToRect * ( shadowProjToTex * shadowWorldToProj ) );
|
|
|
|
for ( uint i = 0; i < pDebugInfo[nCascadeIndex].m_nNumWorldFocusVerts; ++i )
|
|
{
|
|
const Vector &v = pDebugInfo[nCascadeIndex].m_WorldFocusVerts[i];
|
|
Vector4D worldVert( v.x, v.y, v.z, 1.0f );
|
|
|
|
Vector4D rectVert;
|
|
shadowWorldToRect.V4Mul( worldVert, rectVert );
|
|
|
|
rectVert *= ( 1.0f / rectVert.w );
|
|
|
|
Vector2D points[4];
|
|
points[0].Init( rectVert.x - 5, rectVert.y );
|
|
points[1].Init( rectVert.x + 5, rectVert.y );
|
|
points[2].Init( rectVert.x, rectVert.y - 5 );
|
|
points[3].Init( rectVert.x, rectVert.y + 5);
|
|
|
|
m_debugPrimRenderer.AddScreenspaceLineList2D( 2, points, VertexColor_t( 20, 255, 20, 255 ) );
|
|
}
|
|
|
|
for ( int nPrevSplitIndex = 0; nPrevSplitIndex < nCascadeIndex; ++nPrevSplitIndex )
|
|
{
|
|
const CFrustum &prevShadowFrustum = pCascadeFrustums[ nPrevSplitIndex ];
|
|
const VMatrix prevShadowWorldToView( prevShadowFrustum.GetView() );
|
|
const VMatrix &prevShadowViewToProj = prevShadowFrustum.GetProj();
|
|
|
|
const VMatrix prevShadowWorldToProj( prevShadowViewToProj * prevShadowWorldToView );
|
|
VMatrix prevShadowProjToWorld;
|
|
prevShadowWorldToProj.InverseGeneral( prevShadowProjToWorld );
|
|
|
|
const VMatrix prevShadowProjToShadowRect( shadowTexToRect * ( shadowProjToTex * ( shadowWorldToProj * prevShadowProjToWorld ) ) );
|
|
|
|
VertexColor_t prevSplitColor( g_vCascadeFrustumColors[nPrevSplitIndex].x * 255, g_vCascadeFrustumColors[nPrevSplitIndex].y * 255, g_vCascadeFrustumColors[nPrevSplitIndex].z * 255, 255 );
|
|
|
|
m_debugPrimRenderer.AddScreenspaceWireframeFrustum2D( prevShadowProjToShadowRect, prevSplitColor, false );
|
|
}
|
|
}
|
|
|
|
m_debugPrimRenderer.Render2D();
|
|
}
|
|
|
|
void CCascadeLightManager::DrawTextDebugInfo()
|
|
{
|
|
CFullCSMState &curState = GetActiveState();
|
|
|
|
uint cur_y = 105;
|
|
|
|
ScreenText( 5, cur_y, 255, 95, 95, 255, "Active State: %s Pos:(%3.3f, %3.3f, %3.3f), Forward:(%3.3f, %3.3f, %3.3f), Left:(%3.3f, %3.3f, %3.3f), Up:(%3.3f, %3.3f, %3.3f)",
|
|
m_capturedState.m_CSMParallelSplit.IsValid() ? "CAP" : "CUR",
|
|
curState.m_sceneFrustum.GetCameraPosition().x, curState.m_sceneFrustum.GetCameraPosition().y, curState.m_sceneFrustum.GetCameraPosition().z,
|
|
curState.m_sceneFrustum.CameraForward().x, curState.m_sceneFrustum.CameraForward().y, curState.m_sceneFrustum.CameraForward().z,
|
|
curState.m_sceneFrustum.CameraLeft().x, curState.m_sceneFrustum.CameraLeft().y, curState.m_sceneFrustum.CameraLeft().z,
|
|
curState.m_sceneFrustum.CameraUp().x, curState.m_sceneFrustum.CameraUp().y, curState.m_sceneFrustum.CameraUp().z );
|
|
cur_y += 2;
|
|
|
|
if ( GetClientWorldEntity() )
|
|
{
|
|
const Vector &worldMins = GetClientWorldEntity()->m_WorldMins;
|
|
const Vector &worldMaxs = GetClientWorldEntity()->m_WorldMaxs;
|
|
|
|
ScreenText( 5, cur_y, 255, 95, 95, 255, "World Bounds: (%f,%f,%f) - (%f,%f,%f)",
|
|
worldMins.x, worldMins.y, worldMins.z,
|
|
worldMaxs.x, worldMaxs.y, worldMaxs.z );
|
|
cur_y += 2;
|
|
}
|
|
|
|
uint m_nTotalAABB = 0;
|
|
uint m_nTotalAABBPassed = 0;
|
|
uint m_nTotalCenterHalfDiagonal = 0;
|
|
uint m_nTotalCenterHalfDiagonalPassed = 0;
|
|
|
|
uint i;
|
|
for ( i = 0; i < 4; i++ )
|
|
{
|
|
CVolumeCuller &cascadeVolumeCuller = (i == 3) ? m_curViewModelState.m_CSMParallelSplit.GetLightState().m_CascadeVolumeCullers[0] : curState.m_CSMParallelSplit.GetLightState().m_CascadeVolumeCullers[i];
|
|
CVolumeCuller::CullCheckStats_t &cullStats = cascadeVolumeCuller.GetStats();
|
|
|
|
ScreenText( 5, cur_y, 255, 95, 95, 255, "Cascade %u: Total AABB Cull Checks: %u, Passed: %u, Total World AABB Cull Checks: %u, Passed: %u",
|
|
i,
|
|
cullStats.m_nTotalAABB, cullStats.m_nTotalAABBPassed,
|
|
cullStats.m_nTotalCenterHalfDiagonal, cullStats.m_nTotalCenterHalfDiagonalPassed );
|
|
cur_y += 2;
|
|
|
|
m_nTotalAABB += cullStats.m_nTotalAABB;
|
|
m_nTotalAABBPassed += cullStats.m_nTotalAABBPassed;
|
|
m_nTotalCenterHalfDiagonal += cullStats.m_nTotalCenterHalfDiagonal;
|
|
m_nTotalCenterHalfDiagonalPassed += cullStats.m_nTotalCenterHalfDiagonalPassed;
|
|
|
|
cascadeVolumeCuller.ClearCullCheckStats();
|
|
}
|
|
|
|
ScreenText( 5, cur_y, 255, 95, 95, 255, "All Cascades: Total AABB Cull Checks: %u, Passed: %u, Total World AABB Cull Checks: %u, Passed: %u",
|
|
m_nTotalAABB, m_nTotalAABBPassed,
|
|
m_nTotalCenterHalfDiagonal, m_nTotalCenterHalfDiagonalPassed );
|
|
cur_y += 2;
|
|
}
|
|
|
|
void CCascadeLightManager::CFullCSMState::Update( const CViewSetup &viewSetup, const Vector &shadowDir, color32 lightColor, int lightColorScale, float flMaxShadowDist, float flMaxVisibleDist, uint nMaxCascadeSize, uint nAtlasFirstCascadeIndex, int nCSMQualityLevel, bool bSetAllCascadesToFirst )
|
|
{
|
|
m_shadowDir = shadowDir;
|
|
m_flMaxShadowDist = flMaxShadowDist;
|
|
m_flMaxVisibleDist = flMaxVisibleDist;
|
|
m_nMaxCascadeSize = nMaxCascadeSize;
|
|
|
|
m_flSceneAspectRatio = viewSetup.ComputeViewMatrices( &m_sceneWorldToView, &m_sceneViewToProj, &m_sceneWorldToProj );
|
|
|
|
m_sceneFrustum.BuildFrustumFromParameters( viewSetup.origin, viewSetup.angles, viewSetup.zNear, viewSetup.zFar, viewSetup.fov, m_flSceneAspectRatio, m_sceneWorldToView, m_sceneViewToProj );
|
|
|
|
SunLightViewState_t sunLightViewState;
|
|
sunLightViewState.m_Direction = shadowDir;
|
|
sunLightViewState.m_LightColor = lightColor;
|
|
sunLightViewState.m_LightColorScale = lightColorScale;
|
|
sunLightViewState.m_flMaxShadowDist = flMaxShadowDist;
|
|
sunLightViewState.m_flMaxVisibleDist = flMaxVisibleDist;
|
|
sunLightViewState.m_frustum = m_sceneFrustum;
|
|
sunLightViewState.m_nMaxCascadeSize = nMaxCascadeSize;
|
|
sunLightViewState.m_nAtlasFirstCascadeIndex = nAtlasFirstCascadeIndex;
|
|
sunLightViewState.m_nCSMQualityLevel = nCSMQualityLevel;
|
|
sunLightViewState.m_bSetAllCascadesToFirst = bSetAllCascadesToFirst;
|
|
m_CSMParallelSplit.Update( sunLightViewState );
|
|
|
|
m_bValid = ( m_CSMParallelSplit.GetLightState().m_nShadowCascadeSize != 0 );
|
|
}
|
|
|
|
|
|
void CCascadeLightManager::RenderViews( CCascadeLightManager::CFullCSMState &state, bool bIncludeViewModels )
|
|
{
|
|
SunLightState_t &lightState = state.m_CSMParallelSplit.GetLightState();
|
|
|
|
#if 0
|
|
VMatrix computedWorldToView;
|
|
VMatrix computedViewToProj;
|
|
VMatrix computedWorldToProj;
|
|
#endif
|
|
|
|
uint nCascadeIndex = 0;
|
|
if( IsGameConsole() )
|
|
{
|
|
// don't use cascade 0 for console. TODO - at some point we should make better use of texture space, right now we waste 25%
|
|
nCascadeIndex = bIncludeViewModels ? 0 : 1;
|
|
}
|
|
else
|
|
{
|
|
if ( ( !bIncludeViewModels ) && ( GetCSMQualityMode() <= CSMQUALITY_LOW ) )
|
|
nCascadeIndex = 1;
|
|
}
|
|
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
|
|
for ( ; nCascadeIndex < lightState.m_nShadowCascadeSize; ++nCascadeIndex )
|
|
{
|
|
CViewSetup shadowView;
|
|
shadowView.m_flAspectRatio = 1.0f;
|
|
shadowView.x = lightState.m_CascadeViewports[nCascadeIndex].x;
|
|
shadowView.y = lightState.m_CascadeViewports[nCascadeIndex].y;
|
|
shadowView.width = lightState.m_CascadeViewports[nCascadeIndex].width;
|
|
shadowView.height = lightState.m_CascadeViewports[nCascadeIndex].height;
|
|
shadowView.m_bRenderToSubrectOfLargerScreen = true;
|
|
|
|
#if defined(_X360)
|
|
// render into top left viewport
|
|
// resolve to appropriate quadrant of final texture
|
|
shadowView.xCsmDstOffset = shadowView.x;
|
|
shadowView.yCsmDstOffset = shadowView.y;
|
|
shadowView.x = 0;
|
|
shadowView.y = 0;
|
|
#endif
|
|
|
|
const CFrustum &cascadeFrustum = lightState.m_CascadeFrustums[nCascadeIndex];
|
|
|
|
// Force custom matrices, to avoid FP precision issues causing shadow continuity problems. (We need precise control over the matrices used for rendering.)
|
|
if ( cl_csm_use_forced_view_matrices.GetBool() )
|
|
{
|
|
shadowView.m_bCustomViewMatrix = true;
|
|
// Argh - m_matCustomViewMatrix is actually a custom world->camera matrix, not world->view!
|
|
// The view->camera matrix only has 0,-1,1 values so it shouldn't affect precision.
|
|
shadowView.m_matCustomViewMatrix = ( g_matViewToCameraMatrix * VMatrix( cascadeFrustum.GetView() ) ).As3x4();
|
|
|
|
shadowView.m_bCustomProjMatrix = true;
|
|
shadowView.m_matCustomProjMatrix = cascadeFrustum.GetProj();
|
|
}
|
|
|
|
cascadeFrustum.GetClipSpaceBounds( shadowView.m_OrthoLeft, shadowView.m_OrthoBottom, shadowView.m_OrthoRight, shadowView.m_OrthoTop );
|
|
shadowView.origin = cascadeFrustum.GetCameraPosition();
|
|
shadowView.angles = cascadeFrustum.GetCameraAngles();
|
|
shadowView.zNear = shadowView.zNearViewmodel = cascadeFrustum.GetCameraNearPlane();
|
|
shadowView.zFar = shadowView.zFarViewmodel = cascadeFrustum.GetCameraFarPlane();
|
|
|
|
if ( cl_csm_debug_2d.GetBool() && cl_csm_use_forced_view_matrices.GetBool() && cl_csm_hack_proj_matrices_for_cull_debugging.GetBool() )
|
|
{
|
|
MatrixBuildOrtho(
|
|
shadowView.m_matCustomProjMatrix,
|
|
Lerp( -2.0f, shadowView.m_OrthoLeft, shadowView.m_OrthoRight ),
|
|
Lerp( -2.0f, shadowView.m_OrthoTop, shadowView.m_OrthoBottom ),
|
|
Lerp( -2.0f, shadowView.m_OrthoRight, shadowView.m_OrthoLeft ),
|
|
Lerp( -2.0f, shadowView.m_OrthoBottom, shadowView.m_OrthoTop ),
|
|
shadowView.zNear,
|
|
shadowView.zFar );
|
|
}
|
|
|
|
shadowView.m_bDoBloomAndToneMapping = false;
|
|
shadowView.m_nMotionBlurMode = MOTION_BLUR_DISABLE;
|
|
|
|
// Set depth bias factors specific to this cascade
|
|
|
|
#if 0
|
|
float flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();
|
|
float flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();
|
|
#else
|
|
static ConVar *s_csm_slopescales[4] = { &cl_csm_slopescaledepthbias_c0, &cl_csm_slopescaledepthbias_c1, &cl_csm_slopescaledepthbias_c2, &cl_csm_slopescaledepthbias_c3 };
|
|
static ConVar *s_csm_depthbias[4] = { &cl_csm_depthbias_c0, &cl_csm_depthbias_c1, &cl_csm_depthbias_c2, &cl_csm_depthbias_c3 };
|
|
|
|
float flShadowSlopeScaleDepthBias = s_csm_slopescales[nCascadeIndex]->GetFloat();
|
|
float flShadowDepthBias = s_csm_depthbias[nCascadeIndex]->GetFloat();
|
|
|
|
if ( bIncludeViewModels )
|
|
{
|
|
flShadowSlopeScaleDepthBias = cl_csm_viewmodel_slopescaledepthbias.GetFloat();
|
|
flShadowDepthBias = cl_csm_viewmodel_depthbias.GetFloat();
|
|
}
|
|
#endif
|
|
|
|
pRenderContext->PerpareForCascadeDraw( nCascadeIndex, flShadowSlopeScaleDepthBias, flShadowDepthBias );
|
|
pRenderContext->SetShadowDepthBiasFactors( flShadowSlopeScaleDepthBias, flShadowDepthBias );
|
|
pRenderContext->CullMode( MATERIAL_CULLMODE_NONE );
|
|
|
|
shadowView.m_bOrtho = true;
|
|
shadowView.m_bCSMView = true;
|
|
|
|
CVolumeCuller &volumeCuller = lightState.m_CascadeVolumeCullers[nCascadeIndex];
|
|
if ( ( nCascadeIndex == ( lightState.m_nShadowCascadeSize - 1 ) ) && ( GetCSMQualityMode() <= CSMQUALITY_LOW ) )
|
|
{
|
|
volumeCuller.SetCullSmallObjects( true, cl_csm_cull_small_prop_threshold_volume.GetFloat() );
|
|
}
|
|
else
|
|
{
|
|
volumeCuller.SetCullSmallObjects( false, 1e+10f );
|
|
}
|
|
|
|
shadowView.m_pCSMVolumeCuller = &volumeCuller;
|
|
|
|
#if 0
|
|
// Purely for debugging.
|
|
shadowView.m_bCustomViewMatrix = false;
|
|
shadowView.m_bCustomProjMatrix = false;
|
|
|
|
shadowView.ComputeViewMatrices( &computedWorldToView, &computedViewToProj, &computedWorldToProj );
|
|
|
|
if ( cl_csm_use_forced_view_matrices.GetBool() )
|
|
{
|
|
shadowView.m_bCustomViewMatrix = true;
|
|
shadowView.m_bCustomProjMatrix = true;
|
|
}
|
|
|
|
Vector currentViewForward, currentViewRight, currentViewUp;
|
|
AngleVectors( shadowView.angles, ¤tViewForward, ¤tViewRight, ¤tViewUp );
|
|
|
|
// Now compute the culling planes the same way as CRender::OrthoExtractFrustumPlanes() does, so they can be manually
|
|
// compared against the planes the CSM manager computed. The game makes several assumptions about view and ortho projection space.
|
|
VPlane frustumPlanes[6];
|
|
|
|
float orgOffset = DotProduct(shadowView.origin, currentViewForward);
|
|
frustumPlanes[FRUSTUM_FARZ].m_Normal = -currentViewForward;
|
|
frustumPlanes[FRUSTUM_FARZ].m_Dist = -shadowView.zFar - orgOffset;
|
|
|
|
frustumPlanes[FRUSTUM_NEARZ].m_Normal = currentViewForward;
|
|
frustumPlanes[FRUSTUM_NEARZ].m_Dist = shadowView.zNear + orgOffset;
|
|
|
|
orgOffset = DotProduct(shadowView.origin, currentViewRight);
|
|
frustumPlanes[FRUSTUM_LEFT].m_Normal = currentViewRight;
|
|
frustumPlanes[FRUSTUM_LEFT].m_Dist = shadowView.m_OrthoLeft + orgOffset;
|
|
|
|
frustumPlanes[FRUSTUM_RIGHT].m_Normal = -currentViewRight;
|
|
frustumPlanes[FRUSTUM_RIGHT].m_Dist = -shadowView.m_OrthoRight - orgOffset;
|
|
|
|
orgOffset = DotProduct(shadowView.origin, currentViewUp);
|
|
frustumPlanes[FRUSTUM_TOP].m_Normal = currentViewUp;
|
|
frustumPlanes[FRUSTUM_TOP].m_Dist = shadowView.m_OrthoTop + orgOffset;
|
|
|
|
frustumPlanes[FRUSTUM_BOTTOM].m_Normal = -currentViewUp;
|
|
frustumPlanes[FRUSTUM_BOTTOM].m_Dist = -shadowView.m_OrthoBottom - orgOffset;
|
|
#endif
|
|
|
|
// Render to the shadow depth texture with appropriate view
|
|
|
|
view->UpdateShadowDepthTexture( m_DummyColorTexture, m_ShadowDepthTexture, shadowView, true, bIncludeViewModels );
|
|
}
|
|
|
|
pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
|
|
}
|
|
|
|
void CCascadeLightManager::PreRender()
|
|
{
|
|
}
|
|
|
|
Vector CCascadeLightManager::GetShadowDirection()
|
|
{
|
|
Vector vShadowDir( C_CascadeLight::Get()->GetShadowDirection() );
|
|
if ( ( cl_csm_use_env_light_direction.GetBool() ) && ( C_CascadeLight::Get()->UseLightEnvAngles() ) && ( C_CascadeLight::Get()->GetEnvLightShadowDirection().Length() > .5f ) )
|
|
{
|
|
vShadowDir = C_CascadeLight::Get()->GetEnvLightShadowDirection();
|
|
}
|
|
|
|
if ( cl_csm_rot_override.GetBool() )
|
|
{
|
|
const float flRotDegPerSec = 20.0f;
|
|
|
|
float flRotX = cl_csm_rot_x.GetFloat(), flRotY = cl_csm_rot_y.GetFloat();
|
|
if ( m_flRotX[0] || m_flRotX[1] )
|
|
{
|
|
flRotX += m_flRotX[0] * gpGlobals->frametime * flRotDegPerSec;
|
|
flRotX += m_flRotX[1] * gpGlobals->frametime * flRotDegPerSec;
|
|
if ( flRotX > 360.0f )
|
|
flRotX -= 360.0f;
|
|
else if ( flRotX < 0.0f )
|
|
flRotX += 360.0f;
|
|
cl_csm_rot_x.SetValue( flRotX );
|
|
}
|
|
|
|
if ( m_flRotY[0] || m_flRotY[1] )
|
|
{
|
|
flRotY += m_flRotY[0] * gpGlobals->frametime * flRotDegPerSec;
|
|
flRotY += m_flRotY[1] * gpGlobals->frametime * flRotDegPerSec;
|
|
if ( flRotY > 360.0f )
|
|
flRotY -= 360.0f;
|
|
else if ( flRotY < 0.0f )
|
|
flRotY += 360.0f;
|
|
cl_csm_rot_y.SetValue( flRotY );
|
|
}
|
|
|
|
QAngle angles;
|
|
angles.Init( flRotX, flRotY, cl_csm_rot_z.GetFloat() );
|
|
AngleVectors( angles, &vShadowDir );
|
|
}
|
|
|
|
return vShadowDir.Normalized();
|
|
}
|
|
|
|
// bSetup only used on PS3 right now - to support 2 pass Build/Draw rendering
|
|
// bSetup indicates whether to execute once per frame setup code, do this in the buildlist (1st pass) pass only
|
|
// Needed since this will get called twice on PS3 if 2 pass drawing is on
|
|
// bSetup = true otherwise
|
|
void CCascadeLightManager::ComputeShadowDepthTextures( const CViewSetup &viewSetup, bool bSetup )
|
|
{
|
|
m_bStateIsValid = false;
|
|
m_bCSMIsActive = false;
|
|
|
|
if (
|
|
#ifdef OSX
|
|
!OSX_HardwareGoodEnoughForCSMs() ||
|
|
#endif
|
|
!cl_csm_enabled.GetBool() ||
|
|
!g_pMaterialSystemHardwareConfig->SupportsCascadedShadowMapping() ||
|
|
!g_pMaterialSystemHardwareConfig->SupportsShadowDepthTextures()
|
|
)
|
|
{
|
|
cl_csm_enabled.SetValue( 0 );
|
|
|
|
if ( m_bRenderTargetsAllocated )
|
|
{
|
|
// This code path will only execute if CSM initializes successfully at startup, but is then disabled either from the console or a config/device change.
|
|
materials->ReEnableRenderTargetAllocation_IRealizeIfICallThisAllTexturesWillBeUnloadedAndLoadTimeWillSufferHorribly();
|
|
DeinitRenderTargets();
|
|
materials->FinishRenderTargetAllocation();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( !m_bRenderTargetsAllocated )
|
|
{
|
|
// If shadow depth texture is now needed but wasn't allocated, allocate it.
|
|
// (This is not a normal code path - it's only taken if the user turns on CSM from the console.)
|
|
materials->ReEnableRenderTargetAllocation_IRealizeIfICallThisAllTexturesWillBeUnloadedAndLoadTimeWillSufferHorribly();
|
|
InitRenderTargets();
|
|
materials->FinishRenderTargetAllocation();
|
|
}
|
|
else if ( m_nCurRenderTargetQualityMode != GetCSMQualityMode() )
|
|
{
|
|
DeinitRenderTargets();
|
|
|
|
// Quality level has changed - recreate render targets as they may change size.
|
|
materials->ReEnableRenderTargetAllocation_IRealizeIfICallThisAllTexturesWillBeUnloadedAndLoadTimeWillSufferHorribly();
|
|
InitRenderTargets();
|
|
materials->FinishRenderTargetAllocation();
|
|
}
|
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
|
|
if ( !m_bRenderTargetsAllocated || !C_CascadeLight::Get() )
|
|
{
|
|
m_curState.Reset();
|
|
m_curViewModelState.Reset();
|
|
}
|
|
else
|
|
{
|
|
Vector vShadowDir( GetShadowDirection() );
|
|
|
|
color32 lightColor = C_CascadeLight::Get()->GetColor();
|
|
int lightColorScale = C_CascadeLight::Get()->GetColorScale();
|
|
|
|
float flMaxShadowDist = cl_csm_max_shadow_dist.GetFloat();
|
|
if ( flMaxShadowDist <= 0.0f )
|
|
{
|
|
flMaxShadowDist = C_CascadeLight::Get()->GetMaxShadowDist();
|
|
#ifdef OSX
|
|
if ( GetCSMQualityMode() == CSMQUALITY_HIGH )
|
|
{
|
|
// At the highest CSM quality level boost the max shadow distance (match Windows on high end Macs)
|
|
// This seems OK from a CS fairness perspective (it can be argued either way whether this gives a player an advantage, or disadvantage).
|
|
flMaxShadowDist *= 1.4f;
|
|
}
|
|
else if ( GetCSMQualityMode() == CSMQUALITY_LOW )
|
|
{
|
|
// match PS3 distance for lowest performing Macs
|
|
flMaxShadowDist *= 0.8f;
|
|
}
|
|
else if ( GetCSMQualityMode() == CSMQUALITY_VERY_LOW )
|
|
{
|
|
// match PS3 distance for lowest performing Macs
|
|
flMaxShadowDist *= 0.6f;
|
|
}
|
|
#else
|
|
if ( ( !IsGameConsole() ) &&
|
|
( GetCSMQualityMode() == CSMQUALITY_HIGH ) )
|
|
{
|
|
// At the highest CSM quality level boost the max shadow distance.
|
|
// This seems OK from a CS fairness perspective (it can be argued either way whether this gives a player an advantage, or disadvantage).
|
|
flMaxShadowDist *= 1.4f;
|
|
}
|
|
#endif
|
|
}
|
|
if ( flMaxShadowDist <= 0.0f )
|
|
flMaxShadowDist = 400.0f;
|
|
|
|
flMaxShadowDist = clamp<float>( flMaxShadowDist, 1.0f, 10000.0f );
|
|
m_curState.Update( viewSetup, vShadowDir, lightColor, lightColorScale, flMaxShadowDist, cl_csm_max_visible_dist.GetFloat(), MAX_CSM_CASCADES, 0, GetCSMQualityMode(), false );
|
|
|
|
CViewSetup viewModelViewSetup( viewSetup );
|
|
viewModelViewSetup.zNear = .5f;
|
|
viewModelViewSetup.zFar = cl_csm_viewmodel_farz.GetFloat();
|
|
m_curViewModelState.Update( viewModelViewSetup, vShadowDir, lightColor, lightColorScale, cl_csm_viewmodel_max_shadow_dist.GetFloat(), cl_csm_viewmodel_max_visible_dist.GetFloat(), 1, 3, GetCSMQualityMode(), true );
|
|
|
|
if ( cl_csm_capture_state.GetInt() )
|
|
{
|
|
cl_csm_capture_state.SetValue( 0 );
|
|
m_capturedState = m_curState;
|
|
}
|
|
|
|
if ( cl_csm_clear_captured_state.GetInt() )
|
|
{
|
|
cl_csm_clear_captured_state.SetValue( 0 );
|
|
m_capturedState.Clear();
|
|
}
|
|
|
|
// The CSM sampling shader can only handle MAX_CSM_CASCADES cascades to reduce the number of dynamic combos.
|
|
if ( GetActiveState().IsValid() && ( GetActiveState().m_CSMParallelSplit.GetLightState().m_nShadowCascadeSize == MAX_CSM_CASCADES ) )
|
|
{
|
|
pRenderContext->BeginGeneratingCSMs();
|
|
|
|
RenderViews( GetActiveState(), false );
|
|
|
|
bool bRenderViewModelCascade = cl_csm_viewmodel_shadows.GetBool();
|
|
if ( ( !IsGameConsole() ) &&
|
|
( GetCSMQualityMode() == CSMQUALITY_VERY_LOW ) )
|
|
{
|
|
bRenderViewModelCascade = false;
|
|
}
|
|
|
|
if ( bRenderViewModelCascade )
|
|
{
|
|
RenderViews( m_curViewModelState, true );
|
|
}
|
|
|
|
pRenderContext->EndGeneratingCSMs();
|
|
m_bCSMIsActive = true;
|
|
}
|
|
}
|
|
|
|
pRenderContext->SetCascadedShadowMapping( m_bCSMIsActive );
|
|
if ( m_bCSMIsActive )
|
|
{
|
|
pRenderContext->SetCascadedShadowMappingState( GetActiveState().m_CSMParallelSplit.GetLightState().m_SunLightShaderParams, m_ShadowDepthTexture );
|
|
|
|
if ( cl_csm_debug_2d.GetBool() )
|
|
{
|
|
// Draw the text here so it doesn't lag 1 frame behind the other debug info
|
|
DrawTextDebugInfo();
|
|
}
|
|
}
|
|
|
|
m_bStateIsValid = m_bCSMIsActive;
|
|
}
|
|
|
|
void CCascadeLightManager::UnlockAllShadowDepthTextures()
|
|
{
|
|
if ( m_bCSMIsActive )
|
|
{
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->SetCascadedShadowMapping( false );
|
|
|
|
m_bCSMIsActive = false;
|
|
}
|
|
}
|
|
|
|
void CCascadeLightManager::BeginViewModelRendering()
|
|
{
|
|
if ( !IsEnabledAndActive() || !m_bStateIsValid || !cl_csm_viewmodel_shadows.GetBool() )
|
|
return;
|
|
|
|
if ( ( !IsGameConsole() ) &&
|
|
( GetCSMQualityMode() == CSMQUALITY_VERY_LOW ) )
|
|
return;
|
|
|
|
m_bCSMIsActive = true;
|
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->SetCascadedShadowMapping( true );
|
|
|
|
CascadedShadowMappingState_t &viewModelShaderParams = m_curViewModelState.m_CSMParallelSplit.GetLightState().m_SunLightShaderParams;
|
|
viewModelShaderParams.m_bIsRenderingViewModels = true;
|
|
|
|
pRenderContext->SetCascadedShadowMappingState( viewModelShaderParams, m_ShadowDepthTexture );
|
|
}
|
|
|
|
void CCascadeLightManager::EndViewModelRendering()
|
|
{
|
|
if ( !IsEnabledAndActive() || !m_bStateIsValid || !cl_csm_viewmodel_shadows.GetBool() )
|
|
return;
|
|
|
|
if ( ( !IsGameConsole() ) &&
|
|
( GetCSMQualityMode() == CSMQUALITY_VERY_LOW ) )
|
|
return;
|
|
|
|
if ( m_bCSMIsActive )
|
|
{
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->SetCascadedShadowMapping( false );
|
|
|
|
m_bCSMIsActive = false;
|
|
|
|
// 7LS - poke is view model rendering, testing, should move to somewhere more sensible
|
|
m_curViewModelState.m_CSMParallelSplit.GetLightState().m_SunLightShaderParams.m_bIsRenderingViewModels = false;
|
|
}
|
|
}
|
|
|
|
void CCascadeLightManager::BeginReflectionView()
|
|
{
|
|
if ( !m_bCSMIsActive )
|
|
return;
|
|
if ( ( GetCSMQualityMode() > CSMQUALITY_LOW ) && !cl_csm_force_no_csm_in_reflections.GetBool() )
|
|
return;
|
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->SetCascadedShadowMapping( false );
|
|
}
|
|
|
|
void CCascadeLightManager::EndReflectionView()
|
|
{
|
|
if ( !m_bCSMIsActive )
|
|
return;
|
|
if ( ( GetCSMQualityMode() > CSMQUALITY_LOW ) && !cl_csm_force_no_csm_in_reflections.GetBool() )
|
|
return;
|
|
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
pRenderContext->SetCascadedShadowMapping( true );
|
|
}
|
|
|
|
void CCascadeLightManager::DumpStatus()
|
|
{
|
|
Msg( "CSM enabled: %i\n", cl_csm_enabled.GetBool() );
|
|
Msg( "Render targets allocated: %i\n", m_bRenderTargetsAllocated );
|
|
Msg( "Depth texture resolution: %i\n", m_nDepthTextureResolution );
|
|
Msg( "Hardware config supportsCascadedShadowMapping: %i\n", g_pMaterialSystemHardwareConfig->SupportsCascadedShadowMapping() );
|
|
Msg( "Hardware config supportsShadowDepthTextures: %i\n", g_pMaterialSystemHardwareConfig->SupportsShadowDepthTextures() );
|
|
Msg( "Hardware config SupportsBilinearPCFSampling: %i\n", g_pMaterialSystemHardwareConfig->SupportsBilinearPCFSampling() );
|
|
Msg( "Current actual CSM quality level (%i=highest, will be forced to 0 if HW doesn't support bilinear PCF): %i\n", CSMQUALITY_TOTAL_MODES - 1, GetCSMQualityMode() );
|
|
Msg( "env_cascade_light entity exists: %i\n", C_CascadeLight::Get() != NULL );
|
|
if ( C_CascadeLight::Get() )
|
|
{
|
|
Msg( "env_cascade_light values:\n" );
|
|
Msg( "Shadow direction: %f %f %f\n", C_CascadeLight::Get()->GetShadowDirection().x, C_CascadeLight::Get()->GetShadowDirection().y, C_CascadeLight::Get()->GetShadowDirection().z );
|
|
Msg( "Light env shadow direction: %f %f %f\n", C_CascadeLight::Get()->GetEnvLightShadowDirection().x, C_CascadeLight::Get()->GetEnvLightShadowDirection().y, C_CascadeLight::Get()->GetEnvLightShadowDirection().z );
|
|
Msg( "Use light env shadow angles: %u\n", C_CascadeLight::Get()->UseLightEnvAngles() );
|
|
Msg( "Max shadow dist: %f\n", C_CascadeLight::Get()->GetMaxShadowDist() );
|
|
}
|
|
}
|
|
|
|
void CC_CSM_Status( const CCommand& args )
|
|
{
|
|
g_CascadeLightManager.DumpStatus();
|
|
}
|
|
|
|
CSMQualityMode_t CCascadeLightManager::GetCSMQualityMode()
|
|
{
|
|
if ( IsGameConsole() )
|
|
return CSMQUALITY_VERY_LOW;
|
|
|
|
if ( !g_pMaterialSystemHardwareConfig->SupportsBilinearPCFSampling() )
|
|
{
|
|
// DX9-class ATI cards: always using VERY_LOW, because the PCF sampling shader has hardcoded knowledge of the shadow depth texture's res
|
|
return CSMQUALITY_VERY_LOW;
|
|
}
|
|
|
|
return materials->GetCurrentConfigForVideoCard().GetCSMQualityMode();
|
|
}
|