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.

603 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "clienteffectprecachesystem.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. CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectSplash )
  21. CLIENTEFFECT_MATERIAL( "effects/splash1" )
  22. CLIENTEFFECT_MATERIAL( "effects/splash2" )
  23. CLIENTEFFECT_MATERIAL( "effects/splash4" )
  24. CLIENTEFFECT_MATERIAL( "effects/slime1" )
  25. CLIENTEFFECT_REGISTER_END()
  26. #define SPLASH_MIN_SPEED 50.0f
  27. #define SPLASH_MAX_SPEED 100.0f
  28. ConVar cl_show_splashes( "cl_show_splashes", "1" );
  29. static Vector s_vecSlimeColor( 46.0f/255.0f, 90.0f/255.0f, 36.0f/255.0f );
  30. // Each channel does not contribute to the luminosity equally, as represented here
  31. #define RED_CHANNEL_CONTRIBUTION 0.30f
  32. #define GREEN_CHANNEL_CONTRIBUTION 0.59f
  33. #define BLUE_CHANNEL_CONTRIBUTION 0.11f
  34. //-----------------------------------------------------------------------------
  35. // Purpose: Returns a normalized tint and luminosity for a specified color
  36. // Input : &color - normalized input color to extract information from
  37. // *tint - normalized tint of that color
  38. // *luminosity - normalized luminosity of that color
  39. //-----------------------------------------------------------------------------
  40. void UTIL_GetNormalizedColorTintAndLuminosity( const Vector &color, Vector *tint, float *luminosity )
  41. {
  42. // Give luminosity if requested
  43. if ( luminosity != NULL )
  44. {
  45. // Each channel contributes differently than the others
  46. *luminosity = ( color.x * RED_CHANNEL_CONTRIBUTION ) +
  47. ( color.y * GREEN_CHANNEL_CONTRIBUTION ) +
  48. ( color.z * BLUE_CHANNEL_CONTRIBUTION );
  49. }
  50. // Give tint if requested
  51. if ( tint != NULL )
  52. {
  53. if ( color == vec3_origin )
  54. {
  55. *tint = vec3_origin;
  56. }
  57. else
  58. {
  59. float maxComponent = MAX( color.x, MAX( color.y, color.z ) );
  60. *tint = color / maxComponent;
  61. }
  62. }
  63. }
  64. //-----------------------------------------------------------------------------
  65. // Purpose:
  66. // Input : &origin -
  67. // &normal -
  68. // scale -
  69. //-----------------------------------------------------------------------------
  70. void FX_WaterRipple( const Vector &origin, float scale, Vector *pColor, float flLifetime, float flAlpha )
  71. {
  72. VPROF_BUDGET( "FX_WaterRipple", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  73. trace_t tr;
  74. Vector color = pColor ? *pColor : Vector( 0.8f, 0.8f, 0.75f );
  75. Vector startPos = origin + Vector(0,0,8);
  76. Vector endPos = origin + Vector(0,0,-64);
  77. UTIL_TraceLine( startPos, endPos, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
  78. if ( tr.fraction < 1.0f )
  79. {
  80. //Add a ripple quad to the surface
  81. FX_AddQuad( tr.endpos + ( tr.plane.normal * 0.5f ),
  82. tr.plane.normal,
  83. 16.0f*scale,
  84. 128.0f*scale,
  85. 0.7f,
  86. flAlpha, // start alpha
  87. 0.0f, // end alpha
  88. 0.25f,
  89. random->RandomFloat( 0, 360 ),
  90. random->RandomFloat( -16.0f, 16.0f ),
  91. color,
  92. flLifetime,
  93. "effects/splashwake1",
  94. (FXQUAD_BIAS_SCALE|FXQUAD_BIAS_ALPHA) );
  95. }
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Purpose:
  99. // Input : &origin -
  100. // &normal -
  101. //-----------------------------------------------------------------------------
  102. void FX_GunshotSplash( const Vector &origin, const Vector &normal, float scale )
  103. {
  104. VPROF_BUDGET( "FX_GunshotSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  105. if ( cl_show_splashes.GetBool() == false )
  106. return;
  107. Vector color;
  108. float luminosity;
  109. // Get our lighting information
  110. FX_GetSplashLighting( origin + ( normal * scale ), &color, &luminosity );
  111. float flScale = scale / 8.0f;
  112. if ( flScale > 4.0f )
  113. {
  114. flScale = 4.0f;
  115. }
  116. // Setup our trail emitter
  117. CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "splash" );
  118. if ( !sparkEmitter )
  119. return;
  120. sparkEmitter->SetSortOrigin( origin );
  121. sparkEmitter->m_ParticleCollision.SetGravity( 800.0f );
  122. sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
  123. sparkEmitter->SetVelocityDampen( 2.0f );
  124. sparkEmitter->GetBinding().SetBBox( origin - Vector( 32, 32, 32 ), origin + Vector( 32, 32, 32 ) );
  125. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/splash2" );
  126. TrailParticle *tParticle;
  127. Vector offDir;
  128. Vector offset;
  129. float colorRamp;
  130. //Dump out drops
  131. for ( int i = 0; i < 16; i++ )
  132. {
  133. offset = origin;
  134. offset[0] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
  135. offset[1] += random->RandomFloat( -8.0f, 8.0f ) * flScale;
  136. tParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
  137. if ( tParticle == NULL )
  138. break;
  139. tParticle->m_flLifetime = 0.0f;
  140. tParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f );
  141. offDir = normal + RandomVector( -0.8f, 0.8f );
  142. tParticle->m_vecVelocity = offDir * random->RandomFloat( SPLASH_MIN_SPEED * flScale * 3.0f, SPLASH_MAX_SPEED * flScale * 3.0f );
  143. tParticle->m_vecVelocity[2] += random->RandomFloat( 32.0f, 64.0f ) * flScale;
  144. tParticle->m_flWidth = random->RandomFloat( 1.0f, 3.0f );
  145. tParticle->m_flLength = random->RandomFloat( 0.025f, 0.05f );
  146. colorRamp = random->RandomFloat( 0.75f, 1.25f );
  147. tParticle->m_color.r = MIN( 1.0f, color[0] * colorRamp ) * 255;
  148. tParticle->m_color.g = MIN( 1.0f, color[1] * colorRamp ) * 255;
  149. tParticle->m_color.b = MIN( 1.0f, color[2] * colorRamp ) * 255;
  150. tParticle->m_color.a = luminosity * 255;
  151. }
  152. // Setup the particle emitter
  153. CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
  154. pSimple->SetSortOrigin( origin );
  155. pSimple->SetClipHeight( origin.z );
  156. pSimple->SetParticleCullRadius( scale * 2.0f );
  157. pSimple->GetBinding().SetBBox( origin - Vector( 32, 32, 32 ), origin + Vector( 32, 32, 32 ) );
  158. SimpleParticle *pParticle;
  159. //Main gout
  160. for ( int i = 0; i < 8; i++ )
  161. {
  162. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, origin );
  163. if ( pParticle == NULL )
  164. break;
  165. pParticle->m_flLifetime = 0.0f;
  166. pParticle->m_flDieTime = 2.0f; //NOTENOTE: We use a clip plane to realistically control our lifespan
  167. pParticle->m_vecVelocity.Random( -0.2f, 0.2f );
  168. pParticle->m_vecVelocity += ( normal * random->RandomFloat( 4.0f, 6.0f ) );
  169. VectorNormalize( pParticle->m_vecVelocity );
  170. pParticle->m_vecVelocity *= 50 * flScale * (8-i);
  171. colorRamp = random->RandomFloat( 0.75f, 1.25f );
  172. pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
  173. pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
  174. pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
  175. pParticle->m_uchStartSize = 24 * flScale * RemapValClamped( i, 7, 0, 1, 0.5f );
  176. pParticle->m_uchEndSize = MIN( 255, pParticle->m_uchStartSize * 2 );
  177. pParticle->m_uchStartAlpha = RemapValClamped( i, 7, 0, 255, 32 ) * luminosity;
  178. pParticle->m_uchEndAlpha = 0;
  179. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  180. pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f );
  181. }
  182. // Do a ripple
  183. FX_WaterRipple( origin, flScale, &color, 1.5f, luminosity );
  184. //Play a sound
  185. CLocalPlayerFilter filter;
  186. EmitSound_t ep;
  187. ep.m_nChannel = CHAN_VOICE;
  188. ep.m_pSoundName = "Physics.WaterSplash";
  189. ep.m_flVolume = 1.0f;
  190. ep.m_SoundLevel = SNDLVL_NORM;
  191. ep.m_pOrigin = &origin;
  192. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. // Input : &origin -
  197. // &normal -
  198. // scale -
  199. // *pColor -
  200. //-----------------------------------------------------------------------------
  201. void FX_GunshotSlimeSplash( const Vector &origin, const Vector &normal, float scale )
  202. {
  203. if ( cl_show_splashes.GetBool() == false )
  204. return;
  205. VPROF_BUDGET( "FX_GunshotSlimeSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  206. #if 0
  207. float colorRamp;
  208. float flScale = MIN( 1.0f, scale / 8.0f );
  209. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/slime1" );
  210. PMaterialHandle hMaterial2 = ParticleMgr()->GetPMaterial( "effects/splash4" );
  211. Vector color;
  212. float luminosity;
  213. // Get our lighting information
  214. FX_GetSplashLighting( origin + ( normal * scale ), &color, &luminosity );
  215. Vector offDir;
  216. Vector offset;
  217. TrailParticle *tParticle;
  218. CSmartPtr<CTrailParticles> sparkEmitter = CTrailParticles::Create( "splash" );
  219. if ( !sparkEmitter )
  220. return;
  221. sparkEmitter->SetSortOrigin( origin );
  222. sparkEmitter->m_ParticleCollision.SetGravity( 800.0f );
  223. sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN );
  224. sparkEmitter->SetVelocityDampen( 2.0f );
  225. if ( IsXbox() )
  226. {
  227. sparkEmitter->GetBinding().SetBBox( origin - Vector( 32, 32, 64 ), origin + Vector( 32, 32, 64 ) );
  228. }
  229. //Dump out drops
  230. for ( int i = 0; i < 24; i++ )
  231. {
  232. offset = origin;
  233. offset[0] += random->RandomFloat( -16.0f, 16.0f ) * flScale;
  234. offset[1] += random->RandomFloat( -16.0f, 16.0f ) * flScale;
  235. tParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
  236. if ( tParticle == NULL )
  237. break;
  238. tParticle->m_flLifetime = 0.0f;
  239. tParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f );
  240. offDir = normal + RandomVector( -0.6f, 0.6f );
  241. tParticle->m_vecVelocity = offDir * random->RandomFloat( SPLASH_MIN_SPEED * flScale * 3.0f, SPLASH_MAX_SPEED * flScale * 3.0f );
  242. tParticle->m_vecVelocity[2] += random->RandomFloat( 32.0f, 64.0f ) * flScale;
  243. tParticle->m_flWidth = random->RandomFloat( 3.0f, 6.0f ) * flScale;
  244. tParticle->m_flLength = random->RandomFloat( 0.025f, 0.05f ) * flScale;
  245. colorRamp = random->RandomFloat( 0.75f, 1.25f );
  246. tParticle->m_color.r = MIN( 1.0f, color.x * colorRamp ) * 255;
  247. tParticle->m_color.g = MIN( 1.0f, color.y * colorRamp ) * 255;
  248. tParticle->m_color.b = MIN( 1.0f, color.z * colorRamp ) * 255;
  249. tParticle->m_color.a = 255 * luminosity;
  250. }
  251. // Setup splash emitter
  252. CSmartPtr<CSplashParticle> pSimple = CSplashParticle::Create( "splish" );
  253. pSimple->SetSortOrigin( origin );
  254. pSimple->SetClipHeight( origin.z );
  255. pSimple->SetParticleCullRadius( scale * 2.0f );
  256. if ( IsXbox() )
  257. {
  258. pSimple->GetBinding().SetBBox( origin - Vector( 32, 32, 64 ), origin + Vector( 32, 32, 64 ) );
  259. }
  260. SimpleParticle *pParticle;
  261. // Tint
  262. colorRamp = random->RandomFloat( 0.75f, 1.0f );
  263. color = Vector( 1.0f, 0.8f, 0.0f ) * color * colorRamp;
  264. //Main gout
  265. for ( int i = 0; i < 8; i++ )
  266. {
  267. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial2, origin );
  268. if ( pParticle == NULL )
  269. break;
  270. pParticle->m_flLifetime = 0.0f;
  271. pParticle->m_flDieTime = 2.0f; //NOTENOTE: We use a clip plane to realistically control our lifespan
  272. pParticle->m_vecVelocity.Random( -0.2f, 0.2f );
  273. pParticle->m_vecVelocity += ( normal * random->RandomFloat( 4.0f, 6.0f ) );
  274. VectorNormalize( pParticle->m_vecVelocity );
  275. pParticle->m_vecVelocity *= 50 * flScale * (8-i);
  276. colorRamp = random->RandomFloat( 0.75f, 1.25f );
  277. pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
  278. pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
  279. pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
  280. pParticle->m_uchStartSize = 24 * flScale * RemapValClamped( i, 7, 0, 1, 0.5f );
  281. pParticle->m_uchEndSize = MIN( 255, pParticle->m_uchStartSize * 2 );
  282. pParticle->m_uchStartAlpha = RemapValClamped( i, 7, 0, 255, 32 ) * luminosity;
  283. pParticle->m_uchEndAlpha = 0;
  284. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  285. pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f );
  286. }
  287. #else
  288. QAngle vecAngles;
  289. VectorAngles( normal, vecAngles );
  290. if ( scale < 2.0f )
  291. {
  292. DispatchParticleEffect( "slime_splash_01", origin, vecAngles );
  293. }
  294. else if ( scale < 4.0f )
  295. {
  296. DispatchParticleEffect( "slime_splash_02", origin, vecAngles );
  297. }
  298. else
  299. {
  300. DispatchParticleEffect( "slime_splash_03", origin, vecAngles );
  301. }
  302. #endif
  303. //Play a sound
  304. CLocalPlayerFilter filter;
  305. EmitSound_t ep;
  306. ep.m_nChannel = CHAN_VOICE;
  307. ep.m_pSoundName = "Physics.WaterSplash";
  308. ep.m_flVolume = 1.0f;
  309. ep.m_SoundLevel = SNDLVL_NORM;
  310. ep.m_pOrigin = &origin;
  311. C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep );
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose:
  315. //-----------------------------------------------------------------------------
  316. void SplashCallback( const CEffectData &data )
  317. {
  318. Vector normal;
  319. AngleVectors( data.m_vAngles, &normal );
  320. if ( data.m_fFlags & FX_WATER_IN_SLIME )
  321. {
  322. FX_GunshotSlimeSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  323. }
  324. else
  325. {
  326. FX_GunshotSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  327. }
  328. }
  329. DECLARE_CLIENT_EFFECT( "watersplash", SplashCallback );
  330. //-----------------------------------------------------------------------------
  331. // Purpose:
  332. // Input : &data -
  333. //-----------------------------------------------------------------------------
  334. void GunshotSplashCallback( const CEffectData &data )
  335. {
  336. if ( data.m_fFlags & FX_WATER_IN_SLIME )
  337. {
  338. FX_GunshotSlimeSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  339. }
  340. else
  341. {
  342. FX_GunshotSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale );
  343. }
  344. }
  345. DECLARE_CLIENT_EFFECT( "gunshotsplash", GunshotSplashCallback );
  346. //-----------------------------------------------------------------------------
  347. // Purpose:
  348. // Input : &data -
  349. //-----------------------------------------------------------------------------
  350. void RippleCallback( const CEffectData &data )
  351. {
  352. float flScale = data.m_flScale / 8.0f;
  353. Vector color;
  354. float luminosity;
  355. // Get our lighting information
  356. FX_GetSplashLighting( data.m_vOrigin + ( Vector(0,0,1) * 4.0f ), &color, &luminosity );
  357. FX_WaterRipple( data.m_vOrigin, flScale, &color, 1.5f, luminosity );
  358. }
  359. DECLARE_CLIENT_EFFECT( "waterripple", RippleCallback );
  360. //-----------------------------------------------------------------------------
  361. // Purpose:
  362. // Input : *pDebugName -
  363. // Output : WaterDebrisEffect*
  364. //-----------------------------------------------------------------------------
  365. WaterDebrisEffect* WaterDebrisEffect::Create( const char *pDebugName )
  366. {
  367. return new WaterDebrisEffect( pDebugName );
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose:
  371. // Input : *pParticle -
  372. // timeDelta -
  373. // Output : float
  374. //-----------------------------------------------------------------------------
  375. float WaterDebrisEffect::UpdateAlpha( const SimpleParticle *pParticle )
  376. {
  377. return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) );
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose:
  381. // Input : *pParticle -
  382. // timeDelta -
  383. // Output : float
  384. //-----------------------------------------------------------------------------
  385. float CSplashParticle::UpdateRoll( SimpleParticle *pParticle, float timeDelta )
  386. {
  387. pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
  388. pParticle->m_flRollDelta += pParticle->m_flRollDelta * ( timeDelta * -4.0f );
  389. //Cap the minimum roll
  390. if ( fabs( pParticle->m_flRollDelta ) < 0.5f )
  391. {
  392. pParticle->m_flRollDelta = ( pParticle->m_flRollDelta > 0.0f ) ? 0.5f : -0.5f;
  393. }
  394. return pParticle->m_flRoll;
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Purpose:
  398. // Input : *pParticle -
  399. // timeDelta -
  400. //-----------------------------------------------------------------------------
  401. void CSplashParticle::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  402. {
  403. //Decellerate
  404. static float dtime;
  405. static float decay;
  406. if ( dtime != timeDelta )
  407. {
  408. dtime = timeDelta;
  409. float expected = 3.0f;
  410. decay = exp( log( 0.0001f ) * dtime / expected );
  411. }
  412. pParticle->m_vecVelocity *= decay;
  413. pParticle->m_vecVelocity[2] -= ( 800.0f * timeDelta );
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose:
  417. // Input : *pParticle -
  418. // Output : float
  419. //-----------------------------------------------------------------------------
  420. float CSplashParticle::UpdateAlpha( const SimpleParticle *pParticle )
  421. {
  422. if ( m_bUseClipHeight )
  423. {
  424. float flAlpha = pParticle->m_uchStartAlpha / 255.0f;
  425. return flAlpha * RemapValClamped(pParticle->m_Pos.z,
  426. m_flClipHeight,
  427. m_flClipHeight - ( UpdateScale( pParticle ) * 0.5f ),
  428. 1.0f,
  429. 0.0f );
  430. }
  431. return (pParticle->m_uchStartAlpha/255.0f) + ( (float)(pParticle->m_uchEndAlpha/255.0f) - (float)(pParticle->m_uchStartAlpha/255.0f) ) * (pParticle->m_flLifetime / pParticle->m_flDieTime);
  432. }
  433. //-----------------------------------------------------------------------------
  434. // Purpose:
  435. // Input : &clipPlane -
  436. //-----------------------------------------------------------------------------
  437. void CSplashParticle::SetClipHeight( float flClipHeight )
  438. {
  439. m_bUseClipHeight = true;
  440. m_flClipHeight = flClipHeight;
  441. }
  442. //-----------------------------------------------------------------------------
  443. // Purpose:
  444. // Input : *pIterator -
  445. //-----------------------------------------------------------------------------
  446. void CSplashParticle::SimulateParticles( CParticleSimulateIterator *pIterator )
  447. {
  448. float timeDelta = pIterator->GetTimeDelta();
  449. SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst();
  450. while ( pParticle )
  451. {
  452. //Update velocity
  453. UpdateVelocity( pParticle, timeDelta );
  454. pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta;
  455. // Clip by height if requested
  456. if ( m_bUseClipHeight )
  457. {
  458. // See if we're below, and therefore need to clip
  459. if ( pParticle->m_Pos.z + UpdateScale( pParticle ) < m_flClipHeight )
  460. {
  461. pIterator->RemoveParticle( pParticle );
  462. pParticle = (SimpleParticle*)pIterator->GetNext();
  463. continue;
  464. }
  465. }
  466. //Should this particle die?
  467. pParticle->m_flLifetime += timeDelta;
  468. UpdateRoll( pParticle, timeDelta );
  469. if ( pParticle->m_flLifetime >= pParticle->m_flDieTime )
  470. pIterator->RemoveParticle( pParticle );
  471. pParticle = (SimpleParticle*)pIterator->GetNext();
  472. }
  473. }