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.

484 lines
14 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "precache_register.h"
  8. #include "fx_sparks.h"
  9. #include "iefx.h"
  10. #include "c_te_effect_dispatch.h"
  11. #include "particles_ez.h"
  12. #include "decals.h"
  13. #include "engine/IEngineSound.h"
  14. #include "fx_quad.h"
  15. #include "tier0/vprof.h"
  16. #include "fx.h"
  17. #include "fx_water.h"
  18. // memdbgon must be the last include file in a .cpp file!!!
  19. #include "tier0/memdbgon.h"
  20. #ifndef DOTA_DLL
  21. PRECACHE_REGISTER_BEGIN( GLOBAL, PrecacheEffectSplash )
  22. PRECACHE( MATERIAL, "effects/splash1" )
  23. PRECACHE( MATERIAL, "effects/splash2" )
  24. PRECACHE( MATERIAL, "effects/splash4" )
  25. PRECACHE( MATERIAL, "effects/slime1" )
  26. PRECACHE_REGISTER_END()
  27. #endif
  28. #define SPLASH_MIN_SPEED 50.0f
  29. #define SPLASH_MAX_SPEED 100.0f
  30. ConVar cl_show_splashes( "cl_show_splashes", "1" );
  31. static Vector s_vecSlimeColor( 46.0f/255.0f, 90.0f/255.0f, 36.0f/255.0f );
  32. // Each channel does not contribute to the luminosity equally, as represented here
  33. #define RED_CHANNEL_CONTRIBUTION 0.30f
  34. #define GREEN_CHANNEL_CONTRIBUTION 0.59f
  35. #define BLUE_CHANNEL_CONTRIBUTION 0.11f
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Returns a normalized tint and luminosity for a specified color
  38. // Input : &color - normalized input color to extract information from
  39. // *tint - normalized tint of that color
  40. // *luminosity - normalized luminosity of that color
  41. //-----------------------------------------------------------------------------
  42. void UTIL_GetNormalizedColorTintAndLuminosity( const Vector &color, Vector *tint, float *luminosity )
  43. {
  44. // Give luminosity if requested
  45. if ( luminosity != NULL )
  46. {
  47. // Each channel contributes differently than the others
  48. *luminosity = ( color.x * RED_CHANNEL_CONTRIBUTION ) +
  49. ( color.y * GREEN_CHANNEL_CONTRIBUTION ) +
  50. ( color.z * BLUE_CHANNEL_CONTRIBUTION );
  51. }
  52. // Give tint if requested
  53. if ( tint != NULL )
  54. {
  55. if ( color == vec3_origin )
  56. {
  57. *tint = vec3_origin;
  58. }
  59. else
  60. {
  61. float maxComponent = MAX( color.x, MAX( color.y, color.z ) );
  62. *tint = color / maxComponent;
  63. }
  64. }
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. // Input : &origin -
  69. // &normal -
  70. // scale -
  71. //-----------------------------------------------------------------------------
  72. void FX_WaterRipple( const Vector &origin, float scale, Vector *pColor, float flLifetime, float flAlpha )
  73. {
  74. #if defined( _GAMECONSOLE )
  75. // We don't want to generate ripples too close together on the console because it kills perf.
  76. static float sNextRippleTime = 0.0f;
  77. static float MIN_TIME_BETWEEN_RIPPLES = 0.05f;
  78. float curTime = gpGlobals->curtime;
  79. float nextRipple = curTime + MIN_TIME_BETWEEN_RIPPLES;
  80. bool movedBackInTime = nextRipple < sNextRippleTime;
  81. // If we've "moved back in time" then curtime propably got reset because we're in a new game.
  82. // Since sNextRippleTime is static, we need to make sure to reset when curtime gets reset for the game.
  83. if ( curTime < sNextRippleTime && !movedBackInTime )
  84. {
  85. return;
  86. }
  87. sNextRippleTime = nextRipple;
  88. #endif
  89. VPROF_BUDGET( "FX_WaterRipple", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  90. trace_t tr;
  91. Vector color = pColor ? *pColor : Vector( 0.8f, 0.8f, 0.75f );
  92. Vector startPos = origin + Vector(0,0,8);
  93. Vector endPos = origin + Vector(0,0,-64);
  94. UTIL_TraceLine( startPos, endPos, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  95. if ( tr.fraction < 1.0f )
  96. {
  97. QAngle vecAngles;
  98. // we flip the z and the x to match the orientation of how the impact particles are authored
  99. // all impact particles are authored with the effect going "up" (0, 0, 1)
  100. VectorAngles( Vector( tr.plane.normal.z, tr.plane.normal.y, tr.plane.normal.x ), vecAngles );
  101. DispatchParticleEffect( "water_splash_02_surface2", tr.endpos, vecAngles, NULL );
  102. }
  103. }
  104. #ifndef DOTA_DLL
  105. PRECACHE_REGISTER_BEGIN( SHARED_SYSTEM, FX_WaterRipple )
  106. PRECACHE( PARTICLE_SYSTEM, "water_splash_02_surface2" )
  107. //PRECACHE( MATERIAL, "effects/splashwake1" )
  108. PRECACHE_REGISTER_END()
  109. #endif
  110. //-----------------------------------------------------------------------------
  111. // Purpose:
  112. // Input : &origin -
  113. // &normal -
  114. //-----------------------------------------------------------------------------
  115. void FX_GunshotSplashVisuals( const Vector &origin, const Vector &normal, float scale )
  116. {
  117. VPROF_BUDGET( "FX_GunshotSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  118. if ( cl_show_splashes.GetBool() == false )
  119. return;
  120. QAngle vecAngles;
  121. // we flip the z and the x to match the orientation of how the impact particles are authored
  122. // all impact particles are authored with the effect going "up" (0, 0, 1)
  123. VectorAngles( Vector( normal.z, normal.y, normal.x ), vecAngles );
  124. if ( scale < 4.0f )
  125. {
  126. DispatchParticleEffect( "water_splash_01", origin, vecAngles );
  127. }
  128. else if ( scale < 8.0f )
  129. {
  130. DispatchParticleEffect( "water_splash_02", origin, vecAngles );
  131. }
  132. else
  133. {
  134. DispatchParticleEffect( "water_splash_03", origin, vecAngles );
  135. }
  136. }
  137. void FX_GunshotSplashSound( const Vector &origin, const Vector &normal, float scale )
  138. {
  139. //Play a sound
  140. CLocalPlayerFilter filter;
  141. EmitSound_t ep;
  142. ep.m_nChannel = CHAN_VOICE;
  143. ep.m_pSoundName = "Physics.WaterSplash";
  144. ep.m_flVolume = 1.0f;
  145. ep.m_SoundLevel = SNDLVL_NORM;
  146. ep.m_pOrigin = &origin;
  147. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  148. }
  149. PRECACHE_REGISTER_BEGIN( SHARED_SYSTEM, FX_GunshotSplash )
  150. PRECACHE( PARTICLE_SYSTEM, "water_splash_01" )
  151. PRECACHE( PARTICLE_SYSTEM, "water_splash_02" )
  152. PRECACHE( PARTICLE_SYSTEM, "water_splash_03" )
  153. //PRECACHE( MATERIAL, "effects/splash2" )
  154. PRECACHE( GAMESOUND, "Physics.WaterSplash" )
  155. PRECACHE_REGISTER_END()
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. // Input : &origin -
  159. // &normal -
  160. //-----------------------------------------------------------------------------
  161. void FX_GunshotSplash( const Vector &origin, const Vector &normal, float scale )
  162. {
  163. VPROF_BUDGET( "FX_GunshotSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  164. if ( cl_show_splashes.GetBool() == false )
  165. return;
  166. FX_GunshotSplashVisuals( origin, normal, scale );
  167. FX_GunshotSplashSound( origin, normal, scale );
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose:
  171. // Input : &origin -
  172. // &normal -
  173. // scale -
  174. // *pColor -
  175. //-----------------------------------------------------------------------------
  176. void FX_GunshotSlimeSplash( const Vector &origin, const Vector &normal, float scale )
  177. {
  178. if ( cl_show_splashes.GetBool() == false )
  179. return;
  180. VPROF_BUDGET( "FX_GunshotSlimeSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  181. QAngle vecAngles;
  182. // we flip the z and the x to match the orientation of how the impact particles are authored
  183. // all impact particles are authored with the effect going "up" (0, 0, 1)
  184. VectorAngles( Vector( normal.z, normal.y, normal.x ), vecAngles );
  185. if ( scale < 2.0f )
  186. {
  187. DispatchParticleEffect( "slime_splash_01", origin, vecAngles );
  188. }
  189. else if ( scale < 4.0f )
  190. {
  191. DispatchParticleEffect( "slime_splash_02", origin, vecAngles );
  192. }
  193. else
  194. {
  195. DispatchParticleEffect( "slime_splash_03", origin, vecAngles );
  196. }
  197. //Play a sound
  198. CLocalPlayerFilter filter;
  199. EmitSound_t ep;
  200. ep.m_nChannel = CHAN_VOICE;
  201. ep.m_pSoundName = "Physics.WaterSplash";
  202. ep.m_flVolume = 1.0f;
  203. ep.m_SoundLevel = SNDLVL_NORM;
  204. ep.m_pOrigin = &origin;
  205. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  206. }
  207. PRECACHE_REGISTER_BEGIN( SHARED_SYSTEM, FX_GunshotSlimeSplash )
  208. #ifndef DOTA_DLL
  209. PRECACHE( PARTICLE_SYSTEM, "slime_splash_01" )
  210. PRECACHE( PARTICLE_SYSTEM, "slime_splash_02" )
  211. PRECACHE( PARTICLE_SYSTEM, "slime_splash_03" )
  212. PRECACHE( GAMESOUND, "Physics.WaterSplash" )
  213. #endif
  214. PRECACHE_REGISTER_END()
  215. //-----------------------------------------------------------------------------
  216. // Purpose:
  217. //-----------------------------------------------------------------------------
  218. void SplashCallback( const CEffectData &data )
  219. {
  220. Vector normal;
  221. AngleVectors( data.m_vAngles, &normal );
  222. if ( data.m_fFlags & FX_WATER_IN_SLIME )
  223. {
  224. FX_GunshotSlimeSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  225. }
  226. else
  227. {
  228. FX_GunshotSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  229. }
  230. }
  231. DECLARE_CLIENT_EFFECT_BEGIN( watersplash, SplashCallback )
  232. PRECACHE( SHARED, "FX_GunshotSlimeSplash" )
  233. PRECACHE( SHARED, "FX_GunshotSplash" )
  234. DECLARE_CLIENT_EFFECT_END()
  235. //-----------------------------------------------------------------------------
  236. // Purpose:
  237. //-----------------------------------------------------------------------------
  238. void SplashQuietCallback( const CEffectData &data )
  239. {
  240. Vector normal;
  241. AngleVectors( data.m_vAngles, &normal );
  242. if ( data.m_fFlags & FX_WATER_IN_SLIME )
  243. {
  244. FX_GunshotSlimeSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  245. }
  246. else
  247. {
  248. FX_GunshotSplashVisuals( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  249. }
  250. }
  251. DECLARE_CLIENT_EFFECT_BEGIN( watersplashquiet, SplashQuietCallback )
  252. DECLARE_CLIENT_EFFECT_END()
  253. //-----------------------------------------------------------------------------
  254. // Purpose:
  255. // Input : &data -
  256. //-----------------------------------------------------------------------------
  257. void GunshotSplashCallback( const CEffectData &data )
  258. {
  259. if ( data.m_fFlags & FX_WATER_IN_SLIME )
  260. {
  261. FX_GunshotSlimeSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  262. }
  263. else
  264. {
  265. FX_GunshotSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  266. }
  267. }
  268. DECLARE_CLIENT_EFFECT_BEGIN( gunshotsplash, GunshotSplashCallback )
  269. PRECACHE( SHARED, "FX_GunshotSlimeSplash" )
  270. PRECACHE( SHARED, "FX_GunshotSplash" )
  271. DECLARE_CLIENT_EFFECT_END()
  272. //-----------------------------------------------------------------------------
  273. // Purpose:
  274. // Input : &data -
  275. //-----------------------------------------------------------------------------
  276. void RippleCallback( const CEffectData &data )
  277. {
  278. float flScale = data.m_flScale / 8.0f;
  279. Vector color;
  280. float luminosity;
  281. // Get our lighting information
  282. FX_GetSplashLighting( data.m_vOrigin + ( Vector(0,0,1) * 4.0f ), &color, &luminosity );
  283. FX_WaterRipple( data.m_vOrigin, flScale, &color, 1.5f, luminosity );
  284. }
  285. DECLARE_CLIENT_EFFECT_BEGIN( waterripple, RippleCallback )
  286. PRECACHE( SHARED, "FX_WaterRipple" )
  287. DECLARE_CLIENT_EFFECT_END()
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. // Input : *pDebugName -
  291. // Output : WaterDebrisEffect*
  292. //-----------------------------------------------------------------------------
  293. WaterDebrisEffect* WaterDebrisEffect::Create( const char *pDebugName )
  294. {
  295. return new WaterDebrisEffect( pDebugName );
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose:
  299. // Input : *pParticle -
  300. // timeDelta -
  301. // Output : float
  302. //-----------------------------------------------------------------------------
  303. float WaterDebrisEffect::UpdateAlpha( const SimpleParticle *pParticle )
  304. {
  305. return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) );
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. // Input : *pParticle -
  310. // timeDelta -
  311. // Output : float
  312. //-----------------------------------------------------------------------------
  313. float CSplashParticle::UpdateRoll( SimpleParticle *pParticle, float timeDelta )
  314. {
  315. pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
  316. pParticle->m_flRollDelta += pParticle->m_flRollDelta * ( timeDelta * -4.0f );
  317. //Cap the minimum roll
  318. if ( fabs( pParticle->m_flRollDelta ) < 0.5f )
  319. {
  320. pParticle->m_flRollDelta = ( pParticle->m_flRollDelta > 0.0f ) ? 0.5f : -0.5f;
  321. }
  322. return pParticle->m_flRoll;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. // Input : *pParticle -
  327. // timeDelta -
  328. //-----------------------------------------------------------------------------
  329. void CSplashParticle::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  330. {
  331. //Decellerate
  332. static float dtime;
  333. static float decay;
  334. if ( dtime != timeDelta )
  335. {
  336. dtime = timeDelta;
  337. float expected = 3.0f;
  338. decay = exp( log( 0.0001f ) * dtime / expected );
  339. }
  340. pParticle->m_vecVelocity *= decay;
  341. pParticle->m_vecVelocity[2] -= ( 800.0f * timeDelta );
  342. }
  343. //-----------------------------------------------------------------------------
  344. // Purpose:
  345. // Input : *pParticle -
  346. // Output : float
  347. //-----------------------------------------------------------------------------
  348. float CSplashParticle::UpdateAlpha( const SimpleParticle *pParticle )
  349. {
  350. if ( m_bUseClipHeight )
  351. {
  352. float flAlpha = pParticle->m_uchStartAlpha / 255.0f;
  353. return flAlpha * RemapValClamped(pParticle->m_Pos.z,
  354. m_flClipHeight,
  355. m_flClipHeight - ( UpdateScale( pParticle ) * 0.5f ),
  356. 1.0f,
  357. 0.0f );
  358. }
  359. return (pParticle->m_uchStartAlpha/255.0f) + ( (float)(pParticle->m_uchEndAlpha/255.0f) - (float)(pParticle->m_uchStartAlpha/255.0f) ) * (pParticle->m_flLifetime / pParticle->m_flDieTime);
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose:
  363. // Input : &clipPlane -
  364. //-----------------------------------------------------------------------------
  365. void CSplashParticle::SetClipHeight( float flClipHeight )
  366. {
  367. m_bUseClipHeight = true;
  368. m_flClipHeight = flClipHeight;
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. // Input : *pIterator -
  373. //-----------------------------------------------------------------------------
  374. void CSplashParticle::SimulateParticles( CParticleSimulateIterator *pIterator )
  375. {
  376. float timeDelta = pIterator->GetTimeDelta();
  377. SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst();
  378. while ( pParticle )
  379. {
  380. //Update velocity
  381. UpdateVelocity( pParticle, timeDelta );
  382. pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta;
  383. // Clip by height if requested
  384. if ( m_bUseClipHeight )
  385. {
  386. // See if we're below, and therefore need to clip
  387. if ( pParticle->m_Pos.z + UpdateScale( pParticle ) < m_flClipHeight )
  388. {
  389. pIterator->RemoveParticle( pParticle );
  390. pParticle = (SimpleParticle*)pIterator->GetNext();
  391. continue;
  392. }
  393. }
  394. //Should this particle die?
  395. pParticle->m_flLifetime += timeDelta;
  396. UpdateRoll( pParticle, timeDelta );
  397. if ( pParticle->m_flLifetime >= pParticle->m_flDieTime )
  398. pIterator->RemoveParticle( pParticle );
  399. pParticle = (SimpleParticle*)pIterator->GetNext();
  400. }
  401. }