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.

4861 lines
153 KiB

  1. //===== Copyright � 1996-2007, 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 "bitmap/psheet.h"
  9. #include "filesystem.h"
  10. #include "tier2/tier2.h"
  11. #include "tier2/fileutils.h"
  12. #include "tier1/utlbuffer.h"
  13. #include "tier1/UtlStringMap.h"
  14. #include "tier1/strtools.h"
  15. #include "dmxloader/dmxloader.h"
  16. #include "materialsystem/imaterial.h"
  17. #include "materialsystem/imaterialvar.h"
  18. #include "materialsystem/itexture.h"
  19. #include "materialsystem/imesh.h"
  20. #include "tier0/vprof.h"
  21. #include "tier1/keyvalues.h"
  22. #include "tier1/lzmaDecoder.h"
  23. #include "random_floats.h"
  24. #include "vtf/vtf.h"
  25. #include "studio.h"
  26. #include "particles_internal.h"
  27. #include "ivrenderview.h"
  28. #include "materialsystem/imaterialsystem.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. // rename table from the great rename
  32. static char *s_RemapOperatorNameTable[]={
  33. "alpha_fade", "Alpha Fade and Decay",
  34. "alpha_fade_in_random", "Alpha Fade In Random",
  35. "alpha_fade_out_random", "Alpha Fade Out Random",
  36. "basic_movement", "Movement Basic",
  37. "color_fade", "Color Fade",
  38. "controlpoint_light", "Color Light From Control Point",
  39. "Dampen Movement Relative to Control Point", "Movement Dampen Relative to Control Point",
  40. "Distance Between Control Points Scale", "Remap Distance Between Two Control Points to Scalar",
  41. "Distance to Control Points Scale", "Remap Distance to Control Point to Scalar",
  42. "lifespan_decay", "Lifespan Decay",
  43. "lock to bone", "Movement Lock to Bone",
  44. "postion_lock_to_controlpoint", "Movement Lock to Control Point",
  45. "maintain position along path", "Movement Maintain Position Along Path",
  46. "Match Particle Velocities", "Movement Match Particle Velocities",
  47. "Max Velocity", "Movement Max Velocity",
  48. "noise", "Noise Scalar",
  49. "vector noise", "Noise Vector",
  50. "oscillate_scalar", "Oscillate Scalar",
  51. "oscillate_vector", "Oscillate Vector",
  52. "Orient Rotation to 2D Direction", "Rotation Orient to 2D Direction",
  53. "radius_scale", "Radius Scale",
  54. "Random Cull", "Cull Random",
  55. "remap_scalar", "Remap Scalar",
  56. "rotation_movement", "Rotation Basic",
  57. "rotation_spin", "Rotation Spin Roll",
  58. "rotation_spin yaw", "Rotation Spin Yaw",
  59. "alpha_random", "Alpha Random",
  60. "color_random", "Color Random",
  61. "create from parent particles", "Position From Parent Particles",
  62. "Create In Hierarchy", "Position In CP Hierarchy",
  63. "random position along path", "Position Along Path Random",
  64. "random position on model", "Position on Model Random",
  65. "sequential position along path", "Position Along Path Sequential",
  66. "position_offset_random", "Position Modify Offset Random",
  67. "position_warp_random", "Position Modify Warp Random",
  68. "position_within_box", "Position Within Box Random",
  69. "position_within_sphere", "Position Within Sphere Random",
  70. "Inherit Velocity", "Velocity Inherit from Control Point",
  71. "Initial Repulsion Velocity", "Velocity Repulse from World",
  72. "Initial Velocity Noise", "Velocity Noise",
  73. "Initial Scalar Noise", "Remap Noise to Scalar",
  74. "Lifespan from distance to world", "Lifetime from Time to Impact",
  75. "Pre-Age Noise", "Lifetime Pre-Age Noise",
  76. "lifetime_random", "Lifetime Random",
  77. "radius_random", "Radius Random",
  78. "random yaw", "Rotation Yaw Random",
  79. "Randomly Flip Yaw", "Rotation Yaw Flip Random",
  80. "rotation_random", "Rotation Random",
  81. "rotation_speed_random", "Rotation Speed Random",
  82. "sequence_random", "Sequence Random",
  83. "second_sequence_random", "Sequence Two Random",
  84. "trail_length_random", "Trail Length Random",
  85. "velocity_random", "Velocity Random",
  86. };
  87. static char const *RemapOperatorName( char const *pOpName )
  88. {
  89. for( int i = 0 ; i < ARRAYSIZE( s_RemapOperatorNameTable ) ; i += 2 )
  90. {
  91. if ( Q_stricmp( pOpName, s_RemapOperatorNameTable[i] ) == 0 )
  92. {
  93. return s_RemapOperatorNameTable[i + 1 ];
  94. }
  95. }
  96. return pOpName;
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Default implementation of particle system mgr
  100. //-----------------------------------------------------------------------------
  101. static CParticleSystemMgr s_ParticleSystemMgr;
  102. CParticleSystemMgr *g_pParticleSystemMgr = &s_ParticleSystemMgr;
  103. CParticleSystemMgr::ParticleAttribute_t CParticleSystemMgr::s_AttributeTable[ MAX_PARTICLE_ATTRIBUTES ];
  104. int g_nParticle_Multiplier = 1;
  105. //-----------------------------------------------------------------------------
  106. // Particle dictionary
  107. //-----------------------------------------------------------------------------
  108. class CParticleSystemDictionary
  109. {
  110. public:
  111. ~CParticleSystemDictionary();
  112. CParticleSystemDefinition* AddParticleSystem( CDmxElement *pParticleSystem );
  113. int Count() const;
  114. int NameCount() const;
  115. CParticleSystemDefinition* GetParticleSystem( int i );
  116. ParticleSystemHandle_t FindParticleSystemHandle( const char *pName );
  117. ParticleSystemHandle_t FindOrAddParticleSystemHandle( const char *pName );
  118. CParticleSystemDefinition* FindParticleSystem( ParticleSystemHandle_t h );
  119. CParticleSystemDefinition* FindParticleSystem( const char *pName );
  120. CParticleSystemDefinition* FindParticleSystem( const DmObjectId_t &id );
  121. CParticleSystemDefinition* operator[]( int idx )
  122. {
  123. return m_ParticleNameMap[ idx ];
  124. }
  125. private:
  126. typedef CUtlStringMap< CParticleSystemDefinition * > ParticleNameMap_t;
  127. typedef CUtlVector< CParticleSystemDefinition* > ParticleIdMap_t;
  128. void DestroyExistingElement( CDmxElement *pElement );
  129. ParticleNameMap_t m_ParticleNameMap;
  130. ParticleIdMap_t m_ParticleIdMap;
  131. };
  132. //-----------------------------------------------------------------------------
  133. // Destructor
  134. //-----------------------------------------------------------------------------
  135. CParticleSystemDictionary::~CParticleSystemDictionary()
  136. {
  137. int nCount = m_ParticleIdMap.Count();
  138. for ( int i = 0; i < nCount; ++i )
  139. {
  140. delete m_ParticleIdMap[i];
  141. }
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Destroys an existing element, returns if this element should be added to the name list
  145. //-----------------------------------------------------------------------------
  146. void CParticleSystemDictionary::DestroyExistingElement( CDmxElement *pElement )
  147. {
  148. const char *pParticleSystemName = pElement->GetName();
  149. bool bPreventNameBasedLookup = pElement->GetValue<bool>( "preventNameBasedLookup" );
  150. if ( !bPreventNameBasedLookup )
  151. {
  152. if ( m_ParticleNameMap.Defined( pParticleSystemName ) )
  153. {
  154. CParticleSystemDefinition *pDef = m_ParticleNameMap[ pParticleSystemName ];
  155. delete pDef;
  156. m_ParticleNameMap[ pParticleSystemName ] = NULL;
  157. }
  158. return;
  159. }
  160. // Use id based lookup instead
  161. int nCount = m_ParticleIdMap.Count();
  162. const DmObjectId_t& id = pElement->GetId();
  163. for ( int i = 0; i < nCount; ++i )
  164. {
  165. // Was already removed by the name lookup
  166. if ( !IsUniqueIdEqual( m_ParticleIdMap[i]->GetId(), id ) )
  167. continue;
  168. CParticleSystemDefinition *pDef = m_ParticleIdMap[ i ];
  169. m_ParticleIdMap.FastRemove( i );
  170. delete pDef;
  171. break;
  172. }
  173. }
  174. //-----------------------------------------------------------------------------
  175. // Adds a destructor
  176. //-----------------------------------------------------------------------------
  177. CParticleSystemDefinition* CParticleSystemDictionary::AddParticleSystem( CDmxElement *pParticleSystem )
  178. {
  179. if ( Q_stricmp( pParticleSystem->GetTypeString(), "DmeParticleSystemDefinition" ) )
  180. return NULL;
  181. DestroyExistingElement( pParticleSystem );
  182. CParticleSystemDefinition *pDef = new CParticleSystemDefinition;
  183. // Must add the def to the maps before Read() because Read() may create new child particle systems
  184. bool bPreventNameBasedLookup = pParticleSystem->GetValue<bool>( "preventNameBasedLookup" );
  185. if ( !bPreventNameBasedLookup )
  186. {
  187. m_ParticleNameMap[ pParticleSystem->GetName() ] = pDef;
  188. }
  189. else
  190. {
  191. m_ParticleIdMap.AddToTail( pDef );
  192. }
  193. pDef->Read( pParticleSystem );
  194. return pDef;
  195. }
  196. int CParticleSystemDictionary::NameCount() const
  197. {
  198. return m_ParticleNameMap.GetNumStrings();
  199. }
  200. int CParticleSystemDictionary::Count() const
  201. {
  202. return m_ParticleIdMap.Count();
  203. }
  204. CParticleSystemDefinition* CParticleSystemDictionary::GetParticleSystem( int i )
  205. {
  206. return m_ParticleIdMap[i];
  207. }
  208. ParticleSystemHandle_t CParticleSystemDictionary::FindParticleSystemHandle( const char *pName )
  209. {
  210. return m_ParticleNameMap.Find( pName );
  211. }
  212. ParticleSystemHandle_t CParticleSystemDictionary::FindOrAddParticleSystemHandle( const char *pName )
  213. {
  214. int nCount = m_ParticleNameMap.GetNumStrings();
  215. ParticleSystemHandle_t hSystem = m_ParticleNameMap.AddString( pName );
  216. if ( hSystem >= nCount )
  217. {
  218. m_ParticleNameMap[ hSystem ] = NULL;
  219. }
  220. return hSystem;
  221. }
  222. CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( ParticleSystemHandle_t h )
  223. {
  224. if ( h == UTL_INVAL_SYMBOL || h >= m_ParticleNameMap.GetNumStrings() )
  225. return NULL;
  226. return m_ParticleNameMap[ h ];
  227. }
  228. CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( const char *pName )
  229. {
  230. if ( m_ParticleNameMap.Defined( pName ) )
  231. return m_ParticleNameMap[ pName ];
  232. return NULL;
  233. }
  234. CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( const DmObjectId_t &id )
  235. {
  236. int nCount = m_ParticleIdMap.Count();
  237. for ( int i = 0; i < nCount; ++i )
  238. {
  239. if ( IsUniqueIdEqual( m_ParticleIdMap[i]->GetId(), id ) )
  240. return m_ParticleIdMap[i];
  241. }
  242. return NULL;
  243. }
  244. //-----------------------------------------------------------------------------
  245. // For editing, create a faked particle operator definition for children
  246. // The only thing used in here is GetUnpackStructure.
  247. //-----------------------------------------------------------------------------
  248. BEGIN_DMXELEMENT_UNPACK( ParticleChildrenInfo_t )
  249. DMXELEMENT_UNPACK_FIELD( "delay", "0.0", float, m_flDelay )
  250. DMXELEMENT_UNPACK_FIELD( "end cap effect", "0", bool, m_bEndCap )
  251. END_DMXELEMENT_UNPACK( ParticleChildrenInfo_t, s_ChildrenInfoUnpack )
  252. class CChildOperatorDefinition : public IParticleOperatorDefinition
  253. {
  254. public:
  255. virtual const char *GetName() const { Assert(0); return NULL; }
  256. virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const { Assert(0); return NULL; }
  257. // virtual void DestroyInstance( CParticleOperatorInstance *pInstance ) const { Assert(0); }
  258. virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const
  259. {
  260. return s_ChildrenInfoUnpack;
  261. }
  262. virtual ParticleOperatorId_t GetId() const { return OPERATOR_GENERIC; }
  263. virtual uint32 GetFilter() const { return 0; }
  264. virtual bool IsObsolete() const { return false; }
  265. };
  266. static CChildOperatorDefinition s_ChildOperatorDefinition;
  267. //-----------------------------------------------------------------------------
  268. //
  269. // CParticleSystemDefinition
  270. //
  271. //-----------------------------------------------------------------------------
  272. //-----------------------------------------------------------------------------
  273. // Unpack structure for CParticleSystemDefinition
  274. //-----------------------------------------------------------------------------
  275. BEGIN_DMXELEMENT_UNPACK( CParticleSystemDefinition )
  276. DMXELEMENT_UNPACK_FIELD( "max_particles", "1000", int, m_nMaxParticles )
  277. DMXELEMENT_UNPACK_FIELD( "initial_particles", "0", int, m_nInitialParticles )
  278. DMXELEMENT_UNPACK_FIELD_UTLSTRING_USERDATA( "material", "vgui/white", m_MaterialName, "vmtPicker" )
  279. DMXELEMENT_UNPACK_FIELD( "bounding_box_min", "-10 -10 -10", Vector, m_BoundingBoxMin )
  280. DMXELEMENT_UNPACK_FIELD( "bounding_box_max", "10 10 10", Vector, m_BoundingBoxMax )
  281. DMXELEMENT_UNPACK_FIELD( "cull_radius", "0", float, m_flCullRadius )
  282. DMXELEMENT_UNPACK_FIELD( "cull_cost", "1", float, m_flCullFillCost )
  283. DMXELEMENT_UNPACK_FIELD( "cull_control_point", "0", int, m_nCullControlPoint )
  284. DMXELEMENT_UNPACK_FIELD_UTLSTRING( "cull_replacement_definition", "", m_CullReplacementName )
  285. DMXELEMENT_UNPACK_FIELD_UTLSTRING( "fallback replacement definition", "", m_FallbackReplacementName )
  286. DMXELEMENT_UNPACK_FIELD( "fallback max count", "-1", int, m_nFallbackMaxCount )
  287. DMXELEMENT_UNPACK_FIELD( "radius", "5", float, m_flConstantRadius )
  288. DMXELEMENT_UNPACK_FIELD( "color", "255 255 255 255", Color, m_ConstantColor )
  289. DMXELEMENT_UNPACK_FIELD( "rotation", "0", float, m_flConstantRotation )
  290. DMXELEMENT_UNPACK_FIELD( "rotation_speed", "0", float, m_flConstantRotationSpeed )
  291. DMXELEMENT_UNPACK_FIELD( "normal", "0 0 1", Vector, m_ConstantNormal )
  292. DMXELEMENT_UNPACK_FIELD_USERDATA( "sequence_number", "0", int, m_nConstantSequenceNumber, "sheetsequencepicker" )
  293. DMXELEMENT_UNPACK_FIELD_USERDATA( "sequence_number 1", "0", int, m_nConstantSequenceNumber1, "sheetsequencepicker_second" )
  294. DMXELEMENT_UNPACK_FIELD( "group id", "0", int, m_nGroupID )
  295. DMXELEMENT_UNPACK_FIELD( "maximum time step", "0.1", float, m_flMaximumTimeStep )
  296. DMXELEMENT_UNPACK_FIELD( "maximum sim tick rate", "0.0", float, m_flMaximumSimTime )
  297. DMXELEMENT_UNPACK_FIELD( "minimum sim tick rate", "0.0", float, m_flMinimumSimTime )
  298. DMXELEMENT_UNPACK_FIELD( "minimum rendered frames", "0", int, m_nMinimumFrames )
  299. DMXELEMENT_UNPACK_FIELD( "control point to disable rendering if it is the camera", "-1", int, m_nSkipRenderControlPoint )
  300. DMXELEMENT_UNPACK_FIELD( "control point to only enable rendering if it is the camera", "-1", int, m_nAllowRenderControlPoint )
  301. DMXELEMENT_UNPACK_FIELD( "maximum draw distance", "100000.0", float, m_flMaxDrawDistance )
  302. DMXELEMENT_UNPACK_FIELD( "time to sleep when not drawn", "8", float, m_flNoDrawTimeToGoToSleep )
  303. DMXELEMENT_UNPACK_FIELD( "Sort particles", "1", bool, m_bShouldSort )
  304. DMXELEMENT_UNPACK_FIELD( "batch particle systems", "0", bool, m_bShouldBatch )
  305. DMXELEMENT_UNPACK_FIELD( "view model effect", "0", bool, m_bViewModelEffect )
  306. DMXELEMENT_UNPACK_FIELD( "screen space effect", "0", bool, m_bScreenSpaceEffect )
  307. DMXELEMENT_UNPACK_FIELD( "draw through leafsystem", "1", bool, m_bDrawThroughLeafSystem )
  308. DMXELEMENT_UNPACK_FIELD( "maximum portal recursion depth", "8", int, m_nMaxRecursionDepth )
  309. DMXELEMENT_UNPACK_FIELD( "aggregation radius", "0", float, m_flAggregateRadius )
  310. DMXELEMENT_UNPACK_FIELD( "minimum free particles to aggregate", "0", int, m_nAggregationMinAvailableParticles )
  311. DMXELEMENT_UNPACK_FIELD( "minimum simulation time step", "0", float, m_flMinimumTimeStep )
  312. DMXELEMENT_UNPACK_FIELD( "minimum CPU level", "0", int, m_nMinCPULevel )
  313. DMXELEMENT_UNPACK_FIELD( "minimum GPU level", "0", int, m_nMinGPULevel )
  314. DMXELEMENT_UNPACK_FIELD( "freeze simulation after time", "1000000000", float, m_flStopSimulationAfterTime )
  315. END_DMXELEMENT_UNPACK( CParticleSystemDefinition, s_pParticleSystemDefinitionUnpack )
  316. //-----------------------------------------------------------------------------
  317. //
  318. // CParticleOperatorDefinition begins here
  319. // A template describing how a particle system will function
  320. //
  321. //-----------------------------------------------------------------------------
  322. void CParticleSystemDefinition::UnlinkAllCollections()
  323. {
  324. while ( m_pFirstCollection )
  325. {
  326. m_pFirstCollection->UnlinkFromDefList();
  327. }
  328. Assert( m_nFallbackCurrentCount == 0 );
  329. }
  330. const char *CParticleSystemDefinition::GetName() const
  331. {
  332. return m_Name;
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Should we always precache this?
  336. //-----------------------------------------------------------------------------
  337. bool CParticleSystemDefinition::ShouldAlwaysPrecache() const
  338. {
  339. return m_bAlwaysPrecache;
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Precache/uncache
  343. //-----------------------------------------------------------------------------
  344. void CParticleSystemDefinition::Precache()
  345. {
  346. if ( m_bIsPrecached )
  347. return;
  348. m_bIsPrecached = true;
  349. #ifndef DEDICATED
  350. if ( !UTIL_IsDedicatedServer() && g_pMaterialSystem )
  351. {
  352. m_Material.Init( MaterialName(), TEXTURE_GROUP_OTHER, true );
  353. if ( m_Material->HasProxy() )
  354. {
  355. Warning( "Material %s used by particle systems cannot use proxies!\n", m_Material->GetName() );
  356. m_Material.Init( "debug/particleerror", TEXTURE_GROUP_OTHER, true );
  357. }
  358. g_pParticleSystemMgr->FindOrLoadSheet( this );
  359. // NOTE: Subtle. This has to be called after HasProxy, which will
  360. // have loaded all material vars. The "queue friendly" version of a material
  361. // doesn't precache material vars
  362. m_Material->GetColorModulation( &m_vecMaterialModulation[0], &m_vecMaterialModulation[1], &m_vecMaterialModulation[2] );
  363. m_vecMaterialModulation[3] = m_Material->GetAlphaModulation();
  364. }
  365. #endif
  366. if ( HasFallback() )
  367. {
  368. CParticleSystemDefinition *pFallback = GetFallbackReplacementDefinition();
  369. if ( pFallback )
  370. {
  371. pFallback->Precache();
  372. }
  373. }
  374. // call the precache method of the renderers in case they need assets
  375. for( int i = 0; i < m_Renderers.Count(); i++ )
  376. {
  377. CParticleOperatorInstance *pOp = m_Renderers[i];
  378. pOp->Precache();
  379. }
  380. int nChildCount = m_Children.Count();
  381. for ( int i = 0; i < nChildCount; ++i )
  382. {
  383. CParticleSystemDefinition *pChild;
  384. if ( m_Children[i].m_bUseNameBasedLookup )
  385. {
  386. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Name );
  387. }
  388. else
  389. {
  390. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Id );
  391. }
  392. if ( pChild )
  393. {
  394. pChild->Precache();
  395. }
  396. }
  397. }
  398. void CParticleSystemDefinition::Uncache()
  399. {
  400. if ( !m_bIsPrecached )
  401. return;
  402. m_bIsPrecached = false;
  403. m_Material.Shutdown();
  404. // m_Material.Init( "debug/particleerror", TEXTURE_GROUP_OTHER, true );
  405. // m_vecMaterialModulation.Init( 1.0f, 1.0f, 1.0f, 1.0f );
  406. if ( HasFallback() )
  407. {
  408. CParticleSystemDefinition *pFallback = GetFallbackReplacementDefinition();
  409. if ( pFallback )
  410. {
  411. pFallback->Uncache();
  412. }
  413. }
  414. for( int i = 0; i < m_Renderers.Count(); i++ )
  415. {
  416. CParticleOperatorInstance *pOp = m_Renderers[i];
  417. pOp->Uncache();
  418. }
  419. int nChildCount = m_Children.Count();
  420. for ( int i = 0; i < nChildCount; ++i )
  421. {
  422. CParticleSystemDefinition *pChild;
  423. if ( m_Children[i].m_bUseNameBasedLookup )
  424. {
  425. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Name );
  426. }
  427. else
  428. {
  429. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Id );
  430. }
  431. if ( pChild )
  432. {
  433. pChild->Uncache();
  434. }
  435. }
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Has this been precached?
  439. //-----------------------------------------------------------------------------
  440. bool CParticleSystemDefinition::IsPrecached() const
  441. {
  442. return m_bIsPrecached;
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Helper methods to help with unserialization
  446. //-----------------------------------------------------------------------------
  447. void CParticleSystemDefinition::ParseOperators(
  448. const char *pszOpKey, ParticleFunctionType_t nFunctionType,
  449. CDmxElement *pElement,
  450. CUtlVector<CParticleOperatorInstance *> &outList)
  451. {
  452. const CDmxAttribute* pAttribute = pElement->GetAttribute( pszOpKey );
  453. if ( !pAttribute || pAttribute->GetType() != AT_ELEMENT_ARRAY )
  454. return;
  455. const CUtlVector<IParticleOperatorDefinition *> &flist = g_pParticleSystemMgr->GetAvailableParticleOperatorList( nFunctionType );
  456. const CUtlVector< CDmxElement* >& ops = pAttribute->GetArray<CDmxElement*>( );
  457. int nCount = ops.Count();
  458. for ( int i = 0; i < nCount; ++i )
  459. {
  460. const char *pOrigName = ops[i]->GetValueString( "functionName" );
  461. char const *pOpName = RemapOperatorName( pOrigName );
  462. if ( pOpName != pOrigName )
  463. {
  464. pElement->SetValue( "functionName", pOpName );
  465. }
  466. bool bFound = false;
  467. int nFunctionCount = flist.Count();
  468. for( int j = 0; j < nFunctionCount; ++j )
  469. {
  470. if ( Q_stricmp( pOpName, flist[j]->GetName() ) )
  471. continue;
  472. // found it!
  473. bFound = true;
  474. CParticleOperatorInstance *pNewRef = flist[j]->CreateInstance( ops[i]->GetId() );
  475. const DmxElementUnpackStructure_t *pUnpack = flist[j]->GetUnpackStructure();
  476. if ( pUnpack )
  477. {
  478. ops[i]->UnpackIntoStructure( pNewRef, pUnpack );
  479. }
  480. pNewRef->InitParams( this );
  481. pNewRef->CheckForFastPath();
  482. m_nAttributeReadMask |= pNewRef->GetReadAttributes();
  483. m_nControlPointReadMask |= pNewRef->GetReadControlPointMask();
  484. m_nControlPointNonPositionalMask |= pNewRef->GetNonPositionalControlPointMask();
  485. switch( nFunctionType )
  486. {
  487. case FUNCTION_INITIALIZER:
  488. case FUNCTION_EMITTER:
  489. m_nPerParticleInitializedAttributeMask |= pNewRef->GetWrittenAttributes();
  490. Assert( pNewRef->GetReadInitialAttributes() == 0 );
  491. break;
  492. case FUNCTION_OPERATOR:
  493. case FUNCTION_FORCEGENERATOR:
  494. case FUNCTION_CONSTRAINT:
  495. m_nPerParticleUpdatedAttributeMask |= pNewRef->GetWrittenAttributes();
  496. m_nInitialAttributeReadMask |= pNewRef->GetReadInitialAttributes();
  497. break;
  498. case FUNCTION_RENDERER:
  499. m_nPerParticleUpdatedAttributeMask |= pNewRef->GetWrittenAttributes();
  500. m_nInitialAttributeReadMask |= pNewRef->GetReadInitialAttributes();
  501. break;
  502. }
  503. // Special case: Reading particle ID means we're reading the initial particle id
  504. if ( ( pNewRef->GetReadAttributes() | pNewRef->GetReadInitialAttributes() ) & PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK )
  505. {
  506. m_nInitialAttributeReadMask |= PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
  507. m_nPerParticleInitializedAttributeMask |= PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
  508. }
  509. outList.AddToTail( pNewRef );
  510. break;
  511. }
  512. if ( !bFound )
  513. {
  514. if ( flist.Count() ) // don't warn if no ops of that type defined (server)
  515. Warning( "Didn't find particle function %s\n", pOpName );
  516. }
  517. }
  518. }
  519. void CParticleSystemDefinition::ParseChildren( CDmxElement *pElement )
  520. {
  521. const CUtlVector<CDmxElement*>& children = pElement->GetArray<CDmxElement*>( "children" );
  522. int nCount = children.Count();
  523. for ( int i = 0; i < nCount; ++i )
  524. {
  525. CDmxElement *pChild = children[i]->GetValue<CDmxElement*>( "child" );
  526. if ( !pChild || Q_stricmp( pChild->GetTypeString(), "DmeParticleSystemDefinition" ) )
  527. continue;
  528. int j = m_Children.AddToTail();
  529. children[i]->UnpackIntoStructure( &m_Children[j], s_ChildrenInfoUnpack );
  530. m_Children[j].m_bUseNameBasedLookup = !pChild->GetValue<bool>( "preventNameBasedLookup" );
  531. if ( m_Children[j].m_bUseNameBasedLookup )
  532. {
  533. m_Children[j].m_Name = g_pParticleSystemMgr->FindOrAddParticleSystemIndex( pChild->GetName() );
  534. }
  535. else
  536. {
  537. CopyUniqueId( pChild->GetId(), &m_Children[j].m_Id );
  538. }
  539. // Check to see if this child has been encountered already, and if not, then
  540. // create a new particle definition for this child
  541. g_pParticleSystemMgr->AddParticleSystem( pChild );
  542. }
  543. }
  544. void CParticleSystemDefinition::Read( CDmxElement *pElement )
  545. {
  546. m_Name = pElement->GetName();
  547. CopyUniqueId( pElement->GetId(), &m_Id );
  548. pElement->UnpackIntoStructure( this, s_pParticleSystemDefinitionUnpack );
  549. if ( m_nInitialParticles < 0 )
  550. {
  551. m_nInitialParticles = 0;
  552. }
  553. if ( m_nMaxParticles < 1 )
  554. {
  555. m_nMaxParticles = 1;
  556. }
  557. m_nMaxParticles *= g_nParticle_Multiplier;
  558. m_nMaxParticles = MIN( m_nMaxParticles, MAX_PARTICLES_IN_A_SYSTEM );
  559. if ( m_flCullRadius > 0 )
  560. {
  561. m_nControlPointReadMask |= 1ULL << m_nCullControlPoint;
  562. }
  563. ParseOperators( "renderers", FUNCTION_RENDERER, pElement, m_Renderers );
  564. ParseOperators( "operators", FUNCTION_OPERATOR, pElement, m_Operators );
  565. ParseOperators( "initializers", FUNCTION_INITIALIZER, pElement, m_Initializers );
  566. ParseOperators( "emitters", FUNCTION_EMITTER, pElement, m_Emitters );
  567. ParseChildren( pElement );
  568. ParseOperators( "forces", FUNCTION_FORCEGENERATOR, pElement, m_ForceGenerators );
  569. ParseOperators( "constraints", FUNCTION_CONSTRAINT, pElement, m_Constraints );
  570. SetupContextData();
  571. }
  572. IMaterial *CParticleSystemDefinition::GetMaterial() const
  573. {
  574. // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
  575. Assert( IsPrecached() );
  576. if ( !IsPrecached() )
  577. return NULL;
  578. return (IMaterial *) ( (const IMaterial *) m_Material );
  579. }
  580. CUtlSymbol CParticleSystemDefinition::GetSheetSymbol() const
  581. {
  582. Assert( IsSheetSymbolCached() );
  583. return m_SheetSymbol;
  584. }
  585. void CParticleSystemDefinition::CacheSheetSymbol( CUtlSymbol sheetSymbol )
  586. {
  587. m_SheetSymbol = sheetSymbol;
  588. m_bSheetSymbolCached = true;
  589. }
  590. bool CParticleSystemDefinition::IsSheetSymbolCached() const
  591. {
  592. return m_bSheetSymbolCached;
  593. }
  594. void CParticleSystemDefinition::InvalidateSheetSymbol()
  595. {
  596. m_bSheetSymbolCached = false;
  597. }
  598. //----------------------------------------------------------------------------------
  599. // Returns the particle system fallback
  600. //----------------------------------------------------------------------------------
  601. CParticleSystemDefinition *CParticleSystemDefinition::GetFallbackReplacementDefinition() const
  602. {
  603. if ( HasFallback() )
  604. {
  605. if ( !m_pFallback() )
  606. {
  607. const_cast< CParticleSystemDefinition* >( this )->m_pFallback.Set( g_pParticleSystemMgr->FindParticleSystem( m_FallbackReplacementName ) );
  608. }
  609. return m_pFallback();
  610. }
  611. return NULL;
  612. }
  613. //----------------------------------------------------------------------------------
  614. // Does the particle system use the power of two frame buffer texture (refraction?)
  615. //----------------------------------------------------------------------------------
  616. bool CParticleSystemDefinition::UsesPowerOfTwoFrameBufferTexture()
  617. {
  618. // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
  619. Assert( IsPrecached() );
  620. return g_pMaterialSystem && m_Material && m_Material->NeedsPowerOfTwoFrameBufferTexture( false ); // The false checks if it will ever need the frame buffer, not just this frame
  621. }
  622. //----------------------------------------------------------------------------------
  623. // Does the particle system use the power of two frame buffer texture (refraction?)
  624. //----------------------------------------------------------------------------------
  625. bool CParticleSystemDefinition::UsesFullFrameBufferTexture()
  626. {
  627. // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
  628. Assert( IsPrecached() );
  629. return g_pMaterialSystem && m_Material && m_Material->NeedsFullFrameBufferTexture( false ); // The false checks if it will ever need the frame buffer, not just this frame
  630. }
  631. //-----------------------------------------------------------------------------
  632. // Helper methods to write particle systems
  633. //-----------------------------------------------------------------------------
  634. void CParticleSystemDefinition::WriteOperators( CDmxElement *pElement,
  635. const char *pOpKeyName, const CUtlVector<CParticleOperatorInstance *> &inList )
  636. {
  637. CDmxElementModifyScope modify( pElement );
  638. CDmxAttribute* pAttribute = pElement->AddAttribute( pOpKeyName );
  639. CUtlVector< CDmxElement* >& ops = pAttribute->GetArrayForEdit<CDmxElement*>( );
  640. int nCount = inList.Count();
  641. for ( int i = 0; i < nCount; ++i )
  642. {
  643. CDmxElement *pOperator = CreateDmxElement( "DmeParticleOperator" );
  644. ops.AddToTail( pOperator );
  645. const IParticleOperatorDefinition *pDef = inList[i]->GetDefinition();
  646. pOperator->SetValue( "name", pDef->GetName() );
  647. pOperator->SetValue( "functionName", pDef->GetName() );
  648. const DmxElementUnpackStructure_t *pUnpack = pDef->GetUnpackStructure();
  649. if ( pUnpack )
  650. {
  651. pOperator->AddAttributesFromStructure( inList[i], pUnpack );
  652. }
  653. }
  654. }
  655. void CParticleSystemDefinition::WriteChildren( CDmxElement *pElement )
  656. {
  657. CDmxElementModifyScope modify( pElement );
  658. CDmxAttribute* pAttribute = pElement->AddAttribute( "children" );
  659. CUtlVector< CDmxElement* >& children = pAttribute->GetArrayForEdit<CDmxElement*>( );
  660. int nCount = m_Children.Count();
  661. for ( int i = 0; i < nCount; ++i )
  662. {
  663. CDmxElement *pChildRef = CreateDmxElement( "DmeParticleChild" );
  664. children.AddToTail( pChildRef );
  665. children[i]->AddAttributesFromStructure( &m_Children[i], s_ChildrenInfoUnpack );
  666. CDmxElement *pChildParticleSystem;
  667. if ( m_Children[i].m_bUseNameBasedLookup )
  668. {
  669. pChildParticleSystem = g_pParticleSystemMgr->CreateParticleDmxElement(
  670. g_pParticleSystemMgr->GetParticleSystemNameFromIndex( m_Children[i].m_Name ) );
  671. }
  672. else
  673. {
  674. pChildParticleSystem = g_pParticleSystemMgr->CreateParticleDmxElement( m_Children[i].m_Id );
  675. }
  676. pChildRef->SetValue( "name", pChildParticleSystem->GetName() );
  677. pChildRef->SetValue( "child", pChildParticleSystem );
  678. }
  679. }
  680. CDmxElement *CParticleSystemDefinition::Write()
  681. {
  682. const char *pName = GetName();
  683. CDmxElement *pElement = CreateDmxElement( "DmeParticleSystemDefinition" );
  684. pElement->SetValue( "name", pName );
  685. pElement->AddAttributesFromStructure( this, s_pParticleSystemDefinitionUnpack );
  686. WriteOperators( pElement, "renderers",m_Renderers );
  687. WriteOperators( pElement, "operators", m_Operators );
  688. WriteOperators( pElement, "initializers", m_Initializers );
  689. WriteOperators( pElement, "emitters", m_Emitters );
  690. WriteChildren( pElement );
  691. WriteOperators( pElement, "forces", m_ForceGenerators );
  692. WriteOperators( pElement, "constraints", m_Constraints );
  693. return pElement;
  694. }
  695. void CParticleSystemDefinition::SetupContextData( void )
  696. {
  697. // calculate sizes and offsets for context data
  698. CUtlVector<CParticleOperatorInstance *> *olists[] = {
  699. &m_Operators, &m_Renderers, &m_Initializers, &m_Emitters, &m_ForceGenerators,
  700. &m_Constraints
  701. };
  702. CUtlVector<size_t> *offsetLists[] = {
  703. &m_nOperatorsCtxOffsets, &m_nRenderersCtxOffsets,
  704. &m_nInitializersCtxOffsets, &m_nEmittersCtxOffsets,
  705. &m_nForceGeneratorsCtxOffsets, &m_nConstraintsCtxOffsets,
  706. };
  707. // loop through all operators, fill in offset entries, and calulate total data needed
  708. m_nContextDataSize = 0;
  709. for( int i = 0; i < NELEMS( olists ); i++ )
  710. {
  711. int nCount = olists[i]->Count();
  712. for( int j = 0; j < nCount; j++ )
  713. {
  714. offsetLists[i]->AddToTail( m_nContextDataSize );
  715. m_nContextDataSize += (*olists[i])[j]->GetRequiredContextBytes();
  716. // align context data
  717. m_nContextDataSize = (m_nContextDataSize + 15) & (~0xf );
  718. }
  719. }
  720. }
  721. //-----------------------------------------------------------------------------
  722. // Finds an operator by id
  723. //-----------------------------------------------------------------------------
  724. CUtlVector<CParticleOperatorInstance *> *CParticleSystemDefinition::GetOperatorList( ParticleFunctionType_t type )
  725. {
  726. switch( type )
  727. {
  728. case FUNCTION_EMITTER:
  729. return &m_Emitters;
  730. case FUNCTION_RENDERER:
  731. return &m_Renderers;
  732. case FUNCTION_INITIALIZER:
  733. return &m_Initializers;
  734. case FUNCTION_OPERATOR:
  735. return &m_Operators;
  736. case FUNCTION_FORCEGENERATOR:
  737. return &m_ForceGenerators;
  738. case FUNCTION_CONSTRAINT:
  739. return &m_Constraints;
  740. default:
  741. Assert(0);
  742. return NULL;
  743. }
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Finds an operator by id
  747. //-----------------------------------------------------------------------------
  748. CParticleOperatorInstance *CParticleSystemDefinition::FindOperatorById( ParticleFunctionType_t type, const DmObjectId_t &id )
  749. {
  750. CUtlVector<CParticleOperatorInstance *> *pVec = GetOperatorList( type );
  751. if ( !pVec )
  752. return NULL;
  753. int nCount = pVec->Count();
  754. for ( int i = 0; i < nCount; ++i )
  755. {
  756. if ( IsUniqueIdEqual( id, pVec->Element(i)->GetId() ) )
  757. return pVec->Element(i);
  758. }
  759. return NULL;
  760. }
  761. //-----------------------------------------------------------------------------
  762. // Finds an operator by name (slow!)
  763. //-----------------------------------------------------------------------------
  764. CParticleOperatorInstance *CParticleSystemDefinition::FindOperatorByName( const char *pOperatorName )
  765. {
  766. for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; i++ )
  767. {
  768. CUtlVector<CParticleOperatorInstance *> *pVec = ( i == FUNCTION_CHILDREN ) ? NULL : GetOperatorList( (ParticleFunctionType_t)i );
  769. if ( !pVec )
  770. continue;
  771. int nCount = pVec->Count();
  772. for ( int j = 0; j < nCount; ++j )
  773. {
  774. if ( !Q_stricmp( pOperatorName, pVec->Element(j)->GetDefinition()->GetName() ) )
  775. return pVec->Element(j);
  776. }
  777. }
  778. return NULL;
  779. }
  780. //-----------------------------------------------------------------------------
  781. //
  782. // CParticleOperatorInstance
  783. //
  784. //-----------------------------------------------------------------------------
  785. void CParticleOperatorInstance::InitNewParticles( CParticleCollection *pParticles,
  786. int nFirstParticle, int nParticleCount,
  787. int nAttributeWriteMask, void *pContext ) const
  788. {
  789. if ( !nParticleCount )
  790. return;
  791. if ( nParticleCount < 16 ) // don't bother with vectorizing
  792. // unless enough particles to bother
  793. {
  794. InitNewParticlesScalar( pParticles, nFirstParticle, nParticleCount, nAttributeWriteMask, pContext );
  795. return;
  796. }
  797. int nHead = nFirstParticle & 3;
  798. if ( nHead )
  799. {
  800. // need to init up to 3 particles before we are block aligned
  801. int nHeadCount = MIN( nParticleCount, 4 - nHead );
  802. InitNewParticlesScalar( pParticles, nFirstParticle, nHeadCount, nAttributeWriteMask, pContext );
  803. nParticleCount -= nHeadCount;
  804. nFirstParticle += nHeadCount;
  805. }
  806. // now, we are aligned
  807. int nBlockCount = nParticleCount / 4;
  808. if ( nBlockCount )
  809. {
  810. InitNewParticlesBlock( pParticles, nFirstParticle / 4, nBlockCount, nAttributeWriteMask, pContext );
  811. nParticleCount -= 4 * nBlockCount;
  812. nFirstParticle += 4 * nBlockCount;
  813. }
  814. // do tail
  815. if ( nParticleCount )
  816. {
  817. InitNewParticlesScalar( pParticles, nFirstParticle, nParticleCount, nAttributeWriteMask, pContext );
  818. }
  819. }
  820. //-----------------------------------------------------------------------------
  821. //
  822. // CParticleCollection
  823. //
  824. //-----------------------------------------------------------------------------
  825. //------------------------------------------------------------------------------
  826. // need custom new/delete for alignment for simd
  827. //------------------------------------------------------------------------------
  828. #include "tier0/memdbgoff.h"
  829. void *CParticleCollection::operator new( size_t nSize )
  830. {
  831. return MemAlloc_AllocAligned( nSize, 16 );
  832. }
  833. void* CParticleCollection::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  834. {
  835. return MemAlloc_AllocAlignedFileLine( nSize, 16, pFileName, nLine );
  836. }
  837. void CParticleCollection::operator delete(void *pData)
  838. {
  839. if ( pData )
  840. {
  841. MemAlloc_FreeAligned( pData );
  842. }
  843. }
  844. void CParticleCollection::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
  845. {
  846. if ( pData )
  847. {
  848. MemAlloc_FreeAligned( pData );
  849. }
  850. }
  851. void *CWorldCollideContextData::operator new( size_t nSize )
  852. {
  853. return MemAlloc_AllocAligned( nSize, 16 );
  854. }
  855. void* CWorldCollideContextData::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  856. {
  857. return MemAlloc_AllocAlignedFileLine( nSize, 16, pFileName, nLine );
  858. }
  859. void CWorldCollideContextData::operator delete(void *pData)
  860. {
  861. if ( pData )
  862. {
  863. MemAlloc_FreeAligned( pData );
  864. }
  865. }
  866. void CWorldCollideContextData::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
  867. {
  868. if ( pData )
  869. {
  870. MemAlloc_FreeAligned( pData );
  871. }
  872. }
  873. #include "tier0/memdbgon.h"
  874. //-----------------------------------------------------------------------------
  875. // Constructor, destructor
  876. //-----------------------------------------------------------------------------
  877. CParticleCollection::CParticleCollection( )
  878. {
  879. COMPILE_TIME_ASSERT( ( MAX_RANDOM_FLOATS & ( MAX_RANDOM_FLOATS - 1 ) ) == 0 );
  880. COMPILE_TIME_ASSERT( sizeof( s_pRandomFloats ) / sizeof( float ) >= MAX_RANDOM_FLOATS );
  881. Plat_FastMemset( this, 0, sizeof(CParticleCollection) );
  882. m_flOOMaxDistSqr = 1.0f;
  883. m_flPreviousDt = 0.05f;
  884. m_nParticleFlags = PCFLAGS_FIRST_FRAME;
  885. m_LocalLighting = Color(255, 255, 255, 255);
  886. m_LocalLightingCP = -1;
  887. m_bPendingRestart = false;
  888. m_flTargetDrawTime = 0;
  889. m_bQueuedStartEmission = false;
  890. m_bFrozen = false;
  891. m_pCPInfo = NULL;
  892. m_pCachedParticleBatches = NULL;
  893. m_bTriedLoadingSheet = false;
  894. }
  895. CParticleCollection::~CParticleCollection( void )
  896. {
  897. UnlinkFromDefList();
  898. m_Children.Purge();
  899. if ( m_pParticleMemory )
  900. {
  901. MemAlloc_FreeAligned( m_pParticleMemory );
  902. m_pParticleMemory = NULL;
  903. }
  904. if ( m_pPreviousAttributeMemory )
  905. {
  906. MemAlloc_FreeAligned( m_pPreviousAttributeMemory );
  907. m_pPreviousAttributeMemory = NULL;
  908. }
  909. if ( m_pParticleInitialMemory )
  910. {
  911. MemAlloc_FreeAligned( m_pParticleInitialMemory );
  912. m_pParticleInitialMemory = NULL;
  913. }
  914. if ( m_pConstantMemory )
  915. {
  916. MemAlloc_FreeAligned( m_pConstantMemory );
  917. m_pConstantMemory = NULL;
  918. }
  919. if ( m_pOperatorContextData )
  920. {
  921. MemAlloc_FreeAligned( m_pOperatorContextData );
  922. m_pOperatorContextData = NULL;
  923. }
  924. for( int i = 0 ; i < ARRAYSIZE( m_pCollisionCacheData ) ; i++ )
  925. {
  926. if ( m_pCollisionCacheData[i] )
  927. {
  928. delete m_pCollisionCacheData[i];
  929. m_pCollisionCacheData[i] = NULL;
  930. }
  931. }
  932. if ( m_pCPInfo )
  933. {
  934. delete[] m_pCPInfo;
  935. m_pCPInfo = NULL;
  936. }
  937. if ( m_pCachedParticleBatches )
  938. {
  939. delete m_pCachedParticleBatches;
  940. m_pCachedParticleBatches = NULL;
  941. }
  942. }
  943. //-----------------------------------------------------------------------------
  944. // Initialization
  945. //-----------------------------------------------------------------------------
  946. void CParticleCollection::Init( CParticleSystemDefinition *pDef, float flDelay, int nRandomSeed )
  947. {
  948. m_pDef = pDef;
  949. // Link into def list
  950. LinkIntoDefList();
  951. m_pRenderable = NULL;
  952. InitStorage( pDef );
  953. // Initialize sheet data
  954. m_Sheet.Set( g_pParticleSystemMgr->FindOrLoadSheet( pDef ) );
  955. m_bTriedLoadingSheet = false;
  956. // FIXME: This seed needs to be recorded per instance!
  957. m_bIsScrubbable = ( nRandomSeed != 0 );
  958. if ( m_bIsScrubbable )
  959. {
  960. m_nRandomSeed = nRandomSeed;
  961. }
  962. else
  963. {
  964. m_nRandomSeed = (int)(intp)this;
  965. #ifndef _DEBUG
  966. m_nRandomSeed += Plat_MSTime();
  967. #endif
  968. }
  969. SetAttributeToConstant( PARTICLE_ATTRIBUTE_XYZ, 0.0f, 0.0f, 0.0f );
  970. SetAttributeToConstant( PARTICLE_ATTRIBUTE_PREV_XYZ, 0.0f, 0.0f, 0.0f );
  971. SetAttributeToConstant( PARTICLE_ATTRIBUTE_LIFE_DURATION, 1.0f );
  972. SetAttributeToConstant( PARTICLE_ATTRIBUTE_RADIUS, pDef->m_flConstantRadius );
  973. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ROTATION, pDef->m_flConstantRotation );
  974. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ROTATION_SPEED, pDef->m_flConstantRotationSpeed );
  975. SetAttributeToConstant( PARTICLE_ATTRIBUTE_TINT_RGB,
  976. pDef->m_ConstantColor.r() / 255.0f, pDef->m_ConstantColor.g() / 255.0f,
  977. pDef->m_ConstantColor.b() / 255.0f );
  978. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ALPHA, pDef->m_ConstantColor.a() / 255.0f );
  979. SetAttributeToConstant( PARTICLE_ATTRIBUTE_CREATION_TIME, 0.0f );
  980. SetAttributeToConstant( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, pDef->m_nConstantSequenceNumber );
  981. SetAttributeToConstant( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, pDef->m_nConstantSequenceNumber1 );
  982. SetAttributeToConstant( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, 0.1f );
  983. SetAttributeToConstant( PARTICLE_ATTRIBUTE_PARTICLE_ID, 0 );
  984. SetAttributeToConstant( PARTICLE_ATTRIBUTE_YAW, 0.0f );
  985. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ALPHA2, 1.0f );
  986. SetAttributeToConstant( PARTICLE_ATTRIBUTE_PITCH, 0.0f );
  987. SetAttributeToConstant( PARTICLE_ATTRIBUTE_NORMAL,
  988. pDef->m_ConstantNormal.x, pDef->m_ConstantNormal.y, pDef->m_ConstantNormal.z );
  989. SetAttributeToConstant( PARTICLE_ATTRIBUTE_GLOW_RGB, 1.0f, 1.0f, 1.0f );
  990. SetAttributeToConstant( PARTICLE_ATTRIBUTE_GLOW_ALPHA, 1.0f );
  991. // Offset the child in time
  992. m_flCurTime = -flDelay;
  993. m_fl4CurTime = ReplicateX4( m_flCurTime );
  994. if ( m_pDef->m_nContextDataSize )
  995. {
  996. m_pOperatorContextData = reinterpret_cast<uint8 *>
  997. ( MemAlloc_AllocAligned( m_pDef->m_nContextDataSize, 16 ) );
  998. }
  999. m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + pDef->m_flNoDrawTimeToGoToSleep;
  1000. m_nControlPointReadMask = pDef->m_nControlPointReadMask;
  1001. m_nControlPointNonPositionalMask = pDef->m_nControlPointNonPositionalMask;
  1002. // Instance child particle systems
  1003. int nChildCount = pDef->m_Children.Count();
  1004. for ( int i = 0; i < nChildCount; ++i )
  1005. {
  1006. if ( nRandomSeed != 0 )
  1007. {
  1008. nRandomSeed += 129;
  1009. }
  1010. CParticleCollection *pChild;
  1011. if ( pDef->m_Children[i].m_bUseNameBasedLookup )
  1012. {
  1013. pChild = g_pParticleSystemMgr->CreateParticleCollection( pDef->m_Children[i].m_Name, -m_flCurTime + pDef->m_Children[i].m_flDelay, nRandomSeed );
  1014. }
  1015. else
  1016. {
  1017. pChild = g_pParticleSystemMgr->CreateParticleCollection( pDef->m_Children[i].m_Id, -m_flCurTime + pDef->m_Children[i].m_flDelay, nRandomSeed );
  1018. }
  1019. if ( pChild )
  1020. {
  1021. pChild->m_pParent = this;
  1022. m_Children.AddToTail( pChild );
  1023. m_nControlPointReadMask |= pChild->m_nControlPointReadMask;
  1024. m_nControlPointNonPositionalMask |= pChild->m_nControlPointNonPositionalMask;
  1025. if ( pDef->m_Children[i].m_bEndCap )
  1026. {
  1027. pChild->m_flCurTime = -FLT_MAX;
  1028. }
  1029. }
  1030. }
  1031. if ( !IsValid() )
  1032. return;
  1033. m_bIsTranslucent = ComputeIsTranslucent();
  1034. m_bIsTwoPass = ComputeIsTwoPass();
  1035. m_bIsBatchable = ComputeIsBatchable();
  1036. m_bIsOrderImportant = ComputeIsOrderImportant();
  1037. m_bRunForParentApplyKillList = ComputeRunForParentApplyKillList();
  1038. LabelTextureUsage();
  1039. m_bAnyUsesPowerOfTwoFrameBufferTexture = ComputeUsesPowerOfTwoFrameBufferTexture();
  1040. m_bAnyUsesFullFrameBufferTexture = ComputeUsesFullFrameBufferTexture();
  1041. m_bInEndCap = false;
  1042. // now, allocate the control point data.
  1043. // we will always allocate on extra, so that if someone sets a control point that the particle system doesn't
  1044. // use, then there will be a dummy one to write to.
  1045. int nHighest;
  1046. for( nHighest = 63; nHighest > 0 ; nHighest-- )
  1047. {
  1048. if ( m_nControlPointReadMask & ( 1ll << nHighest ) )
  1049. {
  1050. break;
  1051. }
  1052. }
  1053. m_nNumControlPointsAllocated = MIN( MAX_PARTICLE_CONTROL_POINTS, 2 + nHighest );
  1054. //Warning( " save %d bytes by only allocating %d cp's\n", ( 64 - m_nNumControlPointsAllocated ) * sizeof( CParticleCPInfo ), m_nNumControlPointsAllocated );
  1055. m_pCPInfo = new CParticleCPInfo[ m_nNumControlPointsAllocated];
  1056. // align all control point orientations with the global world
  1057. Plat_FastMemset( m_pCPInfo, 0, sizeof( CParticleCPInfo ) * m_nNumControlPointsAllocated );
  1058. for( int i=0; i < m_nNumControlPointsAllocated; i++ )
  1059. {
  1060. ControlPoint(i).m_ForwardVector.y = 1.0f;
  1061. ControlPoint(i).m_UpVector.z = 1.0f;
  1062. ControlPoint(i).m_RightVector.x = 1.0f;
  1063. }
  1064. // now, init context data
  1065. CUtlVector<CParticleOperatorInstance *> *olists[] =
  1066. {
  1067. &(m_pDef->m_Operators), &(m_pDef->m_Renderers),
  1068. &(m_pDef->m_Initializers), &(m_pDef->m_Emitters),
  1069. &(m_pDef->m_ForceGenerators),
  1070. &(m_pDef->m_Constraints),
  1071. };
  1072. CUtlVector<size_t> *offsetlists[]=
  1073. {
  1074. &(m_pDef->m_nOperatorsCtxOffsets), &(m_pDef->m_nRenderersCtxOffsets),
  1075. &(m_pDef->m_nInitializersCtxOffsets), &(m_pDef->m_nEmittersCtxOffsets),
  1076. &(m_pDef->m_nForceGeneratorsCtxOffsets),
  1077. &(m_pDef->m_nConstraintsCtxOffsets),
  1078. };
  1079. for( int i=0; i<NELEMS( olists ); i++ )
  1080. {
  1081. int nOperatorCount = olists[i]->Count();
  1082. for( int j=0; j < nOperatorCount; j++ )
  1083. {
  1084. (*olists[i])[j]->InitializeContextData( this, m_pOperatorContextData+ (*offsetlists)[i][j] );
  1085. }
  1086. }
  1087. }
  1088. int CParticleCollection::GetCurrentParticleDefCount( CParticleSystemDefinition* pDef )
  1089. {
  1090. int nDefCount = pDef->m_nFallbackCurrentCount;
  1091. CParticleSystemDefinition *pFallback = pDef;
  1092. while ( pFallback && pFallback->HasFallback() )
  1093. {
  1094. pFallback = pFallback->GetFallbackReplacementDefinition();
  1095. if ( !pFallback )
  1096. {
  1097. break;
  1098. }
  1099. nDefCount += pFallback->m_nFallbackCurrentCount;
  1100. }
  1101. return nDefCount;
  1102. }
  1103. bool CParticleCollection::Init( CParticleSystemDefinition *pDef )
  1104. {
  1105. if ( pDef->GetMinCPULevel() > g_pParticleSystemMgr->GetParticleCPULevel() || pDef->GetMinGPULevel() > g_pParticleSystemMgr->GetParticleGPULevel() )
  1106. {
  1107. pDef = NULL;
  1108. return false;
  1109. }
  1110. float flFallbackMultiplier = g_pParticleSystemMgr->GetFallbackMultiplier();
  1111. float flFallbackBase = g_pParticleSystemMgr->GetFallbackBase();
  1112. float flThresholdSimMS = g_pParticleSystemMgr->GetSimFallbackThresholdMs();
  1113. int nFallbackCount = GetCurrentParticleDefCount( pDef );
  1114. // Determine if we've gone past our maximum allowable time for simulating particles.
  1115. // NOTE: If particle simulation starts overlapping client operations, then we'll need to
  1116. // make setting and querying of sim duration threadsafe.
  1117. float flPreviousSimMS = g_pParticleSystemMgr->GetLastSimulationDuration() * 1000.0f;
  1118. if ( flPreviousSimMS > flThresholdSimMS )
  1119. {
  1120. float flMSOver = flPreviousSimMS - flThresholdSimMS;
  1121. float flSimFallbackBaseMultiplier = g_pParticleSystemMgr->GetSimFallbackBaseMultiplier();
  1122. // Increase the fallback base by a factor of r_particle_sim_fallback_base_multiplier
  1123. // for each millisecond we're over the threshold
  1124. flFallbackBase += flMSOver * flSimFallbackBaseMultiplier;
  1125. // Uncomment to spew when we're trying to fall back because sim time took too long
  1126. //Warning( "Particle sim took too long: %f, threshold %f\n", flPreviousSimMS, flThresholdSimMS );
  1127. }
  1128. // If our maximum number of simultaneous definitions has been exceeded fallback to the appropriate def
  1129. while ( pDef && pDef->HasFallback() &&
  1130. ( ( nFallbackCount * flFallbackMultiplier ) + flFallbackBase ) >= pDef->m_nFallbackMaxCount )
  1131. {
  1132. nFallbackCount -= pDef->m_nFallbackCurrentCount;
  1133. pDef = pDef->GetFallbackReplacementDefinition();
  1134. }
  1135. if ( !pDef ) // || !pDef->IsPrecached() )
  1136. {
  1137. Warning( "Particlelib: Missing precache for particle system type \"%s\"!\n", pDef ? pDef->GetName() : "unknown" );
  1138. CParticleSystemDefinition *pErrorDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
  1139. if ( pErrorDef )
  1140. {
  1141. pDef = pErrorDef;
  1142. }
  1143. }
  1144. Init( pDef, 0.0f, 0 );
  1145. return IsValid();
  1146. }
  1147. bool CParticleCollection::Init( const char *pParticleSystemName )
  1148. {
  1149. if ( !pParticleSystemName )
  1150. return false;
  1151. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName );
  1152. if ( !pDef )
  1153. {
  1154. Warning( "Attempted to create unknown particle system type \"%s\"!\n", pParticleSystemName );
  1155. return false;
  1156. }
  1157. return Init( pDef );
  1158. }
  1159. bool CParticleCollection::IsFullyValid( void ) const
  1160. {
  1161. if ( m_pDef.GetObject() == NULL )
  1162. return false;
  1163. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1164. {
  1165. if ( !p->IsFullyValid() )
  1166. return false;
  1167. }
  1168. return true;
  1169. }
  1170. bool CParticleCollection::DependsOnSystem( const char *pName ) const
  1171. {
  1172. if ( m_pDef.GetObject() == NULL )
  1173. return false;
  1174. if ( m_pDef->m_Name == pName )
  1175. return true;
  1176. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1177. {
  1178. if ( p->DependsOnSystem(pName) )
  1179. return true;
  1180. }
  1181. return false;
  1182. }
  1183. //-----------------------------------------------------------------------------
  1184. // List management for collections sharing the same particle definition
  1185. //-----------------------------------------------------------------------------
  1186. void CParticleCollection::LinkIntoDefList( )
  1187. {
  1188. Assert( !m_pPrevDef && !m_pNextDef );
  1189. ++( m_pDef->m_nFallbackCurrentCount );
  1190. m_pPrevDef = NULL;
  1191. m_pNextDef = m_pDef->m_pFirstCollection;
  1192. m_pDef->m_pFirstCollection = this;
  1193. if ( m_pNextDef )
  1194. {
  1195. m_pNextDef->m_pPrevDef = this;
  1196. }
  1197. #ifdef _DEBUG
  1198. CParticleCollection *pCollection = m_pDef->FirstCollection();
  1199. while ( pCollection )
  1200. {
  1201. Assert( pCollection->m_pDef == m_pDef );
  1202. pCollection = pCollection->GetNextCollectionUsingSameDef();
  1203. }
  1204. #endif
  1205. }
  1206. void CParticleCollection::UnlinkFromDefList( )
  1207. {
  1208. if ( !m_pDef )
  1209. return;
  1210. --( m_pDef->m_nFallbackCurrentCount );
  1211. if ( m_pDef->m_pFirstCollection == this )
  1212. {
  1213. m_pDef->m_pFirstCollection = m_pNextDef;
  1214. Assert( !m_pPrevDef );
  1215. }
  1216. else
  1217. {
  1218. Assert( m_pPrevDef );
  1219. m_pPrevDef->m_pNextDef = m_pNextDef;
  1220. }
  1221. if ( m_pNextDef )
  1222. {
  1223. m_pNextDef->m_pPrevDef = m_pPrevDef;
  1224. }
  1225. m_pNextDef = m_pPrevDef = NULL;
  1226. #ifdef _DEBUG
  1227. CParticleCollection *pCollection = m_pDef->FirstCollection();
  1228. while ( pCollection )
  1229. {
  1230. Assert( pCollection->m_pDef == m_pDef );
  1231. pCollection = pCollection->GetNextCollectionUsingSameDef();
  1232. }
  1233. #endif
  1234. m_pDef = NULL;
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // Reset the particle cache if the frame has changed
  1238. //-----------------------------------------------------------------------------
  1239. void CParticleCollection::ResetParticleCache()
  1240. {
  1241. if ( m_pCachedParticleBatches )
  1242. {
  1243. uint32 nCurrentFrame = g_pMaterialSystem->GetCurrentFrameCount();
  1244. if ( m_pCachedParticleBatches->m_nLastValidParticleCacheFrame != nCurrentFrame )
  1245. {
  1246. m_pCachedParticleBatches->ClearBatches();
  1247. m_pCachedParticleBatches->m_nLastValidParticleCacheFrame = nCurrentFrame;
  1248. }
  1249. }
  1250. }
  1251. //-----------------------------------------------------------------------------
  1252. // Get the cached particle batches for this particular particle collection
  1253. //-----------------------------------------------------------------------------
  1254. CCachedParticleBatches *CParticleCollection::GetCachedParticleBatches()
  1255. {
  1256. if ( !m_pCachedParticleBatches )
  1257. {
  1258. m_pCachedParticleBatches = new CCachedParticleBatches();
  1259. }
  1260. return m_pCachedParticleBatches;
  1261. }
  1262. //-----------------------------------------------------------------------------
  1263. // Particle memory initialization
  1264. //-----------------------------------------------------------------------------
  1265. void CParticleCollection::InitStorage( CParticleSystemDefinition *pDef )
  1266. {
  1267. Assert( pDef->m_nMaxParticles < 65536 );
  1268. m_nMaxAllowedParticles = MIN( MAX_PARTICLES_IN_A_SYSTEM, pDef->m_nMaxParticles );
  1269. m_nAllocatedParticles = 4 + 4 * ( ( m_nMaxAllowedParticles + 3 ) / 4 );
  1270. int nConstantMemorySize = 3 * 4 * MAX_PARTICLE_ATTRIBUTES * sizeof(float) + 16;
  1271. // Align allocation for constant attributes to 16 byte boundaries
  1272. m_pConstantMemory = (uint8 * ) MemAlloc_AllocAligned( nConstantMemorySize, 16 );
  1273. m_pConstantAttributes = ( float * ) m_pConstantMemory;
  1274. m_nPerParticleInitializedAttributeMask = pDef->m_nPerParticleInitializedAttributeMask;
  1275. m_nPerParticleUpdatedAttributeMask = pDef->m_nPerParticleUpdatedAttributeMask;
  1276. // Only worry about initial attributes that are per-particle *and* are updated at a later time
  1277. // If they aren't updated at a later time, then we can just point the initial + current pointers at the same memory
  1278. m_nPerParticleReadInitialAttributeMask = pDef->m_nInitialAttributeReadMask &
  1279. ( pDef->m_nPerParticleInitializedAttributeMask & pDef->m_nPerParticleUpdatedAttributeMask );
  1280. // This is the mask of attributes which are initialized per-particle, but never updated
  1281. // *and* where operators want to read initial particle state
  1282. int nPerParticleReadConstantAttributeMask = pDef->m_nInitialAttributeReadMask &
  1283. ( pDef->m_nPerParticleInitializedAttributeMask & ( ~pDef->m_nPerParticleUpdatedAttributeMask ) );
  1284. int sz = 0;
  1285. int nInitialAttributeSize = 0;
  1286. int nPerParticleAttributeMask = m_nPerParticleInitializedAttributeMask | m_nPerParticleUpdatedAttributeMask;
  1287. for( int bit = 0; bit < MAX_PARTICLE_ATTRIBUTES; bit++ )
  1288. {
  1289. int nAttrSize = ( ( 1 << bit ) & ATTRIBUTES_WHICH_ARE_VEC3S_MASK ) ? 3 : 1;
  1290. if ( nPerParticleAttributeMask & ( 1 << bit ) )
  1291. {
  1292. sz += nAttrSize;
  1293. }
  1294. if ( m_nPerParticleReadInitialAttributeMask & ( 1 << bit ) )
  1295. {
  1296. nInitialAttributeSize += nAttrSize;
  1297. }
  1298. }
  1299. // Gotta allocate a couple extra floats to account for padding
  1300. int nAllocationSize = m_nAllocatedParticles * sz * sizeof(float) + sizeof( FourVectors );
  1301. m_pParticleMemory = ( uint8 * ) MemAlloc_AllocAligned( nAllocationSize, 16 );
  1302. m_nAttributeMemorySize = nAllocationSize;
  1303. Plat_FastMemset( m_pParticleMemory, 0, nAllocationSize );
  1304. // Allocate space for the initial attributes
  1305. if ( nInitialAttributeSize != 0 )
  1306. {
  1307. int nInitialAllocationSize = m_nAllocatedParticles * nInitialAttributeSize * sizeof(float) + sizeof( FourVectors );
  1308. m_pParticleInitialMemory = ( uint8 * ) MemAlloc_AllocAligned( nInitialAllocationSize, 16 );
  1309. Plat_FastMemset( m_pParticleInitialMemory, 0, nInitialAllocationSize );
  1310. }
  1311. // Align allocation to 16-byte boundaries
  1312. float *pMem = ( float* ) m_pParticleMemory;
  1313. float *pInitialMem = ( float* )( m_pParticleInitialMemory );
  1314. // Point each attribute to memory associated with that attribute
  1315. for( int bit = 0; bit < MAX_PARTICLE_ATTRIBUTES; bit++ )
  1316. {
  1317. int nAttrSize = ( ( 1 << bit ) & ATTRIBUTES_WHICH_ARE_VEC3S_MASK ) ? 3 : 1;
  1318. if ( nPerParticleAttributeMask & ( 1 << bit ) )
  1319. {
  1320. m_ParticleAttributes.m_pAttributes[ bit ] = pMem;
  1321. m_ParticleAttributes.m_nFloatStrides[ bit ] = nAttrSize * 4;
  1322. pMem += nAttrSize * m_nAllocatedParticles;
  1323. }
  1324. else
  1325. {
  1326. m_ParticleAttributes.m_pAttributes[ bit ] = GetConstantAttributeMemory( bit );
  1327. m_ParticleAttributes.m_nFloatStrides[ bit ] = 0;
  1328. }
  1329. // Are we reading
  1330. if ( pDef->m_nInitialAttributeReadMask & ( 1 << bit ) )
  1331. {
  1332. if ( m_nPerParticleReadInitialAttributeMask & ( 1 << bit ) )
  1333. {
  1334. Assert( pInitialMem );
  1335. m_ParticleInitialAttributes.m_pAttributes[ bit ] = pInitialMem;
  1336. m_ParticleInitialAttributes.m_nFloatStrides[ bit ] = nAttrSize * 4;
  1337. pInitialMem += nAttrSize * m_nAllocatedParticles;
  1338. }
  1339. else if ( nPerParticleReadConstantAttributeMask & ( 1 << bit ) )
  1340. {
  1341. m_ParticleInitialAttributes.m_pAttributes[ bit ] = m_ParticleAttributes.m_pAttributes[ bit ];
  1342. m_ParticleInitialAttributes.m_nFloatStrides[ bit ] = m_ParticleAttributes.m_nFloatStrides[ bit ];
  1343. }
  1344. else
  1345. {
  1346. m_ParticleInitialAttributes.m_pAttributes[ bit ] = GetConstantAttributeMemory( bit );
  1347. m_ParticleInitialAttributes.m_nFloatStrides[ bit ] = 0;
  1348. }
  1349. }
  1350. else
  1351. {
  1352. // Catch errors where code is reading data it didn't request
  1353. m_ParticleInitialAttributes.m_pAttributes[ bit ] = NULL;
  1354. m_ParticleInitialAttributes.m_nFloatStrides[ bit ] = 0;
  1355. }
  1356. }
  1357. }
  1358. //-----------------------------------------------------------------------------
  1359. // Returns the particle collection name
  1360. //-----------------------------------------------------------------------------
  1361. const char *CParticleCollection::GetName() const
  1362. {
  1363. return m_pDef ? m_pDef->GetName() : "";
  1364. }
  1365. //-----------------------------------------------------------------------------
  1366. // Does the particle system use the frame buffer texture (refraction?)
  1367. //-----------------------------------------------------------------------------
  1368. bool CParticleCollection::UsesPowerOfTwoFrameBufferTexture( bool bThisFrame ) const
  1369. {
  1370. if ( ! m_bAnyUsesPowerOfTwoFrameBufferTexture ) // quick out if neither us or our children ever use
  1371. {
  1372. return false;
  1373. }
  1374. if ( bThisFrame )
  1375. {
  1376. return SystemContainsParticlesWithBoolSet( &CParticleCollection::m_bUsesPowerOfTwoFrameBufferTexture );
  1377. }
  1378. return true;
  1379. }
  1380. //-----------------------------------------------------------------------------
  1381. // Does the particle system use the full frame buffer texture (soft particles)
  1382. //-----------------------------------------------------------------------------
  1383. bool CParticleCollection::UsesFullFrameBufferTexture( bool bThisFrame ) const
  1384. {
  1385. if ( ! m_bAnyUsesFullFrameBufferTexture ) // quick out if neither us or our children ever use
  1386. {
  1387. return false;
  1388. }
  1389. if ( bThisFrame )
  1390. {
  1391. return SystemContainsParticlesWithBoolSet( &CParticleCollection::m_bUsesFullFrameBufferTexture );
  1392. }
  1393. return true;
  1394. }
  1395. bool CParticleCollection::SystemContainsParticlesWithBoolSet( bool CParticleCollection::*pField ) const
  1396. {
  1397. if ( m_nActiveParticles && ( this->*pField ) )
  1398. return true;
  1399. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1400. {
  1401. if ( p->SystemContainsParticlesWithBoolSet( pField ) )
  1402. return true;
  1403. }
  1404. return false;
  1405. }
  1406. void CParticleCollection::LabelTextureUsage( void )
  1407. {
  1408. if ( m_pDef )
  1409. {
  1410. m_bUsesPowerOfTwoFrameBufferTexture = m_pDef->UsesPowerOfTwoFrameBufferTexture();
  1411. m_bUsesFullFrameBufferTexture = m_pDef->UsesFullFrameBufferTexture();
  1412. }
  1413. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1414. {
  1415. p->LabelTextureUsage();
  1416. }
  1417. }
  1418. bool CParticleCollection::ComputeUsesPowerOfTwoFrameBufferTexture()
  1419. {
  1420. if ( !m_pDef )
  1421. return false;
  1422. if ( m_pDef->UsesPowerOfTwoFrameBufferTexture() )
  1423. return true;
  1424. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1425. {
  1426. if ( p->UsesPowerOfTwoFrameBufferTexture( false ) )
  1427. return true;
  1428. }
  1429. return false;
  1430. }
  1431. bool CParticleCollection::ComputeUsesFullFrameBufferTexture()
  1432. {
  1433. if ( !m_pDef )
  1434. return false;
  1435. if ( m_pDef->UsesFullFrameBufferTexture() )
  1436. return true;
  1437. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1438. {
  1439. if ( p->UsesFullFrameBufferTexture( false ) )
  1440. return true;
  1441. }
  1442. return false;
  1443. }
  1444. //-----------------------------------------------------------------------------
  1445. // Is the particle system two-pass?
  1446. //-----------------------------------------------------------------------------
  1447. bool CParticleCollection::ContainsOpaqueCollections()
  1448. {
  1449. if ( !m_pDef )
  1450. return false;
  1451. IMaterial *pMaterial = m_pDef->GetMaterial();
  1452. if ( pMaterial && ( !m_pDef->GetMaterial()->IsTranslucent() ) )
  1453. return true;
  1454. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1455. {
  1456. if ( p->ContainsOpaqueCollections( ) )
  1457. return true;
  1458. }
  1459. return false;
  1460. }
  1461. //-----------------------------------------------------------------------------
  1462. // Is the particle system two-pass?
  1463. //-----------------------------------------------------------------------------
  1464. bool CParticleCollection::IsTwoPass() const
  1465. {
  1466. return m_bIsTwoPass;
  1467. }
  1468. bool CParticleCollection::ComputeIsTwoPass()
  1469. {
  1470. if ( !ComputeIsTranslucent() )
  1471. return false;
  1472. return ContainsOpaqueCollections();
  1473. }
  1474. //-----------------------------------------------------------------------------
  1475. // Is the particle system translucent
  1476. //-----------------------------------------------------------------------------
  1477. bool CParticleCollection::IsTranslucent() const
  1478. {
  1479. return m_bIsTranslucent;
  1480. }
  1481. bool CParticleCollection::ComputeIsTranslucent()
  1482. {
  1483. if ( !m_pDef )
  1484. return false;
  1485. IMaterial *pMaterial = m_pDef->GetMaterial();
  1486. if ( pMaterial && ( m_pDef->GetMaterial()->IsTranslucent() ) )
  1487. return true;
  1488. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1489. {
  1490. if ( p->IsTranslucent( ) )
  1491. return true;
  1492. }
  1493. return false;
  1494. }
  1495. //-----------------------------------------------------------------------------
  1496. // Is the particle system batchable
  1497. //-----------------------------------------------------------------------------
  1498. bool CParticleCollection::IsBatchable() const
  1499. {
  1500. return m_bIsBatchable;
  1501. }
  1502. bool CParticleCollection::ComputeIsBatchable()
  1503. {
  1504. int nRendererCount = GetRendererCount();
  1505. for( int i = 0; i < nRendererCount; i++ )
  1506. {
  1507. if ( !GetRenderer( i )->IsBatchable() )
  1508. return false;
  1509. }
  1510. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1511. {
  1512. if ( !p->IsBatchable() )
  1513. return false;
  1514. }
  1515. return true;
  1516. }
  1517. //-----------------------------------------------------------------------------
  1518. // Is the order of the particles important
  1519. //-----------------------------------------------------------------------------
  1520. bool CParticleCollection::IsOrderImportant() const
  1521. {
  1522. return m_bIsOrderImportant;
  1523. }
  1524. bool CParticleCollection::ComputeIsOrderImportant()
  1525. {
  1526. int nRendererCount = GetRendererCount();
  1527. for( int i = 0; i < nRendererCount; i++ )
  1528. {
  1529. if ( GetRenderer( i )->IsOrderImportant() )
  1530. return true;
  1531. }
  1532. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1533. {
  1534. if ( p->IsOrderImportant() )
  1535. return true;
  1536. }
  1537. return false;
  1538. }
  1539. //-----------------------------------------------------------------------------
  1540. // Does this system want to run inside its parent's ApplyKillList?
  1541. //-----------------------------------------------------------------------------
  1542. bool CParticleCollection::ShouldRunForParentApplyKillList() const
  1543. {
  1544. return m_bRunForParentApplyKillList;
  1545. }
  1546. bool CParticleCollection::ComputeRunForParentApplyKillList()
  1547. {
  1548. // Only run the system during ApplyKillList if an emitter operator wants to run then
  1549. // (initializers may then run subsequently, but they won't without an emitter!)
  1550. bool bApplyingKillList = true;
  1551. int nEmitterCount = m_pDef->m_Emitters.Count();
  1552. for( int i = 0; i < nEmitterCount; i++ )
  1553. {
  1554. if ( m_pDef->m_Emitters[i]->ShouldRun( bApplyingKillList ) )
  1555. return true;
  1556. }
  1557. return false;
  1558. }
  1559. //-----------------------------------------------------------------------------
  1560. // Renderer iteration
  1561. //-----------------------------------------------------------------------------
  1562. int CParticleCollection::GetRendererCount() const
  1563. {
  1564. return IsValid() ? m_pDef->m_Renderers.Count() : 0;
  1565. }
  1566. CParticleOperatorInstance *CParticleCollection::GetRenderer( int i )
  1567. {
  1568. return IsValid() ? m_pDef->m_Renderers[i] : NULL;
  1569. }
  1570. void *CParticleCollection::GetRendererContext( int i )
  1571. {
  1572. return IsValid() ? m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i] : NULL;
  1573. }
  1574. //-----------------------------------------------------------------------------
  1575. // Visualize operators (for editing/debugging)
  1576. //-----------------------------------------------------------------------------
  1577. void CParticleCollection::VisualizeOperator( const DmObjectId_t *pOpId )
  1578. {
  1579. m_pRenderOp = NULL;
  1580. if ( !pOpId || !m_pDef )
  1581. return;
  1582. m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_EMITTER, *pOpId );
  1583. if ( !m_pRenderOp )
  1584. {
  1585. m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_INITIALIZER, *pOpId );
  1586. if ( !m_pRenderOp )
  1587. {
  1588. m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_OPERATOR, *pOpId );
  1589. }
  1590. }
  1591. }
  1592. float FadeInOut( float flFadeInStart, float flFadeInEnd, float flFadeOutStart, float flFadeOutEnd, float flCurTime )
  1593. {
  1594. if ( flFadeInStart > flCurTime ) // started yet?
  1595. return 0.0;
  1596. if ( ( flFadeOutEnd > 0. ) && ( flFadeOutEnd < flCurTime ) ) // timed out?
  1597. return 0.;
  1598. // handle out of order cases
  1599. flFadeInEnd = MAX( flFadeInEnd, flFadeInStart );
  1600. flFadeOutStart = MAX( flFadeOutStart, flFadeInEnd );
  1601. flFadeOutEnd = MAX( flFadeOutEnd, flFadeOutStart );
  1602. float flStrength = 1.0;
  1603. if (
  1604. ( flFadeInEnd > flCurTime ) &&
  1605. ( flFadeInEnd > flFadeInStart ) )
  1606. flStrength = MIN( flStrength, FLerp( 0, 1, flFadeInStart, flFadeInEnd, flCurTime ) );
  1607. if ( ( flCurTime > flFadeOutStart) &&
  1608. ( flFadeOutEnd > flFadeOutStart) )
  1609. flStrength = MIN( flStrength, FLerp( 0, 1, flFadeOutEnd, flFadeOutStart, flCurTime ) );
  1610. return flStrength;
  1611. }
  1612. bool CParticleCollection::CheckIfOperatorShouldRun(
  1613. CParticleOperatorInstance const * pOp ,
  1614. float *pflCurStrength,
  1615. bool bApplyingParentKillList )
  1616. {
  1617. if ( !pOp->ShouldRun( bApplyingParentKillList ) )
  1618. return false;
  1619. if ( pOp->m_bStrengthFastPath )
  1620. {
  1621. *pflCurStrength = 1.0;
  1622. return true;
  1623. }
  1624. if ( pOp->m_nOpEndCapState != -1 )
  1625. {
  1626. if ( m_bInEndCap != ( pOp->m_nOpEndCapState == 1 ) )
  1627. return false;
  1628. }
  1629. float flTime=m_flCurTime;
  1630. if ( pOp->m_nOpTimeOffsetSeed ) // allow per-instance-of-particle-system random phase control for operator strength.
  1631. {
  1632. float flOffset = RandomFloat( pOp->m_nOpTimeOffsetSeed, pOp->m_flOpTimeOffsetMin, pOp->m_flOpTimeOffsetMax );
  1633. flTime += flOffset;
  1634. flTime = MAX( 0.0, flTime );
  1635. }
  1636. if ( pOp->m_nOpTimeScaleSeed && ( flTime > pOp->m_flOpStartFadeInTime ) )
  1637. {
  1638. float flTimeScalar = 1.0 / MAX( .0001, RandomFloat( pOp->m_nOpTimeScaleSeed, pOp->m_flOpTimeScaleMin, pOp->m_flOpTimeScaleMax ) );
  1639. flTime = pOp->m_flOpStartFadeInTime + flTimeScalar * ( flTime - pOp->m_flOpStartFadeInTime );
  1640. }
  1641. if ( pOp->m_flOpFadeOscillatePeriod > 0.0 )
  1642. {
  1643. flTime = fmod( m_flCurTime*( 1.0/pOp->m_flOpFadeOscillatePeriod ), 1.0 );
  1644. }
  1645. float flStrength = FadeInOut( pOp->m_flOpStartFadeInTime, pOp->m_flOpEndFadeInTime,
  1646. pOp->m_flOpStartFadeOutTime, pOp->m_flOpEndFadeOutTime,
  1647. flTime );
  1648. if ( pOp->m_nOpStrengthScaleSeed )
  1649. {
  1650. float flStrengthMultiplier = RandomFloat( pOp->m_nOpStrengthScaleSeed, pOp->m_flOpStrengthMinScale, pOp->m_flOpStrengthMaxScale );
  1651. flStrength *= MAX( 0., flStrength * flStrengthMultiplier );
  1652. }
  1653. *pflCurStrength = flStrength;
  1654. return ( flStrength > 0.0 );
  1655. }
  1656. void CParticleOperatorInstance::CheckForFastPath( void )
  1657. {
  1658. // store away whether this operator has any of the operator modulation params set (most ops dont)
  1659. if (
  1660. ( m_flOpStartFadeInTime == 0. ) &&
  1661. ( m_flOpEndFadeOutTime == 0. ) &&
  1662. ( m_flOpStartFadeOutTime == 0. ) &&
  1663. ( m_flOpEndFadeOutTime == 0. ) &&
  1664. ( m_flOpTimeOffsetMin == 0 ) &&
  1665. ( m_flOpTimeOffsetMax == 0 ) &&
  1666. ( m_flOpTimeScaleMin == 1 ) &&
  1667. ( m_flOpTimeScaleMax == 1 ) &&
  1668. ( m_flOpStrengthMaxScale == 1 ) &&
  1669. ( m_flOpStrengthMinScale == 1 ) &&
  1670. ( m_nOpEndCapState == -1 ) )
  1671. {
  1672. m_bStrengthFastPath = true;
  1673. }
  1674. else
  1675. {
  1676. m_bStrengthFastPath = false;
  1677. }
  1678. }
  1679. bool CParticleOperatorInstance::HasAttribute( CParticleCollection *pParticles, int nAttribute ) const
  1680. {
  1681. return ( pParticles->m_ParticleAttributes.Stride( nAttribute ) > 0 );
  1682. }
  1683. KillListItem_t *CParticleOperatorInstance::GetParentKillList( CParticleCollection *pParticles, int &nNumParticlesToKill ) const
  1684. {
  1685. if ( pParticles->m_pParent )
  1686. {
  1687. nNumParticlesToKill = pParticles->m_pParent->m_nNumParticlesToKill;
  1688. return pParticles->m_pParent->m_pParticleKillList;
  1689. }
  1690. nNumParticlesToKill = 0;
  1691. return NULL;
  1692. }
  1693. #ifdef NDEBUG
  1694. #define CHECKSYSTEM( p ) 0
  1695. #else
  1696. static void CHECKSYSTEM( CParticleCollection *pParticles )
  1697. {
  1698. // Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
  1699. for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
  1700. {
  1701. const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
  1702. const float *rad = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_RADIUS, i );
  1703. const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
  1704. Assert( IsFinite( rad[0] ) );
  1705. Assert( IsFinite( xyz[0] ) );
  1706. Assert( IsFinite( xyz[4] ) );
  1707. Assert( IsFinite( xyz[8] ) );
  1708. Assert( IsFinite( xyz_prev[0] ) );
  1709. Assert( IsFinite( xyz_prev[4] ) );
  1710. Assert( IsFinite( xyz_prev[8] ) );
  1711. }
  1712. }
  1713. #endif
  1714. void CParticleCollection::RunRestartedEmitters( void )
  1715. {
  1716. // run all emitters once that want to respond to a restart
  1717. if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME ) // in case we aggregated twice before _any_ sim
  1718. {
  1719. SimulateFirstFrame();
  1720. m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
  1721. }
  1722. else
  1723. {
  1724. UpdatePrevControlPoints( m_flPreviousDt ); // make sure control points are virgin
  1725. }
  1726. int nEmitterCount = m_pDef->m_Emitters.Count();
  1727. for( int i=0; i < nEmitterCount; i++ )
  1728. {
  1729. int nOldParticleCount = m_nActiveParticles;
  1730. float flEmitStrength = 0;
  1731. if ( CheckIfOperatorShouldRun( m_pDef->m_Emitters[i], &flEmitStrength ) )
  1732. {
  1733. uint32 nInittedMask = m_pDef->m_Emitters[i]->Emit(
  1734. this, flEmitStrength,
  1735. m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
  1736. if ( nOldParticleCount != m_nActiveParticles )
  1737. {
  1738. // init newly emitted particles
  1739. InitializeNewParticles( nOldParticleCount, m_nActiveParticles - nOldParticleCount, nInittedMask );
  1740. CHECKSYSTEM( this );
  1741. }
  1742. }
  1743. }
  1744. for( CParticleCollection *pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext )
  1745. pChild->RunRestartedEmitters();
  1746. }
  1747. // rj: this may not be the correct thing to do, to set all of the children to the same renderable. particles_new may need to set the renderable to themselves instead.
  1748. void CParticleCollection::SetRenderable( void *pRenderable )
  1749. {
  1750. m_pRenderable = pRenderable;
  1751. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1752. {
  1753. p->SetRenderable( pRenderable );
  1754. }
  1755. }
  1756. //-----------------------------------------------------------------------------
  1757. // Restarts a particle system
  1758. //-----------------------------------------------------------------------------
  1759. void CParticleCollection::Restart( EParticleRestartMode_t eMode )
  1760. {
  1761. // Always reset the framecount for tick rates - this needs to be reset so that systems which are framerate dependent get updated frame counts
  1762. m_nDrawnFrames = 0;
  1763. // if we already have a pending restart, process it now
  1764. if ( m_bPendingRestart )
  1765. {
  1766. RunRestartedEmitters();
  1767. m_bPendingRestart = false;
  1768. }
  1769. if ( eMode == RESTART_RESET_AND_MAKE_SURE_EMITS_HAPPEN )
  1770. {
  1771. m_bPendingRestart = true;
  1772. m_nParticleFlags &= ~PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED;
  1773. }
  1774. int nEmitterCount = m_pDef->m_Emitters.Count();
  1775. for( int i = 0; i < nEmitterCount; i++ )
  1776. {
  1777. m_pDef->m_Emitters[i]->Restart( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
  1778. }
  1779. int nInitializerCount = m_pDef->m_Initializers.Count();
  1780. for( int i = 0; i < nInitializerCount; i++ )
  1781. {
  1782. m_pDef->m_Initializers[i]->Restart( this, m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i] );
  1783. }
  1784. // Update all children
  1785. for( CParticleCollection *pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext )
  1786. {
  1787. // Remove any delays from the time (otherwise we're offset by it oddly)
  1788. pChild->Restart( eMode );
  1789. }
  1790. }
  1791. //-----------------------------------------------------------------------------
  1792. // Main entry point for rendering
  1793. //-----------------------------------------------------------------------------
  1794. void CParticleCollection::Render( int nViewRecursionLevel, IMatRenderContext *pRenderContext, const Vector4D &vecDiffuseModulation, bool bTranslucentOnly, void *pCameraObject )
  1795. {
  1796. if ( !IsValid() )
  1797. return;
  1798. if ( !m_Sheet() && !m_bTriedLoadingSheet )
  1799. {
  1800. m_bTriedLoadingSheet = true;
  1801. m_Sheet.Set( g_pParticleSystemMgr->FindOrLoadSheet( m_pDef, true ) );
  1802. }
  1803. m_flNextSleepTime = MAX( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep ));
  1804. if ( m_nActiveParticles != 0 )
  1805. {
  1806. Vector4D vecActualModulation;
  1807. Vector4DMultiply( vecDiffuseModulation, m_pDef->m_vecMaterialModulation, vecActualModulation );
  1808. IMaterial *pMaterial = m_pDef->GetMaterial();
  1809. if ( pMaterial &&
  1810. ( !bTranslucentOnly || m_pDef->GetMaterial()->IsTranslucent() || ( vecActualModulation[3] != 1.0f ) ) )
  1811. {
  1812. #if MEASURE_PARTICLE_PERF
  1813. double flSTime = Plat_FloatTime();
  1814. #endif
  1815. int nCount = m_pDef->m_Renderers.Count();
  1816. for( int i = 0; i < nCount; i++ )
  1817. {
  1818. float flStrength;
  1819. if ( !CheckIfOperatorShouldRun( m_pDef->m_Renderers[i], &flStrength ) )
  1820. continue;
  1821. if ( m_pDef->IsScreenSpaceEffect() )
  1822. {
  1823. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1824. pRenderContext->PushMatrix();
  1825. pRenderContext->LoadIdentity();
  1826. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1827. pRenderContext->PushMatrix();
  1828. pRenderContext->LoadIdentity();
  1829. pRenderContext->Ortho( -100, -100, 100, 100, -100, 100 );
  1830. m_pDef->m_Renderers[i]->Render(
  1831. pRenderContext, this, vecActualModulation, m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i], nViewRecursionLevel );
  1832. pRenderContext->MatrixMode( MATERIAL_VIEW );
  1833. pRenderContext->PopMatrix();
  1834. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1835. pRenderContext->PopMatrix();
  1836. }
  1837. else
  1838. {
  1839. m_pDef->m_Renderers[i]->Render(
  1840. pRenderContext, this, vecActualModulation, m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i], nViewRecursionLevel );
  1841. }
  1842. }
  1843. #if MEASURE_PARTICLE_PERF
  1844. float flETime = Plat_FloatTime() - flSTime;
  1845. m_pDef->m_flUncomittedTotalRenderTime += flETime;
  1846. m_pDef->m_flMaxMeasuredRenderTime = MAX( m_pDef->m_flMaxMeasuredRenderTime, flETime );
  1847. #endif
  1848. }
  1849. }
  1850. // let children render
  1851. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1852. {
  1853. p->Render( nViewRecursionLevel, pRenderContext, vecDiffuseModulation, bTranslucentOnly, pCameraObject );
  1854. }
  1855. // Visualize specific ops for debugging/editing
  1856. if ( m_pRenderOp )
  1857. {
  1858. m_pRenderOp->Render( this );
  1859. }
  1860. }
  1861. void CParticleCollection::UpdatePrevControlPoints( float dt )
  1862. {
  1863. m_flPreviousDt = dt;
  1864. for(int i=0; i <= m_nHighestCP; i++ )
  1865. ControlPoint( i ).m_PrevPosition = ControlPoint( i ).m_Position;
  1866. m_nParticleFlags |= PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED;
  1867. }
  1868. #if MEASURE_PARTICLE_PERF
  1869. #if VPROF_LEVEL > 0
  1870. #define START_OP float flOpStartTime = Plat_FloatTime(); VPROF_ENTER_SCOPE(pOp->GetDefinition()->GetName())
  1871. #else
  1872. #define START_OP float flOpStartTime = Plat_FloatTime();
  1873. #endif
  1874. #if VPROF_LEVEL > 0
  1875. #define END_OP if ( 1 ) { \
  1876. float flETime = Plat_FloatTime() - flOpStartTime; \
  1877. IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->m_pDef; \
  1878. pDef->RecordExecutionTime( flETime ); \
  1879. } \
  1880. VPROF_EXIT_SCOPE()
  1881. #else
  1882. #define END_OP if ( 1 ) { \
  1883. float flETime = Plat_FloatTime() - flOpStartTime; \
  1884. IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->m_pDef; \
  1885. pDef->RecordExecutionTime( flETime ); \
  1886. }
  1887. #endif
  1888. #else
  1889. #define START_OP
  1890. #define END_OP
  1891. #endif
  1892. void CParticleCollection::InitializeNewParticles( int nFirstParticle, int nParticleCount, uint32 nInittedMask, bool bApplyingParentKillList )
  1893. {
  1894. VPROF_BUDGET( "CParticleCollection::InitializeNewParticles", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
  1895. #ifdef _DEBUG
  1896. m_bIsRunningInitializers = true;
  1897. #endif
  1898. // now, initialize the attributes of all the new particles
  1899. int nPerParticleAttributeMask = m_nPerParticleInitializedAttributeMask | m_nPerParticleUpdatedAttributeMask;
  1900. int nAttrsLeftToInit = nPerParticleAttributeMask & ~nInittedMask;
  1901. int nInitializerCount = m_pDef->m_Initializers.Count();
  1902. for ( int i = 0; i < nInitializerCount; i++ )
  1903. {
  1904. CParticleOperatorInstance *pOp = m_pDef->m_Initializers[i];
  1905. int nInitializerAttrMask = pOp->GetWrittenAttributes();
  1906. if ( ( ( nInitializerAttrMask & nAttrsLeftToInit ) == 0 ) || pOp->InitMultipleOverride() )
  1907. continue;
  1908. if ( !pOp->ShouldRun( bApplyingParentKillList ) )
  1909. continue;
  1910. void *pContext = m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i];
  1911. START_OP;
  1912. if ( m_bIsScrubbable && !pOp->IsScrubSafe() )
  1913. {
  1914. for ( int j = 0; j < nParticleCount; ++j )
  1915. {
  1916. pOp->InitNewParticles( this, nFirstParticle + j, 1, nAttrsLeftToInit, pContext );
  1917. }
  1918. }
  1919. else
  1920. {
  1921. pOp->InitNewParticles( this, nFirstParticle, nParticleCount, nAttrsLeftToInit, pContext );
  1922. }
  1923. END_OP;
  1924. nAttrsLeftToInit &= ~nInitializerAttrMask;
  1925. }
  1926. // always run second tier initializers (modifiers) after first tier - this ensures they don't get stomped.
  1927. for ( int i = 0; i < nInitializerCount; i++ )
  1928. {
  1929. int nInitializerAttrMask = m_pDef->m_Initializers[i]->GetWrittenAttributes();
  1930. CParticleOperatorInstance *pOp = m_pDef->m_Initializers[i];
  1931. if ( !pOp->InitMultipleOverride() )
  1932. continue;
  1933. if ( !pOp->ShouldRun( bApplyingParentKillList ) )
  1934. continue;
  1935. void *pContext = m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i];
  1936. START_OP;
  1937. if ( m_bIsScrubbable && !pOp->IsScrubSafe() )
  1938. {
  1939. for ( int j = 0; j < nParticleCount; ++j )
  1940. {
  1941. pOp->InitNewParticles( this, nFirstParticle + j, 1, nAttrsLeftToInit, pContext );
  1942. }
  1943. }
  1944. else
  1945. {
  1946. pOp->InitNewParticles( this, nFirstParticle, nParticleCount, nAttrsLeftToInit, pContext );
  1947. }
  1948. END_OP;
  1949. nAttrsLeftToInit &= ~nInitializerAttrMask;
  1950. }
  1951. #ifdef _DEBUG
  1952. m_bIsRunningInitializers = false;
  1953. #endif
  1954. InitParticleAttributes( nFirstParticle, nParticleCount, nAttrsLeftToInit );
  1955. CopyInitialAttributeValues( nFirstParticle, nParticleCount );
  1956. }
  1957. void CParticleCollection::SkipToTime( float t )
  1958. {
  1959. if ( t > m_flCurTime )
  1960. {
  1961. UpdatePrevControlPoints( t - m_flCurTime );
  1962. m_flCurTime = t;
  1963. m_fl4CurTime = ReplicateX4( t );
  1964. m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
  1965. // FIXME: In future, we may have to tell operators, initializers about this too
  1966. int nEmitterCount = m_pDef->m_Emitters.Count();
  1967. int i;
  1968. for( i = 0; i < nEmitterCount; i++ )
  1969. {
  1970. m_pDef->m_Emitters[i]->SkipToTime( t, this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
  1971. }
  1972. CParticleCollection *pChild;
  1973. // Update all children
  1974. for( i = 0, pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext, i++ )
  1975. {
  1976. // Remove any delays from the time (otherwise we're offset by it oddly)
  1977. pChild->SkipToTime( t - m_pDef->m_Children[i].m_flDelay );
  1978. }
  1979. }
  1980. }
  1981. void CParticleCollection::SimulateFirstFrame( )
  1982. {
  1983. m_flPrevSimTime = 1.0e23;
  1984. m_flDt = 0.0f;
  1985. m_nDrawnFrames = 0;
  1986. // For the first frame, copy over the initial control points
  1987. if ( ( m_nParticleFlags & PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED ) == 0 )
  1988. {
  1989. UpdatePrevControlPoints( 0.05f );
  1990. }
  1991. m_nOperatorRandomSampleOffset = 0;
  1992. int nCount = m_pDef->m_Operators.Count();
  1993. for( int i = 0; i < nCount; i++ )
  1994. {
  1995. float flStrength = 0;
  1996. CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
  1997. if ( pOp->ShouldRunBeforeEmitters() &&
  1998. CheckIfOperatorShouldRun( pOp, &flStrength ) )
  1999. {
  2000. pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
  2001. CHECKSYSTEM( this );
  2002. UpdatePrevControlPoints( 0.05f );
  2003. }
  2004. m_nOperatorRandomSampleOffset += 17;
  2005. }
  2006. // first, create initial particles
  2007. int nNumToCreate = MIN( m_pDef->m_nInitialParticles, m_nMaxAllowedParticles );
  2008. if ( nNumToCreate > 0 )
  2009. {
  2010. SetNActiveParticles( nNumToCreate );
  2011. InitializeNewParticles( 0, nNumToCreate, 0 );
  2012. CHECKSYSTEM( this );
  2013. }
  2014. }
  2015. void CParticleCollection::EmitAndInit( CParticleCollection *pCollection, bool bApplyingParentKillList ) // static
  2016. {
  2017. if ( bApplyingParentKillList && !pCollection->ShouldRunForParentApplyKillList() )
  2018. return;
  2019. int nEmitterCount = pCollection->m_pDef->m_Emitters.Count();
  2020. for( int i = 0; i < nEmitterCount; i++ )
  2021. {
  2022. int nOldParticleCount = pCollection->m_nActiveParticles;
  2023. float flEmitStrength = 0;
  2024. if ( pCollection->CheckIfOperatorShouldRun( pCollection->m_pDef->m_Emitters[i], &flEmitStrength, bApplyingParentKillList ) )
  2025. {
  2026. uint32 nInittedMask = pCollection->m_pDef->m_Emitters[i]->Emit(
  2027. pCollection, flEmitStrength, pCollection->m_pOperatorContextData + pCollection->m_pDef->m_nEmittersCtxOffsets[i] );
  2028. if ( nOldParticleCount != pCollection->m_nActiveParticles )
  2029. {
  2030. // init newly emitted particles
  2031. pCollection->InitializeNewParticles( nOldParticleCount, pCollection->m_nActiveParticles - nOldParticleCount, nInittedMask, bApplyingParentKillList );
  2032. CHECKSYSTEM( pCollection );
  2033. }
  2034. }
  2035. }
  2036. }
  2037. void CParticleCollection::Simulate( float dt )
  2038. {
  2039. VPROF_BUDGET( "CParticleCollection::Simulate", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
  2040. if ( ( dt < 0.0f ) || ( m_bFrozen ) )
  2041. return;
  2042. if ( !m_pDef )
  2043. return;
  2044. // Don't do anything until we've hit t == 0
  2045. // This is used for delayed children
  2046. if ( m_flCurTime < 0.0f )
  2047. {
  2048. if ( dt >= 1.0e-22 )
  2049. {
  2050. m_flCurTime += dt;
  2051. m_fl4CurTime = ReplicateX4( m_flCurTime );
  2052. UpdatePrevControlPoints( dt );
  2053. }
  2054. return;
  2055. }
  2056. // run initializers if necessary (once we hit t == 0)
  2057. if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME )
  2058. {
  2059. SimulateFirstFrame();
  2060. m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
  2061. }
  2062. else
  2063. {
  2064. // if the system has been Reset, we need to copy the control points to prev control points
  2065. if ( ! ( m_nParticleFlags & PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED ) )
  2066. UpdatePrevControlPoints( m_flPreviousDt );
  2067. }
  2068. if ( dt < 1.0e-22 )
  2069. return;
  2070. m_bPendingRestart = false;
  2071. #if MEASURE_PARTICLE_PERF
  2072. float flStartSimTime = Plat_FloatTime();
  2073. #endif
  2074. bool bAttachedKillList = false;
  2075. if ( ! HasAttachedKillList() )
  2076. {
  2077. g_pParticleSystemMgr->AttachKillList( this );
  2078. bAttachedKillList = true;
  2079. }
  2080. float flRemainingDt = dt;
  2081. float flMaxDT = 0.1; // default
  2082. if ( m_pDef->m_flMaximumTimeStep > 0.0 )
  2083. flMaxDT = m_pDef->m_flMaximumTimeStep;
  2084. // Limit timestep if needed (prevents short lived particles from being created and destroyed before being rendered.
  2085. //if ( m_pDef->m_flMaximumSimTime != 0.0 && !m_bHasDrawnOnce )
  2086. if ( m_pDef->m_flMaximumSimTime != 0.0 && ( m_nDrawnFrames <= m_pDef->m_nMinimumFrames ) )
  2087. {
  2088. if ( ( flRemainingDt + m_flCurTime ) > m_pDef->m_flMaximumSimTime )
  2089. {
  2090. //if delta+current > checkpoint then delta = checkpoint - current
  2091. flRemainingDt = m_pDef->m_flMaximumSimTime - m_flCurTime;
  2092. flRemainingDt = MAX( m_pDef->m_flMinimumSimTime, flRemainingDt );
  2093. }
  2094. m_nDrawnFrames += 1;
  2095. }
  2096. flRemainingDt = MIN( flRemainingDt, 10 * flMaxDT ); // no more than 10 passes ever
  2097. m_flTargetDrawTime += flRemainingDt;
  2098. if ( ( m_flTargetDrawTime >= m_flPrevSimTime ) && ( m_flTargetDrawTime < m_flCurTime ) )
  2099. {
  2100. // we can skip simulation
  2101. flRemainingDt = 0;
  2102. }
  2103. float flMinTime = m_pDef->m_flMinimumTimeStep;
  2104. bool bSaveOldValuesForInterpolation = false;
  2105. while( flRemainingDt > 0.0 )
  2106. {
  2107. float flDT_ThisStep = flRemainingDt;
  2108. if ( flDT_ThisStep > flMaxDT )
  2109. {
  2110. flDT_ThisStep = flMaxDT;
  2111. }
  2112. else
  2113. {
  2114. if ( flMinTime > flDT_ThisStep ) // can't do lerping if its going to take multiple steps?
  2115. {
  2116. flDT_ThisStep = flMinTime;
  2117. bSaveOldValuesForInterpolation = true;
  2118. }
  2119. }
  2120. flRemainingDt -= flDT_ThisStep;
  2121. if ( m_flDt )
  2122. m_flPreviousDt = m_flDt;
  2123. m_flDt = flDT_ThisStep;
  2124. m_flPrevSimTime = m_flCurTime;
  2125. m_flCurTime += flDT_ThisStep;
  2126. m_fl4CurTime = ReplicateX4( m_flCurTime );
  2127. // now, if we are oging to interpolate, copy the current values of all attributes away
  2128. if ( bSaveOldValuesForInterpolation )
  2129. {
  2130. // !! speed - we could copy just the active region.
  2131. if ( ! m_pPreviousAttributeMemory )
  2132. {
  2133. m_pPreviousAttributeMemory = ( uint8 * ) MemAlloc_AllocAligned( m_nAttributeMemorySize, 16 );
  2134. memset( m_pPreviousAttributeMemory, 0, m_nAttributeMemorySize );
  2135. // set up the pointers
  2136. m_PreviousFrameAttributes = m_ParticleAttributes;
  2137. for( int i = 0; i < MAX_PARTICLE_ATTRIBUTES; i++ )
  2138. {
  2139. if ( m_ParticleAttributes.Stride( i ) )
  2140. {
  2141. m_PreviousFrameAttributes.m_pAttributes[i] = ( float * )
  2142. ( GetPrevAttributeMemory() +
  2143. ( m_ParticleAttributes.ByteAddress( i ) - GetAttributeMemory() ) );
  2144. }
  2145. }
  2146. }
  2147. CopyParticleAttributesToPreviousAttributes();
  2148. }
  2149. #ifdef _DEBUG
  2150. m_bIsRunningOperators = true;
  2151. #endif
  2152. m_nOperatorRandomSampleOffset = 0;
  2153. int nCount = m_pDef->m_Operators.Count();
  2154. for( int i = 0; i < nCount; i++ )
  2155. {
  2156. float flStrength;
  2157. CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
  2158. if ( pOp->ShouldRunBeforeEmitters() &&
  2159. CheckIfOperatorShouldRun( pOp, &flStrength ) )
  2160. {
  2161. START_OP;
  2162. pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
  2163. END_OP;
  2164. CHECKSYSTEM( this );
  2165. if ( m_nNumParticlesToKill )
  2166. {
  2167. ApplyKillList();
  2168. }
  2169. m_nOperatorRandomSampleOffset += 17;
  2170. }
  2171. }
  2172. #ifdef _DEBUG
  2173. m_bIsRunningOperators = false;
  2174. #endif
  2175. // Run emitters and initializers:
  2176. EmitAndInit( this );
  2177. #ifdef _DEBUG
  2178. m_bIsRunningOperators = true;
  2179. #endif
  2180. m_nOperatorRandomSampleOffset = 0;
  2181. nCount = m_pDef->m_Operators.Count();
  2182. if ( m_nActiveParticles )
  2183. for( int i = 0; i < nCount; i++ )
  2184. {
  2185. float flStrength;
  2186. CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
  2187. if ( (! pOp->ShouldRunBeforeEmitters() ) &&
  2188. CheckIfOperatorShouldRun( pOp, &flStrength ) )
  2189. {
  2190. START_OP;
  2191. pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
  2192. END_OP;
  2193. CHECKSYSTEM( this );
  2194. if ( m_nNumParticlesToKill )
  2195. {
  2196. ApplyKillList();
  2197. if ( ! m_nActiveParticles )
  2198. break; // don't run any more operators
  2199. }
  2200. m_nOperatorRandomSampleOffset += 17;
  2201. }
  2202. }
  2203. #ifdef _DEBUG
  2204. m_bIsRunningOperators = false;
  2205. #endif
  2206. nCount = m_pDef->m_Renderers.Count();
  2207. for( int i = 0; i < nCount; i++ )
  2208. {
  2209. CParticleOperatorInstance *pOp = m_pDef->m_Renderers[i];
  2210. START_OP;
  2211. pOp->PostSimulate( this, m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i] );
  2212. END_OP;
  2213. }
  2214. }
  2215. #if MEASURE_PARTICLE_PERF
  2216. m_pDef->m_nMaximumActiveParticles = MAX( m_pDef->m_nMaximumActiveParticles, m_nActiveParticles );
  2217. float flETime = Plat_FloatTime() - flStartSimTime;
  2218. m_pDef->m_flUncomittedTotalSimTime += flETime;
  2219. m_pDef->m_flMaxMeasuredSimTime = MAX( m_pDef->m_flMaxMeasuredSimTime, flETime );
  2220. #endif
  2221. // let children simulate
  2222. for( CParticleCollection *i = m_Children.m_pHead; i; i = i->m_pNext )
  2223. {
  2224. LoanKillListTo( i ); // re-use the allocated kill list for the children
  2225. i->Simulate( dt );
  2226. i->m_pParticleKillList = NULL;
  2227. }
  2228. if ( bAttachedKillList )
  2229. g_pParticleSystemMgr->DetachKillList( this );
  2230. UpdatePrevControlPoints( dt );
  2231. // Bloat the bounding box by bounds around the control point
  2232. BloatBoundsUsingControlPoint();
  2233. // check for freezing
  2234. if ( m_pDef->m_flStopSimulationAfterTime < m_flCurTime )
  2235. {
  2236. m_bFrozen = true;
  2237. }
  2238. // FIXME: Is there a way of doing this iteratively?
  2239. // RecomputeBounds();
  2240. }
  2241. //-----------------------------------------------------------------------------
  2242. // Copies the constant attributes into the per-particle attributes
  2243. //-----------------------------------------------------------------------------
  2244. void CParticleCollection::InitParticleAttributes( int nStartParticle, int nNumParticles, int nAttrsLeftToInit )
  2245. {
  2246. if ( nAttrsLeftToInit == 0 )
  2247. return;
  2248. // !! speed!! do sse init here
  2249. for( int i = nStartParticle; i < nStartParticle + nNumParticles; i++ )
  2250. {
  2251. for ( int nAttr = 0; nAttr < MAX_PARTICLE_ATTRIBUTES; ++nAttr )
  2252. {
  2253. if ( ( nAttrsLeftToInit & ( 1 << nAttr ) ) == 0 )
  2254. continue;
  2255. float *pAttrData = GetFloatAttributePtrForWrite( nAttr, i );
  2256. // Special case for particle id
  2257. if ( nAttr == PARTICLE_ATTRIBUTE_PARTICLE_ID )
  2258. {
  2259. *( (int*)pAttrData ) = ( m_nRandomSeed + m_nUniqueParticleId ) & RANDOM_FLOAT_MASK;
  2260. m_nUniqueParticleId++;
  2261. continue;
  2262. }
  2263. // Special case for the creation time mask
  2264. if ( nAttr == PARTICLE_ATTRIBUTE_CREATION_TIME )
  2265. {
  2266. *pAttrData = m_flCurTime;
  2267. continue;
  2268. }
  2269. // If this assertion fails, it means we're writing into constant memory, which is a nono
  2270. Assert( m_ParticleAttributes.Stride( nAttr ) != 0 );
  2271. float *pConstantAttr = GetConstantAttributeMemory( nAttr );
  2272. *pAttrData = *pConstantAttr;
  2273. if ( m_ParticleAttributes.Stride( nAttr ) == 12 )
  2274. {
  2275. pAttrData[4] = pConstantAttr[4];
  2276. pAttrData[8] = pConstantAttr[8];
  2277. }
  2278. }
  2279. }
  2280. }
  2281. void CParticleCollection::CopyInitialAttributeValues( int nStartParticle, int nNumParticles )
  2282. {
  2283. // if doinginterpolated sim, update the previous values to be the current ones, otherwise we may interpolate between
  2284. // old values based upon a previous particle that was in this slot.
  2285. if ( m_nPerParticleReadInitialAttributeMask == 0 )
  2286. return;
  2287. // FIXME: Do SSE copy here
  2288. for( int i = nStartParticle; i < nStartParticle + nNumParticles; i++ )
  2289. {
  2290. for ( int nAttr = 0; nAttr < MAX_PARTICLE_ATTRIBUTES; ++nAttr )
  2291. {
  2292. if ( m_nPerParticleReadInitialAttributeMask & (1 << nAttr) )
  2293. {
  2294. const float *pSrcAttribute = GetFloatAttributePtr( nAttr, i );
  2295. float *pDestAttribute = GetInitialFloatAttributePtrForWrite( nAttr, i );
  2296. Assert( m_ParticleInitialAttributes.Stride( nAttr ) != 0 );
  2297. Assert( m_ParticleAttributes.Stride( nAttr ) == m_ParticleInitialAttributes.Stride( nAttr ) );
  2298. *pDestAttribute = *pSrcAttribute;
  2299. if ( m_ParticleAttributes.Stride( nAttr ) == 12 )
  2300. {
  2301. pDestAttribute[4] = pSrcAttribute[4];
  2302. pDestAttribute[8] = pSrcAttribute[8];
  2303. }
  2304. }
  2305. }
  2306. }
  2307. }
  2308. void CParticleCollection::CopyParticleAttributesToPreviousAttributes( void ) const
  2309. {
  2310. for( int i = 0; i < MAX_PARTICLE_ATTRIBUTES; i++ )
  2311. {
  2312. if ( m_PreviousFrameAttributes.Stride( i ) )
  2313. {
  2314. int nSz = m_nPaddedActiveParticles * 4 * m_PreviousFrameAttributes.Stride( i );
  2315. memcpy( m_PreviousFrameAttributes.Address( i ), m_ParticleAttributes.Address( i ), nSz );
  2316. }
  2317. }
  2318. }
  2319. //-----------------------------------------------------------------------------e
  2320. // Computes a random vector inside a sphere
  2321. //-----------------------------------------------------------------------------
  2322. float CParticleCollection::RandomVectorInUnitSphere( int nRandomSampleId, Vector *pVector )
  2323. {
  2324. // Guarantee uniform random distribution within a sphere
  2325. // Graphics gems III contains this algorithm ("Nonuniform random point sets via warping")
  2326. float u = RandomFloat( nRandomSampleId, 0.0001f, 1.0f );
  2327. float v = RandomFloat( nRandomSampleId+1, 0.0001f, 1.0f );
  2328. float w = RandomFloat( nRandomSampleId+2, 0.0001f, 1.0f );
  2329. float flPhi = acos( 1 - 2 * u );
  2330. float flTheta = 2 * M_PI * v;
  2331. float flRadius = powf( w, 1.0f / 3.0f );
  2332. float flSinPhi, flCosPhi;
  2333. float flSinTheta, flCosTheta;
  2334. SinCos( flPhi, &flSinPhi, &flCosPhi );
  2335. SinCos( flTheta, &flSinTheta, &flCosTheta );
  2336. pVector->x = flRadius * flSinPhi * flCosTheta;
  2337. pVector->y = flRadius * flSinPhi * flSinTheta;
  2338. pVector->z = flRadius * flCosPhi;
  2339. return flRadius;
  2340. }
  2341. //-----------------------------------------------------------------------------
  2342. // Used to retrieve the position of a control point
  2343. // somewhere between m_flCurTime and m_flCurTime - m_fPreviousDT
  2344. //-----------------------------------------------------------------------------
  2345. void CParticleCollection::GetControlPointAtTime( int nControlPoint, float flTime, Vector *pControlPoint )
  2346. {
  2347. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2348. // if ( nControlPoint > GetHighestControlPoint() )
  2349. // {
  2350. // DevWarning(2, "Warning : Particle system (%s) using unassigned ControlPoint %d!\n", GetName(), nControlPoint );
  2351. // }
  2352. float flPrevTime = m_flCurTime - m_flDt;
  2353. // While this assert is valid, the below if statement is a good enough band-aid to make it so that
  2354. // particles aren't appearing a weird locations.
  2355. // Assert( flTime + 0.5f >= flPrevTime && flTime <= m_flCurTime );
  2356. if ( flTime < flPrevTime )
  2357. {
  2358. flTime = flPrevTime;
  2359. }
  2360. float deltaTime = ( flTime - flPrevTime );
  2361. if ( m_flDt == 0.0f || ( deltaTime == 0.0f ) )
  2362. {
  2363. VectorCopy( ControlPoint( nControlPoint ).m_Position, *pControlPoint );
  2364. return;
  2365. }
  2366. float t = deltaTime / m_flDt;
  2367. VectorLerp( ControlPoint( nControlPoint ).m_PrevPosition, ControlPoint( nControlPoint ).m_Position, t, *pControlPoint );
  2368. Assert( IsFinite(pControlPoint->x) && IsFinite(pControlPoint->y) && IsFinite(pControlPoint->z) );
  2369. }
  2370. //-----------------------------------------------------------------------------
  2371. // Used to retrieve the previous position of a control point
  2372. //
  2373. //-----------------------------------------------------------------------------
  2374. void CParticleCollection::GetControlPointAtPrevTime( int nControlPoint, Vector *pControlPoint )
  2375. {
  2376. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2377. *pControlPoint = ControlPoint( nControlPoint ).m_PrevPosition;
  2378. }
  2379. void CParticleCollection::GetControlPointTransformAtCurrentTime( int nControlPoint, matrix3x4_t *pMat )
  2380. {
  2381. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2382. const Vector &vecControlPoint = GetControlPointAtCurrentTime( nControlPoint );
  2383. // FIXME: Use quaternion lerp to get control point transform at time
  2384. Vector left;
  2385. VectorMultiply( ControlPoint( nControlPoint ).m_RightVector, -1.0f, left );
  2386. pMat->Init( ControlPoint( nControlPoint ).m_ForwardVector, left, ControlPoint( nControlPoint ).m_UpVector, vecControlPoint );
  2387. }
  2388. void CParticleCollection::GetControlPointTransformAtCurrentTime( int nControlPoint, VMatrix *pMat )
  2389. {
  2390. GetControlPointTransformAtCurrentTime( nControlPoint, &pMat->As3x4() );
  2391. pMat->m[3][0] = pMat->m[3][1] = pMat->m[3][2] = 0.0f; pMat->m[3][3] = 1.0f;
  2392. }
  2393. void CParticleCollection::GetControlPointOrientationAtTime( int nControlPoint, float flTime, Vector *pForward, Vector *pRight, Vector *pUp )
  2394. {
  2395. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2396. // FIXME: Use quaternion lerp to get control point transform at time
  2397. *pForward = ControlPoint( nControlPoint ).m_ForwardVector;
  2398. *pRight = ControlPoint( nControlPoint ).m_RightVector;
  2399. *pUp = ControlPoint( nControlPoint ).m_UpVector;
  2400. }
  2401. void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, matrix3x4_t *pMat )
  2402. {
  2403. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2404. Vector vecControlPoint;
  2405. GetControlPointAtTime( nControlPoint, flTime, &vecControlPoint );
  2406. // FIXME: Use quaternion lerp to get control point transform at time
  2407. Vector left;
  2408. VectorMultiply( ControlPoint(nControlPoint).m_RightVector, -1.0f, left );
  2409. pMat->Init( ControlPoint( nControlPoint ).m_ForwardVector, left, ControlPoint( nControlPoint ).m_UpVector, vecControlPoint );
  2410. }
  2411. void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, VMatrix *pMat )
  2412. {
  2413. GetControlPointTransformAtTime( nControlPoint, flTime, &pMat->As3x4() );
  2414. pMat->m[3][0] = pMat->m[3][1] = pMat->m[3][2] = 0.0f; pMat->m[3][3] = 1.0f;
  2415. }
  2416. void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, CParticleSIMDTransformation *pXForm )
  2417. {
  2418. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2419. Vector vecControlPoint;
  2420. GetControlPointAtTime( nControlPoint, flTime, &vecControlPoint );
  2421. pXForm->m_v4Origin.DuplicateVector( vecControlPoint );
  2422. pXForm->m_v4Fwd.DuplicateVector( ControlPoint( nControlPoint ).m_ForwardVector );
  2423. pXForm->m_v4Up.DuplicateVector( ControlPoint( nControlPoint ).m_UpVector );
  2424. //Vector left;
  2425. //VectorMultiply( ControlPoint(nControlPoint).m_RightVector, -1.0f, left );
  2426. pXForm->m_v4Right.DuplicateVector( ControlPoint( nControlPoint ).m_RightVector );
  2427. }
  2428. int CParticleCollection::GetHighestControlPoint( void ) const
  2429. {
  2430. return m_nHighestCP;
  2431. }
  2432. //-----------------------------------------------------------------------------
  2433. // Returns the render bounds
  2434. //-----------------------------------------------------------------------------
  2435. void CParticleCollection::GetBounds( Vector *pMin, Vector *pMax )
  2436. {
  2437. *pMin = m_MinBounds;
  2438. *pMax = m_MaxBounds;
  2439. }
  2440. //-----------------------------------------------------------------------------
  2441. // Bloat the bounding box by bounds around the control point
  2442. //-----------------------------------------------------------------------------
  2443. void CParticleCollection::BloatBoundsUsingControlPoint()
  2444. {
  2445. // more specifically, some particle systems were using "start" as an input, so it got set as control point 1,
  2446. // so other particle systems had an extra point in their bounding box, that generally remained at the world origin
  2447. RecomputeBounds();
  2448. // Deal with children
  2449. // NOTE: Bounds have been recomputed for children prior to this call in Simulate
  2450. bool bIsValid = m_bBoundsValid;
  2451. Vector vecMins, vecMaxs;
  2452. for( CParticleCollection *i = m_Children.m_pHead; i; i = i->m_pNext )
  2453. {
  2454. // mdonofrio - skip screen space effects, they have CPs at the origin
  2455. if( (i->m_nActiveParticles > 0 ) &&
  2456. (!i->m_pDef->IsScreenSpaceEffect()) )
  2457. {
  2458. i->GetBounds( &vecMins, &vecMaxs );
  2459. VectorMin( m_MinBounds, vecMins, m_MinBounds );
  2460. VectorMax( m_MaxBounds, vecMaxs, m_MaxBounds );
  2461. bIsValid = ( bIsValid || i->m_bBoundsValid );
  2462. }
  2463. }
  2464. m_bBoundsValid = bIsValid;
  2465. }
  2466. //-----------------------------------------------------------------------------
  2467. // Recomputes the bounds
  2468. //-----------------------------------------------------------------------------
  2469. inline void UpdateBounds( fltx4 &min_, fltx4 &max_, fltx4 &sum_, fltx4 val )
  2470. {
  2471. min_ = MinSIMD( min_, val );
  2472. max_ = MaxSIMD( max_, val );
  2473. sum_ = AddSIMD( sum_, val );
  2474. }
  2475. inline void UpdateBounds( float &min_, float &max_, float &sum_, float val )
  2476. {
  2477. min_ = MIN( min_, val );
  2478. max_ = MAX( max_, val );
  2479. sum_ = sum_ + val;
  2480. }
  2481. void CParticleCollection::RecomputeBounds( void )
  2482. {
  2483. // mdonofrio - skip screen space effects (as they have CPs at the origin) as well as those with 0 active particles
  2484. if( ( m_nActiveParticles == 0 ) ||
  2485. m_pDef->IsScreenSpaceEffect() )
  2486. {
  2487. m_bBoundsValid = false;
  2488. m_MinBounds.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  2489. m_MaxBounds.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  2490. m_Center.Init();
  2491. return;
  2492. }
  2493. fltx4 min_x = ReplicateX4(1.0e23);
  2494. fltx4 min_y = min_x;
  2495. fltx4 min_z = min_x;
  2496. fltx4 max_x = ReplicateX4(-1.0e23);
  2497. fltx4 max_y = max_x;
  2498. fltx4 max_z = max_x;
  2499. fltx4 sum_x = Four_Zeros;
  2500. fltx4 sum_y = Four_Zeros;
  2501. fltx4 sum_z = Four_Zeros;
  2502. float flMaxTail = m_pDef->GetMaxTailLength();
  2503. float flOODt = ( m_flDt != 0.0f ) ? ( 1.0f / m_flDt ) : 1.0f;
  2504. fltx4 maxtail = ReplicateX4( flMaxTail );
  2505. fltx4 oodt = ReplicateX4( flOODt );
  2506. size_t xyz_stride, prev_stride, trail_stride;
  2507. const fltx4 *xyz = GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
  2508. const fltx4 *prev = GetM128AttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, &prev_stride );
  2509. const fltx4 *trail = GetM128AttributePtr( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, &trail_stride );
  2510. int ctr = m_nActiveParticles/4;
  2511. bool bHasTail = ( flMaxTail > 0.0f );
  2512. if ( bHasTail )
  2513. {
  2514. while ( ctr-- )
  2515. {
  2516. UpdateBounds( min_x, max_x, sum_x, xyz[0] );
  2517. UpdateBounds( min_y, max_y, sum_y, xyz[1] );
  2518. UpdateBounds( min_z, max_z, sum_z, xyz[2] );
  2519. fltx4 delta_x = SubSIMD( prev[0], xyz[0] );
  2520. fltx4 delta_y = SubSIMD( prev[1], xyz[1] );
  2521. fltx4 delta_z = SubSIMD( prev[2], xyz[2] );
  2522. fltx4 d2_x = MulSIMD( delta_x, delta_x );
  2523. fltx4 d2_y = MulSIMD( delta_y, delta_y );
  2524. fltx4 d2_z = MulSIMD( delta_z, delta_z );
  2525. fltx4 lensq = AddSIMD( d2_z, AddSIMD( d2_y, d2_x ) );
  2526. fltx4 len = MaxSIMD( ReplicateX4( 0.001f ), SqrtSIMD( lensq ) );
  2527. fltx4 invlen = ReciprocalSIMD( len );
  2528. delta_x = MulSIMD( delta_x, invlen );
  2529. delta_y = MulSIMD( delta_y, invlen );
  2530. delta_z = MulSIMD( delta_z, invlen );
  2531. len = MulSIMD( len, trail[0] );
  2532. len = MulSIMD( len, oodt );
  2533. len = MinSIMD( len, maxtail );
  2534. delta_x = MulSIMD( delta_x, len );
  2535. delta_y = MulSIMD( delta_y, len );
  2536. delta_z = MulSIMD( delta_z, len );
  2537. fltx4 tail_x = AddSIMD( xyz[0], delta_x );
  2538. fltx4 tail_y = AddSIMD( xyz[1], delta_y );
  2539. fltx4 tail_z = AddSIMD( xyz[2], delta_z );
  2540. UpdateBounds( min_x, max_x, sum_x, tail_x );
  2541. UpdateBounds( min_y, max_y, sum_y, tail_y );
  2542. UpdateBounds( min_z, max_z, sum_z, tail_z );
  2543. xyz += xyz_stride;
  2544. prev += prev_stride;
  2545. trail += trail_stride;
  2546. }
  2547. }
  2548. else
  2549. {
  2550. while ( ctr-- )
  2551. {
  2552. UpdateBounds( min_x, max_x, sum_x, xyz[0] );
  2553. UpdateBounds( min_y, max_y, sum_y, xyz[1] );
  2554. UpdateBounds( min_z, max_z, sum_z, xyz[2] );
  2555. xyz += xyz_stride;
  2556. }
  2557. }
  2558. m_bBoundsValid = true;
  2559. m_MinBounds.x = MIN( MIN( SubFloat( min_x, 0 ), SubFloat( min_x, 1 ) ), MIN( SubFloat( min_x, 2 ), SubFloat( min_x, 3 ) ) );
  2560. m_MinBounds.y = MIN( MIN( SubFloat( min_y, 0 ), SubFloat( min_y, 1 ) ), MIN( SubFloat( min_y, 2 ), SubFloat( min_y, 3 ) ) );
  2561. m_MinBounds.z = MIN( MIN( SubFloat( min_z, 0 ), SubFloat( min_z, 1 ) ), MIN( SubFloat( min_z, 2 ), SubFloat( min_z, 3 ) ) );
  2562. m_MaxBounds.x = MAX( MAX( SubFloat( max_x, 0 ), SubFloat( max_x, 1 ) ), MAX( SubFloat( max_x, 2 ), SubFloat( max_x, 3 ) ) );
  2563. m_MaxBounds.y = MAX( MAX( SubFloat( max_y, 0 ), SubFloat( max_y, 1 ) ), MAX( SubFloat( max_y, 2 ), SubFloat( max_y, 3 ) ) );
  2564. m_MaxBounds.z = MAX( MAX( SubFloat( max_z, 0 ), SubFloat( max_z, 1 ) ), MAX( SubFloat( max_z, 2 ), SubFloat( max_z, 3 ) ) );
  2565. float fsum_x = SubFloat( sum_x, 0 ) + SubFloat( sum_x, 1 ) + SubFloat( sum_x, 2 ) + SubFloat( sum_x, 3 );
  2566. float fsum_y = SubFloat( sum_y, 0 ) + SubFloat( sum_y, 1 ) + SubFloat( sum_y, 2 ) + SubFloat( sum_y, 3 );
  2567. float fsum_z = SubFloat( sum_z, 0 ) + SubFloat( sum_z, 1 ) + SubFloat( sum_z, 2 ) + SubFloat( sum_z, 3 );
  2568. // now, handle "tail" in a non-sse manner
  2569. for( int i=0; i < ( m_nActiveParticles & 3 ); i++)
  2570. {
  2571. Vector pos( SubFloat( xyz[0], i ), SubFloat( xyz[1], i ), SubFloat( xyz[2], i ) );
  2572. UpdateBounds( m_MinBounds.x, m_MaxBounds.x, fsum_x, pos.x );
  2573. UpdateBounds( m_MinBounds.y, m_MaxBounds.y, fsum_y, pos.y );
  2574. UpdateBounds( m_MinBounds.z, m_MaxBounds.z, fsum_z, pos.z );
  2575. if ( bHasTail )
  2576. if ( flMaxTail > 0.0f )
  2577. {
  2578. Vector pos_prev( SubFloat( prev[0], i ), SubFloat( prev[1], i ), SubFloat( prev[2], i ) );
  2579. Vector dir = pos_prev - pos;
  2580. float len = VectorNormalize( dir );
  2581. len = MIN( MAX( len, 0.001f ) * SubFloat( trail[0], i ) * flOODt, flMaxTail );
  2582. Vector tail = pos + dir * len;
  2583. UpdateBounds( m_MinBounds.x, m_MaxBounds.x, fsum_x, tail.x );
  2584. UpdateBounds( m_MinBounds.y, m_MaxBounds.y, fsum_y, tail.y );
  2585. UpdateBounds( m_MinBounds.z, m_MaxBounds.z, fsum_z, tail.z );
  2586. }
  2587. }
  2588. VectorAdd( m_MinBounds, m_pDef->m_BoundingBoxMin, m_MinBounds );
  2589. VectorAdd( m_MaxBounds, m_pDef->m_BoundingBoxMax, m_MaxBounds );
  2590. // calculate center
  2591. float flOONumParticles = 1.0 / ( ( flMaxTail > 0.0f ) ? 2 * m_nActiveParticles : m_nActiveParticles );
  2592. m_Center.x = flOONumParticles * fsum_x;
  2593. m_Center.y = flOONumParticles * fsum_y;
  2594. m_Center.z = flOONumParticles * fsum_z;
  2595. }
  2596. //-----------------------------------------------------------------------------
  2597. // Is the particle system finished emitting + all its particles are dead?
  2598. //-----------------------------------------------------------------------------
  2599. bool CParticleCollection::IsFinished( void ) const
  2600. {
  2601. if ( !m_pDef )
  2602. return true;
  2603. if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME )
  2604. return false;
  2605. if ( m_nActiveParticles )
  2606. return false;
  2607. if ( m_bDormant )
  2608. return false;
  2609. // no particles. See if any emmitters intead to create more particles
  2610. int nEmitterCount = m_pDef->m_Emitters.Count();
  2611. for( int i=0; i < nEmitterCount; i++ )
  2612. {
  2613. if ( m_pDef->m_Emitters[i]->MayCreateMoreParticles( this, m_pOperatorContextData+m_pDef->m_nEmittersCtxOffsets[i] ) )
  2614. return false;
  2615. }
  2616. // make sure all children are finished
  2617. CParticleCollection *pChild = m_Children.Head();
  2618. for( int i = 0; pChild != NULL; pChild = pChild->m_pNext, i++ )
  2619. {
  2620. if ( !pChild->IsFinished() && !m_pDef->m_Children[i].m_bEndCap )
  2621. return false;
  2622. // return false if we're currently playing our endcap effect and not finished with it
  2623. if ( m_pDef->m_Children[i].m_bEndCap && !pChild->IsFinished() && m_bInEndCap )
  2624. return false;
  2625. }
  2626. return true;
  2627. }
  2628. //-----------------------------------------------------------------------------
  2629. // Purpose: Stop emitting particles
  2630. //-----------------------------------------------------------------------------
  2631. void CParticleCollection::StopEmission( bool bInfiniteOnly, bool bRemoveAllParticles, bool bWakeOnStop, bool bPlayEndCap )
  2632. {
  2633. if ( !m_pDef )
  2634. return;
  2635. // Whenever we call stop emission, we clear out our dormancy. This ensures we
  2636. // get deleted if we're told to stop emission while dormant. SetDormant() ensures
  2637. // dormancy is set to true after stopping out emission.
  2638. m_bDormant = false;
  2639. if ( bWakeOnStop )
  2640. {
  2641. // Set next sleep time - an additional fudge factor is added over the normal time
  2642. // so that existing particles have a chance to go away.
  2643. m_flNextSleepTime = MAX( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + 10 ));
  2644. }
  2645. m_bEmissionStopped = true;
  2646. for( int i=0; i < m_pDef->m_Emitters.Count(); i++ )
  2647. {
  2648. m_pDef->m_Emitters[i]->StopEmission( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i], bInfiniteOnly );
  2649. }
  2650. if ( bRemoveAllParticles )
  2651. {
  2652. SetNActiveParticles( 0 );
  2653. }
  2654. // Stop our children as well
  2655. if ( bPlayEndCap )
  2656. {
  2657. CParticleCollection *pChild;
  2658. int i;
  2659. m_bInEndCap = true;
  2660. for( i = 0, pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext, i++ )
  2661. {
  2662. pChild->m_bInEndCap = true;
  2663. if ( m_pDef->m_Children[i].m_bEndCap )
  2664. {
  2665. pChild->m_flCurTime = 0.0f;
  2666. pChild->StartEmission( bInfiniteOnly );
  2667. }
  2668. else
  2669. pChild->StopEmission( bInfiniteOnly, bRemoveAllParticles, bWakeOnStop, bPlayEndCap );
  2670. }
  2671. }
  2672. else
  2673. {
  2674. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  2675. {
  2676. p->StopEmission( bInfiniteOnly, bRemoveAllParticles );
  2677. }
  2678. }
  2679. }
  2680. //-----------------------------------------------------------------------------
  2681. // Purpose: Stop emitting particles
  2682. //-----------------------------------------------------------------------------
  2683. void CParticleCollection::StartEmission( bool bInfiniteOnly )
  2684. {
  2685. if ( !m_pDef )
  2686. return;
  2687. m_bEmissionStopped = false;
  2688. for( int i=0; i < m_pDef->m_Emitters.Count(); i++ )
  2689. {
  2690. m_pDef->m_Emitters[i]->StartEmission( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i], bInfiniteOnly );
  2691. }
  2692. // Start our children as well
  2693. CParticleCollection *pChild = m_Children.Head();
  2694. for( int i = 0; pChild != NULL; pChild = pChild->m_pNext, i++ )
  2695. {
  2696. // Don't start End Cap Effects - these only play when stopping emission.
  2697. if ( !m_pDef->m_Children[i].m_bEndCap )
  2698. {
  2699. pChild->StartEmission( bInfiniteOnly );
  2700. }
  2701. }
  2702. // Set our sleep time to some time in the future so we update again
  2703. m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep;
  2704. }
  2705. //-----------------------------------------------------------------------------
  2706. // Purpose: Dormant particle systems simulate their particles, but don't emit
  2707. // new ones. Unlike particle systems that have StopEmission() called
  2708. // dormant particle systems don't remove themselves when they're
  2709. // out of particles, assuming they'll return at some point.
  2710. //-----------------------------------------------------------------------------
  2711. void CParticleCollection::SetDormant( bool bDormant )
  2712. {
  2713. // Don't stop or start emission if we are not changing dormancy state
  2714. if ( bDormant == m_bDormant )
  2715. return;
  2716. // If emissions have already been stopped, don't go dormant, we're supposed to be dying.
  2717. if ( m_bEmissionStopped && bDormant )
  2718. return;
  2719. if ( bDormant )
  2720. {
  2721. StopEmission();
  2722. m_bQueuedStartEmission = false;
  2723. }
  2724. else
  2725. {
  2726. //StartEmission();
  2727. m_bQueuedStartEmission = true; // start emission during next sim step
  2728. // Set our sleep time to some time in the future so we update again
  2729. m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep;
  2730. }
  2731. m_bDormant = bDormant;
  2732. }
  2733. bool CParticleCollection::IsEmitting() const
  2734. {
  2735. return !m_bEmissionStopped;
  2736. }
  2737. void CParticleAttributeAddressTable::CopyParticleAttributes( int nSrcIndex, int nDestIndex ) const
  2738. {
  2739. for( int p = 0; p < ARRAYSIZE( m_pAttributes ); p++ )
  2740. {
  2741. switch( m_nFloatStrides[p] )
  2742. {
  2743. case 4: // move a float
  2744. m_pAttributes[p][nDestIndex] = m_pAttributes[p][nSrcIndex];
  2745. break;
  2746. case 12: // move a vec3
  2747. {
  2748. // sse weirdness
  2749. int oldidxsse = 12 * ( nDestIndex >> 2 );
  2750. int oldofs = oldidxsse + ( nDestIndex & 3 );
  2751. int lastidxsse = 12 * ( nSrcIndex >> 2 );
  2752. int lastofs = lastidxsse + ( nSrcIndex & 3 );
  2753. m_pAttributes[p][oldofs] = m_pAttributes[p][lastofs];
  2754. m_pAttributes[p][4 + oldofs] = m_pAttributes[p][4 + lastofs];
  2755. m_pAttributes[p][8 + oldofs] = m_pAttributes[p][8 + lastofs];
  2756. break;
  2757. }
  2758. }
  2759. }
  2760. }
  2761. void CParticleCollection::MoveParticle( int nInitialIndex, int nNewIndex )
  2762. {
  2763. // Copy the per-particle attributes
  2764. m_ParticleAttributes.CopyParticleAttributes( nInitialIndex, nNewIndex );
  2765. m_ParticleInitialAttributes.CopyParticleAttributes( nInitialIndex, nNewIndex );
  2766. if ( m_pPreviousAttributeMemory )
  2767. {
  2768. m_PreviousFrameAttributes.CopyParticleAttributes( nInitialIndex, nNewIndex );
  2769. }
  2770. }
  2771. //-----------------------------------------------------------------------------
  2772. // Kill List processing.
  2773. //-----------------------------------------------------------------------------
  2774. #define THREADED_PARTICLES 1
  2775. #if THREADED_PARTICLES
  2776. #define MAX_SIMULTANEOUS_KILL_LISTS 16
  2777. static volatile int g_nKillBufferInUse[MAX_SIMULTANEOUS_KILL_LISTS];
  2778. static KillListItem_t *g_pKillBuffers[MAX_SIMULTANEOUS_KILL_LISTS];
  2779. void CParticleSystemMgr::DetachKillList( CParticleCollection *pParticles )
  2780. {
  2781. if ( pParticles->m_pParticleKillList )
  2782. {
  2783. // find which it is
  2784. for(int i=0; i < NELEMS( g_pKillBuffers ); i++)
  2785. {
  2786. if ( g_pKillBuffers[i] == pParticles->m_pParticleKillList )
  2787. {
  2788. pParticles->m_pParticleKillList = NULL;
  2789. g_nKillBufferInUse[i] = 0; // no need to interlock
  2790. return;
  2791. }
  2792. }
  2793. Assert( 0 ); // how did we get here?
  2794. }
  2795. }
  2796. void CParticleSystemMgr::AttachKillList( CParticleCollection *pParticles )
  2797. {
  2798. // look for a free slot
  2799. for(;;)
  2800. {
  2801. for(int i=0; i < NELEMS( g_nKillBufferInUse ); i++)
  2802. {
  2803. if ( ! g_nKillBufferInUse[i] ) // available?
  2804. {
  2805. // try to take it!
  2806. if ( ThreadInterlockedAssignIf( &( g_nKillBufferInUse[i]), 1, 0 ) )
  2807. {
  2808. if ( ! g_pKillBuffers[i] )
  2809. {
  2810. g_pKillBuffers[i] = new KillListItem_t[MAX_PARTICLES_IN_A_SYSTEM];
  2811. }
  2812. pParticles->m_pParticleKillList = g_pKillBuffers[i];
  2813. return; // done!
  2814. }
  2815. }
  2816. }
  2817. Assert(0); // why don't we have enough buffers?
  2818. ThreadSleep();
  2819. }
  2820. }
  2821. #else
  2822. // use one static kill list. no worries because of not threading
  2823. static KillListItem_t g_nParticleKillList[MAX_PARTICLES_IN_A_SYSTEM];
  2824. void CParticleSystemMgr::AttachKillList( CParticleCollection *pParticles )
  2825. {
  2826. pParticles->m_pParticleKillList = g_nParticleKillList;
  2827. }
  2828. void CParticleCollection::DetachKillList( CParticleCollection *pParticles )
  2829. {
  2830. Assert( pParticles->m_nNumParticlesToKill == 0 );
  2831. pParticles->m_pParticleKillList = NULL;
  2832. }
  2833. #endif
  2834. void CParticleCollection::ApplyKillList( void )
  2835. {
  2836. // first, kill particles past bounds
  2837. const KillListItem_t *pCurKillListSlot = m_pParticleKillList;
  2838. while( m_nNumParticlesToKill && pCurKillListSlot[ m_nNumParticlesToKill-1 ].nIndex >= (uint)m_nActiveParticles )
  2839. {
  2840. m_nNumParticlesToKill--;
  2841. }
  2842. Assert( m_nNumParticlesToKill <= m_nActiveParticles );
  2843. if ( m_nNumParticlesToKill == 0 )
  2844. return;
  2845. // next, run any child system emitter/initializer operators which request the parent's kill list:
  2846. bool bApplyingParentKillList = true;
  2847. for( CParticleCollection *pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext )
  2848. {
  2849. // TODO: make this more general (there's a bunch of "first frame" and "frame-to-frame" setup that happens in Simulate() which is skipped here)
  2850. EmitAndInit( pChild, bApplyingParentKillList );
  2851. }
  2852. // now, execute kill list (NOTE: this algorithm assumes the particles listed
  2853. // in the kill list are in ascending order - this is checked in KillParticle)
  2854. unsigned int nParticlesActiveNow = m_nActiveParticles;
  2855. int nLeftInKillList = m_nNumParticlesToKill;
  2856. if ( nLeftInKillList == m_nActiveParticles )
  2857. {
  2858. nParticlesActiveNow = 0; // Simply discard all particles
  2859. }
  2860. // TODO: check KILL_LIST_FLAG_DONT_KILL here (take no action for those kill list entries)
  2861. else if ( IsOrderImportant() )
  2862. {
  2863. // shift
  2864. m_pParticleKillList[ nLeftInKillList ].nIndex = m_nActiveParticles;
  2865. for ( int nKilled = 0; nKilled < nLeftInKillList; )
  2866. {
  2867. int nWriteIndex = m_pParticleKillList[ nKilled ].nIndex - nKilled;
  2868. nKilled++;
  2869. int nNextIndexToKill = m_pParticleKillList[ nKilled ].nIndex - nKilled;
  2870. for ( nWriteIndex; nWriteIndex < nNextIndexToKill; nWriteIndex++ )
  2871. {
  2872. MoveParticle( ( nWriteIndex + nKilled ), nWriteIndex );
  2873. }
  2874. }
  2875. nParticlesActiveNow -= nLeftInKillList;
  2876. }
  2877. else
  2878. {
  2879. while( nLeftInKillList )
  2880. {
  2881. unsigned int nKillIndex = (pCurKillListSlot++)->nIndex;
  2882. nLeftInKillList--;
  2883. // now, we will move a particle from the end to where we are
  2884. // first, we have to find the last particle (which is not in the kill list)
  2885. while ( nLeftInKillList &&
  2886. ( pCurKillListSlot[ nLeftInKillList-1 ].nIndex == nParticlesActiveNow-1 ))
  2887. {
  2888. nLeftInKillList--;
  2889. nParticlesActiveNow--;
  2890. }
  2891. // we might be killing the last particle
  2892. if ( nKillIndex == nParticlesActiveNow-1 )
  2893. {
  2894. // killing last one
  2895. nParticlesActiveNow--;
  2896. break; // we are done
  2897. }
  2898. // move the last particle to this one and chop off the end of the list
  2899. MoveParticle( nParticlesActiveNow-1, nKillIndex );
  2900. nParticlesActiveNow--;
  2901. }
  2902. }
  2903. // set count in system and wipe kill list
  2904. SetNActiveParticles( nParticlesActiveNow );
  2905. m_nNumParticlesToKill = 0;
  2906. }
  2907. void CParticleCollection::CalculatePathValues( CPathParameters const &PathIn,
  2908. float flTimeStamp,
  2909. Vector *pStartPnt,
  2910. Vector *pMidPnt,
  2911. Vector *pEndPnt
  2912. )
  2913. {
  2914. Vector StartPnt;
  2915. GetControlPointAtTime( PathIn.m_nStartControlPointNumber, flTimeStamp, &StartPnt );
  2916. Vector EndPnt;
  2917. GetControlPointAtTime( PathIn.m_nEndControlPointNumber, flTimeStamp, &EndPnt );
  2918. Vector MidP;
  2919. VectorLerp(StartPnt, EndPnt, PathIn.m_flMidPoint, MidP);
  2920. if ( PathIn.m_nBulgeControl )
  2921. {
  2922. Vector vTarget=(EndPnt-StartPnt);
  2923. float flBulgeScale = 0.0;
  2924. int nCP=PathIn.m_nStartControlPointNumber;
  2925. if ( PathIn.m_nBulgeControl == 2)
  2926. nCP = PathIn.m_nEndControlPointNumber;
  2927. Vector Fwd = ControlPoint( nCP ).m_ForwardVector;
  2928. float len=VectorLength( vTarget);
  2929. if ( len > 1.0e-6 )
  2930. {
  2931. vTarget *= (1.0/len); // normalize
  2932. flBulgeScale = 1.0-fabs( DotProduct( vTarget, Fwd )); // bulge inversely scaled
  2933. }
  2934. Vector Potential_MidP=Fwd;
  2935. float flOffsetDist = VectorLength( Potential_MidP );
  2936. if ( flOffsetDist > 1.0e-6 )
  2937. {
  2938. Potential_MidP *= (PathIn.m_flBulge*len*flBulgeScale)/flOffsetDist;
  2939. MidP += Potential_MidP;
  2940. }
  2941. }
  2942. else
  2943. {
  2944. Vector RndVector;
  2945. RandomVector( 0, -PathIn.m_flBulge, PathIn.m_flBulge, &RndVector);
  2946. MidP+=RndVector;
  2947. }
  2948. *pStartPnt = StartPnt;
  2949. *pMidPnt = MidP;
  2950. *pEndPnt = EndPnt;
  2951. }
  2952. //-----------------------------------------------------------------------------
  2953. //
  2954. // Default impelemtation of the query
  2955. //
  2956. //-----------------------------------------------------------------------------
  2957. class CDefaultParticleSystemQuery : public CBaseAppSystem< IParticleSystemQuery >
  2958. {
  2959. public:
  2960. virtual bool IsEditor( ) { return false; }
  2961. virtual void GetLightingAtPoint( const Vector& vecOrigin, Color &tint )
  2962. {
  2963. tint.SetColor( 255, 255, 255, 255 );
  2964. }
  2965. virtual void TraceLine( const Vector& vecAbsStart,
  2966. const Vector& vecAbsEnd, unsigned int mask,
  2967. const class IHandleEntity *ignore,
  2968. int collisionGroup, CBaseTrace *ptr )
  2969. {
  2970. ptr->fraction = 1.0; // no hit
  2971. }
  2972. virtual bool IsPointInSolid( const Vector& vecPos, const int nContentsMask )
  2973. {
  2974. return false;
  2975. }
  2976. virtual void GetRandomPointsOnControllingObjectHitBox(
  2977. CParticleCollection *pParticles,
  2978. int nControlPointNumber,
  2979. int nNumPtsOut,
  2980. float flBBoxScale,
  2981. int nNumTrysToGetAPointInsideTheModel,
  2982. Vector *pPntsOut,
  2983. Vector vecDirectionBias,
  2984. Vector *pHitBoxRelativeCoordOut, int *pHitBoxIndexOut, int nDesiredHitbox,
  2985. const char *pszHitboxSetName )
  2986. {
  2987. for ( int i = 0; i < nNumPtsOut; ++i )
  2988. {
  2989. pPntsOut[i].Init();
  2990. }
  2991. }
  2992. virtual void GetClosestControllingObjectHitBox( CParticleCollection *pParticles,
  2993. int nControlPointNumber,
  2994. int nNumPtsIn,
  2995. float flBBoxScale,
  2996. Vector *pPntsIn,
  2997. Vector *pHitBoxRelativeCoordOut,
  2998. int *pHitBoxIndexOut,
  2999. int nDesiredHitbox,
  3000. const char *pszHitboxSetName )
  3001. {
  3002. for ( int i=0; i < nNumPtsIn; i++ )
  3003. {
  3004. if ( pHitBoxIndexOut )
  3005. pHitBoxIndexOut[i] = 0;
  3006. if ( pHitBoxRelativeCoordOut )
  3007. pHitBoxRelativeCoordOut[i].Init();
  3008. }
  3009. }
  3010. virtual void TraceAgainstRayTraceEnv(
  3011. int envnumber,
  3012. const FourRays &rays, fltx4 TMin, fltx4 TMax,
  3013. RayTracingResult *rslt_out, int32 skip_id ) const
  3014. {
  3015. rslt_out->HitDistance = Four_Ones;
  3016. rslt_out->surface_normal.DuplicateVector( vec3_origin );
  3017. }
  3018. virtual Vector GetCurrentViewOrigin()
  3019. {
  3020. return vec3_origin;
  3021. }
  3022. virtual int GetActivityCount() { return 0; }
  3023. virtual const char *GetActivityNameFromIndex( int nActivityIndex ) { return 0; }
  3024. virtual int GetActivityNumber( void *pModel, const char *m_pszActivityName ) { return -1; }
  3025. virtual float GetPixelVisibility( int *pQueryHandle, const Vector &vecOrigin, float flScale ) { return 0.0f; }
  3026. virtual void PreSimulate( ) { }
  3027. virtual void PostSimulate( ) { }
  3028. virtual void DebugDrawLine( const Vector &origin, const Vector &target, int r, int g, int b, bool noDepthTest, float duration )
  3029. {
  3030. }
  3031. virtual void DrawModel( void *pModel, const matrix3x4_t &DrawMatrix, CParticleCollection *pParticles, int nParticleNumber, int nBodyPart, int nSubModel,
  3032. int nSkin, int nAnimationSequence = 0, float flAnimationRate = 30.0f, float r = 1.0f, float g = 1.0f, float b = 1.0f, float a = 1.0f ) { }
  3033. virtual void UpdateProjectedTexture( const int nParticleID, IMaterial *pMaterial, Vector &vOrigin, float flRadius, float flRotation, float r, float g, float b, float a, void *&pUserVar ) { }
  3034. };
  3035. static CDefaultParticleSystemQuery s_DefaultParticleSystemQuery;
  3036. //-----------------------------------------------------------------------------
  3037. //
  3038. // Particle system manager
  3039. //
  3040. //-----------------------------------------------------------------------------
  3041. //-----------------------------------------------------------------------------
  3042. // Constructor
  3043. //-----------------------------------------------------------------------------
  3044. CParticleSystemMgr::CParticleSystemMgr()
  3045. // m_SheetList( DefLessFunc( ITexture * ) )
  3046. {
  3047. m_pQuery = &s_DefaultParticleSystemQuery;
  3048. m_bDidInit = false;
  3049. m_bUsingDefaultQuery = true;
  3050. m_bShouldLoadSheets = true;
  3051. m_bAllowPrecache = true;
  3052. m_pParticleSystemDictionary = NULL;
  3053. m_nNumFramesMeasured = 0;
  3054. m_flLastSimulationTime = 0.0f;
  3055. m_flLastSimulationDuration = 0.0f;
  3056. m_pShadowDepthMaterial = NULL;
  3057. // Init the attribute table
  3058. InitAttributeTable();
  3059. }
  3060. CParticleSystemMgr::~CParticleSystemMgr()
  3061. {
  3062. FlushAllSheets();
  3063. if ( m_pParticleSystemDictionary )
  3064. {
  3065. delete m_pParticleSystemDictionary;
  3066. m_pParticleSystemDictionary = NULL;
  3067. }
  3068. }
  3069. //-----------------------------------------------------------------------------
  3070. // Initialize the particle system
  3071. //-----------------------------------------------------------------------------
  3072. bool CParticleSystemMgr::Init( IParticleSystemQuery *pQuery, bool bAllowPrecache )
  3073. {
  3074. if ( g_pMaterialSystem && ( !g_pMaterialSystem->QueryInterface( MATERIAL_SYSTEM_INTERFACE_VERSION ) ) )
  3075. {
  3076. Msg( "CParticleSystemMgr compiled using an old IMaterialSystem\n" );
  3077. return false;
  3078. }
  3079. if ( m_bUsingDefaultQuery && pQuery )
  3080. {
  3081. m_pQuery = pQuery;
  3082. m_bUsingDefaultQuery = false;
  3083. }
  3084. if ( !m_bDidInit )
  3085. {
  3086. m_pParticleSystemDictionary = new CParticleSystemDictionary;
  3087. // NOTE: This is for the editor only
  3088. AddParticleOperator( FUNCTION_CHILDREN, &s_ChildOperatorDefinition );
  3089. if ( g_pMaterialSystem )
  3090. {
  3091. MEM_ALLOC_CREDIT();
  3092. KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
  3093. pVMTKeyValues->SetInt( "$no_fullbright", 1 );
  3094. pVMTKeyValues->SetInt( "$model", 0 );
  3095. pVMTKeyValues->SetInt( "$alphatest", 0 );
  3096. m_pShadowDepthMaterial = g_pMaterialSystem->CreateMaterial( "__particlesDepthWrite", pVMTKeyValues );
  3097. }
  3098. SeedRandSIMD( 12345678 );
  3099. m_bDidInit = true;
  3100. }
  3101. m_bAllowPrecache = bAllowPrecache;
  3102. return true;
  3103. }
  3104. //-----------------------------------------------------------------------------
  3105. void CParticleSystemMgr::Shutdown()
  3106. {
  3107. if ( m_pShadowDepthMaterial )
  3108. {
  3109. m_pShadowDepthMaterial->Release();
  3110. m_pShadowDepthMaterial = NULL;
  3111. }
  3112. }
  3113. //-----------------------------------------------------------------------------
  3114. // Init the attribute table
  3115. //-----------------------------------------------------------------------------
  3116. void CParticleSystemMgr::InitAttributeTable( void )
  3117. {
  3118. // Init the attribute table
  3119. #define INITPARTICLE_ATTRIBUTE( name ) \
  3120. { \
  3121. int bit = PARTICLE_ATTRIBUTE_##name; \
  3122. s_AttributeTable[ bit ].nDataType = PARTICLE_ATTRIBUTE_##name##_DATATYPE; \
  3123. s_AttributeTable[ bit ].pName = #name; \
  3124. }
  3125. memset( s_AttributeTable, 0, sizeof( s_AttributeTable ) );
  3126. INITPARTICLE_ATTRIBUTE( XYZ );
  3127. INITPARTICLE_ATTRIBUTE( LIFE_DURATION );
  3128. INITPARTICLE_ATTRIBUTE( PREV_XYZ );
  3129. INITPARTICLE_ATTRIBUTE( RADIUS );
  3130. INITPARTICLE_ATTRIBUTE( ROTATION );
  3131. INITPARTICLE_ATTRIBUTE( ROTATION_SPEED );
  3132. INITPARTICLE_ATTRIBUTE( TINT_RGB );
  3133. INITPARTICLE_ATTRIBUTE( ALPHA );
  3134. INITPARTICLE_ATTRIBUTE( CREATION_TIME );
  3135. INITPARTICLE_ATTRIBUTE( SEQUENCE_NUMBER );
  3136. INITPARTICLE_ATTRIBUTE( TRAIL_LENGTH );
  3137. INITPARTICLE_ATTRIBUTE( PARTICLE_ID );
  3138. INITPARTICLE_ATTRIBUTE( YAW );
  3139. INITPARTICLE_ATTRIBUTE( SEQUENCE_NUMBER1 );
  3140. INITPARTICLE_ATTRIBUTE( HITBOX_INDEX );
  3141. INITPARTICLE_ATTRIBUTE( HITBOX_RELATIVE_XYZ );
  3142. INITPARTICLE_ATTRIBUTE( ALPHA2 );
  3143. INITPARTICLE_ATTRIBUTE( SCRATCH_VEC );
  3144. INITPARTICLE_ATTRIBUTE( SCRATCH_FLOAT );
  3145. INITPARTICLE_ATTRIBUTE( UNUSED );
  3146. INITPARTICLE_ATTRIBUTE( PITCH );
  3147. INITPARTICLE_ATTRIBUTE( NORMAL );
  3148. INITPARTICLE_ATTRIBUTE( GLOW_RGB );
  3149. INITPARTICLE_ATTRIBUTE( GLOW_ALPHA );
  3150. for ( int i = 0; i < MAX_PARTICLE_ATTRIBUTES; i++ )
  3151. {
  3152. if ( !s_AttributeTable[ i ].pName )
  3153. {
  3154. // The above list of initializers needs updating!
  3155. Warning( "CParticleSystemMgr::InitAttributeTable has an out-of-date attribute list! (element %d not set up)\n", i );
  3156. Assert( 0 );
  3157. }
  3158. }
  3159. }
  3160. //----------------------------------------------------------------------------------
  3161. // String -> Attribute mapping
  3162. //----------------------------------------------------------------------------------
  3163. int CParticleSystemMgr::GetParticleAttributeByName( const char *pName ) const
  3164. {
  3165. // TODO: OPTIMIZATION: use Chris's CUtlStringToken class here to speed this up
  3166. for ( int i = 0; i < MAX_PARTICLE_ATTRIBUTES; i++ )
  3167. {
  3168. if ( !Q_stricmp( pName, s_AttributeTable[ i ].pName ) )
  3169. return i;
  3170. }
  3171. return -1;
  3172. }
  3173. //----------------------------------------------------------------------------------
  3174. // Attribute -> String mapping
  3175. //----------------------------------------------------------------------------------
  3176. const char *CParticleSystemMgr::GetParticleAttributeName( int nAttribute ) const
  3177. {
  3178. if ( ( nAttribute < 0 ) || ( nAttribute >= MAX_PARTICLE_ATTRIBUTES ) )
  3179. {
  3180. Assert( 0 );
  3181. return "unknown";
  3182. }
  3183. return s_AttributeTable[ nAttribute ].pName;
  3184. }
  3185. //----------------------------------------------------------------------------------
  3186. // Get the data type of a given attribute
  3187. //----------------------------------------------------------------------------------
  3188. EAttributeDataType CParticleSystemMgr::GetParticleAttributeDataType( int nAttribute ) const
  3189. {
  3190. Assert( nAttribute >= 0 );
  3191. Assert( nAttribute < MAX_PARTICLE_ATTRIBUTES );
  3192. return s_AttributeTable[ nAttribute ].nDataType;
  3193. }
  3194. //----------------------------------------------------------------------------------
  3195. // Cache/uncache materials used by particle systems
  3196. //----------------------------------------------------------------------------------
  3197. void CParticleSystemMgr::PrecacheParticleSystem( int nStringNumber, const char *pName )
  3198. {
  3199. if ( !pName || !pName[0] )
  3200. {
  3201. return;
  3202. }
  3203. ParticleSystemHandle_t hParticleSystem = GetParticleSystemIndex( pName );
  3204. // Used to display an error system if the requested one isn't known from the manifest
  3205. if ( hParticleSystem == UTL_INVAL_SYMBOL )
  3206. {
  3207. Warning( "Attempted to precache unknown particle system \"%s\"!\n", pName );
  3208. hParticleSystem = GetParticleSystemIndex( "error" );
  3209. }
  3210. CParticleSystemDefinition* pDef = FindParticleSystem( hParticleSystem );
  3211. CUtlVector< ParticleSystemHandle_t > &lookup = ( nStringNumber >= 0 ) ? m_PrecacheLookup : m_ClientPrecacheLookup;
  3212. if ( nStringNumber < 0 )
  3213. {
  3214. nStringNumber = - nStringNumber - 1;
  3215. }
  3216. int nCountToAdd = nStringNumber + 1 - lookup.Count();
  3217. for ( int i = 0; i < nCountToAdd; ++i )
  3218. {
  3219. lookup.AddToTail( UTL_INVAL_SYMBOL );
  3220. }
  3221. lookup[ nStringNumber ] = hParticleSystem;
  3222. if ( !pDef )
  3223. {
  3224. Warning( "Attempted to precache unknown particle system \"%s\"!\n", pName );
  3225. return;
  3226. }
  3227. pDef->Precache();
  3228. }
  3229. void CParticleSystemMgr::LevelShutdown( void )
  3230. {
  3231. #ifndef SERVER_PARTICLE_LIB
  3232. // InvalidateGlobalCollisionCache(); // keep the collision cahce out of the server binary for now
  3233. #endif
  3234. }
  3235. void CParticleSystemMgr::UncacheAllParticleSystems()
  3236. {
  3237. m_PrecacheLookup.RemoveAll();
  3238. m_ClientPrecacheLookup.RemoveAll();
  3239. if ( m_pParticleSystemDictionary )
  3240. {
  3241. int nCount = m_pParticleSystemDictionary->Count();
  3242. for ( int i = 0; i < nCount; ++i )
  3243. {
  3244. m_pParticleSystemDictionary->GetParticleSystem( i )->Uncache();
  3245. }
  3246. nCount = m_pParticleSystemDictionary->NameCount();
  3247. for ( ParticleSystemHandle_t h = 0; h < nCount; ++h )
  3248. {
  3249. m_pParticleSystemDictionary->FindParticleSystem( h )->Uncache();
  3250. }
  3251. }
  3252. // Flush sheets, as they can accumulate several MB of memory per map
  3253. FlushAllSheets();
  3254. }
  3255. //-----------------------------------------------------------------------------
  3256. // return the particle field name
  3257. //-----------------------------------------------------------------------------
  3258. static const char *s_pParticleFieldNames[MAX_PARTICLE_ATTRIBUTES] =
  3259. {
  3260. "Position", // XYZ, 0
  3261. "Life Duration", // LIFE_DURATION, 1 );
  3262. "Position Previous",// PREV_XYZ
  3263. "Radius", // RADIUS, 3 );
  3264. "Roll", // ROTATION, 4 );
  3265. "Roll Speed", // ROTATION_SPEED, 5 );
  3266. "Color", // TINT_RGB, 6 );
  3267. "Alpha", // ALPHA, 7 );
  3268. "Creation Time", // CREATION_TIME, 8 );
  3269. "Sequence Number", // SEQUENCE_NUMBER, 9 );
  3270. "Trail Length", // TRAIL_LENGTH, 10 );
  3271. "Particle ID", // PARTICLE_ID, 11 );
  3272. "Yaw", // YAW, 12 );
  3273. "Sequence Number 1",// SEQUENCE_NUMBER1, 13 );
  3274. "Hitbox Index", // HITBOX_INDEX, 14
  3275. "Hitbox Offset Position", // HITBOX_XYZ_RELATIVE 15
  3276. "Alpha Alternate", // ALPHA2, 16
  3277. "Scratch Vector", // SCRATCH_VEC 17
  3278. "Scratch Float", // SCRATCH_FLOAT 18
  3279. NULL,
  3280. "Pitch", // PITCH, 20
  3281. "Normal", // NORMAL, 21
  3282. "Glow RGB", //GLOW_RGB,22
  3283. "Glow Alpha", //GLOW_ALPHA,23
  3284. };
  3285. const char* CParticleSystemMgr::GetParticleFieldName( int nParticleField ) const
  3286. {
  3287. return s_pParticleFieldNames[nParticleField];
  3288. }
  3289. //-----------------------------------------------------------------------------
  3290. // Returns the available particle operators
  3291. //-----------------------------------------------------------------------------
  3292. void CParticleSystemMgr::AddParticleOperator( ParticleFunctionType_t nOpType,
  3293. IParticleOperatorDefinition *pOpFactory )
  3294. {
  3295. m_ParticleOperators[nOpType].AddToTail( pOpFactory );
  3296. }
  3297. static const char *s_pFilterNames[ ] =
  3298. {
  3299. "All",
  3300. "Position and Velocity",
  3301. "Life Duration",
  3302. "Parameter Remapping",
  3303. "Rotation",
  3304. "Size",
  3305. "Color and Opacity",
  3306. "Animation Sequence",
  3307. "Hitbox",
  3308. "Normal",
  3309. "Control Points"
  3310. };
  3311. const char *CParticleSystemMgr::GetFilterName( ParticleFilterType_t nFilterType ) const
  3312. {
  3313. COMPILE_TIME_ASSERT( ARRAYSIZE( s_pFilterNames ) == FILTER_COUNT );
  3314. return s_pFilterNames[nFilterType];
  3315. }
  3316. CUtlVector< IParticleOperatorDefinition *> &CParticleSystemMgr::GetAvailableParticleOperatorList( ParticleFunctionType_t nWhichList )
  3317. {
  3318. return m_ParticleOperators[nWhichList];
  3319. }
  3320. const DmxElementUnpackStructure_t *CParticleSystemMgr::GetParticleSystemDefinitionUnpackStructure()
  3321. {
  3322. return s_pParticleSystemDefinitionUnpack;
  3323. }
  3324. //------------------------------------------------------------------------------
  3325. // custom allocators for operators so simd aligned
  3326. //------------------------------------------------------------------------------
  3327. #include "tier0/memdbgoff.h"
  3328. void *CParticleOperatorInstance::operator new( size_t nSize )
  3329. {
  3330. return MemAlloc_AllocAligned( nSize, 16 );
  3331. }
  3332. void* CParticleOperatorInstance::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  3333. {
  3334. return MemAlloc_AllocAlignedFileLine( nSize, 16, pFileName, nLine );
  3335. }
  3336. void CParticleOperatorInstance::operator delete(void *pData)
  3337. {
  3338. if ( pData )
  3339. {
  3340. MemAlloc_FreeAligned( pData );
  3341. }
  3342. }
  3343. void CParticleOperatorInstance::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
  3344. {
  3345. if ( pData )
  3346. {
  3347. MemAlloc_FreeAligned( pData );
  3348. }
  3349. }
  3350. #include "tier0/memdbgon.h"
  3351. //-----------------------------------------------------------------------------
  3352. // Load a PCF file and list the particle systems in it
  3353. //-----------------------------------------------------------------------------
  3354. void CParticleSystemMgr::GetParticleSystemsInFile( const char *pFileName, CUtlVector< CUtlString > *pOutSystemNameList )
  3355. {
  3356. if( pOutSystemNameList == NULL )
  3357. return;
  3358. pOutSystemNameList->RemoveAll();
  3359. CUtlBuffer buf;
  3360. if ( !g_pFullFileSystem->ReadFile( pFileName, "GAME", buf ) )
  3361. {
  3362. return;
  3363. }
  3364. GetParticleSystemsInBuffer( buf, pOutSystemNameList );
  3365. }
  3366. void CParticleSystemMgr::GetParticleSystemsInBuffer( CUtlBuffer &buf, CUtlVector<CUtlString> *pOutSystemNameList )
  3367. {
  3368. if( pOutSystemNameList == NULL )
  3369. return;
  3370. pOutSystemNameList->RemoveAll();
  3371. DECLARE_DMX_CONTEXT( );
  3372. CDmxElement *pRoot;
  3373. if ( !UnserializeDMX( buf, &pRoot ) )
  3374. {
  3375. return;
  3376. }
  3377. if ( !Q_stricmp( pRoot->GetTypeString(), "DmeParticleSystemDefinition" ) )
  3378. {
  3379. pOutSystemNameList->AddToTail( pRoot->GetName() );
  3380. CleanupDMX( pRoot );
  3381. return;
  3382. }
  3383. const CDmxAttribute *pDefinitions = pRoot->GetAttribute( "particleSystemDefinitions" );
  3384. if ( !pDefinitions || pDefinitions->GetType() != AT_ELEMENT_ARRAY )
  3385. {
  3386. CleanupDMX( pRoot );
  3387. return;
  3388. }
  3389. const CUtlVector< CDmxElement* >& definitions = pDefinitions->GetArray<CDmxElement*>( );
  3390. int nCount = definitions.Count();
  3391. for ( int i = 0; i < nCount; ++i )
  3392. {
  3393. pOutSystemNameList->AddToTail( definitions[i]->GetName() );
  3394. }
  3395. CleanupDMX( pRoot );
  3396. }
  3397. //-----------------------------------------------------------------------------
  3398. // Read the particle config file from a utlbuffer
  3399. //-----------------------------------------------------------------------------
  3400. bool CParticleSystemMgr::ReadParticleDefinitions( CUtlBuffer &buf, const char *pFileName, bool bPrecache, bool bDecommitTempMemory )
  3401. {
  3402. DECLARE_DMX_CONTEXT_DECOMMIT( bDecommitTempMemory );
  3403. CDmxElement *pRoot;
  3404. if ( !UnserializeDMX( buf, &pRoot, pFileName ) )
  3405. {
  3406. Warning( "Unable to read particle definition %s! UtlBuffer is probably the wrong type!\n", pFileName );
  3407. return false;
  3408. }
  3409. if ( !Q_stricmp( pRoot->GetTypeString(), "DmeParticleSystemDefinition" ) )
  3410. {
  3411. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->AddParticleSystem( pRoot );
  3412. if ( pDef && bPrecache )
  3413. {
  3414. pDef->m_bAlwaysPrecache = true;
  3415. if ( m_bAllowPrecache )
  3416. {
  3417. pDef->Precache();
  3418. }
  3419. }
  3420. CleanupDMX( pRoot );
  3421. return true;
  3422. }
  3423. const CDmxAttribute *pDefinitions = pRoot->GetAttribute( "particleSystemDefinitions" );
  3424. if ( !pDefinitions || pDefinitions->GetType() != AT_ELEMENT_ARRAY )
  3425. {
  3426. CleanupDMX( pRoot );
  3427. return false;
  3428. }
  3429. const CUtlVector< CDmxElement* >& definitions = pDefinitions->GetArray<CDmxElement*>( );
  3430. int nCount = definitions.Count();
  3431. for ( int i = 0; i < nCount; ++i )
  3432. {
  3433. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->AddParticleSystem( definitions[i] );
  3434. if ( pDef && bPrecache )
  3435. {
  3436. pDef->m_bAlwaysPrecache = true;
  3437. if ( m_bAllowPrecache )
  3438. {
  3439. pDef->Precache();
  3440. }
  3441. }
  3442. }
  3443. CleanupDMX( pRoot );
  3444. return true;
  3445. }
  3446. //-----------------------------------------------------------------------------
  3447. // Decommits temporary memory
  3448. //-----------------------------------------------------------------------------
  3449. void CParticleSystemMgr::DecommitTempMemory()
  3450. {
  3451. DecommitDMXMemory();
  3452. }
  3453. //-----------------------------------------------------------------------------
  3454. // Sets the last simulation time, used for particle system sleeping logic
  3455. //-----------------------------------------------------------------------------
  3456. void CParticleSystemMgr::SetLastSimulationTime( float flTime )
  3457. {
  3458. m_flLastSimulationTime = flTime;
  3459. }
  3460. float CParticleSystemMgr::GetLastSimulationTime() const
  3461. {
  3462. return m_flLastSimulationTime;
  3463. }
  3464. //-----------------------------------------------------------------------------
  3465. // Sets the last simulation duration ( the amount of time we spent simulating particle ) last frame
  3466. // Used to fallback to cheaper particle systems under load
  3467. //-----------------------------------------------------------------------------
  3468. void CParticleSystemMgr::SetLastSimulationDuration( float flDuration )
  3469. {
  3470. m_flLastSimulationDuration = flDuration;
  3471. }
  3472. float CParticleSystemMgr::GetLastSimulationDuration() const
  3473. {
  3474. return m_flLastSimulationDuration;
  3475. }
  3476. //-----------------------------------------------------------------------------
  3477. // GPU/CPU Level
  3478. //-----------------------------------------------------------------------------
  3479. void CParticleSystemMgr::SetSystemLevel( int nCPULevel, int nGPULevel )
  3480. {
  3481. m_nCPULevel = nCPULevel;
  3482. m_nGPULevel = nGPULevel;
  3483. }
  3484. int CParticleSystemMgr::GetParticleCPULevel() const
  3485. {
  3486. return m_nCPULevel;
  3487. }
  3488. int CParticleSystemMgr::GetParticleGPULevel() const
  3489. {
  3490. return m_nGPULevel;
  3491. }
  3492. //-----------------------------------------------------------------------------
  3493. // Fallback paramters
  3494. //-----------------------------------------------------------------------------
  3495. void CParticleSystemMgr::SetFallbackParameters( float flBase, float flMultiplier, float flSimFallbackBaseMultiplier, float flSimThresholdMs )
  3496. {
  3497. m_flFallbackBase = flBase;
  3498. m_flFallbackMultiplier = flMultiplier;
  3499. m_flSimFallbackBaseMultiplier = flSimFallbackBaseMultiplier;
  3500. m_flSimThresholdMs = flSimThresholdMs;
  3501. }
  3502. float CParticleSystemMgr::GetFallbackBase() const
  3503. {
  3504. return m_flFallbackBase;
  3505. }
  3506. float CParticleSystemMgr::GetFallbackMultiplier() const
  3507. {
  3508. return m_flFallbackMultiplier;
  3509. }
  3510. float CParticleSystemMgr::GetSimFallbackThresholdMs() const
  3511. {
  3512. return m_flSimThresholdMs;
  3513. }
  3514. float CParticleSystemMgr::GetSimFallbackBaseMultiplier() const
  3515. {
  3516. return m_flSimFallbackBaseMultiplier;
  3517. }
  3518. //-----------------------------------------------------------------------------
  3519. // Unserialization-related methods
  3520. //-----------------------------------------------------------------------------
  3521. void CParticleSystemMgr::AddParticleSystem( CDmxElement *pParticleSystem )
  3522. {
  3523. m_pParticleSystemDictionary->AddParticleSystem( pParticleSystem );
  3524. }
  3525. CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( const char *pName )
  3526. {
  3527. return m_pParticleSystemDictionary->FindParticleSystem( pName );
  3528. }
  3529. CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( const DmObjectId_t& id )
  3530. {
  3531. return m_pParticleSystemDictionary->FindParticleSystem( id );
  3532. }
  3533. CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( ParticleSystemHandle_t hParticleSystem )
  3534. {
  3535. return m_pParticleSystemDictionary->FindParticleSystem( hParticleSystem );
  3536. }
  3537. CParticleSystemDefinition* CParticleSystemMgr::FindPrecachedParticleSystem( int nPrecacheIndex )
  3538. {
  3539. CUtlVector< ParticleSystemHandle_t > &lookup = ( nPrecacheIndex >= 0 ) ? m_PrecacheLookup : m_ClientPrecacheLookup;
  3540. if ( nPrecacheIndex < 0 )
  3541. {
  3542. nPrecacheIndex = - nPrecacheIndex - 1;
  3543. }
  3544. if ( nPrecacheIndex >= lookup.Count() )
  3545. return NULL;
  3546. return FindParticleSystem( lookup[nPrecacheIndex] );
  3547. }
  3548. //-----------------------------------------------------------------------------
  3549. // Read the particle config file from a utlbuffer
  3550. //-----------------------------------------------------------------------------
  3551. bool CParticleSystemMgr::ReadParticleConfigFile( CUtlBuffer &buf, bool bPrecache, bool bDecommitTempMemory, const char *pFileName )
  3552. {
  3553. return ReadParticleDefinitions( buf, pFileName, bPrecache, bDecommitTempMemory );
  3554. }
  3555. //-----------------------------------------------------------------------------
  3556. // Read the particle config file from a utlbuffer
  3557. //-----------------------------------------------------------------------------
  3558. bool CParticleSystemMgr::ReadParticleConfigFile( const char *pFileName, bool bPrecache, bool bDecommitTempMemory )
  3559. {
  3560. // Names starting with a '!' are always precached.
  3561. if ( pFileName[0] == '!' )
  3562. {
  3563. bPrecache = true;
  3564. ++pFileName;
  3565. }
  3566. if ( PLATFORM_EXT[0] )
  3567. {
  3568. char szTargetName[MAX_PATH];
  3569. CreatePlatformFilename( pFileName, szTargetName, sizeof( szTargetName ) );
  3570. CUtlBuffer fileBuffer;
  3571. bool bHaveParticles = g_pFullFileSystem->ReadFile( szTargetName, "GAME", fileBuffer );
  3572. if ( bHaveParticles )
  3573. {
  3574. fileBuffer.SetBigEndian( false );
  3575. return ReadParticleConfigFile( fileBuffer, bPrecache, bDecommitTempMemory, szTargetName );
  3576. }
  3577. else
  3578. {
  3579. // 360/PS3 version should have been there, 360/PS3 zips can only have binary particles
  3580. Warning( "Particles: Missing '%s'\n", szTargetName );
  3581. return false;
  3582. }
  3583. }
  3584. // char pFallbackBuf[MAX_PATH];
  3585. if ( IsPC() )
  3586. {
  3587. // Look for fallback particle systems
  3588. char pTemp[MAX_PATH];
  3589. Q_StripExtension( pFileName, pTemp, sizeof(pTemp) );
  3590. const char *pExt = Q_GetFileExtension( pFileName );
  3591. if ( !pExt )
  3592. {
  3593. pExt = "pcf";
  3594. }
  3595. /*
  3596. // FIXME: Hook GPU level and/or CPU level into fallbacks instead of dxsupport level
  3597. if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 )
  3598. {
  3599. Q_snprintf( pFallbackBuf, sizeof(pFallbackBuf), "%s_dx80.%s", pTemp, pExt );
  3600. if ( g_pFullFileSystem->FileExists( pFallbackBuf ) )
  3601. {
  3602. pFileName = pFallbackBuf;
  3603. }
  3604. }
  3605. else if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 90 && g_pMaterialSystemHardwareConfig->PreferReducedFillrate() )
  3606. {
  3607. Q_snprintf( pFallbackBuf, sizeof(pFallbackBuf), "%s_dx90_slow.%s", pTemp, pExt );
  3608. if ( g_pFullFileSystem->FileExists( pFallbackBuf ) )
  3609. {
  3610. pFileName = pFallbackBuf;
  3611. }
  3612. }
  3613. */
  3614. }
  3615. CUtlBuffer buf( 0, 0, 0 );
  3616. if ( IsX360() || IsPS3() )
  3617. {
  3618. // fell through, load as pc particle resource file
  3619. buf.ActivateByteSwapping( true );
  3620. }
  3621. if ( g_pFullFileSystem->ReadFile( pFileName, "GAME", buf ) )
  3622. {
  3623. return ReadParticleConfigFile( buf, bPrecache, bDecommitTempMemory, pFileName );
  3624. }
  3625. Warning( "Particles: Missing '%s'\n", pFileName );
  3626. return false;
  3627. }
  3628. //-----------------------------------------------------------------------------
  3629. // Write a specific particle config to a utlbuffer
  3630. //-----------------------------------------------------------------------------
  3631. bool CParticleSystemMgr::WriteParticleConfigFile( const char *pParticleSystemName, CUtlBuffer &buf, bool bPreventNameBasedLookup )
  3632. {
  3633. DECLARE_DMX_CONTEXT();
  3634. // Create DMX elements representing the particle system definition
  3635. CDmxElement *pParticleSystem = CreateParticleDmxElement( pParticleSystemName );
  3636. return WriteParticleConfigFile( pParticleSystem, buf, bPreventNameBasedLookup );
  3637. }
  3638. bool CParticleSystemMgr::WriteParticleConfigFile( const DmObjectId_t& id, CUtlBuffer &buf, bool bPreventNameBasedLookup )
  3639. {
  3640. DECLARE_DMX_CONTEXT();
  3641. // Create DMX elements representing the particle system definition
  3642. CDmxElement *pParticleSystem = CreateParticleDmxElement( id );
  3643. return WriteParticleConfigFile( pParticleSystem, buf, bPreventNameBasedLookup );
  3644. }
  3645. bool CParticleSystemMgr::WriteParticleConfigFile( CDmxElement *pParticleSystem, CUtlBuffer &buf, bool bPreventNameBasedLookup )
  3646. {
  3647. pParticleSystem->SetValue( "preventNameBasedLookup", bPreventNameBasedLookup );
  3648. CDmxAttribute* pAttribute = pParticleSystem->GetAttribute( "children" );
  3649. const CUtlVector< CDmxElement* >& children = pAttribute->GetArray<CDmxElement*>( );
  3650. int nCount = children.Count();
  3651. for ( int i = 0; i < nCount; ++i )
  3652. {
  3653. CDmxElement *pChildRef = children[ i ];
  3654. CDmxElement *pChild = pChildRef->GetValue<CDmxElement*>( "child" );
  3655. pChild->SetValue( "preventNameBasedLookup", bPreventNameBasedLookup );
  3656. }
  3657. // Now write the DMX elements out
  3658. bool bOk = SerializeDMX( buf, pParticleSystem );
  3659. CleanupDMX( pParticleSystem );
  3660. return bOk;
  3661. }
  3662. ParticleSystemHandle_t CParticleSystemMgr::GetParticleSystemIndex( const char *pParticleSystemName )
  3663. {
  3664. if ( !pParticleSystemName )
  3665. return UTL_INVAL_SYMBOL;
  3666. return m_pParticleSystemDictionary->FindParticleSystemHandle( pParticleSystemName );
  3667. }
  3668. ParticleSystemHandle_t CParticleSystemMgr::FindOrAddParticleSystemIndex( const char *pParticleSystemName )
  3669. {
  3670. if ( !pParticleSystemName )
  3671. return UTL_INVAL_SYMBOL;
  3672. return m_pParticleSystemDictionary->FindOrAddParticleSystemHandle( pParticleSystemName );
  3673. }
  3674. const char *CParticleSystemMgr::GetParticleSystemNameFromIndex( ParticleSystemHandle_t iIndex )
  3675. {
  3676. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( iIndex );
  3677. return pDef ? pDef->GetName() : "Unknown";
  3678. }
  3679. int CParticleSystemMgr::GetParticleSystemCount( void )
  3680. {
  3681. return m_pParticleSystemDictionary->NameCount();
  3682. }
  3683. //-----------------------------------------------------------------------------
  3684. // Factory method for creating particle collections
  3685. //-----------------------------------------------------------------------------
  3686. CParticleCollection *CParticleSystemMgr::CreateParticleCollection( const char *pParticleSystemName, float flDelay, int nRandomSeed )
  3687. {
  3688. if ( !pParticleSystemName )
  3689. return NULL;
  3690. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pParticleSystemName );
  3691. if ( !pDef )
  3692. {
  3693. Warning( "Attempted to create unknown particle system type %s\n", pParticleSystemName );
  3694. return NULL;
  3695. }
  3696. CParticleCollection *pParticleCollection = new CParticleCollection;
  3697. pParticleCollection->Init( pDef, flDelay, nRandomSeed );
  3698. return pParticleCollection;
  3699. }
  3700. CParticleCollection *CParticleSystemMgr::CreateParticleCollection( ParticleSystemHandle_t particleSystemName, float flDelay, int nRandomSeed )
  3701. {
  3702. if ( particleSystemName == UTL_INVAL_SYMBOL )
  3703. return NULL;
  3704. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( particleSystemName );
  3705. if ( !pDef )
  3706. {
  3707. Warning( "Attempted to create unknown particle system with unknown symbol\n" );
  3708. return NULL;
  3709. }
  3710. CParticleCollection *pParticleCollection = new CParticleCollection;
  3711. pParticleCollection->Init( pDef, flDelay, nRandomSeed );
  3712. return pParticleCollection;
  3713. }
  3714. CParticleCollection *CParticleSystemMgr::CreateParticleCollection( const DmObjectId_t &id, float flDelay, int nRandomSeed )
  3715. {
  3716. if ( !IsUniqueIdValid( id ) )
  3717. return NULL;
  3718. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
  3719. if ( !pDef )
  3720. {
  3721. char pBuf[256];
  3722. UniqueIdToString( id, pBuf, sizeof(pBuf) );
  3723. Warning( "Attempted to create unknown particle system id %s\n", pBuf );
  3724. return NULL;
  3725. }
  3726. CParticleCollection *pParticleCollection = new CParticleCollection;
  3727. pParticleCollection->Init( pDef, flDelay, nRandomSeed );
  3728. return pParticleCollection;
  3729. }
  3730. //--------------------------------------------------------------------------------
  3731. // Is a particular particle system defined?
  3732. //--------------------------------------------------------------------------------
  3733. bool CParticleSystemMgr::IsParticleSystemDefined( const DmObjectId_t &id )
  3734. {
  3735. if ( !IsUniqueIdValid( id ) )
  3736. return false;
  3737. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
  3738. return ( pDef != NULL );
  3739. }
  3740. //--------------------------------------------------------------------------------
  3741. // Is a particular particle system defined?
  3742. //--------------------------------------------------------------------------------
  3743. bool CParticleSystemMgr::IsParticleSystemDefined( const char *pName )
  3744. {
  3745. if ( !pName || !pName[0] )
  3746. return false;
  3747. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pName );
  3748. return ( pDef != NULL );
  3749. }
  3750. //--------------------------------------------------------------------------------
  3751. // Particle kill list
  3752. //--------------------------------------------------------------------------------
  3753. //--------------------------------------------------------------------------------
  3754. // Serialization-related methods
  3755. //--------------------------------------------------------------------------------
  3756. CDmxElement *CParticleSystemMgr::CreateParticleDmxElement( const DmObjectId_t &id )
  3757. {
  3758. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
  3759. // Create DMX elements representing the particle system definition
  3760. return pDef->Write( );
  3761. }
  3762. CDmxElement *CParticleSystemMgr::CreateParticleDmxElement( const char *pParticleSystemName )
  3763. {
  3764. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pParticleSystemName );
  3765. // Create DMX elements representing the particle system definition
  3766. return pDef->Write( );
  3767. }
  3768. //--------------------------------------------------------------------------------
  3769. // Particle sheets
  3770. //--------------------------------------------------------------------------------
  3771. static unsigned int s_nBaseTextureVarCache = 0;
  3772. CSheet *CParticleSystemMgr::FindOrLoadSheet( CParticleSystemDefinition *pDef, bool bTryReloading )
  3773. {
  3774. if ( !m_bShouldLoadSheets )
  3775. return NULL;
  3776. if ( !bTryReloading )
  3777. {
  3778. if ( pDef->IsSheetSymbolCached() )
  3779. {
  3780. if ( !pDef->GetSheetSymbol().IsValid() )
  3781. return NULL;
  3782. return m_SheetList[ pDef->GetSheetSymbol() ];
  3783. }
  3784. pDef->CacheSheetSymbol( UTL_INVAL_SYMBOL );
  3785. }
  3786. IMaterial *pMaterial = pDef->GetMaterial();
  3787. if ( !pMaterial )
  3788. return NULL;
  3789. IMaterialVar *pVar = pMaterial->FindVarFast( "$basetexture", &s_nBaseTextureVarCache );
  3790. if ( !pVar || !pVar->IsDefined() )
  3791. return NULL;
  3792. ITexture *pTex = pVar->GetTextureValue();
  3793. if ( !pTex || pTex->IsError() )
  3794. return NULL;
  3795. CSheet *pNewSheet = NULL;
  3796. int nCurCount = m_SheetList.GetNumStrings();
  3797. CUtlSymbol sheetName = m_SheetList.AddString( pTex->GetName() );
  3798. if ( ( sheetName < nCurCount ) && ( !bTryReloading ) )
  3799. {
  3800. // Means the string was already known
  3801. pNewSheet = m_SheetList[ sheetName ];
  3802. }
  3803. else
  3804. {
  3805. // get compact sheet representation held by texture
  3806. size_t numBytes;
  3807. void const *pSheetData = pTex->GetResourceData( VTF_RSRC_SHEET, &numBytes );
  3808. if ( pSheetData )
  3809. {
  3810. // expand compact sheet into fatter runtime form
  3811. CUtlBuffer bufLoad( pSheetData, numBytes, CUtlBuffer::READ_ONLY );
  3812. pNewSheet = new CSheet( bufLoad );
  3813. }
  3814. m_SheetList[ sheetName ] = pNewSheet;
  3815. }
  3816. pDef->CacheSheetSymbol( sheetName );
  3817. return pNewSheet;
  3818. }
  3819. void CParticleSystemMgr::FlushAllSheets( void )
  3820. {
  3821. m_SheetList.PurgeAndDeleteElements();
  3822. if ( !m_pParticleSystemDictionary )
  3823. return;
  3824. int nCount = m_pParticleSystemDictionary->Count();
  3825. for ( int i = 0; i < nCount; ++i )
  3826. {
  3827. CParticleSystemDefinition* pDef = m_pParticleSystemDictionary->GetParticleSystem( i );
  3828. pDef->InvalidateSheetSymbol();
  3829. }
  3830. nCount = m_pParticleSystemDictionary->NameCount();
  3831. for ( ParticleSystemHandle_t h = 0; h < nCount; ++h )
  3832. {
  3833. CParticleSystemDefinition* pDef = m_pParticleSystemDictionary->FindParticleSystem( h );
  3834. pDef->InvalidateSheetSymbol();
  3835. }
  3836. }
  3837. void CParticleSystemMgr::ShouldLoadSheets( bool bLoadSheets )
  3838. {
  3839. // Client loads sheets for rendering, server doesn't need to.
  3840. m_bShouldLoadSheets = bLoadSheets;
  3841. }
  3842. //-----------------------------------------------------------------------------
  3843. // Render cache
  3844. //-----------------------------------------------------------------------------
  3845. void CParticleSystemMgr::ResetRenderCache( void )
  3846. {
  3847. int nCount = m_RenderCache.Count();
  3848. for ( int i = 0; i < nCount; ++i )
  3849. {
  3850. m_RenderCache[i].m_ParticleCollections.RemoveAll();
  3851. }
  3852. }
  3853. void CParticleSystemMgr::AddToRenderCache( CParticleCollection *pParticles )
  3854. {
  3855. if ( !pParticles->IsValid() )
  3856. return;
  3857. IMaterial *pMaterial = pParticles->m_pDef->GetMaterial();
  3858. if ( ( !pMaterial) || pMaterial->IsTranslucent() )
  3859. return;
  3860. pParticles->m_flNextSleepTime = MAX( pParticles->m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + pParticles->m_pDef->m_flNoDrawTimeToGoToSleep ));
  3861. // Find the current rope list.
  3862. int iRenderCache = 0;
  3863. int nRenderCacheCount = m_RenderCache.Count();
  3864. for ( ; iRenderCache < nRenderCacheCount; ++iRenderCache )
  3865. {
  3866. if ( ( pParticles->m_pDef->GetMaterial() == m_RenderCache[iRenderCache].m_pMaterial ) )
  3867. break;
  3868. }
  3869. // A full rope list should have been generate in CreateRenderCache
  3870. // If we didn't find one, then allocate the mofo.
  3871. if ( iRenderCache == nRenderCacheCount )
  3872. {
  3873. iRenderCache = m_RenderCache.AddToTail();
  3874. m_RenderCache[iRenderCache].m_pMaterial = pParticles->m_pDef->GetMaterial();
  3875. }
  3876. m_RenderCache[iRenderCache].m_ParticleCollections.AddToTail( pParticles );
  3877. for( CParticleCollection *p = pParticles->m_Children.m_pHead; p; p = p->m_pNext )
  3878. {
  3879. AddToRenderCache( p );
  3880. }
  3881. }
  3882. void CParticleSystemMgr::BuildBatchList( int iRenderCache, IMatRenderContext *pRenderContext, CUtlVector< Batch_t >& batches )
  3883. {
  3884. batches.RemoveAll();
  3885. IMaterial *pMaterial = m_RenderCache[iRenderCache].m_pMaterial;
  3886. int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
  3887. int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
  3888. int nRemainingVertices = nMaxVertices;
  3889. int nRemainingIndices = nMaxIndices;
  3890. int i = batches.AddToTail();
  3891. Batch_t* pBatch = &batches[i];
  3892. pBatch->m_nVertCount = 0;
  3893. pBatch->m_nIndexCount = 0;
  3894. // Ask each renderer about the # of verts + ints it will draw
  3895. int nCacheCount = m_RenderCache[iRenderCache].m_ParticleCollections.Count();
  3896. for ( int iCache = 0; iCache < nCacheCount; ++iCache )
  3897. {
  3898. CParticleCollection *pParticles = m_RenderCache[iRenderCache].m_ParticleCollections[iCache];
  3899. if ( !pParticles->IsValid() )
  3900. continue;
  3901. int nRenderCount = pParticles->GetRendererCount();
  3902. for ( int j = 0; j < nRenderCount; ++j )
  3903. {
  3904. int nFirstParticle = 0;
  3905. while ( nFirstParticle < pParticles->m_nActiveParticles )
  3906. {
  3907. int i;
  3908. BatchStep_t step;
  3909. step.m_pParticles = pParticles;
  3910. step.m_pRenderer = pParticles->GetRenderer( j );
  3911. step.m_pContext = pParticles->GetRendererContext( j );
  3912. step.m_nFirstParticle = nFirstParticle;
  3913. step.m_nParticleCount = step.m_pRenderer->GetParticlesToRender( pParticles,
  3914. step.m_pContext, nFirstParticle, nRemainingVertices, nRemainingIndices, &step.m_nVertCount, &i );
  3915. nFirstParticle += step.m_nParticleCount;
  3916. if ( step.m_nParticleCount > 0 )
  3917. {
  3918. pBatch->m_nVertCount += step.m_nVertCount;
  3919. pBatch->m_nIndexCount += i;
  3920. pBatch->m_BatchStep.AddToTail( step );
  3921. Assert( pBatch->m_nVertCount <= nMaxVertices && pBatch->m_nIndexCount <= nMaxIndices );
  3922. }
  3923. else
  3924. {
  3925. if ( pBatch->m_nVertCount == 0 )
  3926. break;
  3927. // Not enough room
  3928. Assert( pBatch->m_nVertCount > 0 && pBatch->m_nIndexCount > 0 );
  3929. int j = batches.AddToTail();
  3930. pBatch = &batches[j];
  3931. pBatch->m_nVertCount = 0;
  3932. pBatch->m_nIndexCount = 0;
  3933. nRemainingVertices = nMaxVertices;
  3934. nRemainingIndices = nMaxIndices;
  3935. }
  3936. }
  3937. }
  3938. }
  3939. if ( pBatch->m_nVertCount <= 0 || pBatch->m_nIndexCount <= 0 )
  3940. {
  3941. batches.FastRemove( batches.Count() - 1 );
  3942. }
  3943. }
  3944. void CParticleSystemMgr::DumpParticleList( const char *pNameSubstring /* = NULL */ )
  3945. {
  3946. if ( pNameSubstring )
  3947. {
  3948. DevMsg( "New Particle Systems Matching '%s':\n", pNameSubstring );
  3949. }
  3950. else
  3951. {
  3952. DevMsg( "All Particle Systems:\n" );
  3953. }
  3954. for ( int i = 0; i < m_pParticleSystemDictionary->NameCount(); i++ )
  3955. {
  3956. CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
  3957. if ( !pNameSubstring || V_stristr(p->GetName(),pNameSubstring) )
  3958. {
  3959. for ( CParticleCollection *c = p->FirstCollection(); c; c = c->GetNextCollectionUsingSameDef() )
  3960. {
  3961. Vector min,max,center;
  3962. c->GetBounds( &min, &max );
  3963. center = (min+max)*0.5f;
  3964. DevMsg( "%40s: Age: %6.2f, NumActive: %3d, Bounds Center: (%.2f,%.2f,%.2f) (0x%p)\n", p->GetName(), c->m_flCurTime, c->m_nActiveParticles, center.x, center.y, center.z, c );
  3965. }
  3966. }
  3967. }
  3968. }
  3969. void CParticleSystemMgr::DumpProfileInformation( void )
  3970. {
  3971. #if MEASURE_PARTICLE_PERF
  3972. int nTotalTests = 0;
  3973. int nActualTests = 0;
  3974. FileHandle_t fh = g_pFullFileSystem->Open( "particle_profile.csv", "w", "DEFAULT_WRITE_PATH" );
  3975. if ( fh == FILESYSTEM_INVALID_HANDLE )
  3976. {
  3977. Warning( "*** Unable to open profile file!\n" );
  3978. return;
  3979. }
  3980. g_pFullFileSystem->FPrintf( fh, "numframes,%d\n", m_nNumFramesMeasured );
  3981. g_pFullFileSystem->FPrintf( fh, "name, total time, max time, max particles, allocated particles, total render time, max render time, number of intersection tests, number of actual traces\n");
  3982. for( int i=0; i < m_pParticleSystemDictionary->NameCount(); i++ )
  3983. {
  3984. CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
  3985. if ( p->m_nMaximumActiveParticles )
  3986. {
  3987. nTotalTests += p->m_nNumIntersectionTests;
  3988. nActualTests +=p->m_nNumActualRayTraces;
  3989. g_pFullFileSystem->FPrintf( fh, "%s,%f,%f,%d,%d,%f,%f,%d,%d\n", p->m_Name.Get(),
  3990. p->m_flTotalSimTime, p->m_flMaxMeasuredSimTime, p->m_nMaximumActiveParticles,
  3991. p->m_nMaxParticles, p->m_flTotalRenderTime, p->m_flMaxMeasuredRenderTime, p->m_nNumIntersectionTests(), p->m_nNumActualRayTraces() );
  3992. }
  3993. }
  3994. if ( nTotalTests )
  3995. {
  3996. g_pFullFileSystem->FPrintf( fh, "\n\nTrace cache efficiency = %f%%\n\n", 100.0 * ( 1.0 - ( nActualTests * ( 1.0 / nTotalTests ) ) ) );
  3997. }
  3998. g_pFullFileSystem->FPrintf( fh, "\n\nopname, total time, max time, total render time, max render time\n");
  3999. for(int i=0; i < ARRAYSIZE( m_ParticleOperators ); i++)
  4000. {
  4001. for(int j=0; j < m_ParticleOperators[i].Count() ; j++ )
  4002. {
  4003. float flmax = m_ParticleOperators[i][j]->MaximumRecordedExecutionTime();
  4004. float fltotal = m_ParticleOperators[i][j]->TotalRecordedExecutionTime();
  4005. if ( fltotal > 0.0 )
  4006. {
  4007. g_pFullFileSystem->FPrintf( fh, "%s,%f,%f\n",
  4008. m_ParticleOperators[i][j]->GetName(), fltotal, flmax );
  4009. }
  4010. }
  4011. }
  4012. g_pFullFileSystem->Close( fh );
  4013. #endif
  4014. }
  4015. void CParticleSystemMgr::CommitProfileInformation( bool bCommit )
  4016. {
  4017. #if MEASURE_PARTICLE_PERF
  4018. if ( 1 )
  4019. {
  4020. if ( bCommit )
  4021. {
  4022. m_nNumFramesMeasured++;
  4023. }
  4024. for( int i=0; i < m_pParticleSystemDictionary->NameCount(); i++ )
  4025. {
  4026. CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
  4027. if ( bCommit )
  4028. {
  4029. p->m_flTotalSimTime += p->m_flUncomittedTotalSimTime;
  4030. p->m_flTotalRenderTime += p->m_flUncomittedTotalRenderTime;
  4031. }
  4032. p->m_flUncomittedTotalSimTime = 0.;
  4033. p->m_flUncomittedTotalRenderTime = 0.;
  4034. }
  4035. for(int i=0; i < ARRAYSIZE( m_ParticleOperators ); i++)
  4036. {
  4037. for(int j=0; j < m_ParticleOperators[i].Count() ; j++ )
  4038. {
  4039. if ( bCommit )
  4040. {
  4041. m_ParticleOperators[i][j]->m_flTotalExecutionTime += m_ParticleOperators[i][j]->m_flUncomittedTime;
  4042. }
  4043. m_ParticleOperators[i][j]->m_flUncomittedTime = 0;
  4044. }
  4045. }
  4046. }
  4047. #endif
  4048. }
  4049. void CParticleSystemMgr::DrawRenderCache( IMatRenderContext *pRenderContext, bool bShadowDepth )
  4050. {
  4051. int nRenderCacheCount = m_RenderCache.Count();
  4052. if ( nRenderCacheCount == 0 )
  4053. return;
  4054. VPROF_BUDGET( "CParticleSystemMgr::DrawRenderCache", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  4055. pRenderContext->MatrixMode( MATERIAL_MODEL );
  4056. pRenderContext->PushMatrix();
  4057. pRenderContext->LoadIdentity();
  4058. CUtlVector< Batch_t > batches( 0, 8 );
  4059. for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache )
  4060. {
  4061. int nCacheCount = m_RenderCache[iRenderCache].m_ParticleCollections.Count();
  4062. if ( nCacheCount == 0 )
  4063. continue;
  4064. // FIXME: When rendering shadow depth, do it all in 1 batch
  4065. IMaterial *pMaterial = bShadowDepth ? m_pShadowDepthMaterial : m_RenderCache[iRenderCache].m_pMaterial;
  4066. BuildBatchList( iRenderCache, pRenderContext, batches );
  4067. int nBatchCount = batches.Count();
  4068. if ( nBatchCount == 0 )
  4069. continue;
  4070. pRenderContext->Bind( pMaterial );
  4071. CMeshBuilder meshBuilder;
  4072. IMesh* pMesh = pRenderContext->GetDynamicMesh( );
  4073. for ( int i = 0; i < nBatchCount; ++i )
  4074. {
  4075. const Batch_t& batch = batches[i];
  4076. Assert( batch.m_nVertCount > 0 && batch.m_nIndexCount > 0 );
  4077. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, batch.m_nVertCount, batch.m_nIndexCount );
  4078. int nVertexOffset = 0;
  4079. int nBatchStepCount = batch.m_BatchStep.Count();
  4080. for ( int j = 0; j < nBatchStepCount; ++j )
  4081. {
  4082. const BatchStep_t &step = batch.m_BatchStep[j];
  4083. // FIXME: this will break if it ever calls into C_OP_RenderSprites::Render[TwoSequence]SpriteCard()
  4084. // (need to protect against that and/or split the meshBuilder batch to support that path here)
  4085. step.m_pRenderer->RenderUnsorted( step.m_pParticles, step.m_pContext, pRenderContext,
  4086. meshBuilder, nVertexOffset, step.m_nFirstParticle, step.m_nParticleCount );
  4087. nVertexOffset += step.m_nVertCount;
  4088. }
  4089. meshBuilder.End();
  4090. pMesh->Draw();
  4091. }
  4092. }
  4093. ResetRenderCache( );
  4094. pRenderContext->MatrixMode( MATERIAL_MODEL );
  4095. pRenderContext->PopMatrix();
  4096. }
  4097. void IParticleSystemQuery::GetRandomPointsOnControllingObjectHitBox(
  4098. CParticleCollection *pParticles,
  4099. int nControlPointNumber,
  4100. int nNumPtsOut,
  4101. float flBBoxScale,
  4102. int nNumTrysToGetAPointInsideTheModel,
  4103. Vector *pPntsOut,
  4104. Vector vecDirectionalBias,
  4105. Vector *pHitBoxRelativeCoordOut,
  4106. int *pHitBoxIndexOut,
  4107. int nDesiredHitbox,
  4108. const char *pszHitboxSetName
  4109. )
  4110. {
  4111. for(int i=0; i < nNumPtsOut; i++)
  4112. {
  4113. pPntsOut[i]=pParticles->ControlPoint( nControlPointNumber ).m_Position;
  4114. if ( pHitBoxRelativeCoordOut )
  4115. pHitBoxRelativeCoordOut[i].Init();
  4116. if ( pHitBoxIndexOut )
  4117. pHitBoxIndexOut[i] = -1;
  4118. }
  4119. }
  4120. void CParticleCollection::UpdateHitBoxInfo( int nControlPointNumber, const char *pszHitboxSetName )
  4121. {
  4122. CModelHitBoxesInfo &hb = ControlPointHitBox( nControlPointNumber );
  4123. if ( hb.m_flLastUpdateTime == m_flCurTime )
  4124. return; // up to date
  4125. hb.m_flLastUpdateTime = m_flCurTime;
  4126. // make sure space allocated
  4127. if ( ! hb.m_pHitBoxes )
  4128. hb.m_pHitBoxes = new ModelHitBoxInfo_t[ MAXSTUDIOBONES ];
  4129. if ( ! hb.m_pPrevBoxes )
  4130. hb.m_pPrevBoxes = new ModelHitBoxInfo_t[ MAXSTUDIOBONES ];
  4131. // save current into prev
  4132. hb.m_nNumPrevHitBoxes = hb.m_nNumHitBoxes;
  4133. hb.m_flPrevLastUpdateTime = hb.m_flLastUpdateTime;
  4134. V_swap( hb.m_pHitBoxes, hb.m_pPrevBoxes );
  4135. // issue hitbox query
  4136. hb.m_nNumHitBoxes = g_pParticleSystemMgr->Query()->GetControllingObjectHitBoxInfo(
  4137. this, nControlPointNumber, MAXSTUDIOBONES, hb.m_pHitBoxes, pszHitboxSetName );
  4138. }
  4139. void GetParticleManifest( CUtlVector<CUtlString>& list )
  4140. {
  4141. GetParticleManifest( list, "particles/particles_manifest.txt" );
  4142. }
  4143. void GetParticleManifest( CUtlVector<CUtlString>& list, const char *pFile )
  4144. {
  4145. // Open the manifest file, and read the particles specified inside it
  4146. KeyValues *manifest = new KeyValues( pFile );
  4147. if ( manifest->LoadFromFile( g_pFullFileSystem, pFile, "GAME" ) )
  4148. {
  4149. for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
  4150. {
  4151. if ( !Q_stricmp( sub->GetName(), "file" ) )
  4152. {
  4153. list.AddToTail( sub->GetString() );
  4154. continue;
  4155. }
  4156. Warning( "CParticleMgr::Init: Manifest '%s' with bogus file type '%s', expecting 'file'\n", pFile, sub->GetName() );
  4157. }
  4158. }
  4159. else
  4160. {
  4161. Warning( "PARTICLE SYSTEM: Unable to load manifest file '%s'\n", pFile );
  4162. }
  4163. manifest->deleteThis();
  4164. }