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.

541 lines
15 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "particles_simple.h"
  9. #include "env_wind_shared.h"
  10. #include "keyvalues.h"
  11. #include "toolframework_client.h"
  12. #include "toolframework/itoolframework.h"
  13. #include "vstdlib/ikeyvaluessystem.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. // Used for debugging to make sure all particle effects get freed when we exit.
  17. CUtlLinkedList<CParticleEffect*,int> g_ParticleEffects;
  18. class CEffectChecker
  19. {
  20. public:
  21. ~CEffectChecker()
  22. {
  23. Assert( g_ParticleEffects.Count() == 0 );
  24. }
  25. } g_EffectChecker;
  26. //-----------------------------------------------------------------------------
  27. // Purpose: Constructor
  28. //-----------------------------------------------------------------------------
  29. CParticleEffect::CParticleEffect( const char *pName )
  30. {
  31. m_pDebugName = pName;
  32. m_vSortOrigin.Init();
  33. m_Flags = FLAG_ALLOCATED;
  34. m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
  35. m_RefCount = 0;
  36. m_bSimulate = true;
  37. ParticleMgr()->AddEffect( &m_ParticleEffect, this );
  38. #if defined( _DEBUG )
  39. g_ParticleEffects.AddToTail( this );
  40. #endif
  41. }
  42. //-----------------------------------------------------------------------------
  43. // Purpose: Destructor
  44. //-----------------------------------------------------------------------------
  45. CParticleEffect::~CParticleEffect( void )
  46. {
  47. #if defined( _DEBUG )
  48. int index = g_ParticleEffects.Find( this );
  49. Assert( g_ParticleEffects.IsValidIndex(index) );
  50. g_ParticleEffects.Remove( index );
  51. #endif
  52. // HACKHACK: Prevent re-entering the destructor, clear m_Flags.
  53. // For some reason we'll get a callback into NotifyRemove() after being deleted!
  54. // Investigate dangling pointer
  55. m_Flags = 0;
  56. if ( ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID ) && clienttools->IsInRecordingMode() )
  57. {
  58. KeyValues *msg = new KeyValues( "OldParticleSystem_Destroy" );
  59. msg->SetInt( "id", m_nToolParticleEffectId );
  60. msg->SetFloat( "time", gpGlobals->curtime );
  61. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  62. m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
  63. }
  64. }
  65. void CParticleEffect::SetDynamicallyAllocated( bool bDynamic )
  66. {
  67. if( bDynamic )
  68. m_Flags |= FLAG_ALLOCATED;
  69. else
  70. m_Flags &= ~FLAG_ALLOCATED;
  71. }
  72. int CParticleEffect::IsReleased()
  73. {
  74. return m_RefCount == 0;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. //-----------------------------------------------------------------------------
  79. void CParticleEffect::AddRef()
  80. {
  81. ++m_RefCount;
  82. }
  83. void CParticleEffect::Release()
  84. {
  85. Assert( m_RefCount > 0 );
  86. --m_RefCount;
  87. // If all the particles are already gone, delete ourselves now.
  88. // If there are still particles, wait for the last NotifyDestroyParticle.
  89. if ( m_RefCount == 0 )
  90. {
  91. if ( m_Flags & FLAG_ALLOCATED )
  92. {
  93. if ( m_ParticleEffect.GetNumActiveParticles() == 0 )
  94. {
  95. m_ParticleEffect.SetRemoveFlag();
  96. }
  97. }
  98. }
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose:
  102. // Input : &vSortOrigin -
  103. //-----------------------------------------------------------------------------
  104. const Vector &CParticleEffect::GetSortOrigin()
  105. {
  106. Assert(m_vSortOrigin.IsValid());
  107. return m_vSortOrigin;
  108. }
  109. const char *CParticleEffect::GetEffectName()
  110. {
  111. return m_pDebugName;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose:
  115. // Input : pParticle -
  116. //-----------------------------------------------------------------------------
  117. void CParticleEffect::NotifyDestroyParticle( Particle* pParticle )
  118. {
  119. // Go away if we're released and there are no more particles.
  120. if( m_ParticleEffect.GetNumActiveParticles() == 0 && IsReleased() && (m_Flags & FLAG_ALLOCATED) && !(m_Flags & FLAG_DONT_REMOVE) )
  121. {
  122. m_ParticleEffect.SetRemoveFlag();
  123. }
  124. }
  125. void CParticleEffect::Update( float flTimeDelta )
  126. {
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. void CParticleEffect::NotifyRemove()
  132. {
  133. if( m_Flags & FLAG_ALLOCATED )
  134. {
  135. Assert( IsReleased() );
  136. delete this;
  137. }
  138. }
  139. void CParticleEffect::SetSortOrigin( const Vector &vSortOrigin )
  140. {
  141. if ( GetBinding().GetAutoUpdateBBox() )
  142. {
  143. if ( m_ParticleEffect.EnlargeBBoxToContain( vSortOrigin ) )
  144. {
  145. m_vSortOrigin = vSortOrigin;
  146. }
  147. }
  148. else
  149. {
  150. // If not auto-updating bbox, don't change the bbox, just set the sort origin.
  151. m_vSortOrigin = vSortOrigin;
  152. }
  153. }
  154. void CParticleEffect::SetParticleCullRadius( float radius )
  155. {
  156. m_ParticleEffect.SetParticleCullRadius( radius );
  157. }
  158. //-----------------------------------------------------------------------------
  159. // Purpose:
  160. // Input : *name -
  161. // Output : PMaterialHandle
  162. //-----------------------------------------------------------------------------
  163. PMaterialHandle CParticleEffect::GetPMaterial(const char *name)
  164. {
  165. return m_ParticleEffect.FindOrAddMaterial(name);
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. // Input : particleSize -
  170. // material -
  171. // Output : SimpleParticle
  172. //-----------------------------------------------------------------------------
  173. Particle *CParticleEffect::AddParticle( unsigned int particleSize, PMaterialHandle material, const Vector &origin )
  174. {
  175. // If you get here, then you must call SetSortOrigin before adding particles.
  176. Assert( m_vSortOrigin.IsValid() );
  177. Particle *pParticle = (Particle *) m_ParticleEffect.AddParticle( particleSize, material );
  178. if( pParticle == NULL )
  179. return NULL;
  180. pParticle->m_Pos = origin;
  181. return pParticle;
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Purpose: Constructor
  185. //-----------------------------------------------------------------------------
  186. REGISTER_EFFECT_USING_CREATE( CSimpleEmitter );
  187. CSimpleEmitter::CSimpleEmitter( const char *pDebugName ) : CParticleEffect( pDebugName )
  188. {
  189. m_flNearClipMin = 16.0f;
  190. m_flNearClipMax = 64.0f;
  191. m_nSplitScreenPlayerSlot = -1;
  192. }
  193. CSimpleEmitter::~CSimpleEmitter()
  194. {
  195. }
  196. CSmartPtr<CSimpleEmitter> CSimpleEmitter::Create( const char *pDebugName )
  197. {
  198. CSimpleEmitter *pRet = new CSimpleEmitter( pDebugName );
  199. pRet->SetDynamicallyAllocated( true );
  200. return pRet;
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose: Set the internal near clip range for this particle system
  204. // Input : nearClipMin - beginning of clip range
  205. // nearClipMax - end of clip range
  206. //-----------------------------------------------------------------------------
  207. void CSimpleEmitter::SetNearClip( float nearClipMin, float nearClipMax )
  208. {
  209. m_flNearClipMin = nearClipMin;
  210. m_flNearClipMax = nearClipMax;
  211. }
  212. SimpleParticle* CSimpleEmitter::AddSimpleParticle(
  213. PMaterialHandle hMaterial,
  214. const Vector &vOrigin,
  215. float flDieTime,
  216. unsigned char uchSize )
  217. {
  218. SimpleParticle *pRet = (SimpleParticle*)AddParticle( sizeof( SimpleParticle ), hMaterial, vOrigin );
  219. if ( pRet )
  220. {
  221. pRet->m_Pos = vOrigin;
  222. pRet->m_vecVelocity.Init();
  223. pRet->m_flRoll = 0;
  224. pRet->m_flRollDelta = 0;
  225. pRet->m_flLifetime = 0;
  226. pRet->m_flDieTime = flDieTime;
  227. pRet->m_uchColor[0] = pRet->m_uchColor[1] = pRet->m_uchColor[2] = 0;
  228. pRet->m_uchStartAlpha = pRet->m_uchEndAlpha = 255;
  229. pRet->m_uchStartSize = pRet->m_uchEndSize = uchSize;
  230. pRet->m_iFlags = 0;
  231. }
  232. return pRet;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose:
  236. // Input : fTimeDelta -
  237. // Output : float
  238. //-----------------------------------------------------------------------------
  239. float CSimpleEmitter::UpdateAlpha( const SimpleParticle *pParticle )
  240. {
  241. return (pParticle->m_uchStartAlpha/255.0f) + ( (float)(pParticle->m_uchEndAlpha/255.0f) - (float)(pParticle->m_uchStartAlpha/255.0f) ) * (pParticle->m_flLifetime / pParticle->m_flDieTime);
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose:
  245. // Input : fTimeDelta -
  246. // Output : float
  247. //-----------------------------------------------------------------------------
  248. float CSimpleEmitter::UpdateScale( const SimpleParticle *pParticle )
  249. {
  250. return (float)pParticle->m_uchStartSize + ( (float)pParticle->m_uchEndSize - (float)pParticle->m_uchStartSize ) * (pParticle->m_flLifetime / pParticle->m_flDieTime);
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : fTimeDelta -
  255. // Output : Vector
  256. //-----------------------------------------------------------------------------
  257. #define WIND_ACCEL 50
  258. void CSimpleEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  259. {
  260. if (pParticle->m_iFlags & SIMPLE_PARTICLE_FLAG_WINDBLOWN)
  261. {
  262. Vector vecWind;
  263. GetWindspeedAtTime( gpGlobals->curtime, vecWind );
  264. for ( int i = 0 ; i < 2 ; i++ )
  265. {
  266. if ( pParticle->m_vecVelocity[i] < vecWind[i] )
  267. {
  268. pParticle->m_vecVelocity[i] += ( timeDelta * WIND_ACCEL );
  269. // clamp
  270. if ( pParticle->m_vecVelocity[i] > vecWind[i] )
  271. pParticle->m_vecVelocity[i] = vecWind[i];
  272. }
  273. else if (pParticle->m_vecVelocity[i] > vecWind[i] )
  274. {
  275. pParticle->m_vecVelocity[i] -= ( timeDelta * WIND_ACCEL );
  276. // clamp.
  277. if ( pParticle->m_vecVelocity[i] < vecWind[i] )
  278. pParticle->m_vecVelocity[i] = vecWind[i];
  279. }
  280. }
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose:
  285. // Input : fTimeDelta -
  286. // Output : float
  287. //-----------------------------------------------------------------------------
  288. float CSimpleEmitter::UpdateRoll( SimpleParticle *pParticle, float timeDelta )
  289. {
  290. pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
  291. return pParticle->m_flRoll;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. // Input : *pParticle -
  296. // timeDelta -
  297. //-----------------------------------------------------------------------------
  298. Vector CSimpleEmitter::UpdateColor( const SimpleParticle *pParticle )
  299. {
  300. static Vector cColor;
  301. cColor[0] = pParticle->m_uchColor[0] / 255.0f;
  302. cColor[1] = pParticle->m_uchColor[1] / 255.0f;
  303. cColor[2] = pParticle->m_uchColor[2] / 255.0f;
  304. return cColor;
  305. }
  306. void CSimpleEmitter::SimulateParticles( CParticleSimulateIterator *pIterator )
  307. {
  308. float timeDelta = pIterator->GetTimeDelta();
  309. SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst();
  310. while ( pParticle )
  311. {
  312. //Update velocity
  313. UpdateVelocity( pParticle, timeDelta );
  314. pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta;
  315. //Should this particle die?
  316. pParticle->m_flLifetime += timeDelta;
  317. UpdateRoll( pParticle, timeDelta );
  318. if ( pParticle->m_flLifetime >= pParticle->m_flDieTime )
  319. pIterator->RemoveParticle( pParticle );
  320. pParticle = (SimpleParticle*)pIterator->GetNext();
  321. }
  322. }
  323. void CSimpleEmitter::RenderParticles( CParticleRenderIterator *pIterator )
  324. {
  325. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  326. if ( m_nSplitScreenPlayerSlot != -1 && m_nSplitScreenPlayerSlot != GET_ACTIVE_SPLITSCREEN_SLOT() )
  327. return;
  328. const SimpleParticle *pParticle = (const SimpleParticle *)pIterator->GetFirst();
  329. while ( pParticle )
  330. {
  331. //Render
  332. Vector tPos;
  333. TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos );
  334. float sortKey = (int) tPos.z;
  335. //Render it
  336. RenderParticle_ColorSizeAngle(
  337. pIterator->GetParticleDraw(),
  338. tPos,
  339. UpdateColor( pParticle ),
  340. UpdateAlpha( pParticle ) * GetAlphaDistanceFade( tPos, m_flNearClipMin, m_flNearClipMax ),
  341. UpdateScale( pParticle ),
  342. pParticle->m_flRoll
  343. );
  344. pParticle = (const SimpleParticle *)pIterator->GetNext( sortKey );
  345. }
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Purpose:
  349. // Input : state -
  350. //-----------------------------------------------------------------------------
  351. void CSimpleEmitter::SetDrawBeforeViewModel( bool state )
  352. {
  353. m_ParticleEffect.SetDrawBeforeViewModel( state );
  354. }
  355. void CSimpleEmitter::SetShouldDrawForSplitScreenUser( int nSlot )
  356. {
  357. m_nSplitScreenPlayerSlot = nSlot;
  358. }
  359. //==================================================
  360. // Particle Library
  361. //==================================================
  362. CEmberEffect::CEmberEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName )
  363. {
  364. }
  365. CSmartPtr<CEmberEffect> CEmberEffect::Create( const char *pDebugName )
  366. {
  367. CEmberEffect *pRet = new CEmberEffect( pDebugName );
  368. pRet->SetDynamicallyAllocated( true );
  369. return pRet;
  370. }
  371. void CEmberEffect::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  372. {
  373. float speed = VectorNormalize( pParticle->m_vecVelocity );
  374. Vector offset;
  375. speed -= ( 12.0f * timeDelta );
  376. offset.Random( -0.125f, 0.125f );
  377. pParticle->m_vecVelocity += offset;
  378. VectorNormalize( pParticle->m_vecVelocity );
  379. pParticle->m_vecVelocity *= speed;
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose:
  383. // Input : *pParticle -
  384. // timeDelta -
  385. //-----------------------------------------------------------------------------
  386. Vector CEmberEffect::UpdateColor( const SimpleParticle *pParticle )
  387. {
  388. Vector color;
  389. float ramp = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime );
  390. color[0] = ( (float) pParticle->m_uchColor[0] * ramp ) / 255.0f;
  391. color[1] = ( (float) pParticle->m_uchColor[1] * ramp ) / 255.0f;
  392. color[2] = ( (float) pParticle->m_uchColor[2] * ramp ) / 255.0f;
  393. return color;
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Purpose:
  397. // Input : *pParticle -
  398. // timeDelta -
  399. // Output : float
  400. //-----------------------------------------------------------------------------
  401. CFireSmokeEffect::CFireSmokeEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName )
  402. {
  403. }
  404. CSmartPtr<CFireSmokeEffect> CFireSmokeEffect::Create( const char *pDebugName )
  405. {
  406. CFireSmokeEffect *pRet = new CFireSmokeEffect( pDebugName );
  407. pRet->SetDynamicallyAllocated( true );
  408. return pRet;
  409. }
  410. float CFireSmokeEffect::UpdateAlpha( const SimpleParticle *pParticle )
  411. {
  412. return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) );
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose:
  416. // Input : *pParticle -
  417. // timeDelta -
  418. //-----------------------------------------------------------------------------
  419. void CFireSmokeEffect::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  420. {
  421. }
  422. //-----------------------------------------------------------------------------
  423. // Purpose:
  424. // Input : *pParticle -
  425. // timeDelta -
  426. // Output : Vector
  427. //-----------------------------------------------------------------------------
  428. CFireParticle::CFireParticle( const char *pDebugName ) : CSimpleEmitter( pDebugName )
  429. {
  430. }
  431. CSmartPtr<CFireParticle> CFireParticle::Create( const char *pDebugName )
  432. {
  433. CFireParticle *pRet = new CFireParticle( pDebugName );
  434. pRet->SetDynamicallyAllocated( true );
  435. return pRet;
  436. }
  437. Vector CFireParticle::UpdateColor( const SimpleParticle *pParticle )
  438. {
  439. for ( int i = 0; i < 3; i++ )
  440. {
  441. //FIXME: This is frame dependant... but I don't want to store off start/end colors yet
  442. //pParticle->m_uchColor[i] = MAX( 0, pParticle->m_uchColor[i]-2 );
  443. }
  444. return CSimpleEmitter::UpdateColor( pParticle );
  445. }