Counter Strike : Global Offensive Source Code
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.

801 lines
26 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "flashlighteffect.h"
  8. #include "dlight.h"
  9. #include "iefx.h"
  10. #include "iviewrender.h"
  11. #include "view.h"
  12. #include "engine/ivdebugoverlay.h"
  13. #include "tier0/vprof.h"
  14. #include "tier1/keyvalues.h"
  15. #include "toolframework_client.h"
  16. #ifdef HL2_CLIENT_DLL
  17. #include "c_basehlplayer.h"
  18. #endif // HL2_CLIENT_DLL
  19. #if defined( _GAMECONSOLE )
  20. extern ConVar r_flashlightdepthres;
  21. #else
  22. extern ConVar r_flashlightdepthres;
  23. #endif
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. extern ConVar r_flashlightdepthtexture;
  27. static ConVar r_swingflashlight( "r_swingflashlight", "1", FCVAR_CHEAT );
  28. static ConVar r_flashlightlockposition( "r_flashlightlockposition", "0", FCVAR_CHEAT );
  29. static ConVar r_flashlightfov( "r_flashlightfov", "53.0", FCVAR_CHEAT );
  30. ConVar r_flashlightoffsetx( "r_flashlightoffsetright", "5.0", FCVAR_CHEAT );
  31. ConVar r_flashlightoffsety( "r_flashlightoffsetup", "-5.0", FCVAR_CHEAT );
  32. ConVar r_flashlightoffsetz( "r_flashlightoffsetforward", "0.0", FCVAR_CHEAT );
  33. static ConVar r_flashlightnear( "r_flashlightnear", "4.0", FCVAR_CHEAT );
  34. static ConVar r_flashlightfar( "r_flashlightfar", "750.0", FCVAR_CHEAT );
  35. static ConVar r_flashlightconstant( "r_flashlightconstant", "0.0", FCVAR_CHEAT );
  36. static ConVar r_flashlightlinear( "r_flashlightlinear", "100.0", FCVAR_CHEAT );
  37. static ConVar r_flashlightquadratic( "r_flashlightquadratic", "0.0", FCVAR_CHEAT );
  38. static ConVar r_flashlightvisualizetrace( "r_flashlightvisualizetrace", "0", FCVAR_CHEAT );
  39. static ConVar r_flashlightambient( "r_flashlightambient", "0.0", FCVAR_CHEAT );
  40. static ConVar r_flashlightshadowatten( "r_flashlightshadowatten", "0.35", FCVAR_CHEAT );
  41. static ConVar r_flashlightladderdist( "r_flashlightladderdist", "40.0", FCVAR_CHEAT );
  42. static ConVar r_flashlight_topdown( "r_flashlight_topdown", "0" );
  43. static ConVar r_flashlightnearoffsetscale( "r_flashlightnearoffsetscale", "1.0", FCVAR_CHEAT );
  44. static ConVar r_flashlighttracedistcutoff( "r_flashlighttracedistcutoff", "128" );
  45. static ConVar r_flashlightbacktraceoffset( "r_flashlightbacktraceoffset", "0.4", FCVAR_CHEAT );
  46. //-----------------------------------------------------------------------------
  47. CFlashlightEffectManager & FlashlightEffectManager( int32 nSplitscreenPlayerOverride )
  48. {
  49. static CFlashlightEffectManager s_flashlightEffectManagerArray[ MAX_SPLITSCREEN_PLAYERS ];
  50. if ( nSplitscreenPlayerOverride != -1 )
  51. {
  52. return s_flashlightEffectManagerArray[ nSplitscreenPlayerOverride ];
  53. }
  54. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  55. return s_flashlightEffectManagerArray[ GET_ACTIVE_SPLITSCREEN_SLOT() ];
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. // Input : nEntIndex - The m_nEntIndex of the client entity that is creating us.
  60. // vecPos - The position of the light emitter.
  61. // vecDir - The direction of the light emission.
  62. //-----------------------------------------------------------------------------
  63. CFlashlightEffect::CFlashlightEffect(int nEntIndex, const char *pszTextureName, float flFov, float flFarZ, float flLinearAtten )
  64. {
  65. m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE;
  66. m_nEntIndex = nEntIndex;
  67. m_flCurrentPullBackDist = 1.0f;
  68. m_bMuzzleFlashEnabled = false;
  69. m_flMuzzleFlashBrightness = 1.0f;
  70. m_flFov = flFov;
  71. m_flFarZ = flFarZ;
  72. m_flLinearAtten = flLinearAtten;
  73. m_bCastsShadows = true;
  74. m_bIsOn = false;
  75. UpdateFlashlightTexture( pszTextureName );
  76. m_MuzzleFlashTexture.Init( "effects/flashlight_freezecam", TEXTURE_GROUP_OTHER, true, TEXTUREFLAGS_SRGB );
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose:
  80. //-----------------------------------------------------------------------------
  81. CFlashlightEffect::~CFlashlightEffect()
  82. {
  83. LightOff();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose:
  87. //-----------------------------------------------------------------------------
  88. void CFlashlightEffect::TurnOn()
  89. {
  90. m_bIsOn = true;
  91. m_flCurrentPullBackDist = 1.0f;
  92. }
  93. //-----------------------------------------------------------------------------
  94. void CFlashlightEffect::SetMuzzleFlashEnabled( bool bEnabled, float flBrightness )
  95. {
  96. m_bMuzzleFlashEnabled = bEnabled;
  97. m_flMuzzleFlashBrightness = flBrightness;
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. void CFlashlightEffect::TurnOff()
  103. {
  104. if (m_bIsOn)
  105. {
  106. m_bIsOn = false;
  107. LightOff();
  108. }
  109. }
  110. // Custom trace filter that skips the player and the view model.
  111. // If we don't do this, we'll end up having the light right in front of us all
  112. // the time.
  113. class CTraceFilterSkipPlayerAndViewModel : public CTraceFilter
  114. {
  115. public:
  116. CTraceFilterSkipPlayerAndViewModel( C_BasePlayer *pPlayer, bool bTracePlayers )
  117. {
  118. m_pPlayer = pPlayer;
  119. m_bSkipPlayers = !bTracePlayers;
  120. {
  121. m_pLowerBody = NULL;
  122. }
  123. }
  124. virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  125. {
  126. // Test against the vehicle too?
  127. // FLASHLIGHTFIXME: how do you know that you are actually inside of the vehicle?
  128. C_BaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  129. if ( !pEntity )
  130. return true;
  131. if ( ( ToBaseViewModel( pEntity ) != NULL ) ||
  132. pEntity == m_pPlayer ||
  133. pEntity == m_pLowerBody ||
  134. ( m_bSkipPlayers && pEntity->IsPlayer() ) ||
  135. pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
  136. pEntity->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  137. {
  138. return false;
  139. }
  140. return true;
  141. }
  142. private:
  143. C_BaseEntity *m_pPlayer;
  144. C_BaseEntity *m_pLowerBody;
  145. bool m_bSkipPlayers;
  146. };
  147. void C_BasePlayer::GetFlashlightOffset( const Vector &vecForward, const Vector &vecRight, const Vector &vecUp, Vector *pVecOffset ) const
  148. {
  149. *pVecOffset = r_flashlightoffsety.GetFloat() * vecUp + r_flashlightoffsetx.GetFloat() * vecRight + r_flashlightoffsetz.GetFloat() * vecForward;
  150. }
  151. ConVar r_flashlightmuzzleflashfov( "r_flashlightmuzzleflashfov", "120", FCVAR_CHEAT );
  152. //-----------------------------------------------------------------------------
  153. // Purpose: Update the flashlight for top down camera view
  154. // (flashlight origin doesn't move around when you get near things)
  155. //-----------------------------------------------------------------------------
  156. void CFlashlightEffect::UpdateLightTopDown(const Vector &vecPos, const Vector &vecForward, const Vector &vecRight, const Vector &vecUp )
  157. {
  158. VPROF_BUDGET( "CFlashlightEffect::UpdateLightTopDown", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
  159. FlashlightState_t state;
  160. state.m_vecLightOrigin = vecPos;
  161. Vector vTarget = vecPos + vecForward * r_flashlightfar.GetFloat();
  162. // Work with these local copies of the basis for the rest of the function
  163. Vector vDir = vTarget - vecPos;
  164. Vector vRight = vecRight;
  165. Vector vUp = vecUp;
  166. VectorNormalize( vDir );
  167. VectorNormalize( vRight );
  168. VectorNormalize( vUp );
  169. // Orthonormalize the basis, since the flashlight texture projection will require this later...
  170. vUp -= DotProduct( vDir, vUp ) * vDir;
  171. VectorNormalize( vUp );
  172. vRight -= DotProduct( vDir, vRight ) * vDir;
  173. VectorNormalize( vRight );
  174. vRight -= DotProduct( vUp, vRight ) * vUp;
  175. VectorNormalize( vRight );
  176. AssertFloatEquals( DotProduct( vDir, vRight ), 0.0f, 1e-3 );
  177. AssertFloatEquals( DotProduct( vDir, vUp ), 0.0f, 1e-3 );
  178. AssertFloatEquals( DotProduct( vRight, vUp ), 0.0f, 1e-3 );
  179. BasisToQuaternion( vDir, vRight, vUp, state.m_quatOrientation );
  180. state.m_fConstantAtten = r_flashlightconstant.GetFloat();
  181. state.m_fQuadraticAtten = r_flashlightquadratic.GetFloat();
  182. state.m_fLinearAtten = r_flashlightlinear.GetFloat();
  183. state.m_fHorizontalFOVDegrees = r_flashlightfov.GetFloat();
  184. state.m_fVerticalFOVDegrees = r_flashlightfov.GetFloat();
  185. state.m_Color[0] = 1.0f;
  186. state.m_Color[1] = 1.0f;
  187. state.m_Color[2] = 1.0f;
  188. state.m_Color[3] = r_flashlightambient.GetFloat();
  189. 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
  190. state.m_FarZ = state.m_FarZAtten = r_flashlightfar.GetFloat(); // Strictly speaking, these are different, but the game can treat them the same
  191. state.m_bEnableShadows = state.m_bEnableShadows && r_flashlightdepthtexture.GetBool();
  192. state.m_flShadowMapResolution = r_flashlightdepthres.GetInt();
  193. state.m_pSpotlightTexture = m_FlashlightTexture;
  194. state.m_nSpotlightTextureFrame = 0;
  195. state.m_flShadowAtten = r_flashlightshadowatten.GetFloat();
  196. state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();
  197. state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();
  198. if( m_FlashlightHandle == CLIENTSHADOW_INVALID_HANDLE )
  199. {
  200. m_FlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state );
  201. }
  202. else
  203. {
  204. if( !r_flashlightlockposition.GetBool() )
  205. {
  206. g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
  207. }
  208. }
  209. g_pClientShadowMgr->UpdateProjectedTexture( m_FlashlightHandle, true );
  210. // Kill the old flashlight method if we have one.
  211. // FIXME: This doesn't compile
  212. // LightOffOld();
  213. #ifndef NO_TOOLFRAMEWORK
  214. if ( clienttools->IsInRecordingMode() )
  215. {
  216. KeyValues *msg = new KeyValues( "FlashlightState" );
  217. msg->SetFloat( "time", gpGlobals->curtime );
  218. msg->SetInt( "entindex", m_nEntIndex );
  219. msg->SetInt( "flashlightHandle", m_FlashlightHandle );
  220. msg->SetPtr( "flashlightState", &state );
  221. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  222. msg->deleteThis();
  223. }
  224. #endif
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose: Do the headlight
  228. //-----------------------------------------------------------------------------
  229. void CFlashlightEffect::UpdateLight( int nEntIdx, const Vector &vecPos, const Vector &vecForward, const Vector &vecRight,
  230. const Vector &vecUp, float flFov, float flFarZ, float flLinearAtten, bool castsShadows, const char* pTextureName )
  231. {
  232. VPROF_BUDGET( __FUNCTION__, VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
  233. if ( r_flashlight_topdown.GetBool() )
  234. {
  235. UpdateLightTopDown( vecPos, vecForward, vecRight, vecUp );
  236. return;
  237. }
  238. m_nEntIndex = nEntIdx;
  239. m_flFov = flFov;
  240. m_flFarZ = flFarZ;
  241. m_flLinearAtten = flLinearAtten;
  242. if ( m_bCastsShadows != castsShadows )
  243. {
  244. // requires recreation of the flashlight
  245. LightOff();
  246. }
  247. m_bCastsShadows = castsShadows;
  248. UpdateFlashlightTexture( pTextureName );
  249. FlashlightState_t state;
  250. if ( UpdateDefaultFlashlightState( state, vecPos, vecForward, vecRight, vecUp, castsShadows ) == false )
  251. {
  252. return;
  253. }
  254. if( m_FlashlightHandle == CLIENTSHADOW_INVALID_HANDLE )
  255. {
  256. m_FlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state );
  257. }
  258. else
  259. {
  260. if( !r_flashlightlockposition.GetBool() )
  261. {
  262. g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
  263. }
  264. }
  265. g_pClientShadowMgr->UpdateProjectedTexture( m_FlashlightHandle, true );
  266. #ifndef NO_TOOLFRAMEWORK
  267. if ( clienttools->IsInRecordingMode() )
  268. {
  269. KeyValues *msg = new KeyValues( "FlashlightState" );
  270. msg->SetFloat( "time", gpGlobals->curtime );
  271. msg->SetInt( "entindex", m_nEntIndex );
  272. msg->SetInt( "flashlightHandle", m_FlashlightHandle );
  273. msg->SetPtr( "flashlightState", &state );
  274. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  275. msg->deleteThis();
  276. }
  277. #endif
  278. }
  279. void CFlashlightEffect::UpdateLight( int nEntIdx, const Vector &vecPos, const Vector &vecDir, const Vector &vecRight, const Vector &vecUp,
  280. float flFov, bool castsShadows, ITexture *pFlashlightTexture, const Vector &vecBrightness,
  281. bool bTracePlayers )
  282. {
  283. VPROF_BUDGET( __FUNCTION__, VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
  284. m_nEntIndex = nEntIdx;
  285. if ( m_bCastsShadows != castsShadows )
  286. {
  287. // requires recreation of the flashlight
  288. LightOff();
  289. }
  290. m_bCastsShadows = castsShadows;
  291. FlashlightState_t state;
  292. if ( UpdateDefaultFlashlightState( state, vecPos, vecDir, vecRight, vecUp, castsShadows, bTracePlayers ) == false )
  293. {
  294. return;
  295. }
  296. state.m_fHorizontalFOVDegrees = flFov;
  297. state.m_fVerticalFOVDegrees = flFov;
  298. state.m_Color[0] = vecBrightness.x;
  299. state.m_Color[1] = vecBrightness.y;
  300. state.m_Color[2] = vecBrightness.z;
  301. if ( pFlashlightTexture )
  302. {
  303. state.m_pSpotlightTexture = pFlashlightTexture;
  304. state.m_pProjectedMaterial = NULL;
  305. }
  306. if( m_FlashlightHandle == CLIENTSHADOW_INVALID_HANDLE )
  307. {
  308. m_FlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state );
  309. }
  310. else
  311. {
  312. if( !r_flashlightlockposition.GetBool() )
  313. {
  314. g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
  315. }
  316. }
  317. g_pClientShadowMgr->UpdateProjectedTexture( m_FlashlightHandle, true );
  318. #ifndef NO_TOOLFRAMEWORK
  319. if ( clienttools->IsInRecordingMode() )
  320. {
  321. KeyValues *msg = new KeyValues( "FlashlightState" );
  322. msg->SetFloat( "time", gpGlobals->curtime );
  323. msg->SetInt( "entindex", m_nEntIndex );
  324. msg->SetInt( "flashlightHandle", m_FlashlightHandle );
  325. msg->SetPtr( "flashlightState", &state );
  326. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  327. msg->deleteThis();
  328. }
  329. #endif
  330. }
  331. bool CFlashlightEffect::UpdateDefaultFlashlightState( FlashlightState_t& state, const Vector &vecPos, const Vector &vecForward,
  332. const Vector &vecRight, const Vector &vecUp, bool castsShadows, bool bTracePlayers )
  333. {
  334. VPROF_BUDGET( __FUNCTION__, VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
  335. if ( !m_bIsOn )
  336. {
  337. // return;
  338. }
  339. if ( ComputeLightPosAndOrientation( vecPos, vecForward, vecRight, vecUp, state.m_vecLightOrigin, state.m_quatOrientation, bTracePlayers ) == false )
  340. {
  341. return false;
  342. }
  343. state.m_fQuadraticAtten = r_flashlightquadratic.GetFloat();
  344. bool bFlicker = false;
  345. #ifdef HL2_EPISODIC
  346. C_BaseHLPlayer *pPlayer = (C_BaseHLPlayer *)C_BasePlayer::GetLocalPlayer();
  347. if ( pPlayer )
  348. {
  349. float flBatteryPower = ( pPlayer->m_HL2Local.m_flFlashBattery >= 0.0f ) ? ( pPlayer->m_HL2Local.m_flFlashBattery ) : pPlayer->m_HL2Local.m_flSuitPower;
  350. if ( flBatteryPower <= 10.0f )
  351. {
  352. float flScale;
  353. if ( flBatteryPower >= 0.0f )
  354. {
  355. flScale = ( flBatteryPower <= 4.5f ) ? SimpleSplineRemapVal( flBatteryPower, 4.5f, 0.0f, 1.0f, 0.0f ) : 1.0f;
  356. }
  357. else
  358. {
  359. flScale = SimpleSplineRemapVal( flBatteryPower, 10.0f, 4.8f, 1.0f, 0.0f );
  360. }
  361. flScale = clamp( flScale, 0.0f, 1.0f );
  362. if ( flScale < 0.35f )
  363. {
  364. float flFlicker = cosf( gpGlobals->curtime * 6.0f ) * sinf( gpGlobals->curtime * 15.0f );
  365. if ( flFlicker > 0.25f && flFlicker < 0.75f )
  366. {
  367. // On
  368. state.m_fLinearAtten = r_flashlightlinear.GetFloat() * flScale;
  369. }
  370. else
  371. {
  372. // Off
  373. state.m_fLinearAtten = 0.0f;
  374. }
  375. }
  376. else
  377. {
  378. float flNoise = cosf( gpGlobals->curtime * 7.0f ) * sinf( gpGlobals->curtime * 25.0f );
  379. state.m_fLinearAtten = r_flashlightlinear.GetFloat() * flScale + 1.5f * flNoise;
  380. }
  381. state.m_fHorizontalFOVDegrees = r_flashlightfov.GetFloat() - ( 16.0f * (1.0f-flScale) );
  382. state.m_fVerticalFOVDegrees = r_flashlightfov.GetFloat() - ( 16.0f * (1.0f-flScale) );
  383. bFlicker = true;
  384. }
  385. }
  386. #endif // HL2_EPISODIC
  387. if ( bFlicker == false )
  388. {
  389. if ( m_flLinearAtten > 0.0f )
  390. {
  391. state.m_fLinearAtten = m_flLinearAtten;
  392. }
  393. else
  394. {
  395. state.m_fLinearAtten = r_flashlightlinear.GetFloat();
  396. }
  397. if ( m_flFov > 0.0f )
  398. {
  399. state.m_fHorizontalFOVDegrees = m_flFov;
  400. state.m_fVerticalFOVDegrees = m_flFov;
  401. }
  402. else
  403. {
  404. state.m_fHorizontalFOVDegrees = r_flashlightfov.GetFloat();
  405. state.m_fVerticalFOVDegrees = r_flashlightfov.GetFloat();
  406. }
  407. if ( m_bMuzzleFlashEnabled )
  408. {
  409. state.m_fHorizontalFOVDegrees = state.m_fVerticalFOVDegrees = r_flashlightmuzzleflashfov.GetFloat();
  410. }
  411. }
  412. state.m_fConstantAtten = r_flashlightconstant.GetFloat();
  413. state.m_Color[0] = 1.0f;
  414. state.m_Color[1] = 1.0f;
  415. state.m_Color[2] = 1.0f;
  416. state.m_Color[3] = r_flashlightambient.GetFloat();
  417. 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
  418. if ( m_flFarZ > 0.0f )
  419. {
  420. state.m_FarZ = state.m_FarZAtten = m_flFarZ; // Strictly speaking, these are different, but the game can treat them the same
  421. }
  422. else
  423. {
  424. state.m_FarZ = state.m_FarZAtten = r_flashlightfar.GetFloat(); // Strictly speaking, these are different, but the game can treat them the same
  425. }
  426. state.m_bEnableShadows = castsShadows && r_flashlightdepthtexture.GetBool();
  427. state.m_flShadowMapResolution = r_flashlightdepthres.GetInt();
  428. if ( m_bMuzzleFlashEnabled )
  429. {
  430. state.m_pSpotlightTexture = m_MuzzleFlashTexture;
  431. state.m_pProjectedMaterial = NULL;
  432. state.m_Color[0] = m_flMuzzleFlashBrightness;
  433. state.m_Color[1] = m_flMuzzleFlashBrightness;
  434. state.m_Color[2] = m_flMuzzleFlashBrightness;
  435. }
  436. else
  437. {
  438. state.m_pSpotlightTexture = m_FlashlightTexture;
  439. state.m_pProjectedMaterial = NULL;
  440. }
  441. state.m_nSpotlightTextureFrame = 0;
  442. state.m_flShadowAtten = r_flashlightshadowatten.GetFloat();
  443. state.m_flShadowSlopeScaleDepthBias = g_pMaterialSystemHardwareConfig->GetShadowSlopeScaleDepthBias();
  444. state.m_flShadowDepthBias = g_pMaterialSystemHardwareConfig->GetShadowDepthBias();
  445. return true;
  446. }
  447. void CFlashlightEffect::UpdateFlashlightTexture( const char* pTextureName )
  448. {
  449. static const char *pEmptyString = "";
  450. if ( pTextureName == NULL )
  451. {
  452. pTextureName = pEmptyString;
  453. }
  454. if ( !m_FlashlightTexture.IsValid() ||
  455. V_stricmp( m_textureName, pTextureName ) != 0 )
  456. {
  457. if ( pTextureName == pEmptyString )
  458. {
  459. m_FlashlightTexture.Init( "effects/flashlight001", TEXTURE_GROUP_OTHER, true );
  460. }
  461. else
  462. {
  463. m_FlashlightTexture.Init( pTextureName, TEXTURE_GROUP_OTHER, true );
  464. }
  465. V_strncpy( m_textureName, pTextureName, sizeof( m_textureName ) );
  466. }
  467. }
  468. bool CFlashlightEffect::ComputeLightPosAndOrientation( const Vector &vecPos, const Vector &vecForward, const Vector &vecRight, const Vector &vecUp,
  469. Vector& vecFinalPos, Quaternion& quatOrientation, bool bTracePlayers )
  470. {
  471. const float flEpsilon = 0.1f; // Offset flashlight position along vecUp
  472. float flDistCutoff = r_flashlighttracedistcutoff.GetFloat();
  473. const float flDistDrag = 0.2;
  474. bool bDebugVis = r_flashlightvisualizetrace.GetBool();
  475. C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_nEntIndex );
  476. if ( !pPlayer )
  477. {
  478. #ifdef TERROR
  479. pPlayer = C_TerrorPlayer::GetLocalOrInEyeTerrorPlayer();
  480. #else
  481. pPlayer = C_BasePlayer::GetLocalPlayer();
  482. #endif
  483. if ( !pPlayer )
  484. {
  485. Assert( false );
  486. return false;
  487. }
  488. }
  489. // We will lock some of the flashlight params if player is on a ladder, to prevent oscillations due to the trace-rays
  490. bool bPlayerOnLadder = ( pPlayer->GetMoveType() == MOVETYPE_LADDER );
  491. CTraceFilterSkipPlayerAndViewModel traceFilter( pPlayer, bTracePlayers );
  492. // Vector vOrigin = vecPos + r_flashlightoffsety.GetFloat() * vecUp;
  493. Vector vecOffset;
  494. pPlayer->GetFlashlightOffset( vecForward, vecRight, vecUp, &vecOffset );
  495. Vector vOrigin = vecPos + vecOffset;
  496. // Not on ladder...trace a hull
  497. if ( !bPlayerOnLadder )
  498. {
  499. Vector vecPlayerEyePos = pPlayer->GetRenderOrigin() + pPlayer->GetViewOffset();
  500. trace_t pmOriginTrace;
  501. UTIL_TraceHull( vecPlayerEyePos, vOrigin, Vector(-2, -2, -2), Vector(2, 2, 2), ( MASK_SOLID & ~(CONTENTS_HITBOX) ) | CONTENTS_WINDOW | CONTENTS_GRATE, &traceFilter, &pmOriginTrace );//1
  502. if ( bDebugVis )
  503. {
  504. debugoverlay->AddBoxOverlay( pmOriginTrace.endpos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), QAngle( 0, 0, 0 ), 0, 255, 0, 16, 0 );
  505. if ( pmOriginTrace.DidHit() || pmOriginTrace.startsolid )
  506. {
  507. debugoverlay->AddLineOverlay( pmOriginTrace.startpos, pmOriginTrace.endpos, 255, 128, 128, true, 0 );
  508. }
  509. else
  510. {
  511. debugoverlay->AddLineOverlay( pmOriginTrace.startpos, pmOriginTrace.endpos, 255, 0, 0, true, 0 );
  512. }
  513. }
  514. if ( pmOriginTrace.DidHit() || pmOriginTrace.startsolid )
  515. {
  516. vOrigin = pmOriginTrace.endpos;
  517. }
  518. else
  519. {
  520. if ( pPlayer->m_vecFlashlightOrigin != vecPlayerEyePos )
  521. {
  522. vOrigin = vecPos;
  523. }
  524. }
  525. }
  526. else // on ladder...skip the above hull trace
  527. {
  528. vOrigin = vecPos;
  529. }
  530. // Now do a trace along the flashlight direction to ensure there is nothing within range to pull back from
  531. int iMask = MASK_OPAQUE_AND_NPCS;
  532. iMask &= ~CONTENTS_HITBOX;
  533. iMask |= CONTENTS_WINDOW | CONTENTS_GRATE | CONTENTS_IGNORE_NODRAW_OPAQUE;
  534. Vector vTarget = vOrigin + vecForward * r_flashlightfar.GetFloat();
  535. // Work with these local copies of the basis for the rest of the function
  536. Vector vDir = vTarget - vOrigin;
  537. Vector vRight = vecRight;
  538. Vector vUp = vecUp;
  539. VectorNormalize( vDir );
  540. VectorNormalize( vRight );
  541. VectorNormalize( vUp );
  542. // Orthonormalize the basis, since the flashlight texture projection will require this later...
  543. vUp -= DotProduct( vDir, vUp ) * vDir;
  544. VectorNormalize( vUp );
  545. vRight -= DotProduct( vDir, vRight ) * vDir;
  546. VectorNormalize( vRight );
  547. vRight -= DotProduct( vUp, vRight ) * vUp;
  548. VectorNormalize( vRight );
  549. AssertFloatEquals( DotProduct( vDir, vRight ), 0.0f, 1e-3 );
  550. AssertFloatEquals( DotProduct( vDir, vUp ), 0.0f, 1e-3 );
  551. AssertFloatEquals( DotProduct( vRight, vUp ), 0.0f, 1e-3 );
  552. trace_t pmDirectionTrace;
  553. UTIL_TraceHull( vOrigin, vTarget, Vector( -1.5, -1.5, -1.5 ), Vector( 1.5, 1.5, 1.5 ), iMask, &traceFilter, &pmDirectionTrace );//.5
  554. if ( bDebugVis )
  555. {
  556. debugoverlay->AddBoxOverlay( pmDirectionTrace.endpos, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ), QAngle( 0, 0, 0 ), 0, 0, 255, 16, 0 );
  557. debugoverlay->AddLineOverlay( vOrigin, pmDirectionTrace.endpos, 255, 0, 0, false, 0 );
  558. }
  559. float flTargetPullBackDist = 0.0f;
  560. float flDist = (pmDirectionTrace.endpos - vOrigin).Length();
  561. if ( flDist < flDistCutoff )
  562. {
  563. // We have an intersection with our cutoff range
  564. // Determine how far to pull back, then trace to see if we are clear
  565. float flPullBackDist = bPlayerOnLadder ? r_flashlightladderdist.GetFloat() : flDistCutoff - flDist; // Fixed pull-back distance if on ladder
  566. flTargetPullBackDist = flPullBackDist;
  567. if ( !bPlayerOnLadder )
  568. {
  569. trace_t pmBackTrace;
  570. // start the trace away from the actual trace origin a bit, to avoid getting stuck on small, close "lips"
  571. UTIL_TraceHull( vOrigin - vDir * ( flDistCutoff * r_flashlightbacktraceoffset.GetFloat() ), vOrigin - vDir * ( flPullBackDist - flEpsilon ),
  572. Vector( -1.5f, -1.5f, -1.5f ), Vector( 1.5f, 1.5f, 1.5f ), iMask, &traceFilter, &pmBackTrace );
  573. if ( bDebugVis )
  574. {
  575. debugoverlay->AddLineOverlay( pmBackTrace.startpos, pmBackTrace.endpos, 255, 0, 255, true, 0 );
  576. }
  577. if( pmBackTrace.DidHit() )
  578. {
  579. // We have an intersection behind us as well, so limit our flTargetPullBackDist
  580. float flMaxDist = (pmBackTrace.endpos - vOrigin).Length() - flEpsilon;
  581. flTargetPullBackDist = MIN( flMaxDist, flTargetPullBackDist );
  582. //m_flCurrentPullBackDist = MIN( flMaxDist, m_flCurrentPullBackDist ); // possible pop
  583. }
  584. }
  585. }
  586. if ( bDebugVis )
  587. {
  588. // visualize pullback
  589. debugoverlay->AddBoxOverlay( vOrigin - vDir * m_flCurrentPullBackDist, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), QAngle( 0, 0, 0 ), 255, 255, 0, 16, 0 );
  590. debugoverlay->AddBoxOverlay( vOrigin - vDir * flTargetPullBackDist, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 128, 128, 0, 16, 0 );
  591. }
  592. m_flCurrentPullBackDist = Lerp( flDistDrag, m_flCurrentPullBackDist, flTargetPullBackDist );
  593. m_flCurrentPullBackDist = MIN( m_flCurrentPullBackDist, flDistCutoff ); // clamp to max pullback dist
  594. vOrigin = vOrigin - vDir * m_flCurrentPullBackDist;
  595. vecFinalPos = vOrigin;
  596. BasisToQuaternion( vDir, vRight, vUp, quatOrientation );
  597. return true;
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Purpose:
  601. //-----------------------------------------------------------------------------
  602. void CFlashlightEffect::LightOff()
  603. {
  604. #ifndef NO_TOOLFRAMEWORK
  605. if ( clienttools->IsInRecordingMode() )
  606. {
  607. KeyValues *msg = new KeyValues( "FlashlightState" );
  608. msg->SetFloat( "time", gpGlobals->curtime );
  609. msg->SetInt( "entindex", m_nEntIndex );
  610. msg->SetInt( "flashlightHandle", m_FlashlightHandle );
  611. msg->SetPtr( "flashlightState", NULL );
  612. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  613. msg->deleteThis();
  614. }
  615. #endif
  616. // Clear out the light
  617. if( m_FlashlightHandle != CLIENTSHADOW_INVALID_HANDLE )
  618. {
  619. g_pClientShadowMgr->DestroyFlashlight( m_FlashlightHandle );
  620. m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE;
  621. }
  622. }
  623. CHeadlightEffect::CHeadlightEffect()
  624. {
  625. }
  626. CHeadlightEffect::~CHeadlightEffect()
  627. {
  628. }
  629. void CHeadlightEffect::UpdateLight( const Vector &vecPos, const Vector &vecDir, const Vector &vecRight, const Vector &vecUp, int nDistance )
  630. {
  631. if ( IsOn() == false )
  632. return;
  633. FlashlightState_t state;
  634. Vector basisX, basisY, basisZ;
  635. basisX = vecDir;
  636. basisY = vecRight;
  637. basisZ = vecUp;
  638. VectorNormalize(basisX);
  639. VectorNormalize(basisY);
  640. VectorNormalize(basisZ);
  641. BasisToQuaternion( basisX, basisY, basisZ, state.m_quatOrientation );
  642. state.m_vecLightOrigin = vecPos;
  643. state.m_fHorizontalFOVDegrees = 45.0f;
  644. state.m_fVerticalFOVDegrees = 30.0f;
  645. state.m_fQuadraticAtten = r_flashlightquadratic.GetFloat();
  646. state.m_fLinearAtten = r_flashlightlinear.GetFloat();
  647. state.m_fConstantAtten = r_flashlightconstant.GetFloat();
  648. state.m_Color[0] = 1.0f;
  649. state.m_Color[1] = 1.0f;
  650. state.m_Color[2] = 1.0f;
  651. state.m_Color[3] = r_flashlightambient.GetFloat();
  652. state.m_NearZ = r_flashlightnear.GetFloat();
  653. state.m_FarZ = r_flashlightfar.GetFloat();
  654. state.m_bEnableShadows = true;
  655. state.m_pSpotlightTexture = m_FlashlightTexture;
  656. state.m_pProjectedMaterial = NULL;
  657. state.m_nSpotlightTextureFrame = 0;
  658. if( GetFlashlightHandle() == CLIENTSHADOW_INVALID_HANDLE )
  659. {
  660. SetFlashlightHandle( g_pClientShadowMgr->CreateFlashlight( state ) );
  661. }
  662. else
  663. {
  664. g_pClientShadowMgr->UpdateFlashlightState( GetFlashlightHandle(), state );
  665. }
  666. g_pClientShadowMgr->UpdateProjectedTexture( GetFlashlightHandle(), true );
  667. }