2023-10-03 17:23:56 +03:00
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
2020-04-22 12:56:21 -04:00
//
// Purpose:
//
//===========================================================================//
# include "cbase.h"
# include "flashlighteffect.h"
# include "dlight.h"
# include "iefx.h"
# include "iviewrender.h"
# include "view.h"
# include "engine/ivdebugoverlay.h"
# include "tier0/vprof.h"
# include "tier1/KeyValues.h"
# include "toolframework_client.h"
# ifdef HL2_CLIENT_DLL
# include "c_basehlplayer.h"
# endif // HL2_CLIENT_DLL
# if defined( _X360 )
extern ConVar r_flashlightdepthres ;
# else
extern ConVar r_flashlightdepthres ;
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern ConVar r_flashlightdepthtexture ;
static ConVar r_swingflashlight ( " r_swingflashlight " , " 1 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightlockposition ( " r_flashlightlockposition " , " 0 " , FCVAR_CHEAT ) ;
2023-10-03 17:23:56 +03:00
static ConVar r_flashlightfov ( " r_flashlightfov " , " 53.0 " , FCVAR_CHEAT ) ;
ConVar r_flashlightoffsetx ( " r_flashlightoffsetright " , " 5.0 " , FCVAR_CHEAT ) ;
ConVar r_flashlightoffsety ( " r_flashlightoffsetup " , " -5.0 " , FCVAR_CHEAT ) ;
ConVar r_flashlightoffsetz ( " r_flashlightoffsetforward " , " 0.0 " , FCVAR_CHEAT ) ;
2020-04-22 12:56:21 -04:00
static ConVar r_flashlightnear ( " r_flashlightnear " , " 4.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightfar ( " r_flashlightfar " , " 750.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightconstant ( " r_flashlightconstant " , " 0.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightlinear ( " r_flashlightlinear " , " 100.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightquadratic ( " r_flashlightquadratic " , " 0.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightvisualizetrace ( " r_flashlightvisualizetrace " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightambient ( " r_flashlightambient " , " 0.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightshadowatten ( " r_flashlightshadowatten " , " 0.35 " , FCVAR_CHEAT ) ;
static ConVar r_flashlightladderdist ( " r_flashlightladderdist " , " 40.0 " , FCVAR_CHEAT ) ;
2023-10-03 17:23:56 +03:00
static ConVar r_flashlight_topdown ( " r_flashlight_topdown " , " 0 " ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
static ConVar r_flashlightnearoffsetscale ( " r_flashlightnearoffsetscale " , " 1.0 " , FCVAR_CHEAT ) ;
static ConVar r_flashlighttracedistcutoff ( " r_flashlighttracedistcutoff " , " 128 " ) ;
static ConVar r_flashlightbacktraceoffset ( " r_flashlightbacktraceoffset " , " 0.4 " , FCVAR_CHEAT ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
//-----------------------------------------------------------------------------
CFlashlightEffectManager & FlashlightEffectManager ( int32 nSplitscreenPlayerOverride )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
static CFlashlightEffectManager s_flashlightEffectManagerArray [ MAX_SPLITSCREEN_PLAYERS ] ;
if ( nSplitscreenPlayerOverride ! = - 1 )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
return s_flashlightEffectManagerArray [ nSplitscreenPlayerOverride ] ;
}
ASSERT_LOCAL_PLAYER_RESOLVABLE ( ) ;
return s_flashlightEffectManagerArray [ GET_ACTIVE_SPLITSCREEN_SLOT ( ) ] ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Purpose:
// Input : nEntIndex - The m_nEntIndex of the client entity that is creating us.
// vecPos - The position of the light emitter.
// vecDir - The direction of the light emission.
//-----------------------------------------------------------------------------
2023-10-03 17:23:56 +03:00
CFlashlightEffect : : CFlashlightEffect ( int nEntIndex , const char * pszTextureName , float flFov , float flFarZ , float flLinearAtten )
2020-04-22 12:56:21 -04:00
{
m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE ;
m_nEntIndex = nEntIndex ;
2023-10-03 17:23:56 +03:00
m_flCurrentPullBackDist = 1.0f ;
m_bMuzzleFlashEnabled = false ;
m_flMuzzleFlashBrightness = 1.0f ;
m_flFov = flFov ;
m_flFarZ = flFarZ ;
m_flLinearAtten = flLinearAtten ;
m_bCastsShadows = true ;
2020-04-22 12:56:21 -04:00
m_bIsOn = false ;
2023-10-03 17:23:56 +03:00
UpdateFlashlightTexture ( pszTextureName ) ;
m_MuzzleFlashTexture . Init ( " effects/muzzleflash_light " , TEXTURE_GROUP_OTHER , true ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CFlashlightEffect : : ~ CFlashlightEffect ( )
{
LightOff ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlashlightEffect : : TurnOn ( )
{
m_bIsOn = true ;
2023-10-03 17:23:56 +03:00
m_flCurrentPullBackDist = 1.0f ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
//-----------------------------------------------------------------------------
void CFlashlightEffect : : SetMuzzleFlashEnabled ( bool bEnabled , float flBrightness )
{
m_bMuzzleFlashEnabled = bEnabled ;
m_flMuzzleFlashBrightness = flBrightness ;
}
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFlashlightEffect : : TurnOff ( )
{
if ( m_bIsOn )
{
m_bIsOn = false ;
LightOff ( ) ;
}
}
// Custom trace filter that skips the player and the view model.
// If we don't do this, we'll end up having the light right in front of us all
// the time.
class CTraceFilterSkipPlayerAndViewModel : public CTraceFilter
{
public :
2023-10-03 17:23:56 +03:00
CTraceFilterSkipPlayerAndViewModel ( C_BasePlayer * pPlayer , bool bTracePlayers )
{
m_pPlayer = pPlayer ;
m_bSkipPlayers = ! bTracePlayers ;
{
m_pLowerBody = NULL ;
}
}
2020-04-22 12:56:21 -04:00
virtual bool ShouldHitEntity ( IHandleEntity * pServerEntity , int contentsMask )
{
// Test against the vehicle too?
// FLASHLIGHTFIXME: how do you know that you are actually inside of the vehicle?
C_BaseEntity * pEntity = EntityFromEntityHandle ( pServerEntity ) ;
if ( ! pEntity )
return true ;
2023-10-03 17:23:56 +03:00
if ( ( ToBaseViewModel ( pEntity ) ! = NULL ) | |
pEntity = = m_pPlayer | |
pEntity = = m_pLowerBody | |
( m_bSkipPlayers & & pEntity - > IsPlayer ( ) ) | |
2020-04-22 12:56:21 -04:00
pEntity - > GetCollisionGroup ( ) = = COLLISION_GROUP_DEBRIS | |
pEntity - > GetCollisionGroup ( ) = = COLLISION_GROUP_INTERACTIVE_DEBRIS )
{
return false ;
}
return true ;
}
2023-10-03 17:23:56 +03:00
private :
C_BaseEntity * m_pPlayer ;
C_BaseEntity * m_pLowerBody ;
bool m_bSkipPlayers ;
2020-04-22 12:56:21 -04:00
} ;
2023-10-03 17:23:56 +03:00
void C_BasePlayer : : GetFlashlightOffset ( const Vector & vecForward , const Vector & vecRight , const Vector & vecUp , Vector * pVecOffset ) const
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
* pVecOffset = r_flashlightoffsety . GetFloat ( ) * vecUp + r_flashlightoffsetx . GetFloat ( ) * vecRight + r_flashlightoffsetz . GetFloat ( ) * vecForward ;
}
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
ConVar r_flashlightmuzzleflashfov ( " r_flashlightmuzzleflashfov " , " 120 " , FCVAR_CHEAT ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
//-----------------------------------------------------------------------------
// Purpose: Update the flashlight for top down camera view
// (flashlight origin doesn't move around when you get near things)
//-----------------------------------------------------------------------------
void CFlashlightEffect : : UpdateLightTopDown ( const Vector & vecPos , const Vector & vecForward , const Vector & vecRight , const Vector & vecUp )
{
VPROF_BUDGET ( " CFlashlightEffect::UpdateLightTopDown " , VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
FlashlightState_t state ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
state . m_vecLightOrigin = vecPos ;
2020-04-22 12:56:21 -04:00
Vector vTarget = vecPos + vecForward * r_flashlightfar . GetFloat ( ) ;
// Work with these local copies of the basis for the rest of the function
2023-10-03 17:23:56 +03:00
Vector vDir = vTarget - vecPos ;
2020-04-22 12:56:21 -04:00
Vector vRight = vecRight ;
Vector vUp = vecUp ;
VectorNormalize ( vDir ) ;
VectorNormalize ( vRight ) ;
VectorNormalize ( vUp ) ;
// Orthonormalize the basis, since the flashlight texture projection will require this later...
vUp - = DotProduct ( vDir , vUp ) * vDir ;
VectorNormalize ( vUp ) ;
vRight - = DotProduct ( vDir , vRight ) * vDir ;
VectorNormalize ( vRight ) ;
vRight - = DotProduct ( vUp , vRight ) * vUp ;
VectorNormalize ( vRight ) ;
AssertFloatEquals ( DotProduct ( vDir , vRight ) , 0.0f , 1e-3 ) ;
AssertFloatEquals ( DotProduct ( vDir , vUp ) , 0.0f , 1e-3 ) ;
AssertFloatEquals ( DotProduct ( vRight , vUp ) , 0.0f , 1e-3 ) ;
2023-10-03 17:23:56 +03:00
BasisToQuaternion ( vDir , vRight , vUp , state . m_quatOrientation ) ;
state . m_fConstantAtten = r_flashlightconstant . GetFloat ( ) ;
state . m_fQuadraticAtten = r_flashlightquadratic . GetFloat ( ) ;
state . m_fLinearAtten = r_flashlightlinear . GetFloat ( ) ;
state . m_fHorizontalFOVDegrees = r_flashlightfov . GetFloat ( ) ;
state . m_fVerticalFOVDegrees = r_flashlightfov . GetFloat ( ) ;
state . m_Color [ 0 ] = 1.0f ;
state . m_Color [ 1 ] = 1.0f ;
state . m_Color [ 2 ] = 1.0f ;
state . m_Color [ 3 ] = r_flashlightambient . GetFloat ( ) ;
state . m_NearZ = r_flashlightnear . GetFloat ( ) + m_flCurrentPullBackDist ; // Push near plane out so that we don't clip the world when the flashlight pulls back
state . m_FarZ = state . m_FarZAtten = r_flashlightfar . GetFloat ( ) ; // Strictly speaking, these are different, but the game can treat them the same
state . m_bEnableShadows = state . m_bEnableShadows & & r_flashlightdepthtexture . GetBool ( ) ;
state . m_flShadowMapResolution = r_flashlightdepthres . GetInt ( ) ;
state . m_pSpotlightTexture = m_FlashlightTexture ;
state . m_nSpotlightTextureFrame = 0 ;
state . m_flShadowAtten = r_flashlightshadowatten . GetFloat ( ) ;
state . m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig - > GetShadowSlopeScaleDepthBias ( ) ;
state . m_flShadowDepthBias = g_pMaterialSystemHardwareConfig - > GetShadowDepthBias ( ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
if ( m_FlashlightHandle = = CLIENTSHADOW_INVALID_HANDLE )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
m_FlashlightHandle = g_pClientShadowMgr - > CreateFlashlight ( state ) ;
}
else
{
if ( ! r_flashlightlockposition . GetBool ( ) )
{
g_pClientShadowMgr - > UpdateFlashlightState ( m_FlashlightHandle , state ) ;
}
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
g_pClientShadowMgr - > UpdateProjectedTexture ( m_FlashlightHandle , true ) ;
// Kill the old flashlight method if we have one.
// FIXME: This doesn't compile
// LightOffOld();
# ifndef NO_TOOLFRAMEWORK
if ( clienttools - > IsInRecordingMode ( ) )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
KeyValues * msg = new KeyValues ( " FlashlightState " ) ;
msg - > SetFloat ( " time " , gpGlobals - > curtime ) ;
msg - > SetInt ( " entindex " , m_nEntIndex ) ;
msg - > SetInt ( " flashlightHandle " , m_FlashlightHandle ) ;
msg - > SetPtr ( " flashlightState " , & state ) ;
ToolFramework_PostToolMessage ( HTOOLHANDLE_INVALID , msg ) ;
msg - > deleteThis ( ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Do the headlight
//-----------------------------------------------------------------------------
void CFlashlightEffect : : UpdateLight ( int nEntIdx , const Vector & vecPos , const Vector & vecForward , const Vector & vecRight ,
const Vector & vecUp , float flFov , float flFarZ , float flLinearAtten , bool castsShadows , const char * pTextureName )
{
VPROF_BUDGET ( __FUNCTION__ , VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ) ;
if ( r_flashlight_topdown . GetBool ( ) )
{
UpdateLightTopDown ( vecPos , vecForward , vecRight , vecUp ) ;
return ;
}
m_nEntIndex = nEntIdx ;
m_flFov = flFov ;
m_flFarZ = flFarZ ;
m_flLinearAtten = flLinearAtten ;
if ( m_bCastsShadows ! = castsShadows )
{
// requires recreation of the flashlight
LightOff ( ) ;
}
m_bCastsShadows = castsShadows ;
UpdateFlashlightTexture ( pTextureName ) ;
FlashlightState_t state ;
if ( UpdateDefaultFlashlightState ( state , vecPos , vecForward , vecRight , vecUp , castsShadows ) = = false )
{
return ;
}
if ( m_FlashlightHandle = = CLIENTSHADOW_INVALID_HANDLE )
{
m_FlashlightHandle = g_pClientShadowMgr - > CreateFlashlight ( state ) ;
}
else
{
if ( ! r_flashlightlockposition . GetBool ( ) )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
g_pClientShadowMgr - > UpdateFlashlightState ( m_FlashlightHandle , state ) ;
2020-04-22 12:56:21 -04:00
}
}
2023-10-03 17:23:56 +03:00
g_pClientShadowMgr - > UpdateProjectedTexture ( m_FlashlightHandle , true ) ;
# ifndef NO_TOOLFRAMEWORK
if ( clienttools - > IsInRecordingMode ( ) )
{
KeyValues * msg = new KeyValues ( " FlashlightState " ) ;
msg - > SetFloat ( " time " , gpGlobals - > curtime ) ;
msg - > SetInt ( " entindex " , m_nEntIndex ) ;
msg - > SetInt ( " flashlightHandle " , m_FlashlightHandle ) ;
msg - > SetPtr ( " flashlightState " , & state ) ;
ToolFramework_PostToolMessage ( HTOOLHANDLE_INVALID , msg ) ;
msg - > deleteThis ( ) ;
}
# endif
}
void CFlashlightEffect : : UpdateLight ( int nEntIdx , const Vector & vecPos , const Vector & vecDir , const Vector & vecRight , const Vector & vecUp ,
float flFov , bool castsShadows , ITexture * pFlashlightTexture , const Vector & vecBrightness ,
bool bTracePlayers )
{
VPROF_BUDGET ( __FUNCTION__ , VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ) ;
m_nEntIndex = nEntIdx ;
if ( m_bCastsShadows ! = castsShadows )
{
// requires recreation of the flashlight
LightOff ( ) ;
}
m_bCastsShadows = castsShadows ;
FlashlightState_t state ;
if ( UpdateDefaultFlashlightState ( state , vecPos , vecDir , vecRight , vecUp , castsShadows , bTracePlayers ) = = false )
{
return ;
}
state . m_fHorizontalFOVDegrees = flFov ;
state . m_fVerticalFOVDegrees = flFov ;
state . m_Color [ 0 ] = vecBrightness . x ;
state . m_Color [ 1 ] = vecBrightness . y ;
state . m_Color [ 2 ] = vecBrightness . z ;
if ( pFlashlightTexture )
{
state . m_pSpotlightTexture = pFlashlightTexture ;
state . m_pProjectedMaterial = NULL ;
}
if ( m_FlashlightHandle = = CLIENTSHADOW_INVALID_HANDLE )
{
m_FlashlightHandle = g_pClientShadowMgr - > CreateFlashlight ( state ) ;
}
2020-04-22 12:56:21 -04:00
else
{
2023-10-03 17:23:56 +03:00
if ( ! r_flashlightlockposition . GetBool ( ) )
{
g_pClientShadowMgr - > UpdateFlashlightState ( m_FlashlightHandle , state ) ;
}
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
g_pClientShadowMgr - > UpdateProjectedTexture ( m_FlashlightHandle , true ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
# ifndef NO_TOOLFRAMEWORK
if ( clienttools - > IsInRecordingMode ( ) )
{
KeyValues * msg = new KeyValues ( " FlashlightState " ) ;
msg - > SetFloat ( " time " , gpGlobals - > curtime ) ;
msg - > SetInt ( " entindex " , m_nEntIndex ) ;
msg - > SetInt ( " flashlightHandle " , m_FlashlightHandle ) ;
msg - > SetPtr ( " flashlightState " , & state ) ;
ToolFramework_PostToolMessage ( HTOOLHANDLE_INVALID , msg ) ;
msg - > deleteThis ( ) ;
}
# endif
}
bool CFlashlightEffect : : UpdateDefaultFlashlightState ( FlashlightState_t & state , const Vector & vecPos , const Vector & vecForward ,
const Vector & vecRight , const Vector & vecUp , bool castsShadows , bool bTracePlayers )
{
VPROF_BUDGET ( __FUNCTION__ , VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING ) ;
if ( ! m_bIsOn )
{
// return;
}
if ( ComputeLightPosAndOrientation ( vecPos , vecForward , vecRight , vecUp , state . m_vecLightOrigin , state . m_quatOrientation , bTracePlayers ) = = false )
{
return false ;
}
2020-04-22 12:56:21 -04:00
state . m_fQuadraticAtten = r_flashlightquadratic . GetFloat ( ) ;
bool bFlicker = false ;
# ifdef HL2_EPISODIC
C_BaseHLPlayer * pPlayer = ( C_BaseHLPlayer * ) C_BasePlayer : : GetLocalPlayer ( ) ;
if ( pPlayer )
{
float flBatteryPower = ( pPlayer - > m_HL2Local . m_flFlashBattery > = 0.0f ) ? ( pPlayer - > m_HL2Local . m_flFlashBattery ) : pPlayer - > m_HL2Local . m_flSuitPower ;
if ( flBatteryPower < = 10.0f )
{
float flScale ;
if ( flBatteryPower > = 0.0f )
{
flScale = ( flBatteryPower < = 4.5f ) ? SimpleSplineRemapVal ( flBatteryPower , 4.5f , 0.0f , 1.0f , 0.0f ) : 1.0f ;
}
else
{
flScale = SimpleSplineRemapVal ( flBatteryPower , 10.0f , 4.8f , 1.0f , 0.0f ) ;
}
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
flScale = clamp ( flScale , 0.0f , 1.0f ) ;
if ( flScale < 0.35f )
{
float flFlicker = cosf ( gpGlobals - > curtime * 6.0f ) * sinf ( gpGlobals - > curtime * 15.0f ) ;
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
if ( flFlicker > 0.25f & & flFlicker < 0.75f )
{
// On
state . m_fLinearAtten = r_flashlightlinear . GetFloat ( ) * flScale ;
}
else
{
// Off
state . m_fLinearAtten = 0.0f ;
}
}
else
{
float flNoise = cosf ( gpGlobals - > curtime * 7.0f ) * sinf ( gpGlobals - > curtime * 25.0f ) ;
state . m_fLinearAtten = r_flashlightlinear . GetFloat ( ) * flScale + 1.5f * flNoise ;
}
state . m_fHorizontalFOVDegrees = r_flashlightfov . GetFloat ( ) - ( 16.0f * ( 1.0f - flScale ) ) ;
state . m_fVerticalFOVDegrees = r_flashlightfov . GetFloat ( ) - ( 16.0f * ( 1.0f - flScale ) ) ;
2023-10-03 17:23:56 +03:00
2020-04-22 12:56:21 -04:00
bFlicker = true ;
}
}
# endif // HL2_EPISODIC
if ( bFlicker = = false )
{
2023-10-03 17:23:56 +03:00
if ( m_flLinearAtten > 0.0f )
{
state . m_fLinearAtten = m_flLinearAtten ;
}
else
{
state . m_fLinearAtten = r_flashlightlinear . GetFloat ( ) ;
}
if ( m_flFov > 0.0f )
{
state . m_fHorizontalFOVDegrees = m_flFov ;
state . m_fVerticalFOVDegrees = m_flFov ;
}
else
{
state . m_fHorizontalFOVDegrees = r_flashlightfov . GetFloat ( ) ;
state . m_fVerticalFOVDegrees = r_flashlightfov . GetFloat ( ) ;
}
if ( m_bMuzzleFlashEnabled )
{
state . m_fHorizontalFOVDegrees = state . m_fVerticalFOVDegrees = r_flashlightmuzzleflashfov . GetFloat ( ) ;
}
2020-04-22 12:56:21 -04:00
}
state . m_fConstantAtten = r_flashlightconstant . GetFloat ( ) ;
state . m_Color [ 0 ] = 1.0f ;
state . m_Color [ 1 ] = 1.0f ;
state . m_Color [ 2 ] = 1.0f ;
state . m_Color [ 3 ] = r_flashlightambient . GetFloat ( ) ;
2023-10-03 17:23:56 +03:00
state . m_NearZ = r_flashlightnear . GetFloat ( ) + r_flashlightnearoffsetscale . GetFloat ( ) * m_flCurrentPullBackDist ; // Optionally push near plane out so that we don't clip the world when the flashlight pulls back
if ( m_flFarZ > 0.0f )
{
state . m_FarZ = state . m_FarZAtten = m_flFarZ ; // Strictly speaking, these are different, but the game can treat them the same
}
else
{
state . m_FarZ = state . m_FarZAtten = r_flashlightfar . GetFloat ( ) ; // Strictly speaking, these are different, but the game can treat them the same
}
state . m_bEnableShadows = castsShadows & & r_flashlightdepthtexture . GetBool ( ) ;
2020-04-22 12:56:21 -04:00
state . m_flShadowMapResolution = r_flashlightdepthres . GetInt ( ) ;
2023-10-03 17:23:56 +03:00
if ( m_bMuzzleFlashEnabled )
{
state . m_pSpotlightTexture = m_MuzzleFlashTexture ;
state . m_pProjectedMaterial = NULL ;
state . m_Color [ 0 ] = m_flMuzzleFlashBrightness ;
state . m_Color [ 1 ] = m_flMuzzleFlashBrightness ;
state . m_Color [ 2 ] = m_flMuzzleFlashBrightness ;
}
else
{
state . m_pSpotlightTexture = m_FlashlightTexture ;
state . m_pProjectedMaterial = NULL ;
}
2020-04-22 12:56:21 -04:00
state . m_nSpotlightTextureFrame = 0 ;
state . m_flShadowAtten = r_flashlightshadowatten . GetFloat ( ) ;
2023-10-03 17:23:56 +03:00
state . m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig - > GetShadowSlopeScaleDepthBias ( ) ;
state . m_flShadowDepthBias = g_pMaterialSystemHardwareConfig - > GetShadowDepthBias ( ) ;
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
return true ;
}
void CFlashlightEffect : : UpdateFlashlightTexture ( const char * pTextureName )
{
static const char * pEmptyString = " " ;
if ( pTextureName = = NULL )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
pTextureName = pEmptyString ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
if ( ! m_FlashlightTexture . IsValid ( ) | |
V_stricmp ( m_textureName , pTextureName ) ! = 0 )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
if ( pTextureName = = pEmptyString )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
m_FlashlightTexture . Init ( " effects/flashlight001 " , TEXTURE_GROUP_OTHER , true ) ;
}
else
{
m_FlashlightTexture . Init ( pTextureName , TEXTURE_GROUP_OTHER , true ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
V_strncpy ( m_textureName , pTextureName , sizeof ( m_textureName ) ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
}
2020-04-22 12:56:21 -04:00
2023-10-03 17:23:56 +03:00
bool CFlashlightEffect : : ComputeLightPosAndOrientation ( const Vector & vecPos , const Vector & vecForward , const Vector & vecRight , const Vector & vecUp ,
Vector & vecFinalPos , Quaternion & quatOrientation , bool bTracePlayers )
{
const float flEpsilon = 0.1f ; // Offset flashlight position along vecUp
float flDistCutoff = r_flashlighttracedistcutoff . GetFloat ( ) ;
const float flDistDrag = 0.2 ;
bool bDebugVis = r_flashlightvisualizetrace . GetBool ( ) ;
C_BasePlayer * pPlayer = UTIL_PlayerByIndex ( m_nEntIndex ) ;
if ( ! pPlayer )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( ! pPlayer )
{
Assert ( false ) ;
return false ;
}
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
// We will lock some of the flashlight params if player is on a ladder, to prevent oscillations due to the trace-rays
bool bPlayerOnLadder = ( pPlayer - > GetMoveType ( ) = = MOVETYPE_LADDER ) ;
CTraceFilterSkipPlayerAndViewModel traceFilter ( pPlayer , bTracePlayers ) ;
// Vector vOrigin = vecPos + r_flashlightoffsety.GetFloat() * vecUp;
Vector vecOffset ;
pPlayer - > GetFlashlightOffset ( vecForward , vecRight , vecUp , & vecOffset ) ;
Vector vOrigin = vecPos + vecOffset ;
// Not on ladder...trace a hull
if ( ! bPlayerOnLadder )
{
Vector vecPlayerEyePos = pPlayer - > GetRenderOrigin ( ) + pPlayer - > GetViewOffset ( ) ;
trace_t pmOriginTrace ;
UTIL_TraceHull ( vecPlayerEyePos , vOrigin , Vector ( - 2 , - 2 , - 2 ) , Vector ( 2 , 2 , 2 ) , ( MASK_SOLID & ~ ( CONTENTS_HITBOX ) ) | CONTENTS_WINDOW | CONTENTS_GRATE , & traceFilter , & pmOriginTrace ) ; //1
if ( bDebugVis )
{
debugoverlay - > AddBoxOverlay ( pmOriginTrace . endpos , Vector ( - 2 , - 2 , - 2 ) , Vector ( 2 , 2 , 2 ) , QAngle ( 0 , 0 , 0 ) , 0 , 255 , 0 , 16 , 0 ) ;
if ( pmOriginTrace . DidHit ( ) | | pmOriginTrace . startsolid )
{
debugoverlay - > AddLineOverlay ( pmOriginTrace . startpos , pmOriginTrace . endpos , 255 , 128 , 128 , true , 0 ) ;
}
else
{
debugoverlay - > AddLineOverlay ( pmOriginTrace . startpos , pmOriginTrace . endpos , 255 , 0 , 0 , true , 0 ) ;
}
}
if ( pmOriginTrace . DidHit ( ) | | pmOriginTrace . startsolid )
{
vOrigin = pmOriginTrace . endpos ;
}
else
{
if ( pPlayer - > m_vecFlashlightOrigin ! = vecPlayerEyePos )
{
vOrigin = vecPos ;
}
}
}
else // on ladder...skip the above hull trace
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
vOrigin = vecPos ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
// Now do a trace along the flashlight direction to ensure there is nothing within range to pull back from
int iMask = MASK_OPAQUE_AND_NPCS ;
iMask & = ~ CONTENTS_HITBOX ;
iMask | = CONTENTS_WINDOW | CONTENTS_GRATE | CONTENTS_IGNORE_NODRAW_OPAQUE ;
Vector vTarget = vOrigin + vecForward * r_flashlightfar . GetFloat ( ) ;
// Work with these local copies of the basis for the rest of the function
Vector vDir = vTarget - vOrigin ;
Vector vRight = vecRight ;
Vector vUp = vecUp ;
VectorNormalize ( vDir ) ;
VectorNormalize ( vRight ) ;
VectorNormalize ( vUp ) ;
// Orthonormalize the basis, since the flashlight texture projection will require this later...
vUp - = DotProduct ( vDir , vUp ) * vDir ;
VectorNormalize ( vUp ) ;
vRight - = DotProduct ( vDir , vRight ) * vDir ;
VectorNormalize ( vRight ) ;
vRight - = DotProduct ( vUp , vRight ) * vUp ;
VectorNormalize ( vRight ) ;
AssertFloatEquals ( DotProduct ( vDir , vRight ) , 0.0f , 1e-3 ) ;
AssertFloatEquals ( DotProduct ( vDir , vUp ) , 0.0f , 1e-3 ) ;
AssertFloatEquals ( DotProduct ( vRight , vUp ) , 0.0f , 1e-3 ) ;
trace_t pmDirectionTrace ;
UTIL_TraceHull ( vOrigin , vTarget , Vector ( - 1.5 , - 1.5 , - 1.5 ) , Vector ( 1.5 , 1.5 , 1.5 ) , iMask , & traceFilter , & pmDirectionTrace ) ; //.5
if ( bDebugVis )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
debugoverlay - > AddBoxOverlay ( pmDirectionTrace . endpos , Vector ( - 4 , - 4 , - 4 ) , Vector ( 4 , 4 , 4 ) , QAngle ( 0 , 0 , 0 ) , 0 , 0 , 255 , 16 , 0 ) ;
debugoverlay - > AddLineOverlay ( vOrigin , pmDirectionTrace . endpos , 255 , 0 , 0 , false , 0 ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
float flTargetPullBackDist = 0.0f ;
float flDist = ( pmDirectionTrace . endpos - vOrigin ) . Length ( ) ;
if ( flDist < flDistCutoff )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
// We have an intersection with our cutoff range
// Determine how far to pull back, then trace to see if we are clear
float flPullBackDist = bPlayerOnLadder ? r_flashlightladderdist . GetFloat ( ) : flDistCutoff - flDist ; // Fixed pull-back distance if on ladder
flTargetPullBackDist = flPullBackDist ;
if ( ! bPlayerOnLadder )
{
trace_t pmBackTrace ;
// start the trace away from the actual trace origin a bit, to avoid getting stuck on small, close "lips"
UTIL_TraceHull ( vOrigin - vDir * ( flDistCutoff * r_flashlightbacktraceoffset . GetFloat ( ) ) , vOrigin - vDir * ( flPullBackDist - flEpsilon ) ,
Vector ( - 1.5f , - 1.5f , - 1.5f ) , Vector ( 1.5f , 1.5f , 1.5f ) , iMask , & traceFilter , & pmBackTrace ) ;
if ( bDebugVis )
{
debugoverlay - > AddLineOverlay ( pmBackTrace . startpos , pmBackTrace . endpos , 255 , 0 , 255 , true , 0 ) ;
}
if ( pmBackTrace . DidHit ( ) )
{
// We have an intersection behind us as well, so limit our flTargetPullBackDist
float flMaxDist = ( pmBackTrace . endpos - vOrigin ) . Length ( ) - flEpsilon ;
flTargetPullBackDist = MIN ( flMaxDist , flTargetPullBackDist ) ;
//m_flCurrentPullBackDist = MIN( flMaxDist, m_flCurrentPullBackDist ); // possible pop
}
}
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
if ( bDebugVis )
2020-04-22 12:56:21 -04:00
{
2023-10-03 17:23:56 +03:00
// visualize pullback
debugoverlay - > AddBoxOverlay ( vOrigin - vDir * m_flCurrentPullBackDist , Vector ( - 2 , - 2 , - 2 ) , Vector ( 2 , 2 , 2 ) , QAngle ( 0 , 0 , 0 ) , 255 , 255 , 0 , 16 , 0 ) ;
debugoverlay - > AddBoxOverlay ( vOrigin - vDir * flTargetPullBackDist , Vector ( - 1 , - 1 , - 1 ) , Vector ( 1 , 1 , 1 ) , QAngle ( 0 , 0 , 0 ) , 128 , 128 , 0 , 16 , 0 ) ;
2020-04-22 12:56:21 -04:00
}
2023-10-03 17:23:56 +03:00
m_flCurrentPullBackDist = Lerp ( flDistDrag , m_flCurrentPullBackDist , flTargetPullBackDist ) ;
m_flCurrentPullBackDist = MIN ( m_flCurrentPullBackDist , flDistCutoff ) ; // clamp to max pullback dist
vOrigin = vOrigin - vDir * m_flCurrentPullBackDist ;
vecFinalPos = vOrigin ;
BasisToQuaternion ( vDir , vRight , vUp , quatOrientation ) ;
return true ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
2023-10-03 17:23:56 +03:00
void CFlashlightEffect : : LightOff ( )
2020-04-22 12:56:21 -04:00
{
# ifndef NO_TOOLFRAMEWORK
if ( clienttools - > IsInRecordingMode ( ) )
{
KeyValues * msg = new KeyValues ( " FlashlightState " ) ;
msg - > SetFloat ( " time " , gpGlobals - > curtime ) ;
msg - > SetInt ( " entindex " , m_nEntIndex ) ;
msg - > SetInt ( " flashlightHandle " , m_FlashlightHandle ) ;
msg - > SetPtr ( " flashlightState " , NULL ) ;
ToolFramework_PostToolMessage ( HTOOLHANDLE_INVALID , msg ) ;
msg - > deleteThis ( ) ;
}
# endif
// Clear out the light
if ( m_FlashlightHandle ! = CLIENTSHADOW_INVALID_HANDLE )
{
g_pClientShadowMgr - > DestroyFlashlight ( m_FlashlightHandle ) ;
m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE ;
}
}
CHeadlightEffect : : CHeadlightEffect ( )
{
}
CHeadlightEffect : : ~ CHeadlightEffect ( )
{
}
void CHeadlightEffect : : UpdateLight ( const Vector & vecPos , const Vector & vecDir , const Vector & vecRight , const Vector & vecUp , int nDistance )
{
if ( IsOn ( ) = = false )
return ;
FlashlightState_t state ;
Vector basisX , basisY , basisZ ;
basisX = vecDir ;
basisY = vecRight ;
basisZ = vecUp ;
VectorNormalize ( basisX ) ;
VectorNormalize ( basisY ) ;
VectorNormalize ( basisZ ) ;
BasisToQuaternion ( basisX , basisY , basisZ , state . m_quatOrientation ) ;
state . m_vecLightOrigin = vecPos ;
state . m_fHorizontalFOVDegrees = 45.0f ;
state . m_fVerticalFOVDegrees = 30.0f ;
state . m_fQuadraticAtten = r_flashlightquadratic . GetFloat ( ) ;
state . m_fLinearAtten = r_flashlightlinear . GetFloat ( ) ;
state . m_fConstantAtten = r_flashlightconstant . GetFloat ( ) ;
state . m_Color [ 0 ] = 1.0f ;
state . m_Color [ 1 ] = 1.0f ;
state . m_Color [ 2 ] = 1.0f ;
state . m_Color [ 3 ] = r_flashlightambient . GetFloat ( ) ;
state . m_NearZ = r_flashlightnear . GetFloat ( ) ;
state . m_FarZ = r_flashlightfar . GetFloat ( ) ;
state . m_bEnableShadows = true ;
state . m_pSpotlightTexture = m_FlashlightTexture ;
2023-10-03 17:23:56 +03:00
state . m_pProjectedMaterial = NULL ;
2020-04-22 12:56:21 -04:00
state . m_nSpotlightTextureFrame = 0 ;
if ( GetFlashlightHandle ( ) = = CLIENTSHADOW_INVALID_HANDLE )
{
SetFlashlightHandle ( g_pClientShadowMgr - > CreateFlashlight ( state ) ) ;
}
else
{
g_pClientShadowMgr - > UpdateFlashlightState ( GetFlashlightHandle ( ) , state ) ;
}
g_pClientShadowMgr - > UpdateProjectedTexture ( GetFlashlightHandle ( ) , true ) ;
}