802 lines
26 KiB
C++
802 lines
26 KiB
C++
//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// 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( _GAMECONSOLE )
|
|
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 );
|
|
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 );
|
|
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 );
|
|
static ConVar r_flashlight_topdown( "r_flashlight_topdown", "0" );
|
|
|
|
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 );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CFlashlightEffectManager & FlashlightEffectManager( int32 nSplitscreenPlayerOverride )
|
|
{
|
|
static CFlashlightEffectManager s_flashlightEffectManagerArray[ MAX_SPLITSCREEN_PLAYERS ];
|
|
|
|
if ( nSplitscreenPlayerOverride != -1 )
|
|
{
|
|
return s_flashlightEffectManagerArray[ nSplitscreenPlayerOverride ];
|
|
}
|
|
|
|
ASSERT_LOCAL_PLAYER_RESOLVABLE();
|
|
return s_flashlightEffectManagerArray[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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.
|
|
//-----------------------------------------------------------------------------
|
|
CFlashlightEffect::CFlashlightEffect(int nEntIndex, const char *pszTextureName, float flFov, float flFarZ, float flLinearAtten )
|
|
{
|
|
m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE;
|
|
m_nEntIndex = nEntIndex;
|
|
|
|
m_flCurrentPullBackDist = 1.0f;
|
|
|
|
m_bMuzzleFlashEnabled = false;
|
|
m_flMuzzleFlashBrightness = 1.0f;
|
|
|
|
m_flFov = flFov;
|
|
m_flFarZ = flFarZ;
|
|
m_flLinearAtten = flLinearAtten;
|
|
m_bCastsShadows = true;
|
|
|
|
m_bIsOn = false;
|
|
|
|
UpdateFlashlightTexture( pszTextureName );
|
|
m_MuzzleFlashTexture.Init( "effects/flashlight_freezecam", TEXTURE_GROUP_OTHER, true, TEXTUREFLAGS_SRGB );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CFlashlightEffect::~CFlashlightEffect()
|
|
{
|
|
LightOff();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CFlashlightEffect::TurnOn()
|
|
{
|
|
m_bIsOn = true;
|
|
m_flCurrentPullBackDist = 1.0f;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void CFlashlightEffect::SetMuzzleFlashEnabled( bool bEnabled, float flBrightness )
|
|
{
|
|
m_bMuzzleFlashEnabled = bEnabled;
|
|
m_flMuzzleFlashBrightness = flBrightness;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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:
|
|
CTraceFilterSkipPlayerAndViewModel( C_BasePlayer *pPlayer, bool bTracePlayers )
|
|
{
|
|
m_pPlayer = pPlayer;
|
|
m_bSkipPlayers = !bTracePlayers;
|
|
|
|
{
|
|
m_pLowerBody = NULL;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if ( ( ToBaseViewModel( pEntity ) != NULL ) ||
|
|
pEntity == m_pPlayer ||
|
|
pEntity == m_pLowerBody ||
|
|
( m_bSkipPlayers && pEntity->IsPlayer() ) ||
|
|
pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
|
|
pEntity->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
C_BaseEntity *m_pPlayer;
|
|
C_BaseEntity *m_pLowerBody;
|
|
bool m_bSkipPlayers;
|
|
};
|
|
|
|
void C_BasePlayer::GetFlashlightOffset( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, Vector *pVecOffset ) const
|
|
{
|
|
*pVecOffset = r_flashlightoffsety.GetFloat() * vecUp + r_flashlightoffsetx.GetFloat() * vecRight + r_flashlightoffsetz.GetFloat() * vecForward;
|
|
}
|
|
|
|
ConVar r_flashlightmuzzleflashfov( "r_flashlightmuzzleflashfov", "120", FCVAR_CHEAT );
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 );
|
|
|
|
FlashlightState_t state;
|
|
|
|
state.m_vecLightOrigin = vecPos;
|
|
|
|
Vector vTarget = vecPos + vecForward * r_flashlightfar.GetFloat();
|
|
|
|
// Work with these local copies of the basis for the rest of the function
|
|
Vector vDir = vTarget - vecPos;
|
|
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 );
|
|
|
|
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();
|
|
|
|
if( m_FlashlightHandle == CLIENTSHADOW_INVALID_HANDLE )
|
|
{
|
|
m_FlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state );
|
|
}
|
|
else
|
|
{
|
|
if( !r_flashlightlockposition.GetBool() )
|
|
{
|
|
g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
|
|
}
|
|
}
|
|
|
|
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() )
|
|
{
|
|
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() )
|
|
{
|
|
g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
|
|
}
|
|
}
|
|
|
|
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 );
|
|
}
|
|
else
|
|
{
|
|
if( !r_flashlightlockposition.GetBool() )
|
|
{
|
|
g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
flScale = clamp( flScale, 0.0f, 1.0f );
|
|
|
|
if ( flScale < 0.35f )
|
|
{
|
|
float flFlicker = cosf( gpGlobals->curtime * 6.0f ) * sinf( gpGlobals->curtime * 15.0f );
|
|
|
|
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) );
|
|
|
|
bFlicker = true;
|
|
}
|
|
}
|
|
#endif // HL2_EPISODIC
|
|
|
|
if ( bFlicker == false )
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
|
|
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() + 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();
|
|
state.m_flShadowMapResolution = r_flashlightdepthres.GetInt();
|
|
|
|
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;
|
|
}
|
|
|
|
state.m_nSpotlightTextureFrame = 0;
|
|
|
|
state.m_flShadowAtten = r_flashlightshadowatten.GetFloat();
|
|
state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();
|
|
state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CFlashlightEffect::UpdateFlashlightTexture( const char* pTextureName )
|
|
{
|
|
static const char *pEmptyString = "";
|
|
|
|
if ( pTextureName == NULL )
|
|
{
|
|
pTextureName = pEmptyString;
|
|
}
|
|
|
|
if ( !m_FlashlightTexture.IsValid() ||
|
|
V_stricmp( m_textureName, pTextureName ) != 0 )
|
|
{
|
|
if ( pTextureName == pEmptyString )
|
|
{
|
|
m_FlashlightTexture.Init( "effects/flashlight001", TEXTURE_GROUP_OTHER, true );
|
|
}
|
|
else
|
|
{
|
|
m_FlashlightTexture.Init( pTextureName, TEXTURE_GROUP_OTHER, true );
|
|
}
|
|
V_strncpy( m_textureName, pTextureName, sizeof( m_textureName ) );
|
|
}
|
|
}
|
|
|
|
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 )
|
|
{
|
|
#ifdef TERROR
|
|
pPlayer = C_TerrorPlayer::GetLocalOrInEyeTerrorPlayer();
|
|
#else
|
|
pPlayer = C_BasePlayer::GetLocalPlayer();
|
|
#endif
|
|
if ( !pPlayer )
|
|
{
|
|
Assert( false );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
{
|
|
vOrigin = vecPos;
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
float flTargetPullBackDist = 0.0f;
|
|
float flDist = (pmDirectionTrace.endpos - vOrigin).Length();
|
|
|
|
if ( flDist < flDistCutoff )
|
|
{
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bDebugVis )
|
|
{
|
|
// 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 );
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CFlashlightEffect::LightOff()
|
|
{
|
|
#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;
|
|
state.m_pProjectedMaterial = NULL;
|
|
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 );
|
|
}
|
|
|