Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3881 lines
122 KiB

  1. //========= Copyright 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 "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. // memdbgon must be the last include file in a .cpp file!!!
  28. #include "tier0/memdbgon.h"
  29. // rename table from the great rename
  30. static const char *s_RemapOperatorNameTable[]={
  31. "alpha_fade", "Alpha Fade and Decay",
  32. "alpha_fade_in_random", "Alpha Fade In Random",
  33. "alpha_fade_out_random", "Alpha Fade Out Random",
  34. "basic_movement", "Movement Basic",
  35. "color_fade", "Color Fade",
  36. "controlpoint_light", "Color Light From Control Point",
  37. "Dampen Movement Relative to Control Point", "Movement Dampen Relative to Control Point",
  38. "Distance Between Control Points Scale", "Remap Distance Between Two Control Points to Scalar",
  39. "Distance to Control Points Scale", "Remap Distance to Control Point to Scalar",
  40. "lifespan_decay", "Lifespan Decay",
  41. "lock to bone", "Movement Lock to Bone",
  42. "postion_lock_to_controlpoint", "Movement Lock to Control Point",
  43. "maintain position along path", "Movement Maintain Position Along Path",
  44. "Match Particle Velocities", "Movement Match Particle Velocities",
  45. "Max Velocity", "Movement Max Velocity",
  46. "noise", "Noise Scalar",
  47. "vector noise", "Noise Vector",
  48. "oscillate_scalar", "Oscillate Scalar",
  49. "oscillate_vector", "Oscillate Vector",
  50. "Orient Rotation to 2D Direction", "Rotation Orient to 2D Direction",
  51. "radius_scale", "Radius Scale",
  52. "Random Cull", "Cull Random",
  53. "remap_scalar", "Remap Scalar",
  54. "rotation_movement", "Rotation Basic",
  55. "rotation_spin", "Rotation Spin Roll",
  56. "rotation_spin yaw", "Rotation Spin Yaw",
  57. "alpha_random", "Alpha Random",
  58. "color_random", "Color Random",
  59. "create from parent particles", "Position From Parent Particles",
  60. "Create In Hierarchy", "Position In CP Hierarchy",
  61. "random position along path", "Position Along Path Random",
  62. "random position on model", "Position on Model Random",
  63. "sequential position along path", "Position Along Path Sequential",
  64. "position_offset_random", "Position Modify Offset Random",
  65. "position_warp_random", "Position Modify Warp Random",
  66. "position_within_box", "Position Within Box Random",
  67. "position_within_sphere", "Position Within Sphere Random",
  68. "Inherit Velocity", "Velocity Inherit from Control Point",
  69. "Initial Repulsion Velocity", "Velocity Repulse from World",
  70. "Initial Velocity Noise", "Velocity Noise",
  71. "Initial Scalar Noise", "Remap Noise to Scalar",
  72. "Lifespan from distance to world", "Lifetime from Time to Impact",
  73. "Pre-Age Noise", "Lifetime Pre-Age Noise",
  74. "lifetime_random", "Lifetime Random",
  75. "radius_random", "Radius Random",
  76. "random yaw", "Rotation Yaw Random",
  77. "Randomly Flip Yaw", "Rotation Yaw Flip Random",
  78. "rotation_random", "Rotation Random",
  79. "rotation_speed_random", "Rotation Speed Random",
  80. "sequence_random", "Sequence Random",
  81. "second_sequence_random", "Sequence Two Random",
  82. "trail_length_random", "Trail Length Random",
  83. "velocity_random", "Velocity Random",
  84. };
  85. static char const *RemapOperatorName( char const *pOpName )
  86. {
  87. for( int i = 0 ; i < ARRAYSIZE( s_RemapOperatorNameTable ) ; i += 2 )
  88. {
  89. if ( Q_stricmp( pOpName, s_RemapOperatorNameTable[i] ) == 0 )
  90. {
  91. return s_RemapOperatorNameTable[i + 1 ];
  92. }
  93. }
  94. return pOpName;
  95. }
  96. // This is the soft limit - if we exceed this, we spit out a report of all the particles in the frame
  97. #define MAX_PARTICLE_VERTS 50000
  98. // These are some limits that control g_pParticleSystemMgr->ParticleThrottleScaling() and g_pParticleSystemMgr->ParticleThrottleRandomEnable()
  99. //ConVar cl_particle_scale_lower ( "cl_particle_scale_lower", "20000", FCVAR_CLIENTDLL | FCVAR_CHEAT );
  100. //ConVar cl_particle_scale_upper ( "cl_particle_scale_upper", "40000", FCVAR_CLIENTDLL | FCVAR_CHEAT );
  101. #define CL_PARTICLE_SCALE_LOWER 20000
  102. #define CL_PARTICLE_SCALE_UPPER 40000
  103. //-----------------------------------------------------------------------------
  104. // Default implementation of particle system mgr
  105. //-----------------------------------------------------------------------------
  106. static CParticleSystemMgr s_ParticleSystemMgr;
  107. CParticleSystemMgr *g_pParticleSystemMgr = &s_ParticleSystemMgr;
  108. int g_nParticle_Multiplier = 1;
  109. //-----------------------------------------------------------------------------
  110. // Particle dictionary
  111. //-----------------------------------------------------------------------------
  112. class CParticleSystemDictionary
  113. {
  114. public:
  115. ~CParticleSystemDictionary();
  116. CParticleSystemDefinition* AddParticleSystem( CDmxElement *pParticleSystem );
  117. int Count() const;
  118. int NameCount() const;
  119. CParticleSystemDefinition* GetParticleSystem( int i );
  120. ParticleSystemHandle_t FindParticleSystemHandle( const char *pName );
  121. CParticleSystemDefinition* FindParticleSystem( ParticleSystemHandle_t h );
  122. CParticleSystemDefinition* FindParticleSystem( const char *pName );
  123. CParticleSystemDefinition* FindParticleSystem( const DmObjectId_t &id );
  124. CParticleSystemDefinition* operator[]( int idx )
  125. {
  126. return m_ParticleNameMap[ idx ];
  127. }
  128. private:
  129. typedef CUtlStringMap< CParticleSystemDefinition * > ParticleNameMap_t;
  130. typedef CUtlVector< CParticleSystemDefinition* > ParticleIdMap_t;
  131. void DestroyExistingElement( CDmxElement *pElement );
  132. ParticleNameMap_t m_ParticleNameMap;
  133. ParticleIdMap_t m_ParticleIdMap;
  134. };
  135. //-----------------------------------------------------------------------------
  136. // Destructor
  137. //-----------------------------------------------------------------------------
  138. CParticleSystemDictionary::~CParticleSystemDictionary()
  139. {
  140. int nCount = m_ParticleIdMap.Count();
  141. for ( int i = 0; i < nCount; ++i )
  142. {
  143. delete m_ParticleIdMap[i];
  144. }
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Destroys an existing element, returns if this element should be added to the name list
  148. //-----------------------------------------------------------------------------
  149. void CParticleSystemDictionary::DestroyExistingElement( CDmxElement *pElement )
  150. {
  151. const char *pParticleSystemName = pElement->GetName();
  152. bool bPreventNameBasedLookup = pElement->GetValue<bool>( "preventNameBasedLookup" );
  153. if ( !bPreventNameBasedLookup )
  154. {
  155. if ( m_ParticleNameMap.Defined( pParticleSystemName ) )
  156. {
  157. CParticleSystemDefinition *pDef = m_ParticleNameMap[ pParticleSystemName ];
  158. delete pDef;
  159. m_ParticleNameMap[ pParticleSystemName ] = NULL;
  160. }
  161. return;
  162. }
  163. // Use id based lookup instead
  164. int nCount = m_ParticleIdMap.Count();
  165. const DmObjectId_t& id = pElement->GetId();
  166. for ( int i = 0; i < nCount; ++i )
  167. {
  168. // Was already removed by the name lookup
  169. if ( !IsUniqueIdEqual( m_ParticleIdMap[i]->GetId(), id ) )
  170. continue;
  171. CParticleSystemDefinition *pDef = m_ParticleIdMap[ i ];
  172. m_ParticleIdMap.FastRemove( i );
  173. delete pDef;
  174. break;
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Adds a destructor
  179. //-----------------------------------------------------------------------------
  180. CParticleSystemDefinition* CParticleSystemDictionary::AddParticleSystem( CDmxElement *pParticleSystem )
  181. {
  182. if ( Q_stricmp( pParticleSystem->GetTypeString(), "DmeParticleSystemDefinition" ) )
  183. return NULL;
  184. DestroyExistingElement( pParticleSystem );
  185. CParticleSystemDefinition *pDef = new CParticleSystemDefinition;
  186. // Must add the def to the maps before Read() because Read() may create new child particle systems
  187. bool bPreventNameBasedLookup = pParticleSystem->GetValue<bool>( "preventNameBasedLookup" );
  188. if ( !bPreventNameBasedLookup )
  189. {
  190. m_ParticleNameMap[ pParticleSystem->GetName() ] = pDef;
  191. }
  192. else
  193. {
  194. m_ParticleIdMap.AddToTail( pDef );
  195. }
  196. pDef->Read( pParticleSystem );
  197. return pDef;
  198. }
  199. int CParticleSystemDictionary::NameCount() const
  200. {
  201. return m_ParticleNameMap.GetNumStrings();
  202. }
  203. int CParticleSystemDictionary::Count() const
  204. {
  205. return m_ParticleIdMap.Count();
  206. }
  207. CParticleSystemDefinition* CParticleSystemDictionary::GetParticleSystem( int i )
  208. {
  209. return m_ParticleIdMap[i];
  210. }
  211. ParticleSystemHandle_t CParticleSystemDictionary::FindParticleSystemHandle( const char *pName )
  212. {
  213. return m_ParticleNameMap.Find( pName );
  214. }
  215. CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( ParticleSystemHandle_t h )
  216. {
  217. if ( h == UTL_INVAL_SYMBOL || h >= m_ParticleNameMap.GetNumStrings() )
  218. return NULL;
  219. return m_ParticleNameMap[ h ];
  220. }
  221. CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( const char *pName )
  222. {
  223. if ( m_ParticleNameMap.Defined( pName ) )
  224. return m_ParticleNameMap[ pName ];
  225. return NULL;
  226. }
  227. CParticleSystemDefinition* CParticleSystemDictionary::FindParticleSystem( const DmObjectId_t &id )
  228. {
  229. int nCount = m_ParticleIdMap.Count();
  230. for ( int i = 0; i < nCount; ++i )
  231. {
  232. if ( IsUniqueIdEqual( m_ParticleIdMap[i]->GetId(), id ) )
  233. return m_ParticleIdMap[i];
  234. }
  235. return NULL;
  236. }
  237. //-----------------------------------------------------------------------------
  238. // For editing, create a faked particle operator definition for children
  239. // The only thing used in here is GetUnpackStructure.
  240. //-----------------------------------------------------------------------------
  241. BEGIN_DMXELEMENT_UNPACK( ParticleChildrenInfo_t )
  242. DMXELEMENT_UNPACK_FIELD( "delay", "0.0", float, m_flDelay )
  243. END_DMXELEMENT_UNPACK( ParticleChildrenInfo_t, s_ChildrenInfoUnpack )
  244. class CChildOperatorDefinition : public IParticleOperatorDefinition
  245. {
  246. public:
  247. virtual const char *GetName() const { Assert(0); return NULL; }
  248. virtual CParticleOperatorInstance *CreateInstance( const DmObjectId_t &id ) const { Assert(0); return NULL; }
  249. // virtual void DestroyInstance( CParticleOperatorInstance *pInstance ) const { Assert(0); }
  250. virtual const DmxElementUnpackStructure_t* GetUnpackStructure() const
  251. {
  252. return s_ChildrenInfoUnpack;
  253. }
  254. virtual ParticleOperatorId_t GetId() const { return OPERATOR_GENERIC; }
  255. virtual bool IsObsolete() const { return false; }
  256. virtual size_t GetClassSize() const { return 0; }
  257. };
  258. static CChildOperatorDefinition s_ChildOperatorDefinition;
  259. //-----------------------------------------------------------------------------
  260. //
  261. // CParticleSystemDefinition
  262. //
  263. //-----------------------------------------------------------------------------
  264. //-----------------------------------------------------------------------------
  265. // Unpack structure for CParticleSystemDefinition
  266. //-----------------------------------------------------------------------------
  267. BEGIN_DMXELEMENT_UNPACK( CParticleSystemDefinition )
  268. DMXELEMENT_UNPACK_FIELD( "max_particles", "1000", int, m_nMaxParticles )
  269. DMXELEMENT_UNPACK_FIELD( "initial_particles", "0", int, m_nInitialParticles )
  270. DMXELEMENT_UNPACK_FIELD_STRING_USERDATA( "material", "vgui/white", m_pszMaterialName, "vmtPicker" )
  271. DMXELEMENT_UNPACK_FIELD( "bounding_box_min", "-10 -10 -10", Vector, m_BoundingBoxMin )
  272. DMXELEMENT_UNPACK_FIELD( "bounding_box_max", "10 10 10", Vector, m_BoundingBoxMax )
  273. DMXELEMENT_UNPACK_FIELD( "cull_radius", "0", float, m_flCullRadius )
  274. DMXELEMENT_UNPACK_FIELD( "cull_cost", "1", float, m_flCullFillCost )
  275. DMXELEMENT_UNPACK_FIELD( "cull_control_point", "0", int, m_nCullControlPoint )
  276. DMXELEMENT_UNPACK_FIELD_STRING( "cull_replacement_definition", "", m_pszCullReplacementName )
  277. DMXELEMENT_UNPACK_FIELD( "radius", "5", float, m_flConstantRadius )
  278. DMXELEMENT_UNPACK_FIELD( "color", "255 255 255 255", Color, m_ConstantColor )
  279. DMXELEMENT_UNPACK_FIELD( "rotation", "0", float, m_flConstantRotation )
  280. DMXELEMENT_UNPACK_FIELD( "rotation_speed", "0", float, m_flConstantRotationSpeed )
  281. DMXELEMENT_UNPACK_FIELD( "sequence_number", "0", int, m_nConstantSequenceNumber )
  282. DMXELEMENT_UNPACK_FIELD( "sequence_number 1", "0", int, m_nConstantSequenceNumber1 )
  283. DMXELEMENT_UNPACK_FIELD( "group id", "0", int, m_nGroupID )
  284. DMXELEMENT_UNPACK_FIELD( "maximum time step", "0.1", float, m_flMaximumTimeStep )
  285. DMXELEMENT_UNPACK_FIELD( "maximum sim tick rate", "0.0", float, m_flMaximumSimTime )
  286. DMXELEMENT_UNPACK_FIELD( "minimum sim tick rate", "0.0", float, m_flMinimumSimTime )
  287. DMXELEMENT_UNPACK_FIELD( "minimum rendered frames", "0", int, m_nMinimumFrames )
  288. DMXELEMENT_UNPACK_FIELD( "control point to disable rendering if it is the camera", "-1", int, m_nSkipRenderControlPoint )
  289. DMXELEMENT_UNPACK_FIELD( "maximum draw distance", "100000.0", float, m_flMaxDrawDistance )
  290. DMXELEMENT_UNPACK_FIELD( "time to sleep when not drawn", "8", float, m_flNoDrawTimeToGoToSleep )
  291. DMXELEMENT_UNPACK_FIELD( "Sort particles", "1", bool, m_bShouldSort )
  292. DMXELEMENT_UNPACK_FIELD( "batch particle systems", "0", bool, m_bShouldBatch )
  293. DMXELEMENT_UNPACK_FIELD( "view model effect", "0", bool, m_bViewModelEffect )
  294. END_DMXELEMENT_UNPACK( CParticleSystemDefinition, s_pParticleSystemDefinitionUnpack )
  295. //-----------------------------------------------------------------------------
  296. //
  297. // CParticleOperatorDefinition begins here
  298. // A template describing how a particle system will function
  299. //
  300. //-----------------------------------------------------------------------------
  301. void CParticleSystemDefinition::UnlinkAllCollections()
  302. {
  303. while ( m_pFirstCollection )
  304. {
  305. m_pFirstCollection->UnlinkFromDefList();
  306. }
  307. }
  308. const char *CParticleSystemDefinition::GetName() const
  309. {
  310. return m_Name;
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Should we always precache this?
  314. //-----------------------------------------------------------------------------
  315. bool CParticleSystemDefinition::ShouldAlwaysPrecache() const
  316. {
  317. return m_bAlwaysPrecache;
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Precache/uncache
  321. //-----------------------------------------------------------------------------
  322. void CParticleSystemDefinition::Precache()
  323. {
  324. if ( m_bIsPrecached )
  325. return;
  326. m_bIsPrecached = true;
  327. #ifndef SWDS
  328. m_Material.Init( MaterialName(), TEXTURE_GROUP_OTHER, true );
  329. #endif
  330. int nChildCount = m_Children.Count();
  331. for ( int i = 0; i < nChildCount; ++i )
  332. {
  333. CParticleSystemDefinition *pChild;
  334. if ( m_Children[i].m_bUseNameBasedLookup )
  335. {
  336. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Name );
  337. }
  338. else
  339. {
  340. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Id );
  341. }
  342. if ( pChild )
  343. {
  344. pChild->Precache();
  345. }
  346. }
  347. }
  348. void CParticleSystemDefinition::Uncache()
  349. {
  350. if ( !m_bIsPrecached )
  351. return;
  352. m_bIsPrecached = false;
  353. m_Material.Shutdown();
  354. // m_Material.Init( "debug/particleerror", TEXTURE_GROUP_OTHER, true );
  355. int nChildCount = m_Children.Count();
  356. for ( int i = 0; i < nChildCount; ++i )
  357. {
  358. CParticleSystemDefinition *pChild;
  359. if ( m_Children[i].m_bUseNameBasedLookup )
  360. {
  361. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Name );
  362. }
  363. else
  364. {
  365. pChild = g_pParticleSystemMgr->FindParticleSystem( m_Children[i].m_Id );
  366. }
  367. if ( pChild )
  368. {
  369. pChild->Uncache();
  370. }
  371. }
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Has this been precached?
  375. //-----------------------------------------------------------------------------
  376. bool CParticleSystemDefinition::IsPrecached() const
  377. {
  378. return m_bIsPrecached;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Helper methods to help with unserialization
  382. //-----------------------------------------------------------------------------
  383. void CParticleSystemDefinition::ParseOperators(
  384. const char *pszOpKey, ParticleFunctionType_t nFunctionType,
  385. CDmxElement *pElement,
  386. CUtlVector<CParticleOperatorInstance *> &outList)
  387. {
  388. const CDmxAttribute* pAttribute = pElement->GetAttribute( pszOpKey );
  389. if ( !pAttribute || pAttribute->GetType() != AT_ELEMENT_ARRAY )
  390. return;
  391. const CUtlVector<IParticleOperatorDefinition *> &flist = g_pParticleSystemMgr->GetAvailableParticleOperatorList( nFunctionType );
  392. const CUtlVector< CDmxElement* >& ops = pAttribute->GetArray<CDmxElement*>( );
  393. int nCount = ops.Count();
  394. for ( int i = 0; i < nCount; ++i )
  395. {
  396. const char *pOrigName = ops[i]->GetValueString( "functionName" );
  397. char const *pOpName = RemapOperatorName( pOrigName );
  398. if ( pOpName != pOrigName )
  399. {
  400. pElement->SetValue( "functionName", pOpName );
  401. }
  402. bool bFound = false;
  403. int nFunctionCount = flist.Count();
  404. for( int j = 0; j < nFunctionCount; ++j )
  405. {
  406. if ( Q_stricmp( pOpName, flist[j]->GetName() ) )
  407. continue;
  408. // found it!
  409. bFound = true;
  410. CParticleOperatorInstance *pNewRef = flist[j]->CreateInstance( ops[i]->GetId() );
  411. const DmxElementUnpackStructure_t *pUnpack = flist[j]->GetUnpackStructure();
  412. if ( pUnpack )
  413. {
  414. ops[i]->UnpackIntoStructure( pNewRef, flist[j]->GetClassSize(), pUnpack );
  415. }
  416. pNewRef->InitParams( this, pElement );
  417. m_nAttributeReadMask |= pNewRef->GetReadAttributes();
  418. m_nControlPointReadMask |= pNewRef->GetReadControlPointMask();
  419. switch( nFunctionType )
  420. {
  421. case FUNCTION_INITIALIZER:
  422. case FUNCTION_EMITTER:
  423. m_nPerParticleInitializedAttributeMask |= pNewRef->GetWrittenAttributes();
  424. Assert( pNewRef->GetReadInitialAttributes() == 0 );
  425. break;
  426. case FUNCTION_OPERATOR:
  427. m_nPerParticleUpdatedAttributeMask |= pNewRef->GetWrittenAttributes();
  428. m_nInitialAttributeReadMask |= pNewRef->GetReadInitialAttributes();
  429. break;
  430. case FUNCTION_RENDERER:
  431. m_nPerParticleUpdatedAttributeMask |= pNewRef->GetWrittenAttributes();
  432. m_nInitialAttributeReadMask |= pNewRef->GetReadInitialAttributes();
  433. break;
  434. }
  435. // Special case: Reading particle ID means we're reading the initial particle id
  436. if ( ( pNewRef->GetReadAttributes() | pNewRef->GetReadInitialAttributes() ) & PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK )
  437. {
  438. m_nInitialAttributeReadMask |= PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
  439. m_nPerParticleInitializedAttributeMask |= PARTICLE_ATTRIBUTE_PARTICLE_ID_MASK;
  440. }
  441. outList.AddToTail( pNewRef );
  442. break;
  443. }
  444. if ( !bFound )
  445. {
  446. if ( flist.Count() ) // don't warn if no ops of that type defined (server)
  447. Warning( "Didn't find particle function %s\n", pOpName );
  448. }
  449. }
  450. }
  451. void CParticleSystemDefinition::ParseChildren( CDmxElement *pElement )
  452. {
  453. const CUtlVector<CDmxElement*>& children = pElement->GetArray<CDmxElement*>( "children" );
  454. int nCount = children.Count();
  455. for ( int i = 0; i < nCount; ++i )
  456. {
  457. CDmxElement *pChild = children[i]->GetValue<CDmxElement*>( "child" );
  458. if ( !pChild || Q_stricmp( pChild->GetTypeString(), "DmeParticleSystemDefinition" ) )
  459. continue;
  460. int j = m_Children.AddToTail();
  461. children[i]->UnpackIntoStructure( &m_Children[j], sizeof( m_Children[j] ), s_ChildrenInfoUnpack );
  462. m_Children[j].m_bUseNameBasedLookup = !pChild->GetValue<bool>( "preventNameBasedLookup" );
  463. if ( m_Children[j].m_bUseNameBasedLookup )
  464. {
  465. m_Children[j].m_Name = pChild->GetName();
  466. }
  467. else
  468. {
  469. CopyUniqueId( pChild->GetId(), &m_Children[j].m_Id );
  470. }
  471. // Check to see if this child has been encountered already, and if not, then
  472. // create a new particle definition for this child
  473. g_pParticleSystemMgr->AddParticleSystem( pChild );
  474. }
  475. }
  476. void CParticleSystemDefinition::Read( CDmxElement *pElement )
  477. {
  478. m_Name = pElement->GetName();
  479. CopyUniqueId( pElement->GetId(), &m_Id );
  480. pElement->UnpackIntoStructure( this, sizeof( *this ), s_pParticleSystemDefinitionUnpack );
  481. #ifndef SWDS // avoid material/ texture load
  482. // NOTE: This makes a X appear for uncached particles.
  483. // m_Material.Init( "debug/particleerror", TEXTURE_GROUP_OTHER, true );
  484. #endif
  485. if ( m_nInitialParticles < 0 )
  486. {
  487. m_nInitialParticles = 0;
  488. }
  489. if ( m_nMaxParticles < 1 )
  490. {
  491. m_nMaxParticles = 1;
  492. }
  493. m_nMaxParticles *= g_nParticle_Multiplier;
  494. m_nMaxParticles = min( m_nMaxParticles, MAX_PARTICLES_IN_A_SYSTEM );
  495. if ( m_flCullRadius > 0 )
  496. {
  497. m_nControlPointReadMask |= 1ULL << m_nCullControlPoint;
  498. }
  499. ParseOperators( "renderers", FUNCTION_RENDERER, pElement, m_Renderers );
  500. ParseOperators( "operators", FUNCTION_OPERATOR, pElement, m_Operators );
  501. ParseOperators( "initializers", FUNCTION_INITIALIZER, pElement, m_Initializers );
  502. ParseOperators( "emitters", FUNCTION_EMITTER, pElement, m_Emitters );
  503. ParseChildren( pElement );
  504. ParseOperators( "forces", FUNCTION_FORCEGENERATOR, pElement, m_ForceGenerators );
  505. ParseOperators( "constraints", FUNCTION_CONSTRAINT, pElement, m_Constraints );
  506. SetupContextData();
  507. }
  508. IMaterial *CParticleSystemDefinition::GetMaterial() const
  509. {
  510. // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
  511. Assert( IsPrecached() );
  512. if ( !IsPrecached() )
  513. return NULL;
  514. return m_Material;
  515. }
  516. //----------------------------------------------------------------------------------
  517. // Does the particle system use the power of two frame buffer texture (refraction?)
  518. //----------------------------------------------------------------------------------
  519. bool CParticleSystemDefinition::UsesPowerOfTwoFrameBufferTexture()
  520. {
  521. // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
  522. Assert( IsPrecached() );
  523. return m_Material->NeedsPowerOfTwoFrameBufferTexture( false ); // The false checks if it will ever need the frame buffer, not just this frame
  524. }
  525. //----------------------------------------------------------------------------------
  526. // Does the particle system use the power of two frame buffer texture (refraction?)
  527. //----------------------------------------------------------------------------------
  528. bool CParticleSystemDefinition::UsesFullFrameBufferTexture()
  529. {
  530. // NOTE: This has to be this way to ensure we don't load every freaking material @ startup
  531. Assert( IsPrecached() );
  532. return m_Material->NeedsFullFrameBufferTexture( false ); // The false checks if it will ever need the frame buffer, not just this frame
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Helper methods to write particle systems
  536. //-----------------------------------------------------------------------------
  537. void CParticleSystemDefinition::WriteOperators( CDmxElement *pElement,
  538. const char *pOpKeyName, const CUtlVector<CParticleOperatorInstance *> &inList )
  539. {
  540. CDmxElementModifyScope modify( pElement );
  541. CDmxAttribute* pAttribute = pElement->AddAttribute( pOpKeyName );
  542. CUtlVector< CDmxElement* >& ops = pAttribute->GetArrayForEdit<CDmxElement*>( );
  543. int nCount = inList.Count();
  544. for ( int i = 0; i < nCount; ++i )
  545. {
  546. CDmxElement *pOperator = CreateDmxElement( "DmeParticleOperator" );
  547. ops.AddToTail( pOperator );
  548. const IParticleOperatorDefinition *pDef = inList[i]->GetDefinition();
  549. pOperator->SetValue( "name", pDef->GetName() );
  550. pOperator->SetValue( "functionName", pDef->GetName() );
  551. const DmxElementUnpackStructure_t *pUnpack = pDef->GetUnpackStructure();
  552. if ( pUnpack )
  553. {
  554. pOperator->AddAttributesFromStructure( inList[i], pUnpack );
  555. }
  556. }
  557. }
  558. void CParticleSystemDefinition::WriteChildren( CDmxElement *pElement )
  559. {
  560. CDmxElementModifyScope modify( pElement );
  561. CDmxAttribute* pAttribute = pElement->AddAttribute( "children" );
  562. CUtlVector< CDmxElement* >& children = pAttribute->GetArrayForEdit<CDmxElement*>( );
  563. int nCount = m_Children.Count();
  564. for ( int i = 0; i < nCount; ++i )
  565. {
  566. CDmxElement *pChildRef = CreateDmxElement( "DmeParticleChild" );
  567. children.AddToTail( pChildRef );
  568. children[i]->AddAttributesFromStructure( &m_Children[i], s_ChildrenInfoUnpack );
  569. CDmxElement *pChildParticleSystem;
  570. if ( m_Children[i].m_bUseNameBasedLookup )
  571. {
  572. pChildParticleSystem = g_pParticleSystemMgr->CreateParticleDmxElement( m_Children[i].m_Name );
  573. }
  574. else
  575. {
  576. pChildParticleSystem = g_pParticleSystemMgr->CreateParticleDmxElement( m_Children[i].m_Id );
  577. }
  578. pChildRef->SetValue( "name", pChildParticleSystem->GetName() );
  579. pChildRef->SetValue( "child", pChildParticleSystem );
  580. }
  581. }
  582. CDmxElement *CParticleSystemDefinition::Write()
  583. {
  584. const char *pName = GetName();
  585. CDmxElement *pElement = CreateDmxElement( "DmeParticleSystemDefinition" );
  586. pElement->SetValue( "name", pName );
  587. pElement->AddAttributesFromStructure( this, s_pParticleSystemDefinitionUnpack );
  588. WriteOperators( pElement, "renderers",m_Renderers );
  589. WriteOperators( pElement, "operators", m_Operators );
  590. WriteOperators( pElement, "initializers", m_Initializers );
  591. WriteOperators( pElement, "emitters", m_Emitters );
  592. WriteChildren( pElement );
  593. WriteOperators( pElement, "forces", m_ForceGenerators );
  594. WriteOperators( pElement, "constraints", m_Constraints );
  595. return pElement;
  596. }
  597. void CParticleSystemDefinition::SetupContextData( void )
  598. {
  599. // calcuate sizes and offsets for context data
  600. CUtlVector<CParticleOperatorInstance *> *olists[] = {
  601. &m_Operators, &m_Renderers, &m_Initializers, &m_Emitters, &m_ForceGenerators,
  602. &m_Constraints
  603. };
  604. CUtlVector<size_t> *offsetLists[] = {
  605. &m_nOperatorsCtxOffsets, &m_nRenderersCtxOffsets,
  606. &m_nInitializersCtxOffsets, &m_nEmittersCtxOffsets,
  607. &m_nForceGeneratorsCtxOffsets, &m_nConstraintsCtxOffsets,
  608. };
  609. // loop through all operators, fill in offset entries, and calulate total data needed
  610. m_nContextDataSize = 0;
  611. for( int i = 0; i < NELEMS( olists ); i++ )
  612. {
  613. int nCount = olists[i]->Count();
  614. for( int j = 0; j < nCount; j++ )
  615. {
  616. offsetLists[i]->AddToTail( m_nContextDataSize );
  617. m_nContextDataSize += (*olists[i])[j]->GetRequiredContextBytes();
  618. // align context data
  619. m_nContextDataSize = (m_nContextDataSize + 15) & (~0xf );
  620. }
  621. }
  622. }
  623. //-----------------------------------------------------------------------------
  624. // Finds an operator by id
  625. //-----------------------------------------------------------------------------
  626. CUtlVector<CParticleOperatorInstance *> *CParticleSystemDefinition::GetOperatorList( ParticleFunctionType_t type )
  627. {
  628. switch( type )
  629. {
  630. case FUNCTION_EMITTER:
  631. return &m_Emitters;
  632. case FUNCTION_RENDERER:
  633. return &m_Renderers;
  634. case FUNCTION_INITIALIZER:
  635. return &m_Initializers;
  636. case FUNCTION_OPERATOR:
  637. return &m_Operators;
  638. case FUNCTION_FORCEGENERATOR:
  639. return &m_ForceGenerators;
  640. case FUNCTION_CONSTRAINT:
  641. return &m_Constraints;
  642. default:
  643. Assert(0);
  644. return NULL;
  645. }
  646. }
  647. //-----------------------------------------------------------------------------
  648. // Finds an operator by id
  649. //-----------------------------------------------------------------------------
  650. CParticleOperatorInstance *CParticleSystemDefinition::FindOperatorById( ParticleFunctionType_t type, const DmObjectId_t &id )
  651. {
  652. CUtlVector<CParticleOperatorInstance *> *pVec = GetOperatorList( type );
  653. if ( !pVec )
  654. return NULL;
  655. int nCount = pVec->Count();
  656. for ( int i = 0; i < nCount; ++i )
  657. {
  658. if ( IsUniqueIdEqual( id, pVec->Element(i)->GetId() ) )
  659. return pVec->Element(i);
  660. }
  661. return NULL;
  662. }
  663. //-----------------------------------------------------------------------------
  664. //
  665. // CParticleOperatorInstance
  666. //
  667. //-----------------------------------------------------------------------------
  668. void CParticleOperatorInstance::InitNewParticles( CParticleCollection *pParticles,
  669. int nFirstParticle, int nParticleCount,
  670. int nAttributeWriteMask, void *pContext ) const
  671. {
  672. if ( !nParticleCount )
  673. return;
  674. if ( nParticleCount < 16 ) // don't bother with vectorizing
  675. // unless enough particles to bother
  676. {
  677. InitNewParticlesScalar( pParticles, nFirstParticle, nParticleCount, nAttributeWriteMask, pContext );
  678. return;
  679. }
  680. int nHead = nFirstParticle & 3;
  681. if ( nHead )
  682. {
  683. // need to init up to 3 particles before we are block aligned
  684. int nHeadCount = min( nParticleCount, 4 - nHead );
  685. InitNewParticlesScalar( pParticles, nFirstParticle, nHeadCount, nAttributeWriteMask, pContext );
  686. nParticleCount -= nHeadCount;
  687. nFirstParticle += nHeadCount;
  688. }
  689. // now, we are aligned
  690. int nBlockCount = nParticleCount / 4;
  691. if ( nBlockCount )
  692. {
  693. InitNewParticlesBlock( pParticles, nFirstParticle / 4, nBlockCount, nAttributeWriteMask, pContext );
  694. nParticleCount -= 4 * nBlockCount;
  695. nFirstParticle += 4 * nBlockCount;
  696. }
  697. // do tail
  698. if ( nParticleCount )
  699. {
  700. InitNewParticlesScalar( pParticles, nFirstParticle, nParticleCount, nAttributeWriteMask, pContext );
  701. }
  702. }
  703. //-----------------------------------------------------------------------------
  704. //
  705. // CParticleCollection
  706. //
  707. //-----------------------------------------------------------------------------
  708. //------------------------------------------------------------------------------
  709. // need custom new/delete for alignment for simd
  710. //------------------------------------------------------------------------------
  711. #include "tier0/memdbgoff.h"
  712. void *CParticleCollection::operator new( size_t nSize )
  713. {
  714. return MemAlloc_AllocAligned( nSize, 16 );
  715. }
  716. void* CParticleCollection::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  717. {
  718. return MemAlloc_AllocAligned( nSize, 16, pFileName, nLine );
  719. }
  720. void CParticleCollection::operator delete(void *pData)
  721. {
  722. if ( pData )
  723. {
  724. MemAlloc_FreeAligned( pData );
  725. }
  726. }
  727. void CParticleCollection::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
  728. {
  729. if ( pData )
  730. {
  731. MemAlloc_FreeAligned( pData );
  732. }
  733. }
  734. void *CWorldCollideContextData::operator new( size_t nSize )
  735. {
  736. return MemAlloc_AllocAligned( nSize, 16 );
  737. }
  738. void* CWorldCollideContextData::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  739. {
  740. return MemAlloc_AllocAligned( nSize, 16, pFileName, nLine );
  741. }
  742. void CWorldCollideContextData::operator delete(void *pData)
  743. {
  744. if ( pData )
  745. {
  746. MemAlloc_FreeAligned( pData );
  747. }
  748. }
  749. void CWorldCollideContextData::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
  750. {
  751. if ( pData )
  752. {
  753. MemAlloc_FreeAligned( pData );
  754. }
  755. }
  756. #include "tier0/memdbgon.h"
  757. //-----------------------------------------------------------------------------
  758. // Constructor, destructor
  759. //-----------------------------------------------------------------------------
  760. CParticleCollection::CParticleCollection( )
  761. {
  762. COMPILE_TIME_ASSERT( ( MAX_RANDOM_FLOATS & ( MAX_RANDOM_FLOATS - 1 ) ) == 0 );
  763. COMPILE_TIME_ASSERT( sizeof( s_pRandomFloats ) / sizeof( float ) >= MAX_RANDOM_FLOATS );
  764. m_pNextDef = m_pPrevDef = NULL;
  765. m_nUniqueParticleId = 0;
  766. m_nRandomQueryCount = 0;
  767. m_bIsScrubbable = false;
  768. m_bIsRunningInitializers = false;
  769. m_bIsRunningOperators = false;
  770. m_bIsTranslucent = false;
  771. m_bIsTwoPass = false;
  772. m_bIsBatchable = false;
  773. m_bUsesPowerOfTwoFrameBufferTexture = false;
  774. m_bUsesFullFrameBufferTexture = false;
  775. m_pRenderOp = NULL;
  776. m_nControlPointReadMask = 0;
  777. m_flLastMinDistSqr = m_flLastMaxDistSqr = 0.0f;
  778. m_flMinDistSqr = m_flMaxDistSqr = 0.0f;
  779. m_flOOMaxDistSqr = 1.0f;
  780. m_vecLastCameraPos.Init();
  781. m_MinBounds.Init();
  782. m_MaxBounds.Init();
  783. m_bBoundsValid = false;
  784. memset( m_ControlPoints, 0, sizeof(m_ControlPoints) );
  785. // align all control point orientations with the global world
  786. for( int i=0; i < MAX_PARTICLE_CONTROL_POINTS; i++ )
  787. {
  788. m_ControlPoints[i].m_ForwardVector.Init( 0, 1, 0 );
  789. m_ControlPoints[i].m_UpVector.Init( 0, 0, 1 );
  790. m_ControlPoints[i].m_RightVector.Init( 1, 0, 0 );
  791. }
  792. memset( m_pParticleInitialAttributes, 0, sizeof(m_pParticleInitialAttributes) );
  793. m_nPerParticleUpdatedAttributeMask = 0;
  794. m_nPerParticleInitializedAttributeMask = 0;
  795. m_nPerParticleReadInitialAttributeMask = 0;
  796. m_pParticleMemory = NULL;
  797. m_pParticleInitialMemory = NULL;
  798. m_pConstantMemory = NULL;
  799. m_nActiveParticles = 0;
  800. m_nPaddedActiveParticles = 0;
  801. m_flCurTime = 0.0f;
  802. m_fl4CurTime = Four_Zeros;
  803. m_flDt = 0.0f;
  804. m_flPreviousDt = 0.05f;
  805. m_nParticleFlags = PCFLAGS_FIRST_FRAME;
  806. m_pOperatorContextData = NULL;
  807. m_pNext = m_pPrev = NULL;
  808. m_nRandomSeed = 0;
  809. m_pDef = NULL;
  810. m_nAllocatedParticles = 0;
  811. m_nMaxAllowedParticles = 0;
  812. m_bDormant = false;
  813. m_bEmissionStopped = false;
  814. m_bRequiresOrderInvariance = false;
  815. m_nSimulatedFrames = 0;
  816. m_nNumParticlesToKill = 0;
  817. m_pParticleKillList = NULL;
  818. m_nHighestCP = 0;
  819. memset( m_pCollisionCacheData, 0, sizeof( m_pCollisionCacheData ) );
  820. m_pParent = NULL;
  821. m_LocalLighting = Color(255, 255, 255, 255);
  822. m_LocalLightingCP = -1;
  823. }
  824. CParticleCollection::~CParticleCollection( void )
  825. {
  826. UnlinkFromDefList();
  827. m_Children.Purge();
  828. if ( m_pParticleMemory )
  829. {
  830. delete[] m_pParticleMemory;
  831. }
  832. if ( m_pParticleInitialMemory )
  833. {
  834. delete[] m_pParticleInitialMemory;
  835. }
  836. if ( m_pConstantMemory )
  837. {
  838. delete[] m_pConstantMemory;
  839. }
  840. if ( m_pOperatorContextData )
  841. {
  842. MemAlloc_FreeAligned( m_pOperatorContextData );
  843. }
  844. for( int i = 0 ; i < ARRAYSIZE( m_pCollisionCacheData ) ; i++ )
  845. {
  846. if ( m_pCollisionCacheData[i] )
  847. {
  848. delete m_pCollisionCacheData[i];
  849. }
  850. }
  851. }
  852. //-----------------------------------------------------------------------------
  853. // Initialization
  854. //-----------------------------------------------------------------------------
  855. void CParticleCollection::Init( CParticleSystemDefinition *pDef, float flDelay, int nRandomSeed )
  856. {
  857. m_pDef = pDef;
  858. // Link into def list
  859. LinkIntoDefList();
  860. InitStorage( pDef );
  861. // Initialize sheet data
  862. m_Sheet.Set( g_pParticleSystemMgr->FindOrLoadSheet( pDef->GetMaterial() ) );
  863. // FIXME: This seed needs to be recorded per instance!
  864. m_bIsScrubbable = ( nRandomSeed != 0 );
  865. if ( m_bIsScrubbable )
  866. {
  867. m_nRandomSeed = nRandomSeed;
  868. }
  869. else
  870. {
  871. m_nRandomSeed = (int)this;
  872. #ifndef _DEBUG
  873. m_nRandomSeed += Plat_MSTime();
  874. #endif
  875. }
  876. SetAttributeToConstant( PARTICLE_ATTRIBUTE_XYZ, 0.0f, 0.0f, 0.0f );
  877. SetAttributeToConstant( PARTICLE_ATTRIBUTE_PREV_XYZ, 0.0f, 0.0f, 0.0f );
  878. SetAttributeToConstant( PARTICLE_ATTRIBUTE_LIFE_DURATION, 1.0f );
  879. SetAttributeToConstant( PARTICLE_ATTRIBUTE_RADIUS, pDef->m_flConstantRadius );
  880. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ROTATION, pDef->m_flConstantRotation );
  881. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ROTATION_SPEED, pDef->m_flConstantRotationSpeed );
  882. SetAttributeToConstant( PARTICLE_ATTRIBUTE_TINT_RGB,
  883. pDef->m_ConstantColor.r() / 255.0f, pDef->m_ConstantColor.g() / 255.0f,
  884. pDef->m_ConstantColor.g() / 255.0f );
  885. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ALPHA, pDef->m_ConstantColor.a() / 255.0f );
  886. SetAttributeToConstant( PARTICLE_ATTRIBUTE_CREATION_TIME, 0.0f );
  887. SetAttributeToConstant( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER, pDef->m_nConstantSequenceNumber );
  888. SetAttributeToConstant( PARTICLE_ATTRIBUTE_SEQUENCE_NUMBER1, pDef->m_nConstantSequenceNumber1 );
  889. SetAttributeToConstant( PARTICLE_ATTRIBUTE_TRAIL_LENGTH, 0.1f );
  890. SetAttributeToConstant( PARTICLE_ATTRIBUTE_PARTICLE_ID, 0 );
  891. SetAttributeToConstant( PARTICLE_ATTRIBUTE_YAW, 0 );
  892. SetAttributeToConstant( PARTICLE_ATTRIBUTE_ALPHA2, 1.0f );
  893. // Offset the child in time
  894. m_flCurTime = -flDelay;
  895. m_fl4CurTime = ReplicateX4( m_flCurTime );
  896. if ( m_pDef->m_nContextDataSize )
  897. {
  898. m_pOperatorContextData = reinterpret_cast<uint8 *>
  899. ( MemAlloc_AllocAligned( m_pDef->m_nContextDataSize, 16 ) );
  900. }
  901. m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + pDef->m_flNoDrawTimeToGoToSleep;
  902. // now, init context data
  903. CUtlVector<CParticleOperatorInstance *> *olists[] =
  904. {
  905. &(m_pDef->m_Operators), &(m_pDef->m_Renderers),
  906. &(m_pDef->m_Initializers), &(m_pDef->m_Emitters),
  907. &(m_pDef->m_ForceGenerators),
  908. &(m_pDef->m_Constraints),
  909. };
  910. CUtlVector<size_t> *offsetlists[]=
  911. {
  912. &(m_pDef->m_nOperatorsCtxOffsets), &(m_pDef->m_nRenderersCtxOffsets),
  913. &(m_pDef->m_nInitializersCtxOffsets), &(m_pDef->m_nEmittersCtxOffsets),
  914. &(m_pDef->m_nForceGeneratorsCtxOffsets),
  915. &(m_pDef->m_nConstraintsCtxOffsets),
  916. };
  917. for( int i=0; i<NELEMS( olists ); i++ )
  918. {
  919. int nOperatorCount = olists[i]->Count();
  920. for( int j=0; j < nOperatorCount; j++ )
  921. {
  922. (*olists[i])[j]->InitializeContextData( this, m_pOperatorContextData+ (*offsetlists)[i][j] );
  923. }
  924. }
  925. m_nControlPointReadMask = pDef->m_nControlPointReadMask;
  926. // Instance child particle systems
  927. int nChildCount = pDef->m_Children.Count();
  928. for ( int i = 0; i < nChildCount; ++i )
  929. {
  930. if ( nRandomSeed != 0 )
  931. {
  932. nRandomSeed += 129;
  933. }
  934. CParticleCollection *pChild;
  935. if ( pDef->m_Children[i].m_bUseNameBasedLookup )
  936. {
  937. pChild = g_pParticleSystemMgr->CreateParticleCollection( pDef->m_Children[i].m_Name, -m_flCurTime + pDef->m_Children[i].m_flDelay, nRandomSeed );
  938. }
  939. else
  940. {
  941. pChild = g_pParticleSystemMgr->CreateParticleCollection( pDef->m_Children[i].m_Id, -m_flCurTime + pDef->m_Children[i].m_flDelay, nRandomSeed );
  942. }
  943. if ( pChild )
  944. {
  945. pChild->m_pParent = this;
  946. m_Children.AddToTail( pChild );
  947. m_nControlPointReadMask |= pChild->m_nControlPointReadMask;
  948. }
  949. }
  950. if ( !IsValid() )
  951. return;
  952. m_bIsTranslucent = ComputeIsTranslucent();
  953. m_bIsTwoPass = ComputeIsTwoPass();
  954. m_bIsBatchable = ComputeIsBatchable();
  955. LabelTextureUsage();
  956. m_bAnyUsesPowerOfTwoFrameBufferTexture = ComputeUsesPowerOfTwoFrameBufferTexture();
  957. m_bAnyUsesFullFrameBufferTexture = ComputeUsesFullFrameBufferTexture();
  958. m_bRequiresOrderInvariance = ComputeRequiresOrderInvariance();
  959. }
  960. //-----------------------------------------------------------------------------
  961. // Used by client code
  962. //-----------------------------------------------------------------------------
  963. bool CParticleCollection::Init( CParticleSystemDefinition *pDef )
  964. {
  965. if ( !pDef ) // || !pDef->IsPrecached() )
  966. {
  967. Warning( "Particlelib: Missing precache for particle system type \"%s\"!\n", pDef ? pDef->GetName() : "unknown" );
  968. CParticleSystemDefinition *pErrorDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
  969. if ( pErrorDef )
  970. {
  971. pDef = pErrorDef;
  972. }
  973. }
  974. Init( pDef, 0.0f, 0 );
  975. return IsValid();
  976. }
  977. bool CParticleCollection::Init( const char *pParticleSystemName )
  978. {
  979. if ( !pParticleSystemName )
  980. return false;
  981. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName );
  982. if ( !pDef )
  983. {
  984. Warning( "Attempted to create unknown particle system type \"%s\"!\n", pParticleSystemName );
  985. return false;
  986. }
  987. return Init( pDef );
  988. }
  989. //-----------------------------------------------------------------------------
  990. // List management for collections sharing the same particle definition
  991. //-----------------------------------------------------------------------------
  992. void CParticleCollection::LinkIntoDefList( )
  993. {
  994. Assert( !m_pPrevDef && !m_pNextDef );
  995. m_pPrevDef = NULL;
  996. m_pNextDef = m_pDef->m_pFirstCollection;
  997. m_pDef->m_pFirstCollection = this;
  998. if ( m_pNextDef )
  999. {
  1000. m_pNextDef->m_pPrevDef = this;
  1001. }
  1002. #ifdef _DEBUG
  1003. CParticleCollection *pCollection = m_pDef->FirstCollection();
  1004. while ( pCollection )
  1005. {
  1006. Assert( pCollection->m_pDef == m_pDef );
  1007. pCollection = pCollection->GetNextCollectionUsingSameDef();
  1008. }
  1009. #endif
  1010. }
  1011. void CParticleCollection::UnlinkFromDefList( )
  1012. {
  1013. if ( !m_pDef )
  1014. return;
  1015. if ( m_pDef->m_pFirstCollection == this )
  1016. {
  1017. m_pDef->m_pFirstCollection = m_pNextDef;
  1018. Assert( !m_pPrevDef );
  1019. }
  1020. else
  1021. {
  1022. Assert( m_pPrevDef );
  1023. m_pPrevDef->m_pNextDef = m_pNextDef;
  1024. }
  1025. if ( m_pNextDef )
  1026. {
  1027. m_pNextDef->m_pPrevDef = m_pPrevDef;
  1028. }
  1029. m_pNextDef = m_pPrevDef = NULL;
  1030. #ifdef _DEBUG
  1031. CParticleCollection *pCollection = m_pDef->FirstCollection();
  1032. while ( pCollection )
  1033. {
  1034. Assert( pCollection->m_pDef == m_pDef );
  1035. pCollection = pCollection->GetNextCollectionUsingSameDef();
  1036. }
  1037. #endif
  1038. }
  1039. //-----------------------------------------------------------------------------
  1040. // Determine if this particle has moved since the last time it was simulated,
  1041. // which will let us know if the bbox needs to be updated.
  1042. //-----------------------------------------------------------------------------
  1043. bool CParticleCollection::HasMoved() const
  1044. {
  1045. // It's weird that this is possible, but it apparently is (see the many other functions that
  1046. // check).
  1047. if ( !m_pDef )
  1048. return false;
  1049. Vector prevCP;
  1050. for ( int i = 0; i <= m_nHighestCP; ++i )
  1051. {
  1052. if ( !m_pDef->ReadsControlPoint( i ) )
  1053. continue;
  1054. GetControlPointAtPrevTime( i, &prevCP );
  1055. if ( prevCP != GetControlPointAtCurrentTime( i ) )
  1056. {
  1057. return true;
  1058. }
  1059. }
  1060. for ( CParticleCollection *child = m_Children.m_pHead; child; child = child->m_pNext )
  1061. {
  1062. if ( child->HasMoved() )
  1063. {
  1064. return true;
  1065. }
  1066. }
  1067. return false;
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Particle memory initialization
  1071. //-----------------------------------------------------------------------------
  1072. void CParticleCollection::InitStorage( CParticleSystemDefinition *pDef )
  1073. {
  1074. Assert( pDef->m_nMaxParticles < 65536 );
  1075. m_nMaxAllowedParticles = min ( MAX_PARTICLES_IN_A_SYSTEM, pDef->m_nMaxParticles );
  1076. m_nAllocatedParticles = 4 + 4 * ( ( m_nMaxAllowedParticles + 3 ) / 4 );
  1077. int nConstantMemorySize = 3 * 4 * MAX_PARTICLE_ATTRIBUTES * sizeof(float) + 16;
  1078. // Align allocation for constant attributes to 16 byte boundaries
  1079. m_pConstantMemory = new unsigned char[nConstantMemorySize];
  1080. m_pConstantAttributes = (float*)( (size_t)( m_pConstantMemory + 15 ) & ~0xF );
  1081. // We have to zero-init the memory so that any attributes that are not initialized
  1082. // get predictable and sensible values.
  1083. memset( m_pConstantMemory, 0, nConstantMemorySize );
  1084. m_nPerParticleInitializedAttributeMask = pDef->m_nPerParticleInitializedAttributeMask;
  1085. m_nPerParticleUpdatedAttributeMask = pDef->m_nPerParticleUpdatedAttributeMask;
  1086. // Only worry about initial attributes that are per-particle *and* are updated at a later time
  1087. // If they aren't updated at a later time, then we can just point the initial + current pointers at the same memory
  1088. m_nPerParticleReadInitialAttributeMask = pDef->m_nInitialAttributeReadMask &
  1089. ( pDef->m_nPerParticleInitializedAttributeMask & pDef->m_nPerParticleUpdatedAttributeMask );
  1090. // This is the mask of attributes which are initialized per-particle, but never updated
  1091. // *and* where operators want to read initial particle state
  1092. int nPerParticleReadConstantAttributeMask = pDef->m_nInitialAttributeReadMask &
  1093. ( pDef->m_nPerParticleInitializedAttributeMask & ( ~pDef->m_nPerParticleUpdatedAttributeMask ) );
  1094. int sz = 0;
  1095. int nInitialAttributeSize = 0;
  1096. int nPerParticleAttributeMask = m_nPerParticleInitializedAttributeMask | m_nPerParticleUpdatedAttributeMask;
  1097. for( int bit = 0; bit < MAX_PARTICLE_ATTRIBUTES; bit++ )
  1098. {
  1099. int nAttrSize = ( ( 1 << bit ) & ATTRIBUTES_WHICH_ARE_VEC3S_MASK ) ? 3 : 1;
  1100. if ( nPerParticleAttributeMask & ( 1 << bit ) )
  1101. {
  1102. sz += nAttrSize;
  1103. }
  1104. if ( m_nPerParticleReadInitialAttributeMask & ( 1 << bit ) )
  1105. {
  1106. nInitialAttributeSize += nAttrSize;
  1107. }
  1108. }
  1109. // Gotta allocate a couple extra floats to account for
  1110. int nAllocationSize = m_nAllocatedParticles * sz * sizeof(float) + 16;
  1111. m_pParticleMemory = new unsigned char[ nAllocationSize ];
  1112. memset( m_pParticleMemory, 0, nAllocationSize );
  1113. // Allocate space for the initial attributes
  1114. if ( nInitialAttributeSize != 0 )
  1115. {
  1116. int nInitialAllocationSize = m_nAllocatedParticles * nInitialAttributeSize * sizeof(float) + 16;
  1117. m_pParticleInitialMemory = new unsigned char[ nInitialAllocationSize ];
  1118. memset( m_pParticleInitialMemory, 0, nInitialAllocationSize );
  1119. }
  1120. // Align allocation to 16-byte boundaries
  1121. float *pMem = (float*)( (size_t)( m_pParticleMemory + 15 ) & ~0xF );
  1122. float *pInitialMem = (float*)( (size_t)( m_pParticleInitialMemory + 15 ) & ~0xF );
  1123. // Point each attribute to memory associated with that attribute
  1124. for( int bit = 0; bit < MAX_PARTICLE_ATTRIBUTES; bit++ )
  1125. {
  1126. int nAttrSize = ( ( 1 << bit ) & ATTRIBUTES_WHICH_ARE_VEC3S_MASK ) ? 3 : 1;
  1127. if ( nPerParticleAttributeMask & ( 1 << bit ) )
  1128. {
  1129. m_pParticleAttributes[ bit ] = pMem;
  1130. m_nParticleFloatStrides[ bit ] = nAttrSize * 4;
  1131. pMem += nAttrSize * m_nAllocatedParticles;
  1132. }
  1133. else
  1134. {
  1135. m_pParticleAttributes[ bit ] = GetConstantAttributeMemory( bit );
  1136. m_nParticleFloatStrides[ bit ] = 0;
  1137. }
  1138. // Are we reading
  1139. if ( pDef->m_nInitialAttributeReadMask & ( 1 << bit ) )
  1140. {
  1141. if ( m_nPerParticleReadInitialAttributeMask & ( 1 << bit ) )
  1142. {
  1143. Assert( pInitialMem );
  1144. m_pParticleInitialAttributes[ bit ] = pInitialMem;
  1145. m_nParticleInitialFloatStrides[ bit ] = nAttrSize * 4;
  1146. pInitialMem += nAttrSize * m_nAllocatedParticles;
  1147. }
  1148. else if ( nPerParticleReadConstantAttributeMask & ( 1 << bit ) )
  1149. {
  1150. m_pParticleInitialAttributes[ bit ] = m_pParticleAttributes[ bit ];
  1151. m_nParticleInitialFloatStrides[ bit ] = m_nParticleFloatStrides[ bit ];
  1152. }
  1153. else
  1154. {
  1155. m_pParticleInitialAttributes[ bit ] = GetConstantAttributeMemory( bit );
  1156. m_nParticleInitialFloatStrides[ bit ] = 0;
  1157. }
  1158. }
  1159. else
  1160. {
  1161. // Catch errors where code is reading data it didn't request
  1162. m_pParticleInitialAttributes[ bit ] = NULL;
  1163. m_nParticleInitialFloatStrides[ bit ] = 0;
  1164. }
  1165. }
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. // Returns the particle collection name
  1169. //-----------------------------------------------------------------------------
  1170. const char *CParticleCollection::GetName() const
  1171. {
  1172. return m_pDef ? m_pDef->GetName() : "";
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Does the particle system use the frame buffer texture (refraction?)
  1176. //-----------------------------------------------------------------------------
  1177. bool CParticleCollection::UsesPowerOfTwoFrameBufferTexture( bool bThisFrame ) const
  1178. {
  1179. if ( ! m_bAnyUsesPowerOfTwoFrameBufferTexture ) // quick out if neither us or our children ever use
  1180. {
  1181. return false;
  1182. }
  1183. if ( bThisFrame )
  1184. {
  1185. return SystemContainsParticlesWithBoolSet( &CParticleCollection::m_bUsesPowerOfTwoFrameBufferTexture );
  1186. }
  1187. return true;
  1188. }
  1189. //-----------------------------------------------------------------------------
  1190. // Does the particle system use the full frame buffer texture (soft particles)
  1191. //-----------------------------------------------------------------------------
  1192. bool CParticleCollection::UsesFullFrameBufferTexture( bool bThisFrame ) const
  1193. {
  1194. if ( ! m_bAnyUsesFullFrameBufferTexture ) // quick out if neither us or our children ever use
  1195. {
  1196. return false;
  1197. }
  1198. if ( bThisFrame )
  1199. {
  1200. return SystemContainsParticlesWithBoolSet( &CParticleCollection::m_bUsesFullFrameBufferTexture );
  1201. }
  1202. return true;
  1203. }
  1204. bool CParticleCollection::SystemContainsParticlesWithBoolSet( bool CParticleCollection::*pField ) const
  1205. {
  1206. if ( m_nActiveParticles && ( this->*pField ) )
  1207. return true;
  1208. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1209. {
  1210. if ( p->SystemContainsParticlesWithBoolSet( pField ) )
  1211. return true;
  1212. }
  1213. return false;
  1214. }
  1215. void CParticleCollection::LabelTextureUsage( void )
  1216. {
  1217. if ( m_pDef )
  1218. {
  1219. m_bUsesPowerOfTwoFrameBufferTexture = m_pDef->UsesPowerOfTwoFrameBufferTexture();
  1220. m_bUsesFullFrameBufferTexture = m_pDef->UsesFullFrameBufferTexture();
  1221. }
  1222. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1223. {
  1224. p->LabelTextureUsage();
  1225. }
  1226. }
  1227. bool CParticleCollection::ComputeUsesPowerOfTwoFrameBufferTexture()
  1228. {
  1229. if ( !m_pDef )
  1230. return false;
  1231. if ( m_pDef->UsesPowerOfTwoFrameBufferTexture() )
  1232. return true;
  1233. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1234. {
  1235. if ( p->UsesPowerOfTwoFrameBufferTexture( false ) )
  1236. return true;
  1237. }
  1238. return false;
  1239. }
  1240. bool CParticleCollection::ComputeUsesFullFrameBufferTexture()
  1241. {
  1242. if ( !m_pDef )
  1243. return false;
  1244. if ( m_pDef->UsesFullFrameBufferTexture() )
  1245. return true;
  1246. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1247. {
  1248. if ( p->UsesFullFrameBufferTexture( false ) )
  1249. return true;
  1250. }
  1251. return false;
  1252. }
  1253. //-----------------------------------------------------------------------------
  1254. // Is the particle system two-pass?
  1255. //-----------------------------------------------------------------------------
  1256. bool CParticleCollection::ContainsOpaqueCollections()
  1257. {
  1258. if ( !m_pDef )
  1259. return false;
  1260. if ( !m_pDef->GetMaterial()->IsTranslucent() )
  1261. return true;
  1262. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1263. {
  1264. if ( p->ContainsOpaqueCollections( ) )
  1265. return true;
  1266. }
  1267. return false;
  1268. }
  1269. //-----------------------------------------------------------------------------
  1270. // Is the particle system two-pass?
  1271. //-----------------------------------------------------------------------------
  1272. bool CParticleCollection::IsTwoPass() const
  1273. {
  1274. return m_bIsTwoPass;
  1275. }
  1276. bool CParticleCollection::ComputeIsTwoPass()
  1277. {
  1278. if ( !ComputeIsTranslucent() )
  1279. return false;
  1280. return ContainsOpaqueCollections();
  1281. }
  1282. //-----------------------------------------------------------------------------
  1283. // Is the particle system translucent
  1284. //-----------------------------------------------------------------------------
  1285. bool CParticleCollection::IsTranslucent() const
  1286. {
  1287. return m_bIsTranslucent;
  1288. }
  1289. bool CParticleCollection::ComputeIsTranslucent()
  1290. {
  1291. if ( !m_pDef )
  1292. return false;
  1293. if ( m_pDef->GetMaterial()->IsTranslucent() )
  1294. return true;
  1295. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1296. {
  1297. if ( p->IsTranslucent( ) )
  1298. return true;
  1299. }
  1300. return false;
  1301. }
  1302. //-----------------------------------------------------------------------------
  1303. // Is the particle system batchable
  1304. //-----------------------------------------------------------------------------
  1305. bool CParticleCollection::IsBatchable() const
  1306. {
  1307. return m_bIsBatchable;
  1308. }
  1309. bool CParticleCollection::ComputeIsBatchable()
  1310. {
  1311. int nRendererCount = GetRendererCount();
  1312. for( int i = 0; i < nRendererCount; i++ )
  1313. {
  1314. if ( !GetRenderer( i )->IsBatchable() )
  1315. return false;
  1316. }
  1317. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1318. {
  1319. if ( !p->IsBatchable() )
  1320. return false;
  1321. }
  1322. return true;
  1323. }
  1324. //-----------------------------------------------------------------------------
  1325. // Does this system require order invariance of the particles?
  1326. //-----------------------------------------------------------------------------
  1327. bool CParticleCollection::ComputeRequiresOrderInvariance()
  1328. {
  1329. const int nRendererCount = GetRendererCount();
  1330. for( int i = 0; i < nRendererCount; i++ )
  1331. {
  1332. if ( GetRenderer( i )->RequiresOrderInvariance() )
  1333. return true;
  1334. }
  1335. for (CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext)
  1336. {
  1337. if ( p->m_bRequiresOrderInvariance )
  1338. return true;
  1339. }
  1340. return false;
  1341. }
  1342. //-----------------------------------------------------------------------------
  1343. // Renderer iteration
  1344. //-----------------------------------------------------------------------------
  1345. int CParticleCollection::GetRendererCount() const
  1346. {
  1347. return IsValid() ? m_pDef->m_Renderers.Count() : 0;
  1348. }
  1349. CParticleOperatorInstance *CParticleCollection::GetRenderer( int i )
  1350. {
  1351. return IsValid() ? m_pDef->m_Renderers[i] : NULL;
  1352. }
  1353. void *CParticleCollection::GetRendererContext( int i )
  1354. {
  1355. return IsValid() ? m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i] : NULL;
  1356. }
  1357. //-----------------------------------------------------------------------------
  1358. // Visualize operators (for editing/debugging)
  1359. //-----------------------------------------------------------------------------
  1360. void CParticleCollection::VisualizeOperator( const DmObjectId_t *pOpId )
  1361. {
  1362. m_pRenderOp = NULL;
  1363. if ( !pOpId || !m_pDef )
  1364. return;
  1365. m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_EMITTER, *pOpId );
  1366. if ( !m_pRenderOp )
  1367. {
  1368. m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_INITIALIZER, *pOpId );
  1369. if ( !m_pRenderOp )
  1370. {
  1371. m_pRenderOp = m_pDef->FindOperatorById( FUNCTION_OPERATOR, *pOpId );
  1372. }
  1373. }
  1374. }
  1375. float FadeInOut( float flFadeInStart, float flFadeInEnd, float flFadeOutStart, float flFadeOutEnd, float flCurTime )
  1376. {
  1377. if ( flFadeInStart > flCurTime ) // started yet?
  1378. return 0.0;
  1379. if ( ( flFadeOutEnd > 0. ) && ( flFadeOutEnd < flCurTime ) ) // timed out?
  1380. return 0.;
  1381. // handle out of order cases
  1382. flFadeInEnd = max( flFadeInEnd, flFadeInStart );
  1383. flFadeOutStart = max( flFadeOutStart, flFadeInEnd );
  1384. flFadeOutEnd = max( flFadeOutEnd, flFadeOutStart );
  1385. float flStrength = 1.0;
  1386. if (
  1387. ( flFadeInEnd > flCurTime ) &&
  1388. ( flFadeInEnd > flFadeInStart ) )
  1389. flStrength = min( flStrength, FLerp( 0, 1, flFadeInStart, flFadeInEnd, flCurTime ) );
  1390. if ( ( flCurTime > flFadeOutStart) &&
  1391. ( flFadeOutEnd > flFadeOutStart) )
  1392. flStrength = min ( flStrength, FLerp( 0, 1, flFadeOutEnd, flFadeOutStart, flCurTime ) );
  1393. return flStrength;
  1394. }
  1395. bool CParticleCollection::CheckIfOperatorShouldRun(
  1396. CParticleOperatorInstance const * pOp ,
  1397. float *pflCurStrength)
  1398. {
  1399. float flTime=m_flCurTime;
  1400. if ( pOp->m_flOpFadeOscillatePeriod > 0.0 )
  1401. {
  1402. flTime=fmod( m_flCurTime*( 1.0/pOp->m_flOpFadeOscillatePeriod ), 1.0 );
  1403. }
  1404. float flStrength = FadeInOut( pOp->m_flOpStartFadeInTime, pOp->m_flOpEndFadeInTime,
  1405. pOp->m_flOpStartFadeOutTime, pOp->m_flOpEndFadeOutTime,
  1406. flTime );
  1407. if ( pflCurStrength )
  1408. *pflCurStrength = flStrength;
  1409. return ( flStrength > 0.0 );
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Restarts a particle system
  1413. //-----------------------------------------------------------------------------
  1414. void CParticleCollection::Restart()
  1415. {
  1416. int i;
  1417. int nEmitterCount = m_pDef->m_Emitters.Count();
  1418. for( i = 0; i < nEmitterCount; i++ )
  1419. {
  1420. m_pDef->m_Emitters[i]->Restart( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
  1421. }
  1422. // Update all children
  1423. CParticleCollection *pChild;
  1424. for( i = 0, pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext, i++ )
  1425. {
  1426. // Remove any delays from the time (otherwise we're offset by it oddly)
  1427. pChild->Restart( );
  1428. }
  1429. }
  1430. //-----------------------------------------------------------------------------
  1431. // Main entry point for rendering
  1432. //-----------------------------------------------------------------------------
  1433. void CParticleCollection::Render( IMatRenderContext *pRenderContext, bool bTranslucentOnly, void *pCameraObject )
  1434. {
  1435. if ( !IsValid() )
  1436. return;
  1437. m_flNextSleepTime = Max ( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep ));
  1438. if ( m_nActiveParticles != 0 )
  1439. {
  1440. if ( !bTranslucentOnly || m_pDef->GetMaterial()->IsTranslucent() )
  1441. {
  1442. int nCount = m_pDef->m_Renderers.Count();
  1443. for( int i = 0; i < nCount; i++ )
  1444. {
  1445. if ( CheckIfOperatorShouldRun( m_pDef->m_Renderers[i] ) )
  1446. {
  1447. // pRenderContext->MatrixMode( MATERIAL_VIEW );
  1448. // pRenderContext->PushMatrix();
  1449. // pRenderContext->LoadIdentity();
  1450. // pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1451. // pRenderContext->PushMatrix();
  1452. // pRenderContext->LoadIdentity();
  1453. // pRenderContext->Ortho( -100, -100, 100, 100, -100, 100 );
  1454. m_pDef->m_Renderers[i]->Render(
  1455. pRenderContext, this, m_pOperatorContextData + m_pDef->m_nRenderersCtxOffsets[i] );
  1456. // pRenderContext->MatrixMode( MATERIAL_VIEW );
  1457. // pRenderContext->PopMatrix();
  1458. // pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  1459. // pRenderContext->PopMatrix();
  1460. }
  1461. }
  1462. }
  1463. }
  1464. // let children render
  1465. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  1466. {
  1467. p->Render( pRenderContext, bTranslucentOnly, pCameraObject );
  1468. }
  1469. // Visualize specific ops for debugging/editing
  1470. if ( m_pRenderOp )
  1471. {
  1472. m_pRenderOp->Render( this );
  1473. }
  1474. }
  1475. void CParticleCollection::UpdatePrevControlPoints( float dt )
  1476. {
  1477. m_flPreviousDt = dt;
  1478. for(int i=0; i <= m_nHighestCP; i++ )
  1479. m_ControlPoints[i].m_PrevPosition = m_ControlPoints[i].m_Position;
  1480. m_nParticleFlags |= PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED;
  1481. }
  1482. #if MEASURE_PARTICLE_PERF
  1483. #if VPROF_LEVEL > 0
  1484. #define START_OP float flOpStartTime = Plat_FloatTime(); VPROF_ENTER_SCOPE(pOp->GetDefinition()->GetName())
  1485. #else
  1486. #define START_OP float flOpStartTime = Plat_FloatTime();
  1487. #endif
  1488. #if VPROF_LEVEL > 0
  1489. #define END_OP if ( 1 ) { \
  1490. float flETime = Plat_FloatTime() - flOpStartTime; \
  1491. IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->m_pDef; \
  1492. pDef->RecordExecutionTime( flETime ); \
  1493. } \
  1494. VPROF_EXIT_SCOPE()
  1495. #else
  1496. #define END_OP if ( 1 ) { \
  1497. float flETime = Plat_FloatTime() - flOpStartTime; \
  1498. IParticleOperatorDefinition *pDef = (IParticleOperatorDefinition *) pOp->m_pDef; \
  1499. pDef->RecordExecutionTime( flETime ); \
  1500. }
  1501. #endif
  1502. #else
  1503. #define START_OP
  1504. #define END_OP
  1505. #endif
  1506. void CParticleCollection::InitializeNewParticles( int nFirstParticle, int nParticleCount, uint32 nInittedMask )
  1507. {
  1508. VPROF_BUDGET( "CParticleCollection::InitializeNewParticles", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
  1509. #ifdef _DEBUG
  1510. m_bIsRunningInitializers = true;
  1511. #endif
  1512. // now, initialize the attributes of all the new particles
  1513. int nPerParticleAttributeMask = m_nPerParticleInitializedAttributeMask | m_nPerParticleUpdatedAttributeMask;
  1514. int nAttrsLeftToInit = nPerParticleAttributeMask & ~nInittedMask;
  1515. int nInitializerCount = m_pDef->m_Initializers.Count();
  1516. for ( int i = 0; i < nInitializerCount; i++ )
  1517. {
  1518. CParticleOperatorInstance *pOp = m_pDef->m_Initializers[i];
  1519. int nInitializerAttrMask = pOp->GetWrittenAttributes();
  1520. if ( ( ( nInitializerAttrMask & nAttrsLeftToInit ) == 0 ) || pOp->InitMultipleOverride() )
  1521. continue;
  1522. void *pContext = m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i];
  1523. START_OP;
  1524. if ( m_bIsScrubbable && !pOp->IsScrubSafe() )
  1525. {
  1526. for ( int j = 0; j < nParticleCount; ++j )
  1527. {
  1528. pOp->InitNewParticles( this, nFirstParticle + j, 1, nAttrsLeftToInit, pContext );
  1529. }
  1530. }
  1531. else
  1532. {
  1533. pOp->InitNewParticles( this, nFirstParticle, nParticleCount, nAttrsLeftToInit, pContext );
  1534. }
  1535. END_OP;
  1536. nAttrsLeftToInit &= ~nInitializerAttrMask;
  1537. }
  1538. // always run second tier initializers (modifiers) after first tier - this ensures they don't get stomped.
  1539. for ( int i = 0; i < nInitializerCount; i++ )
  1540. {
  1541. int nInitializerAttrMask = m_pDef->m_Initializers[i]->GetWrittenAttributes();
  1542. CParticleOperatorInstance *pOp = m_pDef->m_Initializers[i];
  1543. if ( !pOp->InitMultipleOverride() )
  1544. continue;
  1545. void *pContext = m_pOperatorContextData + m_pDef->m_nInitializersCtxOffsets[i];
  1546. START_OP;
  1547. if ( m_bIsScrubbable && !pOp->IsScrubSafe() )
  1548. {
  1549. for ( int j = 0; j < nParticleCount; ++j )
  1550. {
  1551. pOp->InitNewParticles( this, nFirstParticle + j, 1, nAttrsLeftToInit, pContext );
  1552. }
  1553. }
  1554. else
  1555. {
  1556. pOp->InitNewParticles( this, nFirstParticle, nParticleCount, nAttrsLeftToInit, pContext );
  1557. }
  1558. END_OP;
  1559. nAttrsLeftToInit &= ~nInitializerAttrMask;
  1560. }
  1561. #ifdef _DEBUG
  1562. m_bIsRunningInitializers = false;
  1563. #endif
  1564. InitParticleAttributes( nFirstParticle, nParticleCount, nAttrsLeftToInit );
  1565. CopyInitialAttributeValues( nFirstParticle, nParticleCount );
  1566. }
  1567. void CParticleCollection::SkipToTime( float t )
  1568. {
  1569. if ( t > m_flCurTime )
  1570. {
  1571. UpdatePrevControlPoints( t - m_flCurTime );
  1572. m_flCurTime = t;
  1573. m_fl4CurTime = ReplicateX4( t );
  1574. m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
  1575. // FIXME: In future, we may have to tell operators, initializers about this too
  1576. int nEmitterCount = m_pDef->m_Emitters.Count();
  1577. int i;
  1578. for( i = 0; i < nEmitterCount; i++ )
  1579. {
  1580. m_pDef->m_Emitters[i]->SkipToTime( t, this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
  1581. }
  1582. CParticleCollection *pChild;
  1583. // Update all children
  1584. for( i = 0, pChild = m_Children.m_pHead; pChild != NULL; pChild = pChild->m_pNext, i++ )
  1585. {
  1586. // Remove any delays from the time (otherwise we're offset by it oddly)
  1587. pChild->SkipToTime( t - m_pDef->m_Children[i].m_flDelay );
  1588. }
  1589. }
  1590. }
  1591. #ifdef NDEBUG
  1592. #define CHECKSYSTEM( p ) 0
  1593. #else
  1594. static void CHECKSYSTEM( CParticleCollection *pParticles )
  1595. {
  1596. // Assert( pParticles->m_nActiveParticles <= pParticles->m_pDef->m_nMaxParticles );
  1597. for ( int i = 0; i < pParticles->m_nActiveParticles; ++i )
  1598. {
  1599. const float *xyz = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_XYZ, i );
  1600. const float *xyz_prev = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_PREV_XYZ, i );
  1601. /*
  1602. const float *rad = pParticles->GetFloatAttributePtr( PARTICLE_ATTRIBUTE_RADIUS, i );
  1603. Assert( IsFinite( rad[0] ) );
  1604. RJ: Disabling this assert. While the proper way is to fix the math which leads to the bad number, the fix would result in more particles being drawn and in a post shipping world, users were not happy.
  1605. See Changelists #1368648, #1368635, and #1368434 for proper math calculation fixes.
  1606. In a post shipping world, as these particles would not render with infinites, code was added C_OP_RenderSprites::RenderSpriteCard() to check for the infinite and not add the vert to meshbuilder.
  1607. */
  1608. Assert( IsFinite( xyz[0] ) );
  1609. Assert( IsFinite( xyz[4] ) );
  1610. Assert( IsFinite( xyz[8] ) );
  1611. Assert( IsFinite( xyz_prev[0] ) );
  1612. Assert( IsFinite( xyz_prev[4] ) );
  1613. Assert( IsFinite( xyz_prev[8] ) );
  1614. }
  1615. }
  1616. #endif
  1617. void CParticleCollection::SimulateFirstFrame( )
  1618. {
  1619. m_flDt = 0.0f;
  1620. m_nDrawnFrames = 0;
  1621. m_nSimulatedFrames = 1;
  1622. // For the first frame, copy over the initial control points
  1623. if ( ( m_nParticleFlags & PCFLAGS_PREV_CONTROL_POINTS_INITIALIZED ) == 0 )
  1624. {
  1625. UpdatePrevControlPoints( 0.05f );
  1626. }
  1627. m_nOperatorRandomSampleOffset = 0;
  1628. int nCount = m_pDef->m_Operators.Count();
  1629. for( int i = 0; i < nCount; i++ )
  1630. {
  1631. float flStrength;
  1632. CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
  1633. if ( pOp->ShouldRunBeforeEmitters() &&
  1634. CheckIfOperatorShouldRun( pOp, &flStrength ) )
  1635. {
  1636. pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
  1637. CHECKSYSTEM( this );
  1638. UpdatePrevControlPoints( 0.05f );
  1639. }
  1640. m_nOperatorRandomSampleOffset += 17;
  1641. }
  1642. // first, create initial particles
  1643. int nNumToCreate = min( m_pDef->m_nInitialParticles, m_nMaxAllowedParticles );
  1644. if ( nNumToCreate > 0 )
  1645. {
  1646. SetNActiveParticles( nNumToCreate );
  1647. InitializeNewParticles( 0, nNumToCreate, 0 );
  1648. CHECKSYSTEM( this );
  1649. }
  1650. }
  1651. void CParticleCollection::Simulate( float dt, bool updateBboxOnly )
  1652. {
  1653. VPROF_BUDGET( "CParticleCollection::Simulate", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
  1654. if ( dt < 0.0f )
  1655. return;
  1656. if ( !m_pDef )
  1657. return;
  1658. // Don't do anything until we've hit t == 0
  1659. // This is used for delayed children
  1660. if ( m_flCurTime < 0.0f )
  1661. {
  1662. if ( dt >= 1.0e-22 )
  1663. {
  1664. m_flCurTime += dt;
  1665. m_fl4CurTime = ReplicateX4( m_flCurTime );
  1666. UpdatePrevControlPoints( dt );
  1667. }
  1668. return;
  1669. }
  1670. // run initializers if necessary (once we hit t == 0)
  1671. if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME )
  1672. {
  1673. SimulateFirstFrame();
  1674. m_nParticleFlags &= ~PCFLAGS_FIRST_FRAME;
  1675. }
  1676. if ( dt < 1.0e-22 )
  1677. return;
  1678. #if MEASURE_PARTICLE_PERF
  1679. float flStartSimTime = Plat_FloatTime();
  1680. #endif
  1681. bool bAttachedKillList = false;
  1682. if (!HasAttachedKillList())
  1683. {
  1684. g_pParticleSystemMgr->AttachKillList(this);
  1685. bAttachedKillList = true;
  1686. }
  1687. if (!updateBboxOnly)
  1688. {
  1689. ++m_nSimulatedFrames;
  1690. float flRemainingDt = dt;
  1691. float flMaxDT = 0.1; // default
  1692. if ( m_pDef->m_flMaximumTimeStep > 0.0 )
  1693. flMaxDT = m_pDef->m_flMaximumTimeStep;
  1694. // Limit timestep if needed (prevents short lived particles from being created and destroyed before being rendered.
  1695. //if ( m_pDef->m_flMaximumSimTime != 0.0 && !m_bHasDrawnOnce )
  1696. if ( m_pDef->m_flMaximumSimTime != 0.0 && ( m_nDrawnFrames <= m_pDef->m_nMinimumFrames ) )
  1697. {
  1698. if ( ( flRemainingDt + m_flCurTime ) > m_pDef->m_flMaximumSimTime )
  1699. {
  1700. //if delta+current > checkpoint then delta = checkpoint - current
  1701. flRemainingDt = m_pDef->m_flMaximumSimTime - m_flCurTime;
  1702. flRemainingDt = max( m_pDef->m_flMinimumSimTime, flRemainingDt );
  1703. }
  1704. m_nDrawnFrames += 1;
  1705. }
  1706. flRemainingDt = min( flRemainingDt, 10 * flMaxDT ); // no more than 10 passes ever
  1707. while( flRemainingDt > 0.0 )
  1708. {
  1709. float flDT_ThisStep = min( flRemainingDt, flMaxDT );
  1710. flRemainingDt -= flDT_ThisStep;
  1711. m_flDt = flDT_ThisStep;
  1712. m_flCurTime += flDT_ThisStep;
  1713. m_fl4CurTime = ReplicateX4( m_flCurTime );
  1714. #ifdef _DEBUG
  1715. m_bIsRunningOperators = true;
  1716. #endif
  1717. m_nOperatorRandomSampleOffset = 0;
  1718. int nCount = m_pDef->m_Operators.Count();
  1719. for( int i = 0; i < nCount; i++ )
  1720. {
  1721. float flStrength;
  1722. CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
  1723. if ( pOp->ShouldRunBeforeEmitters() &&
  1724. CheckIfOperatorShouldRun( pOp, &flStrength ) )
  1725. {
  1726. START_OP;
  1727. pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
  1728. END_OP;
  1729. CHECKSYSTEM( this );
  1730. if ( m_nNumParticlesToKill )
  1731. {
  1732. ApplyKillList();
  1733. }
  1734. m_nOperatorRandomSampleOffset += 17;
  1735. }
  1736. }
  1737. #ifdef _DEBUG
  1738. m_bIsRunningOperators = false;
  1739. #endif
  1740. int nEmitterCount = m_pDef->m_Emitters.Count();
  1741. for( int i=0; i < nEmitterCount; i++ )
  1742. {
  1743. int nOldParticleCount = m_nActiveParticles;
  1744. float flEmitStrength;
  1745. if ( CheckIfOperatorShouldRun( m_pDef->m_Emitters[i], &flEmitStrength ) )
  1746. {
  1747. uint32 nInittedMask = m_pDef->m_Emitters[i]->Emit(
  1748. this, flEmitStrength,
  1749. m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i] );
  1750. if ( nOldParticleCount != m_nActiveParticles )
  1751. {
  1752. // init newly emitted particles
  1753. InitializeNewParticles( nOldParticleCount, m_nActiveParticles - nOldParticleCount, nInittedMask );
  1754. CHECKSYSTEM( this );
  1755. }
  1756. }
  1757. }
  1758. m_nOperatorRandomSampleOffset = 0;
  1759. nCount = m_pDef->m_Operators.Count();
  1760. if ( m_nActiveParticles )
  1761. {
  1762. #ifdef FP_EXCEPTIONS_ENABLED
  1763. const int processedParticles = m_nPaddedActiveParticles * 4;
  1764. for ( int unusedParticle = m_nActiveParticles; unusedParticle < processedParticles; ++unusedParticle )
  1765. {
  1766. // Set the unused-but-processed particle lifetimes to a value that
  1767. // won't cause division by zero or other madness. This allows us
  1768. // to enable floating-point exceptions during particle processing,
  1769. // which helps us to find bugs.
  1770. float *dtime = GetFloatAttributePtrForWrite( PARTICLE_ATTRIBUTE_LIFE_DURATION, unusedParticle );
  1771. *dtime = 1.0f;
  1772. }
  1773. #endif
  1774. #ifdef _DEBUG
  1775. m_bIsRunningOperators = true;
  1776. #endif
  1777. for( int i = 0; i < nCount; i++ )
  1778. {
  1779. float flStrength;
  1780. CParticleOperatorInstance *pOp = m_pDef->m_Operators[i];
  1781. if ( (! pOp->ShouldRunBeforeEmitters() ) &&
  1782. CheckIfOperatorShouldRun( pOp, &flStrength ) )
  1783. {
  1784. START_OP;
  1785. pOp->Operate( this, flStrength, m_pOperatorContextData + m_pDef->m_nOperatorsCtxOffsets[i] );
  1786. END_OP;
  1787. CHECKSYSTEM( this );
  1788. if ( m_nNumParticlesToKill )
  1789. {
  1790. ApplyKillList();
  1791. if ( ! m_nActiveParticles )
  1792. break; // don't run any more operators
  1793. }
  1794. m_nOperatorRandomSampleOffset += 17;
  1795. }
  1796. }
  1797. #ifdef _DEBUG
  1798. m_bIsRunningOperators = false;
  1799. #endif
  1800. }
  1801. }
  1802. #if MEASURE_PARTICLE_PERF
  1803. m_pDef->m_nMaximumActiveParticles = max( m_pDef->m_nMaximumActiveParticles, m_nActiveParticles );
  1804. float flETime = Plat_FloatTime() - flStartSimTime;
  1805. m_pDef->m_flUncomittedTotalSimTime += flETime;
  1806. m_pDef->m_flMaxMeasuredSimTime = max( m_pDef->m_flMaxMeasuredSimTime, flETime );
  1807. #endif
  1808. }
  1809. // let children simulate
  1810. for (CParticleCollection *i = m_Children.m_pHead; i; i = i->m_pNext)
  1811. {
  1812. LoanKillListTo(i); // re-use the allocated kill list for the children
  1813. i->Simulate(dt, updateBboxOnly);
  1814. i->m_pParticleKillList = NULL;
  1815. }
  1816. if (bAttachedKillList)
  1817. g_pParticleSystemMgr->DetachKillList(this);
  1818. UpdatePrevControlPoints(dt);
  1819. // Bloat the bounding box by bounds around the control point
  1820. BloatBoundsUsingControlPoint();
  1821. }
  1822. //-----------------------------------------------------------------------------
  1823. // Copies the constant attributes into the per-particle attributes
  1824. //-----------------------------------------------------------------------------
  1825. void CParticleCollection::InitParticleAttributes( int nStartParticle, int nNumParticles, int nAttrsLeftToInit )
  1826. {
  1827. if ( nAttrsLeftToInit == 0 )
  1828. return;
  1829. // !! speed!! do sse init here
  1830. for( int i = nStartParticle; i < nStartParticle + nNumParticles; i++ )
  1831. {
  1832. for ( int nAttr = 0; nAttr < MAX_PARTICLE_ATTRIBUTES; ++nAttr )
  1833. {
  1834. if ( ( nAttrsLeftToInit & ( 1 << nAttr ) ) == 0 )
  1835. continue;
  1836. float *pAttrData = GetFloatAttributePtrForWrite( nAttr, i );
  1837. // Special case for particle id
  1838. if ( nAttr == PARTICLE_ATTRIBUTE_PARTICLE_ID )
  1839. {
  1840. *( (int*)pAttrData ) = ( m_nRandomSeed + m_nUniqueParticleId ) & RANDOM_FLOAT_MASK;
  1841. m_nUniqueParticleId++;
  1842. continue;
  1843. }
  1844. // Special case for the creation time mask
  1845. if ( nAttr == PARTICLE_ATTRIBUTE_CREATION_TIME )
  1846. {
  1847. *pAttrData = m_flCurTime;
  1848. continue;
  1849. }
  1850. // If this assertion fails, it means we're writing into constant memory, which is a nono
  1851. Assert( m_nParticleFloatStrides[nAttr] != 0 );
  1852. float *pConstantAttr = GetConstantAttributeMemory( nAttr );
  1853. *pAttrData = *pConstantAttr;
  1854. if ( m_nParticleFloatStrides[nAttr] == 12 )
  1855. {
  1856. pAttrData[4] = pConstantAttr[4];
  1857. pAttrData[8] = pConstantAttr[8];
  1858. }
  1859. }
  1860. }
  1861. }
  1862. void CParticleCollection::CopyInitialAttributeValues( int nStartParticle, int nNumParticles )
  1863. {
  1864. if ( m_nPerParticleReadInitialAttributeMask == 0 )
  1865. return;
  1866. // FIXME: Do SSE copy here
  1867. for( int i = nStartParticle; i < nStartParticle + nNumParticles; i++ )
  1868. {
  1869. for ( int nAttr = 0; nAttr < MAX_PARTICLE_ATTRIBUTES; ++nAttr )
  1870. {
  1871. if ( m_nPerParticleReadInitialAttributeMask & (1 << nAttr) )
  1872. {
  1873. const float *pSrcAttribute = GetFloatAttributePtr( nAttr, i );
  1874. float *pDestAttribute = GetInitialFloatAttributePtrForWrite( nAttr, i );
  1875. Assert( m_nParticleInitialFloatStrides[nAttr] != 0 );
  1876. Assert( m_nParticleFloatStrides[nAttr] == m_nParticleInitialFloatStrides[nAttr] );
  1877. *pDestAttribute = *pSrcAttribute;
  1878. if ( m_nParticleFloatStrides[nAttr] == 12 )
  1879. {
  1880. pDestAttribute[4] = pSrcAttribute[4];
  1881. pDestAttribute[8] = pSrcAttribute[8];
  1882. }
  1883. }
  1884. }
  1885. }
  1886. }
  1887. //-----------------------------------------------------------------------------e
  1888. // Computes a random vector inside a sphere
  1889. //-----------------------------------------------------------------------------
  1890. float CParticleCollection::RandomVectorInUnitSphere( int nRandomSampleId, Vector *pVector )
  1891. {
  1892. // Guarantee uniform random distribution within a sphere
  1893. // Graphics gems III contains this algorithm ("Nonuniform random point sets via warping")
  1894. float u = RandomFloat( nRandomSampleId, 0.0001f, 1.0f );
  1895. float v = RandomFloat( nRandomSampleId+1, 0.0001f, 1.0f );
  1896. float w = RandomFloat( nRandomSampleId+2, 0.0001f, 1.0f );
  1897. float flPhi = acos( 1 - 2 * u );
  1898. float flTheta = 2 * M_PI * v;
  1899. float flRadius = powf( w, 1.0f / 3.0f );
  1900. float flSinPhi, flCosPhi;
  1901. float flSinTheta, flCosTheta;
  1902. SinCos( flPhi, &flSinPhi, &flCosPhi );
  1903. SinCos( flTheta, &flSinTheta, &flCosTheta );
  1904. pVector->x = flRadius * flSinPhi * flCosTheta;
  1905. pVector->y = flRadius * flSinPhi * flSinTheta;
  1906. pVector->z = flRadius * flCosPhi;
  1907. return flRadius;
  1908. }
  1909. //-----------------------------------------------------------------------------
  1910. // Used to retrieve the position of a control point
  1911. // somewhere between m_flCurTime and m_flCurTime - m_fPreviousDT
  1912. //-----------------------------------------------------------------------------
  1913. void CParticleCollection::GetControlPointAtTime( int nControlPoint, float flTime, Vector *pControlPoint ) const
  1914. {
  1915. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  1916. if ( nControlPoint > GetHighestControlPoint() )
  1917. {
  1918. DevWarning(2, "Warning : Particle system (%s) using unassigned ControlPoint %d!\n", GetName(), nControlPoint );
  1919. }
  1920. if ( m_flDt == 0.0f )
  1921. {
  1922. VectorCopy( m_ControlPoints[nControlPoint].m_Position, *pControlPoint );
  1923. return;
  1924. }
  1925. // The original calculation for 't' was this:
  1926. // float flPrevTime = m_flCurTime - m_flDt;
  1927. // float t = ( flTime - flPrevTime ) / m_flDt;
  1928. // Which is mathematically equivalent to this:
  1929. // float t = ( flTime - ( m_flCurTime - m_flDt ) ) / m_flDt;
  1930. // However if m_flCurTime and flTime are large then significant precision
  1931. // is lost during subtraction -- catastrophic cancellation
  1932. // is the technical term. This starts out being an error of one part in
  1933. // ten million, but after running for just a few minutes it increases to
  1934. // one part in ten thousand -- and continues to get worse.
  1935. // This calculation even fails in the simple case where flTime == m_flCurTime,
  1936. // giving an answer that is not 1.0 and may be out of range.
  1937. // If the calculation is arranged as shown below then, because flTime and
  1938. // m_flCurTime are close to each other, the subtraction loses *no* precision.
  1939. // The subtraction will not necessarily be 'correct', since eventually flTime
  1940. // and m_flCurTime will not have enough precision, but it will give results
  1941. // that are as accurate as possible given the inputs.
  1942. // flHowLongAgo stores how far before the current time flTime is.
  1943. const float flHowLongAgo = m_flCurTime - flTime;
  1944. float t = ( m_flDt - flHowLongAgo ) / m_flDt;
  1945. // The original code had a comment saying:
  1946. // Precision errors can cause this problem
  1947. // in regards to issues that can cause t to go negative. Actually this function
  1948. // is just sometimes called (from InitNewParticlesScalar) with values that cause
  1949. // 't' to go massively negative. So I clamp it.
  1950. if ( t < 0.0f )
  1951. t = 0.0f;
  1952. Assert( t <= 1.0f );
  1953. VectorLerp( m_ControlPoints[nControlPoint].m_PrevPosition, m_ControlPoints[nControlPoint].m_Position, t, *pControlPoint );
  1954. Assert( IsFinite(pControlPoint->x) && IsFinite(pControlPoint->y) && IsFinite(pControlPoint->z) );
  1955. }
  1956. //-----------------------------------------------------------------------------
  1957. // Used to retrieve the previous position of a control point
  1958. //
  1959. //-----------------------------------------------------------------------------
  1960. void CParticleCollection::GetControlPointAtPrevTime( int nControlPoint, Vector *pControlPoint ) const
  1961. {
  1962. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  1963. *pControlPoint = m_ControlPoints[nControlPoint].m_PrevPosition;
  1964. }
  1965. void CParticleCollection::GetControlPointTransformAtCurrentTime( int nControlPoint, matrix3x4_t *pMat )
  1966. {
  1967. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  1968. const Vector &vecControlPoint = GetControlPointAtCurrentTime( nControlPoint );
  1969. // FIXME: Use quaternion lerp to get control point transform at time
  1970. Vector left;
  1971. VectorMultiply( m_ControlPoints[nControlPoint].m_RightVector, -1.0f, left );
  1972. pMat->Init( m_ControlPoints[nControlPoint].m_ForwardVector, left, m_ControlPoints[nControlPoint].m_UpVector, vecControlPoint );
  1973. }
  1974. void CParticleCollection::GetControlPointTransformAtCurrentTime( int nControlPoint, VMatrix *pMat )
  1975. {
  1976. GetControlPointTransformAtCurrentTime( nControlPoint, const_cast<matrix3x4_t *> ( &pMat->As3x4() ) );
  1977. pMat->m[3][0] = pMat->m[3][1] = pMat->m[3][2] = 0.0f; pMat->m[3][3] = 1.0f;
  1978. }
  1979. void CParticleCollection::GetControlPointOrientationAtTime( int nControlPoint, float flTime, Vector *pForward, Vector *pRight, Vector *pUp )
  1980. {
  1981. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  1982. // FIXME: Use quaternion lerp to get control point transform at time
  1983. *pForward = m_ControlPoints[nControlPoint].m_ForwardVector;
  1984. *pRight = m_ControlPoints[nControlPoint].m_RightVector;
  1985. *pUp = m_ControlPoints[nControlPoint].m_UpVector;
  1986. }
  1987. void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, matrix3x4_t *pMat )
  1988. {
  1989. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  1990. Vector vecControlPoint;
  1991. GetControlPointAtTime( nControlPoint, flTime, &vecControlPoint );
  1992. // FIXME: Use quaternion lerp to get control point transform at time
  1993. Vector left;
  1994. VectorMultiply( m_ControlPoints[nControlPoint].m_RightVector, -1.0f, left );
  1995. pMat->Init( m_ControlPoints[nControlPoint].m_ForwardVector, left, m_ControlPoints[nControlPoint].m_UpVector, vecControlPoint );
  1996. }
  1997. void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, VMatrix *pMat )
  1998. {
  1999. GetControlPointTransformAtTime( nControlPoint, flTime, const_cast< matrix3x4_t * > ( &pMat->As3x4() ) );
  2000. pMat->m[3][0] = pMat->m[3][1] = pMat->m[3][2] = 0.0f; pMat->m[3][3] = 1.0f;
  2001. }
  2002. void CParticleCollection::GetControlPointTransformAtTime( int nControlPoint, float flTime, CParticleSIMDTransformation *pXForm )
  2003. {
  2004. Assert( m_pDef->ReadsControlPoint( nControlPoint ) );
  2005. Vector vecControlPoint;
  2006. GetControlPointAtTime( nControlPoint, flTime, &vecControlPoint );
  2007. pXForm->m_v4Fwd.DuplicateVector( m_ControlPoints[nControlPoint].m_ForwardVector );
  2008. pXForm->m_v4Up.DuplicateVector( m_ControlPoints[nControlPoint].m_UpVector );
  2009. //Vector left;
  2010. //VectorMultiply( m_ControlPoints[nControlPoint].m_RightVector, -1.0f, left );
  2011. pXForm->m_v4Right.DuplicateVector( m_ControlPoints[nControlPoint].m_RightVector );
  2012. }
  2013. int CParticleCollection::GetHighestControlPoint( void ) const
  2014. {
  2015. return m_nHighestCP;
  2016. }
  2017. //-----------------------------------------------------------------------------
  2018. // Returns the render bounds
  2019. //-----------------------------------------------------------------------------
  2020. void CParticleCollection::GetBounds( Vector *pMin, Vector *pMax )
  2021. {
  2022. *pMin = m_MinBounds;
  2023. *pMax = m_MaxBounds;
  2024. }
  2025. //-----------------------------------------------------------------------------
  2026. // Bloat the bounding box by bounds around the control point
  2027. //-----------------------------------------------------------------------------
  2028. void CParticleCollection::BloatBoundsUsingControlPoint()
  2029. {
  2030. // more specifically, some particle systems were using "start" as an input, so it got set as control point 1,
  2031. // so other particle systems had an extra point in their bounding box, that generally remained at the world origin
  2032. RecomputeBounds();
  2033. // Don't do the bounding box fixup until after the second simulation (first real simulation)
  2034. // so that we know they're in their correct position.
  2035. if ( m_nSimulatedFrames > 2 )
  2036. {
  2037. // Include control points in the bbox.
  2038. for (int i = 0; i <= m_nHighestCP; ++i) {
  2039. if ( !m_pDef->ReadsControlPoint( i ) )
  2040. continue;
  2041. const Vector& cp = GetControlPointAtCurrentTime(i);
  2042. VectorMin( m_MinBounds, cp, m_MinBounds );
  2043. VectorMax( m_MaxBounds, cp, m_MaxBounds );
  2044. }
  2045. }
  2046. // Deal with children
  2047. // NOTE: Bounds have been recomputed for children prior to this call in Simulate
  2048. Vector vecMins, vecMaxs;
  2049. for( CParticleCollection *i = m_Children.m_pHead; i; i = i->m_pNext )
  2050. {
  2051. i->GetBounds( &vecMins, &vecMaxs );
  2052. VectorMin( m_MinBounds, vecMins, m_MinBounds );
  2053. VectorMax( m_MaxBounds, vecMaxs, m_MaxBounds );
  2054. }
  2055. }
  2056. //-----------------------------------------------------------------------------
  2057. // Recomputes the bounds
  2058. //-----------------------------------------------------------------------------
  2059. void CParticleCollection::RecomputeBounds( void )
  2060. {
  2061. if ( m_nActiveParticles == 0.0f )
  2062. {
  2063. m_bBoundsValid = false;
  2064. m_MinBounds.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  2065. m_MaxBounds.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  2066. m_Center.Init();
  2067. return;
  2068. }
  2069. fltx4 min_x = ReplicateX4(1.0e23);
  2070. fltx4 min_y = min_x;
  2071. fltx4 min_z = min_x;
  2072. fltx4 max_x = ReplicateX4(-1.0e23);
  2073. fltx4 max_y = max_x;
  2074. fltx4 max_z = max_x;
  2075. fltx4 sum_x = Four_Zeros;
  2076. fltx4 sum_y = Four_Zeros;
  2077. fltx4 sum_z = Four_Zeros;
  2078. size_t xyz_stride;
  2079. const fltx4 *xyz = GetM128AttributePtr( PARTICLE_ATTRIBUTE_XYZ, &xyz_stride );
  2080. int ctr = m_nActiveParticles/4;
  2081. while ( ctr-- )
  2082. {
  2083. min_x = MinSIMD( min_x, xyz[0] );
  2084. max_x = MaxSIMD( max_x, xyz[0] );
  2085. sum_x = AddSIMD( sum_x, xyz[0] );
  2086. min_y = MinSIMD( min_y, xyz[1] );
  2087. max_y = MaxSIMD( max_y, xyz[1] );
  2088. sum_y = AddSIMD( sum_y, xyz[1] );
  2089. min_z = MinSIMD( min_z, xyz[2] );
  2090. max_z = MaxSIMD( max_z, xyz[2] );
  2091. sum_z = AddSIMD( sum_z, xyz[2] );
  2092. xyz += xyz_stride;
  2093. }
  2094. m_bBoundsValid = true;
  2095. m_MinBounds.x = min( min( SubFloat( min_x, 0 ), SubFloat( min_x, 1 ) ), min( SubFloat( min_x, 2 ), SubFloat( min_x, 3 ) ) );
  2096. m_MinBounds.y = min( min( SubFloat( min_y, 0 ), SubFloat( min_y, 1 ) ), min( SubFloat( min_y, 2 ), SubFloat( min_y, 3 ) ) );
  2097. m_MinBounds.z = min( min( SubFloat( min_z, 0 ), SubFloat( min_z, 1 ) ), min( SubFloat( min_z, 2 ), SubFloat( min_z, 3 ) ) );
  2098. m_MaxBounds.x = max( max( SubFloat( max_x, 0 ), SubFloat( max_x, 1 ) ), max( SubFloat( max_x, 2 ), SubFloat( max_x, 3 ) ) );
  2099. m_MaxBounds.y = max( max( SubFloat( max_y, 0 ), SubFloat( max_y, 1 ) ), max( SubFloat( max_y, 2 ), SubFloat( max_y, 3 ) ) );
  2100. m_MaxBounds.z = max( max( SubFloat( max_z, 0 ), SubFloat( max_z, 1 ) ), max( SubFloat( max_z, 2 ), SubFloat( max_z, 3 ) ) );
  2101. float fsum_x = SubFloat( sum_x, 0 ) + SubFloat( sum_x, 1 ) + SubFloat( sum_x, 2 ) + SubFloat( sum_x, 3 );
  2102. float fsum_y = SubFloat( sum_y, 0 ) + SubFloat( sum_y, 1 ) + SubFloat( sum_y, 2 ) + SubFloat( sum_y, 3 );
  2103. float fsum_z = SubFloat( sum_z, 0 ) + SubFloat( sum_z, 1 ) + SubFloat( sum_z, 2 ) + SubFloat( sum_z, 3 );
  2104. // now, handle "tail" in a non-sse manner
  2105. for( int i=0; i < (m_nActiveParticles & 3); i++)
  2106. {
  2107. m_MinBounds.x = min( m_MinBounds.x, SubFloat( xyz[0], i ) );
  2108. m_MaxBounds.x = max( m_MaxBounds.x, SubFloat( xyz[0], i ) );
  2109. fsum_x += SubFloat( xyz[0], i );
  2110. m_MinBounds.y = min( m_MinBounds.y, SubFloat( xyz[1], i ) );
  2111. m_MaxBounds.y = max( m_MaxBounds.y, SubFloat( xyz[1], i ) );
  2112. fsum_y += SubFloat( xyz[1], i );
  2113. m_MinBounds.z = min( m_MinBounds.z, SubFloat( xyz[2], i ) );
  2114. m_MaxBounds.z = max( m_MaxBounds.z, SubFloat( xyz[2], i ) );
  2115. fsum_z += SubFloat( xyz[2], i );
  2116. }
  2117. VectorAdd( m_MinBounds, m_pDef->m_BoundingBoxMin, m_MinBounds );
  2118. VectorAdd( m_MaxBounds, m_pDef->m_BoundingBoxMax, m_MaxBounds );
  2119. // calculate center
  2120. float flOONumParticles = 1.0 / m_nActiveParticles;
  2121. m_Center.x = flOONumParticles * fsum_x;
  2122. m_Center.y = flOONumParticles * fsum_y;
  2123. m_Center.z = flOONumParticles * fsum_z;
  2124. }
  2125. //-----------------------------------------------------------------------------
  2126. // Is the particle system finished emitting + all its particles are dead?
  2127. //-----------------------------------------------------------------------------
  2128. bool CParticleCollection::IsFinished( void )
  2129. {
  2130. if ( !m_pDef )
  2131. return true;
  2132. if ( m_nParticleFlags & PCFLAGS_FIRST_FRAME )
  2133. return false;
  2134. if ( m_nActiveParticles )
  2135. return false;
  2136. if ( m_bDormant )
  2137. return false;
  2138. // no particles. See if any emmitters intead to create more particles
  2139. int nEmitterCount = m_pDef->m_Emitters.Count();
  2140. for( int i=0; i < nEmitterCount; i++ )
  2141. {
  2142. if ( m_pDef->m_Emitters[i]->MayCreateMoreParticles( this, m_pOperatorContextData+m_pDef->m_nEmittersCtxOffsets[i] ) )
  2143. return false;
  2144. }
  2145. // make sure all children are finished
  2146. for( CParticleCollection *i = m_Children.m_pHead; i; i=i->m_pNext )
  2147. {
  2148. if ( !i->IsFinished() )
  2149. return false;
  2150. }
  2151. return true;
  2152. }
  2153. //-----------------------------------------------------------------------------
  2154. // Purpose: Stop emitting particles
  2155. //-----------------------------------------------------------------------------
  2156. void CParticleCollection::StopEmission( bool bInfiniteOnly, bool bRemoveAllParticles, bool bWakeOnStop )
  2157. {
  2158. if ( !m_pDef )
  2159. return;
  2160. // Whenever we call stop emission, we clear out our dormancy. This ensures we
  2161. // get deleted if we're told to stop emission while dormant. SetDormant() ensures
  2162. // dormancy is set to true after stopping out emission.
  2163. m_bDormant = false;
  2164. if ( bWakeOnStop )
  2165. {
  2166. // Set next sleep time - an additional fudge factor is added over the normal time
  2167. // so that existing particles have a chance to go away.
  2168. m_flNextSleepTime = Max ( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + 10 ));
  2169. }
  2170. m_bEmissionStopped = true;
  2171. for( int i=0; i < m_pDef->m_Emitters.Count(); i++ )
  2172. {
  2173. m_pDef->m_Emitters[i]->StopEmission( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i], bInfiniteOnly );
  2174. }
  2175. if ( bRemoveAllParticles )
  2176. {
  2177. SetNActiveParticles( 0 );
  2178. }
  2179. // Stop our children as well
  2180. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  2181. {
  2182. p->StopEmission( bInfiniteOnly, bRemoveAllParticles );
  2183. }
  2184. }
  2185. //-----------------------------------------------------------------------------
  2186. // Purpose: Stop emitting particles
  2187. //-----------------------------------------------------------------------------
  2188. void CParticleCollection::StartEmission( bool bInfiniteOnly )
  2189. {
  2190. if ( !m_pDef )
  2191. return;
  2192. m_bEmissionStopped = false;
  2193. for( int i=0; i < m_pDef->m_Emitters.Count(); i++ )
  2194. {
  2195. m_pDef->m_Emitters[i]->StartEmission( this, m_pOperatorContextData + m_pDef->m_nEmittersCtxOffsets[i], bInfiniteOnly );
  2196. }
  2197. // Stop our children as well
  2198. for( CParticleCollection *p = m_Children.m_pHead; p; p = p->m_pNext )
  2199. {
  2200. p->StartEmission( bInfiniteOnly );
  2201. }
  2202. // Set our sleep time to some time in the future so we update again
  2203. m_flNextSleepTime = g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep;
  2204. }
  2205. //-----------------------------------------------------------------------------
  2206. // Purpose: Dormant particle systems simulate their particles, but don't emit
  2207. // new ones. Unlike particle systems that have StopEmission() called
  2208. // dormant particle systems don't remove themselves when they're
  2209. // out of particles, assuming they'll return at some point.
  2210. //-----------------------------------------------------------------------------
  2211. void CParticleCollection::SetDormant( bool bDormant )
  2212. {
  2213. // Don't stop or start emission if we are not changing dormancy state
  2214. if ( bDormant == m_bDormant )
  2215. return;
  2216. // If emissions have already been stopped, don't go dormant, we're supposed to be dying.
  2217. if ( m_bEmissionStopped && bDormant )
  2218. return;
  2219. if ( bDormant )
  2220. {
  2221. StopEmission();
  2222. }
  2223. else
  2224. {
  2225. StartEmission();
  2226. }
  2227. m_bDormant = bDormant;
  2228. }
  2229. void CParticleCollection::MoveParticle( int nInitialIndex, int nNewIndex )
  2230. {
  2231. // Copy the per-particle attributes
  2232. for( int p = 0; p < MAX_PARTICLE_ATTRIBUTES; ++p )
  2233. {
  2234. switch( m_nParticleFloatStrides[ p ] )
  2235. {
  2236. case 4: // move a float
  2237. m_pParticleAttributes[p][nNewIndex] = m_pParticleAttributes[p][nInitialIndex];
  2238. break;
  2239. case 12: // move a vec3
  2240. {
  2241. // sse weirdness
  2242. int oldidxsse = 12 * ( nNewIndex >> 2 );
  2243. int oldofs = oldidxsse + ( nNewIndex & 3 );
  2244. int lastidxsse = 12 * ( nInitialIndex >> 2 );
  2245. int lastofs = lastidxsse + ( nInitialIndex & 3 );
  2246. m_pParticleAttributes[p][oldofs] = m_pParticleAttributes[p][lastofs];
  2247. m_pParticleAttributes[p][4+oldofs] = m_pParticleAttributes[p][4+lastofs];
  2248. m_pParticleAttributes[p][8+oldofs] = m_pParticleAttributes[p][8+lastofs];
  2249. break;
  2250. }
  2251. }
  2252. switch( m_nParticleInitialFloatStrides[ p ] )
  2253. {
  2254. case 4: // move a float
  2255. m_pParticleInitialAttributes[p][nNewIndex] = m_pParticleInitialAttributes[p][nInitialIndex];
  2256. break;
  2257. case 12: // move a vec3
  2258. {
  2259. // sse weirdness
  2260. int oldidxsse = 12 * ( nNewIndex>>2 );
  2261. int oldofs = oldidxsse + ( nNewIndex & 3 );
  2262. int lastidxsse = 12 * ( nInitialIndex >> 2 );
  2263. int lastofs = lastidxsse + ( nInitialIndex & 3 );
  2264. m_pParticleInitialAttributes[p][oldofs] = m_pParticleInitialAttributes[p][lastofs];
  2265. m_pParticleInitialAttributes[p][4+oldofs] = m_pParticleInitialAttributes[p][4+lastofs];
  2266. m_pParticleInitialAttributes[p][8+oldofs] = m_pParticleInitialAttributes[p][8+lastofs];
  2267. break;
  2268. }
  2269. }
  2270. }
  2271. }
  2272. //-----------------------------------------------------------------------------
  2273. // Kill List processing.
  2274. //-----------------------------------------------------------------------------
  2275. #define THREADED_PARTICLES 1
  2276. #if THREADED_PARTICLES
  2277. #define MAX_SIMULTANEOUS_KILL_LISTS 16
  2278. static volatile int g_nKillBufferInUse[MAX_SIMULTANEOUS_KILL_LISTS];
  2279. static int32 *g_pKillBuffers[MAX_SIMULTANEOUS_KILL_LISTS];
  2280. void CParticleSystemMgr::DetachKillList( CParticleCollection *pParticles )
  2281. {
  2282. if ( pParticles->m_pParticleKillList )
  2283. {
  2284. // find which it is
  2285. for(int i=0; i < NELEMS( g_pKillBuffers ); i++)
  2286. {
  2287. if ( g_pKillBuffers[i] == pParticles->m_pParticleKillList )
  2288. {
  2289. pParticles->m_pParticleKillList = NULL;
  2290. g_nKillBufferInUse[i] = 0; // no need to interlock
  2291. return;
  2292. }
  2293. }
  2294. Assert( 0 ); // how did we get here?
  2295. }
  2296. }
  2297. void CParticleSystemMgr::AttachKillList( CParticleCollection *pParticles )
  2298. {
  2299. // look for a free slot
  2300. for(;;)
  2301. {
  2302. for(int i=0; i < NELEMS( g_nKillBufferInUse ); i++)
  2303. {
  2304. if ( ! g_nKillBufferInUse[i] ) // available?
  2305. {
  2306. // try to take it!
  2307. if ( ThreadInterlockedAssignIf( &( g_nKillBufferInUse[i]), 1, 0 ) )
  2308. {
  2309. if ( ! g_pKillBuffers[i] )
  2310. {
  2311. g_pKillBuffers[i] = new int32[MAX_PARTICLES_IN_A_SYSTEM];
  2312. }
  2313. pParticles->m_pParticleKillList = g_pKillBuffers[i];
  2314. return; // done!
  2315. }
  2316. }
  2317. }
  2318. Assert(0); // why don't we have enough buffers?
  2319. ThreadSleep();
  2320. }
  2321. }
  2322. #else
  2323. // use one static kill list. no worries because of not threading
  2324. static int g_nParticleKillList[MAX_PARTICLES_IN_A_SYSTEM];
  2325. void CParticleSystemMgr::AttachKillList( CParticleCollection *pParticles )
  2326. {
  2327. pParticles->m_pParticleKillList = g_nParticleKillList;
  2328. }
  2329. void CParticleCollection::DetachKillList( CParticleCollection *pParticles )
  2330. {
  2331. Assert( pParticles->m_nNumParticlesToKill == 0 );
  2332. pParticles->m_pParticleKillList = NULL;
  2333. }
  2334. #endif
  2335. void CParticleCollection::ApplyKillList( void )
  2336. {
  2337. int nLeftInKillList = m_nNumParticlesToKill;
  2338. if ( nLeftInKillList == 0 )
  2339. return;
  2340. int nParticlesActiveNow = m_nActiveParticles;
  2341. const int *pCurKillListSlot = m_pParticleKillList;
  2342. #ifdef _DEBUG
  2343. // This algorithm assumes the particles listed in the kill list are in ascending order
  2344. for ( int i = 1; i < nLeftInKillList; ++i )
  2345. {
  2346. Assert( pCurKillListSlot[i] > pCurKillListSlot[i-1] );
  2347. }
  2348. #endif
  2349. // first, kill particles past bounds
  2350. while ( nLeftInKillList && pCurKillListSlot[nLeftInKillList - 1] >= nParticlesActiveNow )
  2351. {
  2352. nLeftInKillList--;
  2353. }
  2354. Assert( nLeftInKillList <= m_nActiveParticles );
  2355. // now, execute kill list
  2356. // Previously, this code would swap the last item that wasn't dead into this slot.
  2357. // However, some lists require order invariance, so we need to collapse over holes instead of
  2358. // doing the (cheaper) swap from the end to the hole.
  2359. if ( !m_bRequiresOrderInvariance )
  2360. {
  2361. while( nLeftInKillList )
  2362. {
  2363. int nKillIndex = *(pCurKillListSlot++);
  2364. nLeftInKillList--;
  2365. // now, we will move a particle from the end to where we are
  2366. // first, we have to find the last particle (which is not in the kill list)
  2367. while ( nLeftInKillList &&
  2368. ( pCurKillListSlot[ nLeftInKillList-1 ] == nParticlesActiveNow-1 ))
  2369. {
  2370. nLeftInKillList--;
  2371. nParticlesActiveNow--;
  2372. }
  2373. // we might be killing the last particle
  2374. if ( nKillIndex == nParticlesActiveNow-1 )
  2375. {
  2376. // killing last one
  2377. nParticlesActiveNow--;
  2378. break; // we are done
  2379. }
  2380. // move the last particle to this one and chop off the end of the list
  2381. MoveParticle( nParticlesActiveNow-1, nKillIndex );
  2382. nParticlesActiveNow--;
  2383. }
  2384. }
  2385. else
  2386. {
  2387. // The calling code may tell us to kill particles that are already out of bounds.
  2388. // That causes this code to kill more particles than we're supposed to (possibly even causing a crash).
  2389. // So remember how many particles we had left to kill so we can properly decrement the count below.
  2390. int decrementValue = nLeftInKillList;
  2391. int writeLoc = *(pCurKillListSlot++);
  2392. --nLeftInKillList;
  2393. for ( int readLoc = 1 + writeLoc; readLoc < nParticlesActiveNow; ++readLoc )
  2394. {
  2395. if ( nLeftInKillList > 0 && readLoc == *pCurKillListSlot )
  2396. {
  2397. pCurKillListSlot++;
  2398. --nLeftInKillList;
  2399. continue;
  2400. }
  2401. MoveParticle( readLoc, writeLoc );
  2402. ++writeLoc;
  2403. }
  2404. nParticlesActiveNow -= decrementValue;
  2405. }
  2406. // set count in system and wipe kill list
  2407. SetNActiveParticles( nParticlesActiveNow );
  2408. m_nNumParticlesToKill = 0;
  2409. }
  2410. void CParticleCollection::CalculatePathValues( CPathParameters const &PathIn,
  2411. float flTimeStamp,
  2412. Vector *pStartPnt,
  2413. Vector *pMidPnt,
  2414. Vector *pEndPnt
  2415. )
  2416. {
  2417. Vector StartPnt;
  2418. GetControlPointAtTime( PathIn.m_nStartControlPointNumber, flTimeStamp, &StartPnt );
  2419. Vector EndPnt;
  2420. GetControlPointAtTime( PathIn.m_nEndControlPointNumber, flTimeStamp, &EndPnt );
  2421. Vector MidP;
  2422. VectorLerp(StartPnt, EndPnt, PathIn.m_flMidPoint, MidP);
  2423. if ( PathIn.m_nBulgeControl )
  2424. {
  2425. Vector vTarget=(EndPnt-StartPnt);
  2426. float flBulgeScale = 0.0;
  2427. int nCP=PathIn.m_nStartControlPointNumber;
  2428. if ( PathIn.m_nBulgeControl == 2)
  2429. nCP = PathIn.m_nEndControlPointNumber;
  2430. Vector Fwd = m_ControlPoints[nCP].m_ForwardVector;
  2431. float len=VectorLength( vTarget);
  2432. if ( len > 1.0e-6 )
  2433. {
  2434. vTarget *= (1.0/len); // normalize
  2435. flBulgeScale = 1.0-fabs( DotProduct( vTarget, Fwd )); // bulge inversely scaled
  2436. }
  2437. Vector Potential_MidP=Fwd;
  2438. float flOffsetDist = VectorLength( Potential_MidP );
  2439. if ( flOffsetDist > 1.0e-6 )
  2440. {
  2441. Potential_MidP *= (PathIn.m_flBulge*len*flBulgeScale)/flOffsetDist;
  2442. MidP += Potential_MidP;
  2443. }
  2444. }
  2445. else
  2446. {
  2447. Vector RndVector;
  2448. RandomVector( 0, -PathIn.m_flBulge, PathIn.m_flBulge, &RndVector);
  2449. MidP+=RndVector;
  2450. }
  2451. *pStartPnt = StartPnt;
  2452. *pMidPnt = MidP;
  2453. *pEndPnt = EndPnt;
  2454. }
  2455. //-----------------------------------------------------------------------------
  2456. //
  2457. // Default impelemtation of the query
  2458. //
  2459. //-----------------------------------------------------------------------------
  2460. class CDefaultParticleSystemQuery : public CBaseAppSystem< IParticleSystemQuery >
  2461. {
  2462. public:
  2463. virtual void GetLightingAtPoint( const Vector& vecOrigin, Color &tint )
  2464. {
  2465. tint.SetColor( 255, 255, 255, 255 );
  2466. }
  2467. virtual void TraceLine( const Vector& vecAbsStart,
  2468. const Vector& vecAbsEnd, unsigned int mask,
  2469. const class IHandleEntity *ignore,
  2470. int collisionGroup, CBaseTrace *ptr )
  2471. {
  2472. ptr->fraction = 1.0; // no hit
  2473. }
  2474. virtual void GetRandomPointsOnControllingObjectHitBox(
  2475. CParticleCollection *pParticles,
  2476. int nControlPointNumber,
  2477. int nNumPtsOut,
  2478. float flBBoxScale,
  2479. int nNumTrysToGetAPointInsideTheModel,
  2480. Vector *pPntsOut,
  2481. Vector vecDirectionBias,
  2482. Vector *pHitBoxRelativeCoordOut, int *pHitBoxIndexOut )
  2483. {
  2484. for ( int i = 0; i < nNumPtsOut; ++i )
  2485. {
  2486. pPntsOut[i].Init();
  2487. }
  2488. }
  2489. virtual float GetPixelVisibility( int *pQueryHandle, const Vector &vecOrigin, float flScale ) { return 0.0f; }
  2490. };
  2491. static CDefaultParticleSystemQuery s_DefaultParticleSystemQuery;
  2492. //-----------------------------------------------------------------------------
  2493. //
  2494. // Particle system manager
  2495. //
  2496. //-----------------------------------------------------------------------------
  2497. //-----------------------------------------------------------------------------
  2498. // Constructor
  2499. //-----------------------------------------------------------------------------
  2500. CParticleSystemMgr::CParticleSystemMgr()
  2501. // m_SheetList( DefLessFunc( ITexture * ) )
  2502. {
  2503. m_pQuery = &s_DefaultParticleSystemQuery;
  2504. m_bDidInit = false;
  2505. m_bUsingDefaultQuery = true;
  2506. m_bShouldLoadSheets = true;
  2507. m_pParticleSystemDictionary = NULL;
  2508. m_nNumFramesMeasured = 0;
  2509. m_flLastSimulationTime = 0.0f;
  2510. m_nParticleVertexCount = m_nParticleIndexCount = 0;
  2511. m_bFrameWarningNeeded = false;
  2512. for ( int i = 0; i < c_nNumFramesTracked; i++ )
  2513. {
  2514. m_nParticleVertexCountHistory[i] = 0;
  2515. }
  2516. m_fParticleCountScaling = 1.0f;
  2517. }
  2518. CParticleSystemMgr::~CParticleSystemMgr()
  2519. {
  2520. if ( m_pParticleSystemDictionary )
  2521. {
  2522. delete m_pParticleSystemDictionary;
  2523. m_pParticleSystemDictionary = NULL;
  2524. }
  2525. FlushAllSheets();
  2526. }
  2527. //-----------------------------------------------------------------------------
  2528. // Initialize the particle system
  2529. //-----------------------------------------------------------------------------
  2530. bool CParticleSystemMgr::Init( IParticleSystemQuery *pQuery )
  2531. {
  2532. if ( !g_pMaterialSystem->QueryInterface( MATERIAL_SYSTEM_INTERFACE_VERSION ) )
  2533. {
  2534. Msg( "CParticleSystemMgr compiled using an old IMaterialSystem\n" );
  2535. return false;
  2536. }
  2537. if ( m_bUsingDefaultQuery && pQuery )
  2538. {
  2539. m_pQuery = pQuery;
  2540. m_bUsingDefaultQuery = false;
  2541. }
  2542. if ( !m_bDidInit )
  2543. {
  2544. m_pParticleSystemDictionary = new CParticleSystemDictionary;
  2545. // NOTE: This is for the editor only
  2546. AddParticleOperator( FUNCTION_CHILDREN, &s_ChildOperatorDefinition );
  2547. m_pShadowDepthMaterial = NULL;
  2548. if( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 90 )
  2549. {
  2550. KeyValues *pVMTKeyValues = new KeyValues( "DepthWrite" );
  2551. pVMTKeyValues->SetInt( "$no_fullbright", 1 );
  2552. pVMTKeyValues->SetInt( "$model", 0 );
  2553. pVMTKeyValues->SetInt( "$alphatest", 0 );
  2554. m_pShadowDepthMaterial = g_pMaterialSystem->CreateMaterial( "__particlesDepthWrite", pVMTKeyValues );
  2555. }
  2556. SeedRandSIMD( 12345678 );
  2557. m_bDidInit = true;
  2558. }
  2559. return true;
  2560. }
  2561. //----------------------------------------------------------------------------------
  2562. // Cache/uncache materials used by particle systems
  2563. //----------------------------------------------------------------------------------
  2564. void CParticleSystemMgr::PrecacheParticleSystem( const char *pName )
  2565. {
  2566. if ( !pName || !pName[0] )
  2567. {
  2568. return;
  2569. }
  2570. CParticleSystemDefinition* pDef = FindParticleSystem( pName );
  2571. if ( !pDef )
  2572. {
  2573. Warning( "Attemped to precache unknown particle system \"%s\"!\n", pName );
  2574. return;
  2575. }
  2576. pDef->Precache();
  2577. }
  2578. void CParticleSystemMgr::UncacheAllParticleSystems()
  2579. {
  2580. if ( !m_pParticleSystemDictionary )
  2581. return;
  2582. int nCount = m_pParticleSystemDictionary->Count();
  2583. for ( int i = 0; i < nCount; ++i )
  2584. {
  2585. m_pParticleSystemDictionary->GetParticleSystem( i )->Uncache();
  2586. }
  2587. nCount = m_pParticleSystemDictionary->NameCount();
  2588. for ( ParticleSystemHandle_t h = 0; h < nCount; ++h )
  2589. {
  2590. m_pParticleSystemDictionary->FindParticleSystem( h )->Uncache();
  2591. }
  2592. // Flush sheets, as they can accumulate several MB of memory per map
  2593. FlushAllSheets();
  2594. }
  2595. //-----------------------------------------------------------------------------
  2596. // return the particle field name
  2597. //-----------------------------------------------------------------------------
  2598. static const char *s_pParticleFieldNames[MAX_PARTICLE_ATTRIBUTES] =
  2599. {
  2600. "Position", // XYZ, 0
  2601. "Life Duration", // LIFE_DURATION, 1 );
  2602. NULL, // PREV_XYZ is for internal use only
  2603. "Radius", // RADIUS, 3 );
  2604. "Roll", // ROTATION, 4 );
  2605. "Roll Speed", // ROTATION_SPEED, 5 );
  2606. "Color", // TINT_RGB, 6 );
  2607. "Alpha", // ALPHA, 7 );
  2608. "Creation Time", // CREATION_TIME, 8 );
  2609. "Sequence Number", // SEQUENCE_NUMBER, 9 );
  2610. "Trail Length", // TRAIL_LENGTH, 10 );
  2611. "Particle ID", // PARTICLE_ID, 11 );
  2612. "Yaw", // YAW, 12 );
  2613. "Sequence Number 1", // SEQUENCE_NUMBER1, 13 );
  2614. NULL, // HITBOX_INDEX is for internal use only
  2615. NULL, // HITBOX_XYZ_RELATIVE is for internal use only
  2616. "Alpha Alternate", // ALPHA2, 16
  2617. NULL,
  2618. NULL,
  2619. NULL,
  2620. NULL,
  2621. NULL,
  2622. NULL,
  2623. NULL,
  2624. NULL,
  2625. NULL,
  2626. NULL,
  2627. NULL,
  2628. NULL,
  2629. NULL,
  2630. NULL,
  2631. NULL,
  2632. };
  2633. const char* CParticleSystemMgr::GetParticleFieldName( int nParticleField ) const
  2634. {
  2635. return s_pParticleFieldNames[nParticleField];
  2636. }
  2637. //-----------------------------------------------------------------------------
  2638. // Returns the available particle operators
  2639. //-----------------------------------------------------------------------------
  2640. void CParticleSystemMgr::AddParticleOperator( ParticleFunctionType_t nOpType,
  2641. IParticleOperatorDefinition *pOpFactory )
  2642. {
  2643. m_ParticleOperators[nOpType].AddToTail( pOpFactory );
  2644. }
  2645. CUtlVector< IParticleOperatorDefinition *> &CParticleSystemMgr::GetAvailableParticleOperatorList( ParticleFunctionType_t nWhichList )
  2646. {
  2647. return m_ParticleOperators[nWhichList];
  2648. }
  2649. const DmxElementUnpackStructure_t *CParticleSystemMgr::GetParticleSystemDefinitionUnpackStructure()
  2650. {
  2651. return s_pParticleSystemDefinitionUnpack;
  2652. }
  2653. //------------------------------------------------------------------------------
  2654. // custom allocators for operators so simd aligned
  2655. //------------------------------------------------------------------------------
  2656. #include "tier0/memdbgoff.h"
  2657. void *CParticleOperatorInstance::operator new( size_t nSize )
  2658. {
  2659. return MemAlloc_AllocAligned( nSize, 16 );
  2660. }
  2661. void* CParticleOperatorInstance::operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  2662. {
  2663. return MemAlloc_AllocAligned( nSize, 16, pFileName, nLine );
  2664. }
  2665. void CParticleOperatorInstance::operator delete(void *pData)
  2666. {
  2667. if ( pData )
  2668. {
  2669. MemAlloc_FreeAligned( pData );
  2670. }
  2671. }
  2672. void CParticleOperatorInstance::operator delete( void* pData, int nBlockUse, const char *pFileName, int nLine )
  2673. {
  2674. if ( pData )
  2675. {
  2676. MemAlloc_FreeAligned( pData );
  2677. }
  2678. }
  2679. #include "tier0/memdbgon.h"
  2680. //-----------------------------------------------------------------------------
  2681. // Read the particle config file from a utlbuffer
  2682. //-----------------------------------------------------------------------------
  2683. bool CParticleSystemMgr::ReadParticleDefinitions( CUtlBuffer &buf, const char *pFileName, bool bPrecache, bool bDecommitTempMemory )
  2684. {
  2685. DECLARE_DMX_CONTEXT_DECOMMIT( bDecommitTempMemory );
  2686. CDmxElement *pRoot;
  2687. if ( !UnserializeDMX( buf, &pRoot, pFileName ) || !pRoot )
  2688. {
  2689. Warning( "Unable to read particle definition %s! UtlBuffer is the wrong type!\n", pFileName );
  2690. return false;
  2691. }
  2692. if ( !Q_stricmp( pRoot->GetTypeString(), "DmeParticleSystemDefinition" ) )
  2693. {
  2694. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->AddParticleSystem( pRoot );
  2695. if ( pDef && bPrecache )
  2696. {
  2697. pDef->m_bAlwaysPrecache = true;
  2698. if ( IsPC() )
  2699. {
  2700. pDef->Precache();
  2701. }
  2702. }
  2703. CleanupDMX( pRoot );
  2704. return true;
  2705. }
  2706. const CDmxAttribute *pDefinitions = pRoot->GetAttribute( "particleSystemDefinitions" );
  2707. if ( !pDefinitions || pDefinitions->GetType() != AT_ELEMENT_ARRAY )
  2708. {
  2709. CleanupDMX( pRoot );
  2710. return false;
  2711. }
  2712. const CUtlVector< CDmxElement* >& definitions = pDefinitions->GetArray<CDmxElement*>( );
  2713. int nCount = definitions.Count();
  2714. for ( int i = 0; i < nCount; ++i )
  2715. {
  2716. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->AddParticleSystem( definitions[i] );
  2717. if ( pDef && bPrecache )
  2718. {
  2719. pDef->m_bAlwaysPrecache = true;
  2720. if ( IsPC() )
  2721. {
  2722. pDef->Precache();
  2723. }
  2724. }
  2725. }
  2726. CleanupDMX( pRoot );
  2727. return true;
  2728. }
  2729. //-----------------------------------------------------------------------------
  2730. // Decommits temporary memory
  2731. //-----------------------------------------------------------------------------
  2732. void CParticleSystemMgr::DecommitTempMemory()
  2733. {
  2734. DecommitDMXMemory();
  2735. }
  2736. //-----------------------------------------------------------------------------
  2737. // Sets the last simulation time, used for particle system sleeping logic
  2738. //-----------------------------------------------------------------------------
  2739. void CParticleSystemMgr::SetLastSimulationTime( float flTime )
  2740. {
  2741. m_flLastSimulationTime = flTime;
  2742. int nParticleVertexCountHistoryMax = 0;
  2743. for ( int i = 0; i < c_nNumFramesTracked-1; i++ )
  2744. {
  2745. m_nParticleVertexCountHistory[i] = m_nParticleVertexCountHistory[i+1];
  2746. nParticleVertexCountHistoryMax = Max ( nParticleVertexCountHistoryMax, m_nParticleVertexCountHistory[i] );
  2747. }
  2748. m_nParticleVertexCountHistory[c_nNumFramesTracked-1] = m_nParticleVertexCount;
  2749. nParticleVertexCountHistoryMax = Max ( nParticleVertexCountHistoryMax, m_nParticleVertexCount );
  2750. // We need to take an average over a decent number of frames because this throttling has a direct feedback effect. Worried about oscillation problems!
  2751. int nLower = CL_PARTICLE_SCALE_LOWER;//cl_particle_scale_lower.GetInt();
  2752. m_fParticleCountScaling = 1.0f;
  2753. int nHowManyOver = nParticleVertexCountHistoryMax - nLower;
  2754. if ( nHowManyOver > 0 )
  2755. {
  2756. int nUpper = CL_PARTICLE_SCALE_UPPER;//cl_particle_scale_upper.GetInt();
  2757. int nRange = nUpper - nLower;
  2758. m_fParticleCountScaling = 1.0f - ( (float)nHowManyOver / (float)nRange );
  2759. m_fParticleCountScaling = Clamp ( m_fParticleCountScaling, 0.0f, 1.0f );
  2760. }
  2761. m_nParticleVertexCount = m_nParticleIndexCount = 0;
  2762. }
  2763. float CParticleSystemMgr::GetLastSimulationTime() const
  2764. {
  2765. return m_flLastSimulationTime;
  2766. }
  2767. bool CParticleSystemMgr::Debug_FrameWarningNeededTestAndReset()
  2768. {
  2769. bool bTemp = m_bFrameWarningNeeded;
  2770. m_bFrameWarningNeeded = false;
  2771. return bTemp;
  2772. }
  2773. int CParticleSystemMgr::Debug_GetTotalParticleCount() const
  2774. {
  2775. return m_nParticleVertexCountHistory[c_nNumFramesTracked-1];
  2776. }
  2777. float CParticleSystemMgr::ParticleThrottleScaling() const
  2778. {
  2779. return m_fParticleCountScaling;
  2780. }
  2781. bool CParticleSystemMgr::ParticleThrottleRandomEnable() const
  2782. {
  2783. if ( m_fParticleCountScaling == 1.0f )
  2784. {
  2785. // No throttling.
  2786. return true;
  2787. }
  2788. else if ( m_fParticleCountScaling > RandomFloat ( 0.0f, 1.0f ) )
  2789. {
  2790. return true;
  2791. }
  2792. return false;
  2793. }
  2794. //-----------------------------------------------------------------------------
  2795. // Unserialization-related methods
  2796. //-----------------------------------------------------------------------------
  2797. void CParticleSystemMgr::AddParticleSystem( CDmxElement *pParticleSystem )
  2798. {
  2799. m_pParticleSystemDictionary->AddParticleSystem( pParticleSystem );
  2800. }
  2801. CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( const char *pName )
  2802. {
  2803. return m_pParticleSystemDictionary->FindParticleSystem( pName );
  2804. }
  2805. CParticleSystemDefinition* CParticleSystemMgr::FindParticleSystem( const DmObjectId_t& id )
  2806. {
  2807. return m_pParticleSystemDictionary->FindParticleSystem( id );
  2808. }
  2809. //-----------------------------------------------------------------------------
  2810. // Read the particle config file from a utlbuffer
  2811. //-----------------------------------------------------------------------------
  2812. bool CParticleSystemMgr::ReadParticleConfigFile( CUtlBuffer &buf, bool bPrecache, bool bDecommitTempMemory, const char *pFileName )
  2813. {
  2814. return ReadParticleDefinitions( buf, pFileName, bPrecache, bDecommitTempMemory );
  2815. }
  2816. //-----------------------------------------------------------------------------
  2817. // Read the particle config file from a utlbuffer
  2818. //-----------------------------------------------------------------------------
  2819. bool CParticleSystemMgr::ReadParticleConfigFile( const char *pFileName, bool bPrecache, bool bDecommitTempMemory )
  2820. {
  2821. // Names starting with a '!' are always precached.
  2822. if ( pFileName[0] == '!' )
  2823. {
  2824. bPrecache = true;
  2825. ++pFileName;
  2826. }
  2827. if ( IsX360() )
  2828. {
  2829. char szTargetName[MAX_PATH];
  2830. CreateX360Filename( pFileName, szTargetName, sizeof( szTargetName ) );
  2831. CUtlBuffer fileBuffer;
  2832. bool bHaveParticles = g_pFullFileSystem->ReadFile( szTargetName, "GAME", fileBuffer );
  2833. if ( bHaveParticles )
  2834. {
  2835. fileBuffer.SetBigEndian( false );
  2836. return ReadParticleConfigFile( fileBuffer, bPrecache, bDecommitTempMemory, szTargetName );
  2837. }
  2838. else if ( g_pFullFileSystem->GetDVDMode() != DVDMODE_OFF )
  2839. {
  2840. // 360 version should have been there, 360 zips can only have binary particles
  2841. Warning( "Particles: Missing '%s'\n", szTargetName );
  2842. return false;
  2843. }
  2844. }
  2845. char pFallbackBuf[MAX_PATH];
  2846. if ( IsPC() )
  2847. {
  2848. // Look for fallback particle systems
  2849. char pTemp[MAX_PATH];
  2850. Q_StripExtension( pFileName, pTemp, sizeof(pTemp) );
  2851. const char *pExt = Q_GetFileExtension( pFileName );
  2852. if ( !pExt )
  2853. {
  2854. pExt = "pcf";
  2855. }
  2856. if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 90 )
  2857. {
  2858. Q_snprintf( pFallbackBuf, sizeof(pFallbackBuf), "%s_dx80.%s", pTemp, pExt );
  2859. if ( g_pFullFileSystem->FileExists( pFallbackBuf ) )
  2860. {
  2861. pFileName = pFallbackBuf;
  2862. }
  2863. }
  2864. else if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() == 90 && g_pMaterialSystemHardwareConfig->PreferReducedFillrate() )
  2865. {
  2866. Q_snprintf( pFallbackBuf, sizeof(pFallbackBuf), "%s_dx90_slow.%s", pTemp, pExt );
  2867. if ( g_pFullFileSystem->FileExists( pFallbackBuf ) )
  2868. {
  2869. pFileName = pFallbackBuf;
  2870. }
  2871. }
  2872. }
  2873. CUtlBuffer buf( 0, 0, 0 );
  2874. if ( IsX360() )
  2875. {
  2876. // fell through, load as pc particle resource file
  2877. buf.ActivateByteSwapping( true );
  2878. }
  2879. if ( g_pFullFileSystem->ReadFile( pFileName, "GAME", buf ) )
  2880. {
  2881. return ReadParticleConfigFile( buf, bPrecache, bDecommitTempMemory, pFileName );
  2882. }
  2883. Warning( "Particles: Missing '%s'\n", pFileName );
  2884. return false;
  2885. }
  2886. //-----------------------------------------------------------------------------
  2887. // Write a specific particle config to a utlbuffer
  2888. //-----------------------------------------------------------------------------
  2889. bool CParticleSystemMgr::WriteParticleConfigFile( const char *pParticleSystemName, CUtlBuffer &buf, bool bPreventNameBasedLookup )
  2890. {
  2891. DECLARE_DMX_CONTEXT();
  2892. // Create DMX elements representing the particle system definition
  2893. CDmxElement *pParticleSystem = CreateParticleDmxElement( pParticleSystemName );
  2894. return WriteParticleConfigFile( pParticleSystem, buf, bPreventNameBasedLookup );
  2895. }
  2896. bool CParticleSystemMgr::WriteParticleConfigFile( const DmObjectId_t& id, CUtlBuffer &buf, bool bPreventNameBasedLookup )
  2897. {
  2898. DECLARE_DMX_CONTEXT();
  2899. // Create DMX elements representing the particle system definition
  2900. CDmxElement *pParticleSystem = CreateParticleDmxElement( id );
  2901. return WriteParticleConfigFile( pParticleSystem, buf, bPreventNameBasedLookup );
  2902. }
  2903. bool CParticleSystemMgr::WriteParticleConfigFile( CDmxElement *pParticleSystem, CUtlBuffer &buf, bool bPreventNameBasedLookup )
  2904. {
  2905. pParticleSystem->SetValue( "preventNameBasedLookup", bPreventNameBasedLookup );
  2906. CDmxAttribute* pAttribute = pParticleSystem->GetAttribute( "children" );
  2907. const CUtlVector< CDmxElement* >& children = pAttribute->GetArray<CDmxElement*>( );
  2908. int nCount = children.Count();
  2909. for ( int i = 0; i < nCount; ++i )
  2910. {
  2911. CDmxElement *pChildRef = children[ i ];
  2912. CDmxElement *pChild = pChildRef->GetValue<CDmxElement*>( "child" );
  2913. pChild->SetValue( "preventNameBasedLookup", bPreventNameBasedLookup );
  2914. }
  2915. // Now write the DMX elements out
  2916. bool bOk = SerializeDMX( buf, pParticleSystem );
  2917. CleanupDMX( pParticleSystem );
  2918. return bOk;
  2919. }
  2920. ParticleSystemHandle_t CParticleSystemMgr::GetParticleSystemIndex( const char *pParticleSystemName )
  2921. {
  2922. if ( !pParticleSystemName )
  2923. return UTL_INVAL_SYMBOL;
  2924. return m_pParticleSystemDictionary->FindParticleSystemHandle( pParticleSystemName );
  2925. }
  2926. const char *CParticleSystemMgr::GetParticleSystemNameFromIndex( ParticleSystemHandle_t iIndex )
  2927. {
  2928. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( iIndex );
  2929. return pDef ? pDef->GetName() : "Unknown";
  2930. }
  2931. int CParticleSystemMgr::GetParticleSystemCount( void )
  2932. {
  2933. return m_pParticleSystemDictionary->NameCount();
  2934. }
  2935. //-----------------------------------------------------------------------------
  2936. // Factory method for creating particle collections
  2937. //-----------------------------------------------------------------------------
  2938. CParticleCollection *CParticleSystemMgr::CreateParticleCollection( const char *pParticleSystemName, float flDelay, int nRandomSeed )
  2939. {
  2940. if ( !pParticleSystemName )
  2941. return NULL;
  2942. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pParticleSystemName );
  2943. if ( !pDef )
  2944. {
  2945. Warning( "Attempted to create unknown particle system type %s\n", pParticleSystemName );
  2946. return NULL;
  2947. }
  2948. CParticleCollection *pParticleCollection = new CParticleCollection;
  2949. pParticleCollection->Init( pDef, flDelay, nRandomSeed );
  2950. return pParticleCollection;
  2951. }
  2952. CParticleCollection *CParticleSystemMgr::CreateParticleCollection( const DmObjectId_t &id, float flDelay, int nRandomSeed )
  2953. {
  2954. if ( !IsUniqueIdValid( id ) )
  2955. return NULL;
  2956. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
  2957. if ( !pDef )
  2958. {
  2959. char pBuf[256];
  2960. UniqueIdToString( id, pBuf, sizeof(pBuf) );
  2961. Warning( "Attempted to create unknown particle system id %s\n", pBuf );
  2962. return NULL;
  2963. }
  2964. CParticleCollection *pParticleCollection = new CParticleCollection;
  2965. pParticleCollection->Init( pDef, flDelay, nRandomSeed );
  2966. return pParticleCollection;
  2967. }
  2968. //--------------------------------------------------------------------------------
  2969. // Is a particular particle system defined?
  2970. //--------------------------------------------------------------------------------
  2971. bool CParticleSystemMgr::IsParticleSystemDefined( const DmObjectId_t &id )
  2972. {
  2973. if ( !IsUniqueIdValid( id ) )
  2974. return false;
  2975. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
  2976. return ( pDef != NULL );
  2977. }
  2978. //--------------------------------------------------------------------------------
  2979. // Is a particular particle system defined?
  2980. //--------------------------------------------------------------------------------
  2981. bool CParticleSystemMgr::IsParticleSystemDefined( const char *pName )
  2982. {
  2983. if ( !pName || !pName[0] )
  2984. return false;
  2985. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pName );
  2986. return ( pDef != NULL );
  2987. }
  2988. //--------------------------------------------------------------------------------
  2989. // Particle kill list
  2990. //--------------------------------------------------------------------------------
  2991. //--------------------------------------------------------------------------------
  2992. // Serialization-related methods
  2993. //--------------------------------------------------------------------------------
  2994. CDmxElement *CParticleSystemMgr::CreateParticleDmxElement( const DmObjectId_t &id )
  2995. {
  2996. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( id );
  2997. // Create DMX elements representing the particle system definition
  2998. return pDef->Write( );
  2999. }
  3000. CDmxElement *CParticleSystemMgr::CreateParticleDmxElement( const char *pParticleSystemName )
  3001. {
  3002. CParticleSystemDefinition *pDef = m_pParticleSystemDictionary->FindParticleSystem( pParticleSystemName );
  3003. // Create DMX elements representing the particle system definition
  3004. return pDef->Write( );
  3005. }
  3006. //--------------------------------------------------------------------------------
  3007. // Client loads sheets for rendering, server doesn't need to.
  3008. //--------------------------------------------------------------------------------
  3009. void CParticleSystemMgr::ShouldLoadSheets( bool bLoadSheets )
  3010. {
  3011. m_bShouldLoadSheets = bLoadSheets;
  3012. }
  3013. //--------------------------------------------------------------------------------
  3014. // Particle sheets
  3015. //--------------------------------------------------------------------------------
  3016. CSheet *CParticleSystemMgr::FindOrLoadSheet( char const *pszFname, ITexture *pTexture )
  3017. {
  3018. if ( !m_bShouldLoadSheets )
  3019. return NULL;
  3020. if ( m_SheetList.Defined( pszFname ) )
  3021. return m_SheetList[ pszFname ];
  3022. CSheet *pNewSheet = NULL;
  3023. size_t numBytes;
  3024. void const *pSheet = pTexture->GetResourceData( VTF_RSRC_SHEET, &numBytes );
  3025. if ( pSheet )
  3026. {
  3027. CUtlBuffer bufLoad( pSheet, numBytes, CUtlBuffer::READ_ONLY );
  3028. pNewSheet = new CSheet( bufLoad );
  3029. }
  3030. m_SheetList[ pszFname ] = pNewSheet;
  3031. return pNewSheet;
  3032. }
  3033. CSheet *CParticleSystemMgr::FindOrLoadSheet( IMaterial *pMaterial )
  3034. {
  3035. if ( !pMaterial )
  3036. return NULL;
  3037. bool bFoundVar = false;
  3038. IMaterialVar *pVar = pMaterial->FindVar( "$basetexture", &bFoundVar, true );
  3039. if ( bFoundVar && pVar && pVar->IsDefined() )
  3040. {
  3041. ITexture *pTex = pVar->GetTextureValue();
  3042. if ( pTex && !pTex->IsError() )
  3043. return FindOrLoadSheet( pTex->GetName(), pTex );
  3044. }
  3045. return NULL;
  3046. }
  3047. void CParticleSystemMgr::FlushAllSheets( void )
  3048. {
  3049. // for( int i = 0, iEnd = m_SheetList.Count(); i < iEnd; i++ )
  3050. // {
  3051. // delete m_SheetList.Element(i);
  3052. // }
  3053. //
  3054. // m_SheetList.RemoveAll();
  3055. m_SheetList.PurgeAndDeleteElements();
  3056. }
  3057. //-----------------------------------------------------------------------------
  3058. // Render cache
  3059. //-----------------------------------------------------------------------------
  3060. void CParticleSystemMgr::ResetRenderCache( void )
  3061. {
  3062. int nCount = m_RenderCache.Count();
  3063. for ( int i = 0; i < nCount; ++i )
  3064. {
  3065. m_RenderCache[i].m_ParticleCollections.RemoveAll();
  3066. }
  3067. }
  3068. void CParticleSystemMgr::AddToRenderCache( CParticleCollection *pParticles )
  3069. {
  3070. if ( !pParticles->IsValid() || pParticles->m_pDef->GetMaterial()->IsTranslucent() )
  3071. return;
  3072. pParticles->m_flNextSleepTime = Max ( pParticles->m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + pParticles->m_pDef->m_flNoDrawTimeToGoToSleep ));
  3073. // Find the current rope list.
  3074. int iRenderCache = 0;
  3075. int nRenderCacheCount = m_RenderCache.Count();
  3076. for ( ; iRenderCache < nRenderCacheCount; ++iRenderCache )
  3077. {
  3078. if ( ( pParticles->m_pDef->GetMaterial() == m_RenderCache[iRenderCache].m_pMaterial ) )
  3079. break;
  3080. }
  3081. // A full rope list should have been generate in CreateRenderCache
  3082. // If we didn't find one, then allocate the mofo.
  3083. if ( iRenderCache == nRenderCacheCount )
  3084. {
  3085. iRenderCache = m_RenderCache.AddToTail();
  3086. m_RenderCache[iRenderCache].m_pMaterial = pParticles->m_pDef->GetMaterial();
  3087. }
  3088. m_RenderCache[iRenderCache].m_ParticleCollections.AddToTail( pParticles );
  3089. for( CParticleCollection *p = pParticles->m_Children.m_pHead; p; p = p->m_pNext )
  3090. {
  3091. AddToRenderCache( p );
  3092. }
  3093. }
  3094. void CParticleSystemMgr::BuildBatchList( int iRenderCache, IMatRenderContext *pRenderContext, CUtlVector< Batch_t >& batches )
  3095. {
  3096. batches.RemoveAll();
  3097. IMaterial *pMaterial = m_RenderCache[iRenderCache].m_pMaterial;
  3098. int nMaxVertices = pRenderContext->GetMaxVerticesToRender( pMaterial );
  3099. int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
  3100. int nRemainingVertices = nMaxVertices;
  3101. int nRemainingIndices = nMaxIndices;
  3102. int i = batches.AddToTail();
  3103. Batch_t* pBatch = &batches[i];
  3104. pBatch->m_nVertCount = 0;
  3105. pBatch->m_nIndexCount = 0;
  3106. // Ask each renderer about the # of verts + ints it will draw
  3107. int nCacheCount = m_RenderCache[iRenderCache].m_ParticleCollections.Count();
  3108. for ( int iCache = 0; iCache < nCacheCount; ++iCache )
  3109. {
  3110. CParticleCollection *pParticles = m_RenderCache[iRenderCache].m_ParticleCollections[iCache];
  3111. if ( !pParticles->IsValid() )
  3112. continue;
  3113. int nRenderCount = pParticles->GetRendererCount();
  3114. for ( int j = 0; j < nRenderCount; ++j )
  3115. {
  3116. int nFirstParticle = 0;
  3117. while ( nFirstParticle < pParticles->m_nActiveParticles )
  3118. {
  3119. int iPart;
  3120. BatchStep_t step;
  3121. step.m_pParticles = pParticles;
  3122. step.m_pRenderer = pParticles->GetRenderer( j );
  3123. step.m_pContext = pParticles->GetRendererContext( j );
  3124. step.m_nFirstParticle = nFirstParticle;
  3125. step.m_nParticleCount = step.m_pRenderer->GetParticlesToRender( pParticles,
  3126. step.m_pContext, nFirstParticle, nRemainingVertices, nRemainingIndices, &step.m_nVertCount, &iPart );
  3127. nFirstParticle += step.m_nParticleCount;
  3128. if ( step.m_nParticleCount > 0 )
  3129. {
  3130. pBatch->m_nVertCount += step.m_nVertCount;
  3131. pBatch->m_nIndexCount += iPart;
  3132. pBatch->m_BatchStep.AddToTail( step );
  3133. Assert( pBatch->m_nVertCount <= nMaxVertices && pBatch->m_nIndexCount <= nMaxIndices );
  3134. }
  3135. else
  3136. {
  3137. if ( pBatch->m_nVertCount == 0 )
  3138. break;
  3139. // Not enough room
  3140. Assert( pBatch->m_nVertCount > 0 && pBatch->m_nIndexCount > 0 );
  3141. pBatch = &batches[batches.AddToTail()];
  3142. pBatch->m_nVertCount = 0;
  3143. pBatch->m_nIndexCount = 0;
  3144. nRemainingVertices = nMaxVertices;
  3145. nRemainingIndices = nMaxIndices;
  3146. }
  3147. }
  3148. }
  3149. }
  3150. if ( pBatch->m_nVertCount <= 0 || pBatch->m_nIndexCount <= 0 )
  3151. {
  3152. batches.FastRemove( batches.Count() - 1 );
  3153. }
  3154. }
  3155. void CParticleSystemMgr::DumpProfileInformation( void )
  3156. {
  3157. #if MEASURE_PARTICLE_PERF
  3158. FileHandle_t fh = g_pFullFileSystem->Open( "particle_profile.csv", "w" );
  3159. g_pFullFileSystem->FPrintf( fh, "numframes,%d\n", m_nNumFramesMeasured );
  3160. g_pFullFileSystem->FPrintf( fh, "name, total time, max time, max particles, allocated particles\n");
  3161. for( int i=0; i < m_pParticleSystemDictionary->NameCount(); i++ )
  3162. {
  3163. CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
  3164. if ( p->m_nMaximumActiveParticles )
  3165. g_pFullFileSystem->FPrintf( fh, "%s,%f,%f,%d,%d\n", p->m_Name.Get(), p->m_flTotalSimTime, p->m_flMaxMeasuredSimTime, p->m_nMaximumActiveParticles, p->m_nMaxParticles );
  3166. }
  3167. g_pFullFileSystem->FPrintf( fh, "\n\nopname, total time, max time\n");
  3168. for(int i=0; i < ARRAYSIZE( m_ParticleOperators ); i++)
  3169. {
  3170. for(int j=0; j < m_ParticleOperators[i].Count() ; j++ )
  3171. {
  3172. float flmax = m_ParticleOperators[i][j]->MaximumRecordedExecutionTime();
  3173. float fltotal = m_ParticleOperators[i][j]->TotalRecordedExecutionTime();
  3174. if ( fltotal > 0.0 )
  3175. g_pFullFileSystem->FPrintf( fh, "%s,%f,%f\n",
  3176. m_ParticleOperators[i][j]->GetName(), fltotal, flmax );
  3177. }
  3178. }
  3179. g_pFullFileSystem->Close( fh );
  3180. #endif
  3181. }
  3182. void CParticleSystemMgr::CommitProfileInformation( bool bCommit )
  3183. {
  3184. #if MEASURE_PARTICLE_PERF
  3185. if ( 1 )
  3186. {
  3187. if ( bCommit )
  3188. m_nNumFramesMeasured++;
  3189. for( int i=0; i < m_pParticleSystemDictionary->NameCount(); i++ )
  3190. {
  3191. CParticleSystemDefinition *p = ( *m_pParticleSystemDictionary )[ i ];
  3192. if ( bCommit )
  3193. p->m_flTotalSimTime += p->m_flUncomittedTotalSimTime;
  3194. p->m_flUncomittedTotalSimTime = 0.;
  3195. }
  3196. for(int i=0; i < ARRAYSIZE( m_ParticleOperators ); i++)
  3197. {
  3198. for(int j=0; j < m_ParticleOperators[i].Count() ; j++ )
  3199. {
  3200. if ( bCommit )
  3201. m_ParticleOperators[i][j]->m_flTotalExecutionTime += m_ParticleOperators[i][j]->m_flUncomittedTime;
  3202. m_ParticleOperators[i][j]->m_flUncomittedTime = 0;
  3203. }
  3204. }
  3205. }
  3206. #endif
  3207. }
  3208. void CParticleSystemMgr::DrawRenderCache( bool bShadowDepth )
  3209. {
  3210. int nRenderCacheCount = m_RenderCache.Count();
  3211. if ( nRenderCacheCount == 0 )
  3212. return;
  3213. VPROF_BUDGET( "CParticleSystemMgr::DrawRenderCache", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  3214. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  3215. pRenderContext->MatrixMode( MATERIAL_MODEL );
  3216. pRenderContext->PushMatrix();
  3217. pRenderContext->LoadIdentity();
  3218. CUtlVector< Batch_t > batches( 0, 8 );
  3219. for ( int iRenderCache = 0; iRenderCache < nRenderCacheCount; ++iRenderCache )
  3220. {
  3221. int nCacheCount = m_RenderCache[iRenderCache].m_ParticleCollections.Count();
  3222. if ( nCacheCount == 0 )
  3223. continue;
  3224. // FIXME: When rendering shadow depth, do it all in 1 batch
  3225. IMaterial *pMaterial = bShadowDepth ? m_pShadowDepthMaterial : m_RenderCache[iRenderCache].m_pMaterial;
  3226. BuildBatchList( iRenderCache, pRenderContext, batches );
  3227. int nBatchCount = batches.Count();
  3228. if ( nBatchCount == 0 )
  3229. continue;
  3230. pRenderContext->Bind( pMaterial );
  3231. CMeshBuilder meshBuilder;
  3232. IMesh* pMesh = pRenderContext->GetDynamicMesh( );
  3233. for ( int i = 0; i < nBatchCount; ++i )
  3234. {
  3235. const Batch_t& batch = batches[i];
  3236. Assert( batch.m_nVertCount > 0 && batch.m_nIndexCount > 0 );
  3237. g_pParticleSystemMgr->TallyParticlesRendered( batch.m_nVertCount * 3, batch.m_nIndexCount * 3 );
  3238. meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, batch.m_nVertCount, batch.m_nIndexCount );
  3239. int nVertexOffset = 0;
  3240. int nBatchStepCount = batch.m_BatchStep.Count();
  3241. for ( int j = 0; j < nBatchStepCount; ++j )
  3242. {
  3243. const BatchStep_t &step = batch.m_BatchStep[j];
  3244. // FIXME: this will break if it ever calls into C_OP_RenderSprites::Render[TwoSequence]SpriteCard()
  3245. // (need to protect against that and/or split the meshBuilder batch to support that path here)
  3246. step.m_pRenderer->RenderUnsorted( step.m_pParticles, step.m_pContext, pRenderContext,
  3247. meshBuilder, nVertexOffset, step.m_nFirstParticle, step.m_nParticleCount );
  3248. nVertexOffset += step.m_nVertCount;
  3249. }
  3250. meshBuilder.End();
  3251. pMesh->Draw();
  3252. }
  3253. }
  3254. ResetRenderCache( );
  3255. pRenderContext->MatrixMode( MATERIAL_MODEL );
  3256. pRenderContext->PopMatrix();
  3257. }
  3258. void CParticleSystemMgr::TallyParticlesRendered( int nVertexCount, int nIndexCount )
  3259. {
  3260. m_nParticleIndexCount += nIndexCount;
  3261. m_nParticleVertexCount += nVertexCount;
  3262. if ( m_nParticleVertexCount > MAX_PARTICLE_VERTS )
  3263. {
  3264. m_bFrameWarningNeeded = true;
  3265. }
  3266. }
  3267. void IParticleSystemQuery::GetRandomPointsOnControllingObjectHitBox(
  3268. CParticleCollection *pParticles,
  3269. int nControlPointNumber,
  3270. int nNumPtsOut,
  3271. float flBBoxScale,
  3272. int nNumTrysToGetAPointInsideTheModel,
  3273. Vector *pPntsOut,
  3274. Vector vecDirectionalBias,
  3275. Vector *pHitBoxRelativeCoordOut,
  3276. int *pHitBoxIndexOut
  3277. )
  3278. {
  3279. for(int i=0; i < nNumPtsOut; i++)
  3280. {
  3281. pPntsOut[i]=pParticles->m_ControlPoints[nControlPointNumber].m_Position;
  3282. if ( pHitBoxRelativeCoordOut )
  3283. pHitBoxRelativeCoordOut[i].Init();
  3284. if ( pHitBoxIndexOut )
  3285. pHitBoxIndexOut[i] = -1;
  3286. }
  3287. }
  3288. void CParticleCollection::UpdateHitBoxInfo( int nControlPointNumber )
  3289. {
  3290. CModelHitBoxesInfo &hb = m_ControlPointHitBoxes[nControlPointNumber];
  3291. if ( hb.m_flLastUpdateTime == m_flCurTime )
  3292. return; // up to date
  3293. hb.m_flLastUpdateTime = m_flCurTime;
  3294. // make sure space allocated
  3295. if ( ! hb.m_pHitBoxes )
  3296. hb.m_pHitBoxes = new ModelHitBoxInfo_t[ MAXSTUDIOBONES ];
  3297. if ( ! hb.m_pPrevBoxes )
  3298. hb.m_pPrevBoxes = new ModelHitBoxInfo_t[ MAXSTUDIOBONES ];
  3299. // save current into prev
  3300. hb.m_nNumPrevHitBoxes = hb.m_nNumHitBoxes;
  3301. hb.m_flPrevLastUpdateTime = hb.m_flLastUpdateTime;
  3302. V_swap( hb.m_pHitBoxes, hb.m_pPrevBoxes );
  3303. // issue hitbox query
  3304. hb.m_nNumHitBoxes = g_pParticleSystemMgr->Query()->GetControllingObjectHitBoxInfo(
  3305. this, nControlPointNumber, MAXSTUDIOBONES, hb.m_pHitBoxes );
  3306. }