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.

1182 lines
40 KiB

  1. //===== Copyright � 1996-2006, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: particle system code
  4. //
  5. //===========================================================================//
  6. #include "tier0/platform.h"
  7. #include "particles/particles.h"
  8. #include "filesystem.h"
  9. #include "tier2/tier2.h"
  10. #include "tier2/fileutils.h"
  11. #include "tier1/UtlStringMap.h"
  12. #include "tier1/strtools.h"
  13. // memdbgon must be the last include file in a .cpp file!!!
  14. #include "tier0/memdbgon.h"
  15. extern int g_nParticle_Multiplier;
  16. //-----------------------------------------------------------------------------
  17. // Emits particles immediately
  18. //-----------------------------------------------------------------------------
  19. struct InstantaneousEmitterContext_t
  20. {
  21. int m_nRemainingParticles;
  22. int m_StartingParticlesToEmit;
  23. bool m_bComputedActualParticlesToEmit;
  24. float m_flTimeOffset;
  25. float m_flRandomStartTime;
  26. bool m_bOn;
  27. };
  28. class C_OP_InstantaneousEmitter : public CParticleOperatorInstance
  29. {
  30. DECLARE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter );
  31. uint32 GetWrittenAttributes( void ) const
  32. {
  33. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  34. }
  35. uint32 GetReadAttributes( void ) const
  36. {
  37. return 0;
  38. }
  39. virtual uint64 GetReadControlPointMask() const
  40. {
  41. uint64 nMask = 0;
  42. if ( m_nScaleControlPoint >= 0 )
  43. nMask |= ( 1ULL << m_nScaleControlPoint );
  44. if ( m_nSnapshotControlPoint >= 0 )
  45. nMask |= ( 1ULL << m_nSnapshotControlPoint );
  46. return nMask;
  47. }
  48. virtual uint64 GetNonPositionalControlPointMask() const
  49. {
  50. if ( m_nScaleControlPoint >= 0 )
  51. return ( 1ULL << m_nScaleControlPoint );
  52. return 0;
  53. }
  54. virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
  55. void *pContext ) const;
  56. // unpack structure will be applied by creator. add extra initialization needed here
  57. virtual void InitParams( CParticleSystemDefinition *pDef )
  58. {
  59. if ( ( m_nMinParticlesToEmit >= 0 ) && ( m_nMinParticlesToEmit > m_nParticlesToEmit ) )
  60. {
  61. V_swap( m_nParticlesToEmit, m_nMinParticlesToEmit );
  62. }
  63. if ( m_nMaxEmittedPerFrame < 0 )
  64. {
  65. m_nMaxEmittedPerFrame = MIN( pDef->m_nMaxParticles, MAX_PARTICLES_IN_A_SYSTEM );
  66. }
  67. m_nScaleControlPointField = clamp( m_nScaleControlPointField, 0, 2 );
  68. m_nScaleControlPoint = clamp( m_nScaleControlPoint, -1, MAX_PARTICLE_CONTROL_POINTS );
  69. m_nSnapshotControlPoint = clamp( m_nSnapshotControlPoint, -1, MAX_PARTICLE_CONTROL_POINTS );
  70. }
  71. virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  72. {
  73. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  74. if ( !bInfiniteOnly )
  75. {
  76. pCtx->m_bOn = false;
  77. }
  78. }
  79. virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  80. {
  81. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  82. if ( !bInfiniteOnly )
  83. {
  84. // Recompute the number of particles to emit (control points may have changed) - this will reset the remaining particle count:
  85. UpdateActualParticlesToEmit( pParticles, pCtx, true );
  86. SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
  87. pCtx->m_bOn = true;
  88. }
  89. }
  90. // Called when the SFM wants to skip forward in time
  91. virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
  92. {
  93. // NOTE: This is a bit of a hack. We're saying that if we're skipping more than two seconds, that we're
  94. // probably not going to bother emitting at all. Really, this would have to know the maximum
  95. // lifetime of the child particles and only skip if past that.
  96. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  97. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  98. if ( flTime > ( flStartTime + 2.0f ) )
  99. {
  100. pCtx->m_bOn = false;
  101. }
  102. }
  103. virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
  104. {
  105. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  106. // Compute the desired number of particles to emit
  107. if ( m_nMinParticlesToEmit >= 0 )
  108. {
  109. pCtx->m_StartingParticlesToEmit = pParticles->RandomInt( m_nMinParticlesToEmit, m_nParticlesToEmit );
  110. }
  111. else
  112. {
  113. pCtx->m_StartingParticlesToEmit = m_nParticlesToEmit;
  114. }
  115. // Start with that many particles 'remaining to emit'
  116. pCtx->m_nRemainingParticles = pCtx->m_StartingParticlesToEmit;
  117. // Later, when we can access control points, we may modify the number of particles to emit
  118. pCtx->m_bComputedActualParticlesToEmit = false;
  119. pCtx->m_flTimeOffset = 0.0f;
  120. pCtx->m_bOn = true;
  121. if ( m_flStartTimeMax > 0 )
  122. pCtx->m_flRandomStartTime = pParticles->RandomFloat( m_flStartTime, m_flStartTimeMax );
  123. else
  124. pCtx->m_flRandomStartTime = m_flStartTime;
  125. }
  126. virtual void Restart( CParticleCollection *pParticles, void *pContext )
  127. {
  128. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  129. // Recompute the number of particles to emit (control points may have changed) - this will reset the remaining particle count:
  130. UpdateActualParticlesToEmit( pParticles, pCtx, true );
  131. pCtx->m_flTimeOffset = pParticles->m_flCurTime;
  132. pCtx->m_bOn = true;
  133. if ( m_flStartTimeMax > 0 )
  134. pCtx->m_flRandomStartTime = pParticles->RandomFloat( m_flStartTime, m_flStartTimeMax );
  135. else
  136. pCtx->m_flRandomStartTime = m_flStartTime;
  137. }
  138. size_t GetRequiredContextBytes( void ) const
  139. {
  140. return sizeof( InstantaneousEmitterContext_t );
  141. }
  142. virtual bool MayCreateMoreParticles( CParticleCollection const *pParticles, void *pContext ) const
  143. {
  144. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  145. return ( pCtx->m_bOn && (pCtx->m_nRemainingParticles > 0) );
  146. }
  147. void UpdateActualParticlesToEmit( CParticleCollection *pParticles, InstantaneousEmitterContext_t *pCtx, bool bForceUpdate = false ) const
  148. {
  149. if ( !bForceUpdate && pCtx->m_bComputedActualParticlesToEmit )
  150. return; // Already initted
  151. int nActualParticlesToEmit = pCtx->m_StartingParticlesToEmit;
  152. if ( m_nSnapshotControlPoint >= 0 )
  153. {
  154. // Optionally override the number of particles to emit with the size a CP-attached Snapshot (can't do this at Init time)
  155. // NOTE: this causes m_nScaleControlPoint to be ignored
  156. CParticleSnapshot *pSnapshot = pParticles->GetControlPointSnapshot( m_nSnapshotControlPoint );
  157. if ( pSnapshot )
  158. {
  159. nActualParticlesToEmit = pSnapshot->NumCols(); // TODO: may want to add a NumParticles() accessor, for snapshots w/ multi-dimensional data
  160. }
  161. }
  162. else if ( m_nScaleControlPoint >= 0 )
  163. {
  164. Vector vecScale;
  165. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  166. if ( ( flStartTime <= pParticles->m_flCurTime ) && ( flStartTime >= pParticles->m_flCurTime - pParticles->m_flPreviousDt ) )
  167. {
  168. pParticles->GetControlPointAtTime( m_nScaleControlPoint, flStartTime, &vecScale );
  169. }
  170. else
  171. {
  172. pParticles->GetControlPointAtPrevTime( m_nScaleControlPoint, &vecScale );
  173. }
  174. nActualParticlesToEmit = pCtx->m_StartingParticlesToEmit * vecScale[m_nScaleControlPointField];
  175. }
  176. pCtx->m_nRemainingParticles = MAX( 0, nActualParticlesToEmit );
  177. pCtx->m_bComputedActualParticlesToEmit = true;
  178. }
  179. int m_nParticlesToEmit;
  180. int m_nMinParticlesToEmit;
  181. float m_flStartTime;
  182. float m_flStartTimeMax;
  183. int m_nMaxEmittedPerFrame;
  184. int m_nScaleControlPoint;
  185. int m_nScaleControlPointField;
  186. int m_nSnapshotControlPoint;
  187. };
  188. DEFINE_PARTICLE_OPERATOR( C_OP_InstantaneousEmitter, "emit_instantaneously", OPERATOR_GENERIC );
  189. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter )
  190. DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
  191. DMXELEMENT_UNPACK_FIELD( "emission_start_time max", "-1", float, m_flStartTimeMax )
  192. DMXELEMENT_UNPACK_FIELD( "num_to_emit_minimum", "-1", int, m_nMinParticlesToEmit )
  193. DMXELEMENT_UNPACK_FIELD( "num_to_emit", "100", int, m_nParticlesToEmit )
  194. DMXELEMENT_UNPACK_FIELD( "maximum emission per frame", "-1", int, m_nMaxEmittedPerFrame )
  195. DMXELEMENT_UNPACK_FIELD( "emission count scale control point", "-1", int, m_nScaleControlPoint )
  196. DMXELEMENT_UNPACK_FIELD( "emission count scale control point field", "0", int, m_nScaleControlPointField )
  197. DMXELEMENT_UNPACK_FIELD( "control point with snapshot data", "-1", int, m_nSnapshotControlPoint )
  198. END_PARTICLE_OPERATOR_UNPACK( C_OP_InstantaneousEmitter )
  199. uint32 C_OP_InstantaneousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
  200. void *pContext ) const
  201. {
  202. InstantaneousEmitterContext_t *pCtx = reinterpret_cast<InstantaneousEmitterContext_t *>( pContext );
  203. if ( !pCtx->m_bOn )
  204. return 0;
  205. // Wait until we're told to start emitting
  206. float flStartTime = pCtx->m_flRandomStartTime + pCtx->m_flTimeOffset;
  207. if ( pParticles->m_flCurTime < flStartTime )
  208. return 0;
  209. // Update how many particles we're supposed to be emitting
  210. UpdateActualParticlesToEmit( pParticles, pCtx );
  211. // Don't emit any more if the particle system has emitted all it's supposed to.
  212. if ( pCtx->m_nRemainingParticles <= 0 )
  213. return 0;
  214. // Tick down remaining particles, capped to the maximum emission per frame
  215. int nParticlesThisFrame = MIN( m_nMaxEmittedPerFrame, pCtx->m_nRemainingParticles );
  216. pCtx->m_nRemainingParticles -= nParticlesThisFrame;
  217. // We're only allowed to own so many particles, though... if we run out of room, only emit the last N particles
  218. int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
  219. int nActualParticlesToEmit = MIN( nAllowedParticlesToEmit, nParticlesThisFrame );
  220. if ( nActualParticlesToEmit == 0 )
  221. return 0;
  222. // !! speed!! do sse init here
  223. int nStartParticle = pParticles->m_nActiveParticles;
  224. pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
  225. for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
  226. {
  227. float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
  228. *pTimeStamp = flStartTime;
  229. }
  230. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Emits particles over time
  234. //-----------------------------------------------------------------------------
  235. struct ContinuousEmitterContext_t
  236. {
  237. float m_flTotalActualParticlesSoFar;
  238. int m_nTotalEmittedSoFar;
  239. float m_flNextEmitTime;
  240. float m_flTimeOffset;
  241. bool m_bOn;
  242. };
  243. bool g_bDontMakeSkipToTimeTakeForever = false;
  244. class C_OP_ContinuousEmitter : public CParticleOperatorInstance
  245. {
  246. DECLARE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter );
  247. uint32 GetWrittenAttributes( void ) const
  248. {
  249. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  250. }
  251. uint32 GetReadAttributes( void ) const
  252. {
  253. return 0;
  254. }
  255. virtual uint64 GetReadControlPointMask() const
  256. {
  257. if ( m_nScaleControlPoint >= 0 )
  258. return ( 1ULL << m_nScaleControlPoint );
  259. return 0;
  260. }
  261. virtual uint64 GetNonPositionalControlPointMask() const
  262. {
  263. if ( m_nScaleControlPoint >= 0 )
  264. return ( 1ULL << m_nScaleControlPoint );
  265. return 0;
  266. }
  267. virtual void InitParams( CParticleSystemDefinition *pDef )
  268. {
  269. if ( m_flEmitRate < 0.0f )
  270. {
  271. m_flEmitRate = 0.0f;
  272. }
  273. if ( m_flEmissionDuration < 0.0f )
  274. {
  275. m_flEmissionDuration = 0.0f;
  276. }
  277. m_flEmitRate *= g_nParticle_Multiplier;
  278. }
  279. virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
  280. void *pContext ) const ;
  281. inline bool IsInfinitelyEmitting() const
  282. {
  283. return ( m_flEmissionDuration == 0.0f );
  284. }
  285. virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  286. {
  287. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  288. if ( !bInfiniteOnly || IsInfinitelyEmitting() )
  289. {
  290. pCtx->m_bOn = false;
  291. }
  292. }
  293. virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  294. {
  295. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  296. if ( !bInfiniteOnly || IsInfinitelyEmitting() )
  297. {
  298. pCtx->m_bOn = true;
  299. SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
  300. }
  301. }
  302. virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
  303. {
  304. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  305. pCtx->m_flNextEmitTime = m_flStartTime;
  306. pCtx->m_flTotalActualParticlesSoFar = 0.0f;
  307. pCtx->m_nTotalEmittedSoFar = 0;
  308. pCtx->m_flTimeOffset = 0.0f;
  309. pCtx->m_bOn = true;
  310. }
  311. virtual void Restart( CParticleCollection *pParticles, void *pContext )
  312. {
  313. if ( !IsInfinitelyEmitting() )
  314. {
  315. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  316. pCtx->m_flNextEmitTime = pParticles->m_flCurTime + m_flStartTime;
  317. pCtx->m_flTotalActualParticlesSoFar = 0.0f;
  318. pCtx->m_nTotalEmittedSoFar = 0;
  319. pCtx->m_flTimeOffset = pParticles->m_flCurTime;
  320. }
  321. }
  322. // Called when the SFM wants to skip forward in time
  323. // Currently hacked for save/load pre-sim - correct solution is to serialize rather
  324. // than skip-to-time and simulate
  325. virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
  326. {
  327. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  328. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  329. if ( flTime <= flStartTime )
  330. return;
  331. if ( m_bInitFromKilledParentParticles ) // Only emit when parent particles die
  332. return;
  333. float flControlPointScale = pParticles->GetHighestControlPoint();
  334. flControlPointScale *= m_flEmissionScale;
  335. float flEmissionRate = m_flEmitRate;
  336. float flEmitStrength;
  337. if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) )
  338. {
  339. flEmissionRate *= flEmitStrength;
  340. }
  341. if ( ( m_nScaleControlPoint >= 0 ) )
  342. {
  343. Vector vecScale;
  344. pParticles->GetControlPointAtTime( m_nScaleControlPoint, pParticles->m_flCurTime, &vecScale );
  345. float flScale = vecScale[m_nScaleControlPointField];
  346. Assert( flScale >= 0.0f );
  347. flEmissionRate *= MAX( 0.0f, flScale );
  348. }
  349. if ( flControlPointScale != 0.0f )
  350. {
  351. flEmissionRate *= flControlPointScale;
  352. }
  353. float flPrevDrawTime = pParticles->m_flCurTime - flTime;
  354. float flCurrDrawTime = pParticles->m_flCurTime;
  355. if ( !IsInfinitelyEmitting() )
  356. {
  357. if ( flPrevDrawTime < flStartTime )
  358. {
  359. flPrevDrawTime = flStartTime;
  360. }
  361. //if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
  362. //{
  363. // flCurrDrawTime = flStartTime + m_flEmissionDuration;
  364. //}
  365. }
  366. float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
  367. flDeltaTime = fpmin (flDeltaTime, 4.0f);
  368. flPrevDrawTime = flCurrDrawTime - flDeltaTime;
  369. //disabled for now
  370. pCtx->m_flTotalActualParticlesSoFar = flDeltaTime * flEmissionRate;
  371. //if ( !IsInfinitelyEmitting() )
  372. // pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar );
  373. pCtx->m_nTotalEmittedSoFar = 0;
  374. //simulate a bunch
  375. int nActualParticlesToEmit = floor (pCtx->m_flTotalActualParticlesSoFar);
  376. int nStartParticle = pParticles->m_nActiveParticles;
  377. if ( pParticles->m_nMaxAllowedParticles < nStartParticle + nActualParticlesToEmit )
  378. {
  379. nActualParticlesToEmit = pParticles->m_nMaxAllowedParticles - nStartParticle;
  380. }
  381. pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
  382. float flTimeStampStep = ( flDeltaTime ) / ( nActualParticlesToEmit );
  383. float flTimeStep = flPrevDrawTime + flTimeStampStep;
  384. // Set the particle creation time to the exact sub-frame particle emission time
  385. // !! speed!! do sse init here
  386. for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
  387. {
  388. float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
  389. flTimeStep = MIN( flTimeStep, flCurrDrawTime );
  390. *pTimeStamp = flTimeStep;
  391. flTimeStep += flTimeStampStep;
  392. }
  393. if ( !g_bDontMakeSkipToTimeTakeForever )
  394. {
  395. flPrevDrawTime = MAX( flPrevDrawTime, flCurrDrawTime - pParticles->m_pDef->m_flNoDrawTimeToGoToSleep );
  396. pParticles->m_flCurTime = flPrevDrawTime;
  397. pParticles->m_fl4CurTime = ReplicateX4( flPrevDrawTime );
  398. pParticles->m_flTargetDrawTime = flPrevDrawTime;
  399. for( float i = flPrevDrawTime; i < flCurrDrawTime; i += 0.1 )
  400. {
  401. pParticles->Simulate( .1 );
  402. }
  403. }
  404. }
  405. size_t GetRequiredContextBytes( void ) const
  406. {
  407. return sizeof( ContinuousEmitterContext_t );
  408. }
  409. virtual bool MayCreateMoreParticles( CParticleCollection const *pParticles, void *pContext ) const
  410. {
  411. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  412. if ( !pCtx->m_bOn )
  413. return false;
  414. if ( m_bInitFromKilledParentParticles ) // We only emit when parent particles die, so defer to what the parent returns from MayCreateMoreParticles
  415. return false;
  416. if ( m_flEmitRate <= 0.0f )
  417. return false;
  418. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  419. if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) )
  420. return false;
  421. return true;
  422. }
  423. virtual bool ShouldRun( bool bApplyingParentKillList ) const
  424. {
  425. if ( m_bInitFromKilledParentParticles )
  426. return bApplyingParentKillList;
  427. else
  428. return !bApplyingParentKillList;
  429. }
  430. float m_flEmissionDuration;
  431. float m_flStartTime;
  432. float m_flEmitRate;
  433. float m_flTimePerEmission;
  434. float m_flEmissionScale;
  435. int m_nScaleControlPoint;
  436. int m_nScaleControlPointField;
  437. bool m_bScalePerParticle;
  438. bool m_bInitFromKilledParentParticles;
  439. };
  440. DEFINE_PARTICLE_OPERATOR( C_OP_ContinuousEmitter, "emit_continuously", OPERATOR_GENERIC );
  441. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter )
  442. DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
  443. DMXELEMENT_UNPACK_FIELD( "emission_rate", "100", float, m_flEmitRate )
  444. DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration )
  445. DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale )
  446. DMXELEMENT_UNPACK_FIELD( "use parent particles for emission scaling", "0", bool, m_bScalePerParticle )
  447. DMXELEMENT_UNPACK_FIELD( "emission count scale control point", "-1", int, m_nScaleControlPoint )
  448. DMXELEMENT_UNPACK_FIELD( "emission count scale control point field", "0", int, m_nScaleControlPointField )
  449. DMXELEMENT_UNPACK_FIELD( "emit particles for killed parent particles", "0", bool, m_bInitFromKilledParentParticles )
  450. END_PARTICLE_OPERATOR_UNPACK( C_OP_ContinuousEmitter )
  451. uint32 C_OP_ContinuousEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
  452. void *pContext ) const
  453. {
  454. ContinuousEmitterContext_t *pCtx = reinterpret_cast<ContinuousEmitterContext_t *>( pContext );
  455. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  456. if ( pParticles->m_flCurTime < flStartTime )
  457. return 0;
  458. float flActualParticlesToEmit, flDeltaTime, flPrevDrawTime, flCurrDrawTime;
  459. if ( m_bInitFromKilledParentParticles )
  460. {
  461. // Emit one particle for each parent particle that was killed
  462. // TODO; support scaling the number of particles emitted (requires communicating this to initializers
  463. // though, somehow, since they'll want to read data from the killed particles)
  464. int nNumParticlesToKill;
  465. GetParentKillList( pParticles, nNumParticlesToKill );
  466. flActualParticlesToEmit = nNumParticlesToKill;
  467. Assert( flActualParticlesToEmit > 0 );
  468. // Just emit all particles at once since this time isn't advancing here (TODO: is there a better way?)
  469. flCurrDrawTime = pParticles->m_flCurTime;
  470. if ( !IsInfinitelyEmitting() )
  471. flCurrDrawTime = clamp( flCurrDrawTime, flStartTime, ( flStartTime + m_flEmissionDuration ) );
  472. flPrevDrawTime = flCurrDrawTime;
  473. flDeltaTime = 0;
  474. }
  475. else
  476. {
  477. //Allows for dynamic scaling via changes in number of control points.
  478. float flControlPointScale = pParticles->GetHighestControlPoint();
  479. //The emission scale here allows for a scalar value per controlpoint, like 2 or .25...
  480. flControlPointScale *= m_flEmissionScale;
  481. //Global strength scale brought in by operator fade in/fade out/oscillate
  482. float flEmissionRate = m_flEmitRate * flCurStrength;
  483. if ( ( m_nScaleControlPoint >= 0 ) )
  484. {
  485. Vector vecScale;
  486. pParticles->GetControlPointAtTime( m_nScaleControlPoint, pParticles->m_flCurTime, &vecScale );
  487. Assert( vecScale[m_nScaleControlPointField] >= 0.0f );
  488. flEmissionRate *= MAX( 0.0f, vecScale[m_nScaleControlPointField] );
  489. }
  490. if ( flControlPointScale != 0.0f || m_bScalePerParticle )
  491. {
  492. if ( m_bScalePerParticle )
  493. {
  494. if ( pParticles->m_pParent )
  495. {
  496. flControlPointScale = pParticles->m_pParent->m_nActiveParticles * m_flEmissionScale;
  497. }
  498. else
  499. {
  500. flControlPointScale = m_flEmissionScale;
  501. }
  502. }
  503. flEmissionRate *= flControlPointScale;
  504. }
  505. if ( flEmissionRate == 0.0f )
  506. return 0;
  507. // Have we emitted all the particles we're going to emit?
  508. // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
  509. if ( !C_OP_ContinuousEmitter::MayCreateMoreParticles( pParticles, pContext ) )
  510. return 0;
  511. // determine our previous and current draw times and clamp them to start time and emission duration
  512. flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
  513. flCurrDrawTime = pParticles->m_flCurTime;
  514. if ( !IsInfinitelyEmitting() )
  515. {
  516. flPrevDrawTime = MAX( flPrevDrawTime, flStartTime );
  517. flCurrDrawTime = MIN( flCurrDrawTime, ( flStartTime + m_flEmissionDuration ) );
  518. }
  519. flDeltaTime = flCurrDrawTime - flPrevDrawTime;
  520. //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
  521. flActualParticlesToEmit = flEmissionRate * flDeltaTime;
  522. }
  523. //Add emitted particle to float counter to allow for fractional emission
  524. pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;
  525. //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
  526. int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;
  527. //Add emitted particles to running int total.
  528. pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;
  529. if ( nParticlesToEmit == 0 )
  530. return 0;
  531. // We're only allowed to emit so many particles, though..
  532. // If we run out of room, only emit the last N particles
  533. int nActualParticlesToEmit = nParticlesToEmit;
  534. int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
  535. if ( nAllowedParticlesToEmit < nParticlesToEmit )
  536. {
  537. nActualParticlesToEmit = nAllowedParticlesToEmit;
  538. //flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit;
  539. }
  540. if ( nActualParticlesToEmit == 0 )
  541. return 0;
  542. int nStartParticle = pParticles->m_nActiveParticles;
  543. pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
  544. float flEmitTimeStep = flDeltaTime / nActualParticlesToEmit;
  545. float flEmitTime = flPrevDrawTime + flEmitTimeStep;
  546. // Set the particle creation time to the exact sub-frame particle emission time
  547. // !! speed!! do sse init here
  548. for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
  549. {
  550. float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
  551. flEmitTime = MIN( flEmitTime, flCurrDrawTime );
  552. *pTimeStamp = flEmitTime;
  553. flEmitTime += flEmitTimeStep;
  554. }
  555. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Noise Emitter
  559. //-----------------------------------------------------------------------------
  560. struct NoiseEmitterContext_t
  561. {
  562. float m_flTotalActualParticlesSoFar;
  563. int m_nTotalEmittedSoFar;
  564. float m_flNextEmitTime;
  565. float m_flTimeOffset;
  566. bool m_bOn;
  567. };
  568. class C_OP_NoiseEmitter : public CParticleOperatorInstance
  569. {
  570. DECLARE_PARTICLE_OPERATOR( C_OP_NoiseEmitter );
  571. uint32 GetWrittenAttributes( void ) const
  572. {
  573. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  574. }
  575. uint32 GetReadAttributes( void ) const
  576. {
  577. return 0;
  578. }
  579. virtual void InitParams( CParticleSystemDefinition *pDef )
  580. {
  581. if ( m_flEmissionDuration < 0.0f )
  582. {
  583. m_flEmissionDuration = 0.0f;
  584. }
  585. }
  586. virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
  587. void *pContext ) const ;
  588. inline bool IsInfinitelyEmitting() const
  589. {
  590. return ( m_flEmissionDuration == 0.0f );
  591. }
  592. virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  593. {
  594. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  595. if ( !bInfiniteOnly || IsInfinitelyEmitting() )
  596. {
  597. pCtx->m_bOn = false;
  598. }
  599. }
  600. virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  601. {
  602. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  603. if ( !bInfiniteOnly || IsInfinitelyEmitting() )
  604. {
  605. pCtx->m_bOn = true;
  606. SkipToTime( pParticles->m_flCurTime, pParticles, pCtx );
  607. }
  608. }
  609. virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
  610. {
  611. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  612. pCtx->m_flNextEmitTime = m_flStartTime;
  613. pCtx->m_flTotalActualParticlesSoFar = 1.0f;
  614. pCtx->m_nTotalEmittedSoFar = 0;
  615. pCtx->m_flTimeOffset = 0.0f;
  616. pCtx->m_bOn = true;
  617. }
  618. virtual void Restart( CParticleCollection *pParticles, void *pContext )
  619. {
  620. if ( !IsInfinitelyEmitting() )
  621. {
  622. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  623. pCtx->m_flNextEmitTime = m_flStartTime + pParticles->m_flCurTime;
  624. pCtx->m_flTotalActualParticlesSoFar = 1.0f;
  625. pCtx->m_nTotalEmittedSoFar = 0;
  626. pCtx->m_flTimeOffset = pParticles->m_flCurTime;
  627. }
  628. }
  629. // Called when the SFM wants to skip forward in time
  630. virtual void SkipToTime( float flTime, CParticleCollection *pParticles, void *pContext ) const
  631. {
  632. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  633. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  634. if ( flTime <= flStartTime )
  635. return;
  636. float flControlPointScale = pParticles->GetHighestControlPoint();
  637. flControlPointScale *= m_flEmissionScale;
  638. float flEmissionRate = 1.0f;
  639. float flEmitStrength;
  640. if ( pParticles->CheckIfOperatorShouldRun( this, &flEmitStrength ) )
  641. {
  642. flEmissionRate *= flEmitStrength;
  643. }
  644. if ( flControlPointScale != 0.0f )
  645. {
  646. flEmissionRate *= flControlPointScale;
  647. }
  648. pCtx->m_flTotalActualParticlesSoFar = 1.0f;
  649. //if ( !IsInfinitelyEmitting() )
  650. // pCtx->m_flTotalActualParticlesSoFar = min( pCtx->m_ActualParticlesToEmit, pCtx->m_flTotalActualParticlesSoFar );
  651. pCtx->m_nTotalEmittedSoFar = 0;
  652. }
  653. size_t GetRequiredContextBytes( void ) const
  654. {
  655. return sizeof( NoiseEmitterContext_t );
  656. }
  657. virtual bool MayCreateMoreParticles( CParticleCollection const *pParticles, void *pContext ) const
  658. {
  659. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  660. if ( !pCtx->m_bOn )
  661. return false;
  662. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  663. if ( m_flEmissionDuration != 0.0f && ( pParticles->m_flCurTime - pParticles->m_flDt ) > ( flStartTime + m_flEmissionDuration ) )
  664. return false;
  665. return true;
  666. }
  667. float m_flEmissionDuration;
  668. float m_flStartTime;
  669. float m_flTimePerEmission;
  670. float m_flEmissionScale;
  671. bool m_bAbsVal, m_bAbsValInv;
  672. float m_flOffset;
  673. float m_flOutputMin;
  674. float m_flOutputMax;
  675. float m_flNoiseScale, m_flNoiseScaleLoc;
  676. Vector m_vecOffsetLoc;
  677. float m_flWorldTimeScale;
  678. };
  679. DEFINE_PARTICLE_OPERATOR( C_OP_NoiseEmitter, "emit noise", OPERATOR_GENERIC );
  680. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter )
  681. DMXELEMENT_UNPACK_FIELD( "emission_start_time", "0", float, m_flStartTime )
  682. DMXELEMENT_UNPACK_FIELD( "emission_duration", "0", float, m_flEmissionDuration )
  683. DMXELEMENT_UNPACK_FIELD( "scale emission to used control points", "0.0", float, m_flEmissionScale )
  684. DMXELEMENT_UNPACK_FIELD( "time noise coordinate scale","0.1",float,m_flNoiseScale)
  685. //DMXELEMENT_UNPACK_FIELD( "spatial noise coordinate scale","0.001",float,m_flNoiseScaleLoc)
  686. DMXELEMENT_UNPACK_FIELD( "time coordinate offset","0", float, m_flOffset )
  687. //DMXELEMENT_UNPACK_FIELD( "spatial coordinate offset","0 0 0", Vector, m_vecOffsetLoc )
  688. DMXELEMENT_UNPACK_FIELD( "absolute value","0", bool, m_bAbsVal )
  689. DMXELEMENT_UNPACK_FIELD( "invert absolute value","0", bool, m_bAbsValInv )
  690. DMXELEMENT_UNPACK_FIELD( "emission minimum","0", float, m_flOutputMin )
  691. DMXELEMENT_UNPACK_FIELD( "emission maximum","100", float, m_flOutputMax )
  692. DMXELEMENT_UNPACK_FIELD( "world time noise coordinate scale","0", float, m_flWorldTimeScale )
  693. END_PARTICLE_OPERATOR_UNPACK( C_OP_NoiseEmitter )
  694. uint32 C_OP_NoiseEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
  695. void *pContext ) const
  696. {
  697. // Have we emitted all the particles we're going to emit?
  698. // NOTE: Using C_OP_ContinuousEmitter:: avoids a virtual function call
  699. NoiseEmitterContext_t *pCtx = reinterpret_cast<NoiseEmitterContext_t *>( pContext );
  700. //Allows for dynamic scaling via changes in number of control points.
  701. float flControlPointScale = pParticles->GetHighestControlPoint();
  702. //The emission scale here allows for a scalar value per controlpoint, like 2 or .25...
  703. flControlPointScale *= m_flEmissionScale;
  704. float flAbsScale;
  705. int nAbsVal;
  706. nAbsVal = 0xffffffff;
  707. flAbsScale = 0.5;
  708. if ( m_bAbsVal )
  709. {
  710. nAbsVal = 0x7fffffff;
  711. flAbsScale = 1.0;
  712. }
  713. float fMin = m_flOutputMin;
  714. float fMax = m_flOutputMax;
  715. float CoordScale = m_flNoiseScale;
  716. //float CoordScaleLoc = m_flNoiseScaleLoc;
  717. float ValueScale, ValueBase;
  718. Vector Coord, CoordLoc, CoordWorldTime;
  719. //CoordLoc.x = pxyz[0];
  720. //CoordLoc.y = pxyz[4];
  721. //CoordLoc.z = pxyz[8];
  722. //CoordLoc += m_vecOffsetLoc;
  723. float Offset = m_flOffset;
  724. Coord = Vector ( (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset), (pParticles->m_flCurTime + Offset) );
  725. Coord *= CoordScale;
  726. //CoordLoc *= CoordScaleLoc;
  727. //Coord += CoordLoc;
  728. CoordWorldTime = Vector( (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale), (Plat_MSTime() * m_flWorldTimeScale) );
  729. Coord += CoordWorldTime;
  730. fltx4 flNoise128;
  731. FourVectors fvNoise;
  732. fvNoise.DuplicateVector( Coord );
  733. flNoise128 = NoiseSIMD( fvNoise );
  734. float flNoise = SubFloat( flNoise128, 0 );
  735. *( (int *) &flNoise) &= nAbsVal;
  736. ValueScale = ( flAbsScale *( fMax - fMin ) );
  737. ValueBase = ( fMin+ ( ( 1.0 - flAbsScale ) *( fMax - fMin ) ) );
  738. if ( m_bAbsValInv )
  739. {
  740. flNoise = 1.0 - flNoise;
  741. }
  742. float flInitialNoise = ( ValueBase + ( ValueScale * flNoise ) );
  743. flInitialNoise = clamp(flInitialNoise, 0.0f, INT_MAX );
  744. //Global strength scale brought in by operator fade in/fade out/oscillate
  745. float flEmissionRate = flInitialNoise * flCurStrength;
  746. if ( flControlPointScale != 0.0f )
  747. {
  748. flEmissionRate *= flControlPointScale;
  749. }
  750. if ( flEmissionRate == 0.0f )
  751. return 0;
  752. if ( !C_OP_NoiseEmitter::MayCreateMoreParticles( pParticles, pContext ) )
  753. return 0;
  754. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  755. if ( pParticles->m_flCurTime < flStartTime )
  756. return 0;
  757. Assert( flEmissionRate != 0.0f );
  758. // determine our previous and current draw times and clamp them to start time and emission duration
  759. float flPrevDrawTime = pParticles->m_flCurTime - pParticles->m_flDt;
  760. float flCurrDrawTime = pParticles->m_flCurTime;
  761. if ( !IsInfinitelyEmitting() )
  762. {
  763. if ( flPrevDrawTime < flStartTime )
  764. {
  765. flPrevDrawTime = flStartTime;
  766. }
  767. if ( flCurrDrawTime > flStartTime + m_flEmissionDuration )
  768. {
  769. flCurrDrawTime = flStartTime + m_flEmissionDuration;
  770. }
  771. }
  772. float flDeltaTime = flCurrDrawTime - flPrevDrawTime;
  773. //Calculate emission rate by delta time from last frame to determine number of particles to emit this frame as a fractional float
  774. float flActualParticlesToEmit = flEmissionRate * flDeltaTime;
  775. //Add emitted particle to float counter to allow for fractional emission
  776. pCtx->m_flTotalActualParticlesSoFar += flActualParticlesToEmit;
  777. //Floor float accumulated value and subtract whole int emitted so far from the result to determine total whole particles to emit this frame
  778. int nParticlesToEmit = floor ( pCtx->m_flTotalActualParticlesSoFar ) - pCtx->m_nTotalEmittedSoFar;
  779. //Add emitted particles to running int total.
  780. pCtx->m_nTotalEmittedSoFar += nParticlesToEmit;
  781. if ( nParticlesToEmit == 0 )
  782. return 0;
  783. // We're only allowed to emit so many particles, though..
  784. // If we run out of room, only emit the last N particles
  785. int nActualParticlesToEmit = nParticlesToEmit;
  786. int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
  787. if ( nAllowedParticlesToEmit < nParticlesToEmit )
  788. {
  789. nActualParticlesToEmit = nAllowedParticlesToEmit;
  790. //flStartEmissionTime = pCtx->m_flNextEmitTime - flTimePerEmission * nActualParticlesToEmit;
  791. }
  792. if ( nActualParticlesToEmit <= 0 )
  793. return 0;
  794. int nStartParticle = pParticles->m_nActiveParticles;
  795. pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
  796. float flTimeStampStep = ( flCurrDrawTime - flPrevDrawTime ) / ( nActualParticlesToEmit );
  797. float flTimeStep = flPrevDrawTime + flTimeStampStep;
  798. // Set the particle creation time to the exact sub-frame particle emission time
  799. // !! speed!! do sse init here
  800. for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
  801. {
  802. float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
  803. flTimeStep = MIN( flTimeStep, flCurrDrawTime );
  804. *pTimeStamp = flTimeStep;
  805. flTimeStep += flTimeStampStep;
  806. }
  807. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  808. }
  809. struct MaintainEmitterContext_t
  810. {
  811. int m_ActualParticlesToMaintain;
  812. float m_flTimeOffset;
  813. bool m_bOn;
  814. };
  815. class C_OP_MaintainEmitter : public CParticleOperatorInstance
  816. {
  817. DECLARE_PARTICLE_OPERATOR( C_OP_MaintainEmitter );
  818. uint32 GetWrittenAttributes( void ) const
  819. {
  820. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  821. }
  822. uint32 GetReadAttributes( void ) const
  823. {
  824. return 0;
  825. }
  826. virtual uint64 GetReadControlPointMask() const
  827. {
  828. uint64 nMask = 0;
  829. if ( m_nScaleControlPoint >= 0 )
  830. nMask |= ( 1ULL << m_nScaleControlPoint );
  831. if ( m_nSnapshotControlPoint >= 0 )
  832. nMask |= ( 1ULL << m_nSnapshotControlPoint );
  833. return nMask;
  834. }
  835. virtual uint64 GetNonPositionalControlPointMask() const
  836. {
  837. uint64 nMask = 0;
  838. if ( m_nScaleControlPoint >= 0 )
  839. nMask |= ( 1ULL << m_nScaleControlPoint );
  840. if ( m_nSnapshotControlPoint >= 0 )
  841. nMask |= ( 1ULL << m_nSnapshotControlPoint );
  842. return nMask;
  843. }
  844. virtual uint32 Emit( CParticleCollection *pParticles, float flCurStrength,
  845. void *pContext ) const;
  846. // unpack structure will be applied by creator. add extra initialization needed here
  847. virtual void InitParams( CParticleSystemDefinition *pDef )
  848. {
  849. m_nScaleControlPointField = clamp( m_nScaleControlPointField, 0, 2 );
  850. m_nScaleControlPoint = clamp( m_nScaleControlPoint, -1, MAX_PARTICLE_CONTROL_POINTS );
  851. m_nSnapshotControlPoint = clamp( m_nSnapshotControlPoint, -1, MAX_PARTICLE_CONTROL_POINTS );
  852. }
  853. virtual void InitializeContextData( CParticleCollection *pParticles, void *pContext ) const
  854. {
  855. MaintainEmitterContext_t *pCtx = reinterpret_cast<MaintainEmitterContext_t *>( pContext );
  856. pCtx->m_flTimeOffset = 0.0f;
  857. pCtx->m_bOn = true;
  858. pCtx->m_ActualParticlesToMaintain = m_nParticlesToMaintain;
  859. }
  860. virtual void StartEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  861. {
  862. MaintainEmitterContext_t *pCtx = reinterpret_cast<MaintainEmitterContext_t *>( pContext );
  863. pCtx->m_bOn = true;
  864. }
  865. virtual void StopEmission( CParticleCollection *pParticles, void *pContext, bool bInfiniteOnly ) const
  866. {
  867. MaintainEmitterContext_t *pCtx = reinterpret_cast<MaintainEmitterContext_t *>( pContext );
  868. pCtx->m_bOn = false;
  869. }
  870. virtual void Restart( CParticleCollection *pParticles, void *pContext )
  871. {
  872. MaintainEmitterContext_t *pCtx = reinterpret_cast<MaintainEmitterContext_t *>( pContext );
  873. pCtx->m_flTimeOffset = pParticles->m_flCurTime;
  874. pCtx->m_bOn = true;
  875. }
  876. virtual bool MayCreateMoreParticles( CParticleCollection const *pParticles, void *pContext ) const
  877. {
  878. MaintainEmitterContext_t *pCtx = reinterpret_cast<MaintainEmitterContext_t *>( pContext );
  879. return pCtx->m_bOn;
  880. }
  881. size_t GetRequiredContextBytes( void ) const
  882. {
  883. return sizeof( MaintainEmitterContext_t );
  884. }
  885. void UpdateActualParticlesToMaintain( CParticleCollection *pParticles, MaintainEmitterContext_t *pCtx ) const
  886. {
  887. pCtx->m_ActualParticlesToMaintain = m_nParticlesToMaintain;
  888. if ( m_nSnapshotControlPoint >= 0 )
  889. {
  890. // Optionally override the number of particles to emit with the size a CP-attached Snapshot (can't do this at Init time)
  891. // NOTE: this causes m_nScaleControlPoint to be ignored
  892. CParticleSnapshot *pSnapshot = pParticles->GetControlPointSnapshot( m_nSnapshotControlPoint );
  893. if ( pSnapshot )
  894. {
  895. pCtx->m_ActualParticlesToMaintain = pSnapshot->NumCols(); // TODO: may want to add a NumParticles() accessor, for snapshots w/ multi-dimensional data
  896. }
  897. }
  898. else if ( m_nScaleControlPoint >= 0 )
  899. {
  900. Vector vecScale;
  901. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  902. if ( ( flStartTime <= pParticles->m_flCurTime ) && ( flStartTime >= pParticles->m_flCurTime - pParticles->m_flPreviousDt ) )
  903. {
  904. pParticles->GetControlPointAtTime( m_nScaleControlPoint, flStartTime, &vecScale );
  905. }
  906. else
  907. {
  908. pParticles->GetControlPointAtPrevTime( m_nScaleControlPoint, &vecScale );
  909. }
  910. pCtx->m_ActualParticlesToMaintain = m_nParticlesToMaintain * vecScale[m_nScaleControlPointField];
  911. }
  912. pCtx->m_ActualParticlesToMaintain = MAX( 0, pCtx->m_ActualParticlesToMaintain );
  913. }
  914. int m_nParticlesToMaintain;
  915. float m_flStartTime;
  916. int m_nScaleControlPoint;
  917. int m_nScaleControlPointField;
  918. int m_nSnapshotControlPoint;
  919. };
  920. DEFINE_PARTICLE_OPERATOR( C_OP_MaintainEmitter, "emit to maintain count", OPERATOR_GENERIC );
  921. BEGIN_PARTICLE_OPERATOR_UNPACK( C_OP_MaintainEmitter )
  922. DMXELEMENT_UNPACK_FIELD( "emission start time", "0", float, m_flStartTime )
  923. DMXELEMENT_UNPACK_FIELD( "count to maintain", "100", int, m_nParticlesToMaintain )
  924. DMXELEMENT_UNPACK_FIELD( "maintain count scale control point", "-1", int, m_nScaleControlPoint )
  925. DMXELEMENT_UNPACK_FIELD( "maintain count scale control point field", "0", int, m_nScaleControlPointField )
  926. DMXELEMENT_UNPACK_FIELD( "control point with snapshot data", "-1", int, m_nSnapshotControlPoint )
  927. END_PARTICLE_OPERATOR_UNPACK( C_OP_MaintainEmitter )
  928. uint32 C_OP_MaintainEmitter::Emit( CParticleCollection *pParticles, float flCurStrength,
  929. void *pContext ) const
  930. {
  931. MaintainEmitterContext_t *pCtx = reinterpret_cast<MaintainEmitterContext_t *>( pContext );
  932. if ( !pCtx->m_bOn )
  933. return 0;
  934. // Wait until we're told to start emitting
  935. float flStartTime = m_flStartTime + pCtx->m_flTimeOffset;
  936. if ( pParticles->m_flCurTime < flStartTime )
  937. return 0;
  938. // Update how many particles were supposed to be emitting
  939. UpdateActualParticlesToMaintain( pParticles, pCtx );
  940. // Don't emit any more if the particle system has emitted all it's supposed to.
  941. if ( pParticles->m_nActiveParticles >= pCtx->m_ActualParticlesToMaintain || pCtx->m_ActualParticlesToMaintain <= 0 )
  942. return 0;
  943. // We're only allowed to emit so many particles, though..
  944. // If we run out of room, only emit the last N particles
  945. int nAllowedParticlesToEmit = pParticles->m_nMaxAllowedParticles - pParticles->m_nActiveParticles;
  946. // Cap to the maximum emission
  947. int nParticlesToTryToEmit = pCtx->m_ActualParticlesToMaintain - pParticles->m_nActiveParticles;
  948. int nActualParticlesToEmit = MIN( nAllowedParticlesToEmit, nParticlesToTryToEmit );
  949. if ( nActualParticlesToEmit < 0 )
  950. return 0;
  951. int nStartParticle = pParticles->m_nActiveParticles;
  952. pParticles->SetNActiveParticles( nActualParticlesToEmit + pParticles->m_nActiveParticles );
  953. // While we always try to kick up to the specified number of particles,
  954. // we'll space their creation times over the last frame to avoid clumping
  955. float flEmissionStart = MAX( pParticles->m_flPrevSimTime, flStartTime );
  956. float flDeltaTime = pParticles->m_flCurTime - flEmissionStart;
  957. float flEmitTimeStep = flDeltaTime / nActualParticlesToEmit;
  958. float flEmitTime = flEmissionStart;
  959. // !! speed!! do sse init here
  960. for( int i = nStartParticle; i < nStartParticle + nActualParticlesToEmit; i++ )
  961. {
  962. float *pTimeStamp = pParticles->GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_CREATION_TIME, i );
  963. flEmitTime = MIN( flEmitTime, pParticles->m_flCurTime );
  964. *pTimeStamp = flEmitTime;
  965. flEmitTime += flEmitTimeStep;
  966. }
  967. return PARTICLE_ATTRIBUTE_CREATION_TIME_MASK;
  968. }
  969. void AddBuiltInParticleEmitters( void )
  970. {
  971. REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_ContinuousEmitter );
  972. REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_InstantaneousEmitter );
  973. REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_NoiseEmitter );
  974. REGISTER_PARTICLE_OPERATOR( FUNCTION_EMITTER, C_OP_MaintainEmitter );
  975. }