Team Fortress 2 Source Code as on 22/4/2020
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.

543 lines
17 KiB

  1. //========= Copyright 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( _X360 )
  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. void r_newflashlightCallback_f( IConVar *pConVar, const char *pOldString, float flOldValue );
  28. static ConVar r_newflashlight( "r_newflashlight", "1", FCVAR_CHEAT, "", r_newflashlightCallback_f );
  29. static ConVar r_swingflashlight( "r_swingflashlight", "1", FCVAR_CHEAT );
  30. static ConVar r_flashlightlockposition( "r_flashlightlockposition", "0", FCVAR_CHEAT );
  31. static ConVar r_flashlightfov( "r_flashlightfov", "45.0", FCVAR_CHEAT );
  32. static ConVar r_flashlightoffsetx( "r_flashlightoffsetx", "10.0", FCVAR_CHEAT );
  33. static ConVar r_flashlightoffsety( "r_flashlightoffsety", "-20.0", FCVAR_CHEAT );
  34. static ConVar r_flashlightoffsetz( "r_flashlightoffsetz", "24.0", FCVAR_CHEAT );
  35. static ConVar r_flashlightnear( "r_flashlightnear", "4.0", FCVAR_CHEAT );
  36. static ConVar r_flashlightfar( "r_flashlightfar", "750.0", FCVAR_CHEAT );
  37. static ConVar r_flashlightconstant( "r_flashlightconstant", "0.0", FCVAR_CHEAT );
  38. static ConVar r_flashlightlinear( "r_flashlightlinear", "100.0", FCVAR_CHEAT );
  39. static ConVar r_flashlightquadratic( "r_flashlightquadratic", "0.0", FCVAR_CHEAT );
  40. static ConVar r_flashlightvisualizetrace( "r_flashlightvisualizetrace", "0", FCVAR_CHEAT );
  41. static ConVar r_flashlightambient( "r_flashlightambient", "0.0", FCVAR_CHEAT );
  42. static ConVar r_flashlightshadowatten( "r_flashlightshadowatten", "0.35", FCVAR_CHEAT );
  43. static ConVar r_flashlightladderdist( "r_flashlightladderdist", "40.0", FCVAR_CHEAT );
  44. static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT );
  45. static ConVar mat_depthbias_shadowmap( "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT );
  46. void r_newflashlightCallback_f( IConVar *pConVar, const char *pOldString, float flOldValue )
  47. {
  48. if( engine->GetDXSupportLevel() < 70 )
  49. {
  50. r_newflashlight.SetValue( 0 );
  51. }
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose:
  55. // Input : nEntIndex - The m_nEntIndex of the client entity that is creating us.
  56. // vecPos - The position of the light emitter.
  57. // vecDir - The direction of the light emission.
  58. //-----------------------------------------------------------------------------
  59. CFlashlightEffect::CFlashlightEffect(int nEntIndex)
  60. {
  61. m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE;
  62. m_nEntIndex = nEntIndex;
  63. m_bIsOn = false;
  64. m_pPointLight = NULL;
  65. if( engine->GetDXSupportLevel() < 70 )
  66. {
  67. r_newflashlight.SetValue( 0 );
  68. }
  69. if ( g_pMaterialSystemHardwareConfig->SupportsBorderColor() )
  70. {
  71. m_FlashlightTexture.Init( "effects/flashlight_border", TEXTURE_GROUP_OTHER, true );
  72. }
  73. else
  74. {
  75. m_FlashlightTexture.Init( "effects/flashlight001", TEXTURE_GROUP_OTHER, true );
  76. }
  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_flDistMod = 1.0f;
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. //-----------------------------------------------------------------------------
  96. void CFlashlightEffect::TurnOff()
  97. {
  98. if (m_bIsOn)
  99. {
  100. m_bIsOn = false;
  101. LightOff();
  102. }
  103. }
  104. // Custom trace filter that skips the player and the view model.
  105. // If we don't do this, we'll end up having the light right in front of us all
  106. // the time.
  107. class CTraceFilterSkipPlayerAndViewModel : public CTraceFilter
  108. {
  109. public:
  110. virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
  111. {
  112. // Test against the vehicle too?
  113. // FLASHLIGHTFIXME: how do you know that you are actually inside of the vehicle?
  114. C_BaseEntity *pEntity = EntityFromEntityHandle( pServerEntity );
  115. if ( !pEntity )
  116. return true;
  117. if ( ( dynamic_cast<C_BaseViewModel *>( pEntity ) != NULL ) ||
  118. ( dynamic_cast<C_BasePlayer *>( pEntity ) != NULL ) ||
  119. pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
  120. pEntity->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS )
  121. {
  122. return false;
  123. }
  124. return true;
  125. }
  126. };
  127. //-----------------------------------------------------------------------------
  128. // Purpose: Do the headlight
  129. //-----------------------------------------------------------------------------
  130. void CFlashlightEffect::UpdateLightNew(const Vector &vecPos, const Vector &vecForward, const Vector &vecRight, const Vector &vecUp )
  131. {
  132. VPROF_BUDGET( "CFlashlightEffect::UpdateLightNew", VPROF_BUDGETGROUP_SHADOW_DEPTH_TEXTURING );
  133. FlashlightState_t state;
  134. // We will lock some of the flashlight params if player is on a ladder, to prevent oscillations due to the trace-rays
  135. bool bPlayerOnLadder = ( C_BasePlayer::GetLocalPlayer()->GetMoveType() == MOVETYPE_LADDER );
  136. const float flEpsilon = 0.1f; // Offset flashlight position along vecUp
  137. const float flDistCutoff = 128.0f;
  138. const float flDistDrag = 0.2;
  139. CTraceFilterSkipPlayerAndViewModel traceFilter;
  140. float flOffsetY = r_flashlightoffsety.GetFloat();
  141. if( r_swingflashlight.GetBool() )
  142. {
  143. // This projects the view direction backwards, attempting to raise the vertical
  144. // offset of the flashlight, but only when the player is looking down.
  145. Vector vecSwingLight = vecPos + vecForward * -12.0f;
  146. if( vecSwingLight.z > vecPos.z )
  147. {
  148. flOffsetY += (vecSwingLight.z - vecPos.z);
  149. }
  150. }
  151. Vector vOrigin = vecPos + flOffsetY * vecUp;
  152. // Not on ladder...trace a hull
  153. if ( !bPlayerOnLadder )
  154. {
  155. trace_t pmOriginTrace;
  156. UTIL_TraceHull( vecPos, vOrigin, Vector(-4, -4, -4), Vector(4, 4, 4), MASK_SOLID & ~(CONTENTS_HITBOX), &traceFilter, &pmOriginTrace );
  157. if ( pmOriginTrace.DidHit() )
  158. {
  159. vOrigin = vecPos;
  160. }
  161. }
  162. else // on ladder...skip the above hull trace
  163. {
  164. vOrigin = vecPos;
  165. }
  166. // Now do a trace along the flashlight direction to ensure there is nothing within range to pull back from
  167. int iMask = MASK_OPAQUE_AND_NPCS;
  168. iMask &= ~CONTENTS_HITBOX;
  169. iMask |= CONTENTS_WINDOW;
  170. Vector vTarget = vecPos + vecForward * r_flashlightfar.GetFloat();
  171. // Work with these local copies of the basis for the rest of the function
  172. Vector vDir = vTarget - vOrigin;
  173. Vector vRight = vecRight;
  174. Vector vUp = vecUp;
  175. VectorNormalize( vDir );
  176. VectorNormalize( vRight );
  177. VectorNormalize( vUp );
  178. // Orthonormalize the basis, since the flashlight texture projection will require this later...
  179. vUp -= DotProduct( vDir, vUp ) * vDir;
  180. VectorNormalize( vUp );
  181. vRight -= DotProduct( vDir, vRight ) * vDir;
  182. VectorNormalize( vRight );
  183. vRight -= DotProduct( vUp, vRight ) * vUp;
  184. VectorNormalize( vRight );
  185. AssertFloatEquals( DotProduct( vDir, vRight ), 0.0f, 1e-3 );
  186. AssertFloatEquals( DotProduct( vDir, vUp ), 0.0f, 1e-3 );
  187. AssertFloatEquals( DotProduct( vRight, vUp ), 0.0f, 1e-3 );
  188. trace_t pmDirectionTrace;
  189. UTIL_TraceHull( vOrigin, vTarget, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ), iMask, &traceFilter, &pmDirectionTrace );
  190. if ( r_flashlightvisualizetrace.GetBool() == true )
  191. {
  192. if ( debugoverlay )
  193. {
  194. debugoverlay->AddBoxOverlay( pmDirectionTrace.endpos, Vector( -4, -4, -4 ), Vector( 4, 4, 4 ), QAngle( 0, 0, 0 ), 0, 0, 255, 16, 0 );
  195. debugoverlay->AddLineOverlay( vOrigin, pmDirectionTrace.endpos, 255, 0, 0, false, 0 );
  196. }
  197. }
  198. float flDist = (pmDirectionTrace.endpos - vOrigin).Length();
  199. if ( flDist < flDistCutoff )
  200. {
  201. // We have an intersection with our cutoff range
  202. // Determine how far to pull back, then trace to see if we are clear
  203. float flPullBackDist = bPlayerOnLadder ? r_flashlightladderdist.GetFloat() : flDistCutoff - flDist; // Fixed pull-back distance if on ladder
  204. m_flDistMod = Lerp( flDistDrag, m_flDistMod, flPullBackDist );
  205. if ( !bPlayerOnLadder )
  206. {
  207. trace_t pmBackTrace;
  208. UTIL_TraceHull( vOrigin, vOrigin - vDir*(flPullBackDist-flEpsilon), Vector( -4, -4, -4 ), Vector( 4, 4, 4 ), iMask, &traceFilter, &pmBackTrace );
  209. if( pmBackTrace.DidHit() )
  210. {
  211. // We have an intersection behind us as well, so limit our m_flDistMod
  212. float flMaxDist = (pmBackTrace.endpos - vOrigin).Length() - flEpsilon;
  213. if( m_flDistMod > flMaxDist )
  214. m_flDistMod = flMaxDist;
  215. }
  216. }
  217. }
  218. else
  219. {
  220. m_flDistMod = Lerp( flDistDrag, m_flDistMod, 0.0f );
  221. }
  222. vOrigin = vOrigin - vDir * m_flDistMod;
  223. state.m_vecLightOrigin = vOrigin;
  224. BasisToQuaternion( vDir, vRight, vUp, state.m_quatOrientation );
  225. state.m_fQuadraticAtten = r_flashlightquadratic.GetFloat();
  226. bool bFlicker = false;
  227. #ifdef HL2_EPISODIC
  228. C_BaseHLPlayer *pPlayer = (C_BaseHLPlayer *)C_BasePlayer::GetLocalPlayer();
  229. if ( pPlayer )
  230. {
  231. float flBatteryPower = ( pPlayer->m_HL2Local.m_flFlashBattery >= 0.0f ) ? ( pPlayer->m_HL2Local.m_flFlashBattery ) : pPlayer->m_HL2Local.m_flSuitPower;
  232. if ( flBatteryPower <= 10.0f )
  233. {
  234. float flScale;
  235. if ( flBatteryPower >= 0.0f )
  236. {
  237. flScale = ( flBatteryPower <= 4.5f ) ? SimpleSplineRemapVal( flBatteryPower, 4.5f, 0.0f, 1.0f, 0.0f ) : 1.0f;
  238. }
  239. else
  240. {
  241. flScale = SimpleSplineRemapVal( flBatteryPower, 10.0f, 4.8f, 1.0f, 0.0f );
  242. }
  243. flScale = clamp( flScale, 0.0f, 1.0f );
  244. if ( flScale < 0.35f )
  245. {
  246. float flFlicker = cosf( gpGlobals->curtime * 6.0f ) * sinf( gpGlobals->curtime * 15.0f );
  247. if ( flFlicker > 0.25f && flFlicker < 0.75f )
  248. {
  249. // On
  250. state.m_fLinearAtten = r_flashlightlinear.GetFloat() * flScale;
  251. }
  252. else
  253. {
  254. // Off
  255. state.m_fLinearAtten = 0.0f;
  256. }
  257. }
  258. else
  259. {
  260. float flNoise = cosf( gpGlobals->curtime * 7.0f ) * sinf( gpGlobals->curtime * 25.0f );
  261. state.m_fLinearAtten = r_flashlightlinear.GetFloat() * flScale + 1.5f * flNoise;
  262. }
  263. state.m_fHorizontalFOVDegrees = r_flashlightfov.GetFloat() - ( 16.0f * (1.0f-flScale) );
  264. state.m_fVerticalFOVDegrees = r_flashlightfov.GetFloat() - ( 16.0f * (1.0f-flScale) );
  265. bFlicker = true;
  266. }
  267. }
  268. #endif // HL2_EPISODIC
  269. if ( bFlicker == false )
  270. {
  271. state.m_fLinearAtten = r_flashlightlinear.GetFloat();
  272. state.m_fHorizontalFOVDegrees = r_flashlightfov.GetFloat();
  273. state.m_fVerticalFOVDegrees = r_flashlightfov.GetFloat();
  274. }
  275. state.m_fConstantAtten = r_flashlightconstant.GetFloat();
  276. state.m_Color[0] = 1.0f;
  277. state.m_Color[1] = 1.0f;
  278. state.m_Color[2] = 1.0f;
  279. state.m_Color[3] = r_flashlightambient.GetFloat();
  280. state.m_NearZ = r_flashlightnear.GetFloat() + m_flDistMod; // Push near plane out so that we don't clip the world when the flashlight pulls back
  281. state.m_FarZ = r_flashlightfar.GetFloat();
  282. state.m_bEnableShadows = r_flashlightdepthtexture.GetBool();
  283. state.m_flShadowMapResolution = r_flashlightdepthres.GetInt();
  284. state.m_pSpotlightTexture = m_FlashlightTexture;
  285. state.m_nSpotlightTextureFrame = 0;
  286. state.m_flShadowAtten = r_flashlightshadowatten.GetFloat();
  287. state.m_flShadowSlopeScaleDepthBias = mat_slopescaledepthbias_shadowmap.GetFloat();
  288. state.m_flShadowDepthBias = mat_depthbias_shadowmap.GetFloat();
  289. if( m_FlashlightHandle == CLIENTSHADOW_INVALID_HANDLE )
  290. {
  291. m_FlashlightHandle = g_pClientShadowMgr->CreateFlashlight( state );
  292. }
  293. else
  294. {
  295. if( !r_flashlightlockposition.GetBool() )
  296. {
  297. g_pClientShadowMgr->UpdateFlashlightState( m_FlashlightHandle, state );
  298. }
  299. }
  300. g_pClientShadowMgr->UpdateProjectedTexture( m_FlashlightHandle, true );
  301. // Kill the old flashlight method if we have one.
  302. LightOffOld();
  303. #ifndef NO_TOOLFRAMEWORK
  304. if ( clienttools->IsInRecordingMode() )
  305. {
  306. KeyValues *msg = new KeyValues( "FlashlightState" );
  307. msg->SetFloat( "time", gpGlobals->curtime );
  308. msg->SetInt( "entindex", m_nEntIndex );
  309. msg->SetInt( "flashlightHandle", m_FlashlightHandle );
  310. msg->SetPtr( "flashlightState", &state );
  311. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  312. msg->deleteThis();
  313. }
  314. #endif
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: Do the headlight
  318. //-----------------------------------------------------------------------------
  319. void CFlashlightEffect::UpdateLightOld(const Vector &vecPos, const Vector &vecDir, int nDistance)
  320. {
  321. if ( !m_pPointLight || ( m_pPointLight->key != m_nEntIndex ))
  322. {
  323. // Set up the environment light
  324. m_pPointLight = effects->CL_AllocDlight(m_nEntIndex);
  325. m_pPointLight->flags = 0.0f;
  326. m_pPointLight->radius = 80;
  327. }
  328. // For bumped lighting
  329. VectorCopy(vecDir, m_pPointLight->m_Direction);
  330. Vector end;
  331. end = vecPos + nDistance * vecDir;
  332. // Trace a line outward, skipping the player model and the view model.
  333. trace_t pm;
  334. CTraceFilterSkipPlayerAndViewModel traceFilter;
  335. UTIL_TraceLine( vecPos, end, MASK_ALL, &traceFilter, &pm );
  336. VectorCopy( pm.endpos, m_pPointLight->origin );
  337. float falloff = pm.fraction * nDistance;
  338. if ( falloff < 500 )
  339. falloff = 1.0;
  340. else
  341. falloff = 500.0 / falloff;
  342. falloff *= falloff;
  343. m_pPointLight->radius = 80;
  344. m_pPointLight->color.r = m_pPointLight->color.g = m_pPointLight->color.b = 255 * falloff;
  345. m_pPointLight->color.exponent = 0;
  346. // Make it live for a bit
  347. m_pPointLight->die = gpGlobals->curtime + 0.2f;
  348. // Update list of surfaces we influence
  349. render->TouchLight( m_pPointLight );
  350. // kill the new flashlight if we have one
  351. LightOffNew();
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Purpose: Do the headlight
  355. //-----------------------------------------------------------------------------
  356. void CFlashlightEffect::UpdateLight(const Vector &vecPos, const Vector &vecDir, const Vector &vecRight, const Vector &vecUp, int nDistance)
  357. {
  358. if ( !m_bIsOn )
  359. {
  360. return;
  361. }
  362. if( r_newflashlight.GetBool() )
  363. {
  364. UpdateLightNew( vecPos, vecDir, vecRight, vecUp );
  365. }
  366. else
  367. {
  368. UpdateLightOld( vecPos, vecDir, nDistance );
  369. }
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose:
  373. //-----------------------------------------------------------------------------
  374. void CFlashlightEffect::LightOffNew()
  375. {
  376. #ifndef NO_TOOLFRAMEWORK
  377. if ( clienttools->IsInRecordingMode() )
  378. {
  379. KeyValues *msg = new KeyValues( "FlashlightState" );
  380. msg->SetFloat( "time", gpGlobals->curtime );
  381. msg->SetInt( "entindex", m_nEntIndex );
  382. msg->SetInt( "flashlightHandle", m_FlashlightHandle );
  383. msg->SetPtr( "flashlightState", NULL );
  384. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  385. msg->deleteThis();
  386. }
  387. #endif
  388. // Clear out the light
  389. if( m_FlashlightHandle != CLIENTSHADOW_INVALID_HANDLE )
  390. {
  391. g_pClientShadowMgr->DestroyFlashlight( m_FlashlightHandle );
  392. m_FlashlightHandle = CLIENTSHADOW_INVALID_HANDLE;
  393. }
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose:
  397. //-----------------------------------------------------------------------------
  398. void CFlashlightEffect::LightOffOld()
  399. {
  400. if ( m_pPointLight && ( m_pPointLight->key == m_nEntIndex ) )
  401. {
  402. m_pPointLight->die = gpGlobals->curtime;
  403. m_pPointLight = NULL;
  404. }
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose:
  408. //-----------------------------------------------------------------------------
  409. void CFlashlightEffect::LightOff()
  410. {
  411. LightOffOld();
  412. LightOffNew();
  413. }
  414. CHeadlightEffect::CHeadlightEffect()
  415. {
  416. }
  417. CHeadlightEffect::~CHeadlightEffect()
  418. {
  419. }
  420. void CHeadlightEffect::UpdateLight( const Vector &vecPos, const Vector &vecDir, const Vector &vecRight, const Vector &vecUp, int nDistance )
  421. {
  422. if ( IsOn() == false )
  423. return;
  424. FlashlightState_t state;
  425. Vector basisX, basisY, basisZ;
  426. basisX = vecDir;
  427. basisY = vecRight;
  428. basisZ = vecUp;
  429. VectorNormalize(basisX);
  430. VectorNormalize(basisY);
  431. VectorNormalize(basisZ);
  432. BasisToQuaternion( basisX, basisY, basisZ, state.m_quatOrientation );
  433. state.m_vecLightOrigin = vecPos;
  434. state.m_fHorizontalFOVDegrees = 45.0f;
  435. state.m_fVerticalFOVDegrees = 30.0f;
  436. state.m_fQuadraticAtten = r_flashlightquadratic.GetFloat();
  437. state.m_fLinearAtten = r_flashlightlinear.GetFloat();
  438. state.m_fConstantAtten = r_flashlightconstant.GetFloat();
  439. state.m_Color[0] = 1.0f;
  440. state.m_Color[1] = 1.0f;
  441. state.m_Color[2] = 1.0f;
  442. state.m_Color[3] = r_flashlightambient.GetFloat();
  443. state.m_NearZ = r_flashlightnear.GetFloat();
  444. state.m_FarZ = r_flashlightfar.GetFloat();
  445. state.m_bEnableShadows = true;
  446. state.m_pSpotlightTexture = m_FlashlightTexture;
  447. state.m_nSpotlightTextureFrame = 0;
  448. if( GetFlashlightHandle() == CLIENTSHADOW_INVALID_HANDLE )
  449. {
  450. SetFlashlightHandle( g_pClientShadowMgr->CreateFlashlight( state ) );
  451. }
  452. else
  453. {
  454. g_pClientShadowMgr->UpdateFlashlightState( GetFlashlightHandle(), state );
  455. }
  456. g_pClientShadowMgr->UpdateProjectedTexture( GetFlashlightHandle(), true );
  457. }