Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2259 lines
62 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "particlemgr.h"
  10. #include "particledraw.h"
  11. #include "materialsystem/imesh.h"
  12. #include "materialsystem/imaterialvar.h"
  13. #include "mempool.h"
  14. #include "iclientmode.h"
  15. #include "view_scene.h"
  16. #include "tier0/vprof.h"
  17. #include "engine/ivdebugoverlay.h"
  18. #include "view.h"
  19. #include "keyvalues.h"
  20. #include "particles/particles.h" // get new particle system access
  21. #include "tier1/utlintrusivelist.h"
  22. #include "particles_new.h"
  23. #include "vstdlib/jobthread.h"
  24. #include "filesystem.h"
  25. #include "particle_parse.h"
  26. #include "model_types.h"
  27. #include "tier0/icommandline.h"
  28. // memdbgon must be the last include file in a .cpp file!!!
  29. #include "tier0/memdbgon.h"
  30. extern IParticleSystemQuery *g_pParticleSystemQuery;
  31. static int g_nParticlesDrawn;
  32. // CCycleCount g_ParticleTimer;
  33. ConVar r_DrawParticles("r_drawparticles", "1", FCVAR_CHEAT, "Enable/disable particle rendering");
  34. static ConVar particle_simulateoverflow( "particle_simulateoverflow", "0", FCVAR_CHEAT, "Used for stress-testing particle systems. Randomly denies creation of particles." );
  35. ConVar cl_particles_show_bbox( "cl_particles_show_bbox", "0", FCVAR_CHEAT );
  36. ConVar cl_particle_fallback_multiplier( "cl_particle_fallback_multiplier", "1", FCVAR_NONE, "Multiplier for falling back to cheaper effects under load." );
  37. ConVar cl_particle_fallback_base( "cl_particle_fallback_base", "0", FCVAR_NONE, "Base for falling back to cheaper effects under load." );
  38. #ifdef _GAMECONSOLE
  39. // This is how long simulation has to take before we start going to fallback particle definitions
  40. static ConVar cl_particle_sim_fallback_threshold_ms( "cl_particle_sim_fallback_threshold_ms", "4.0", FCVAR_NONE, "Amount of simulation time that can elapse before new systems start falling back to cheaper versions" );
  41. // This is the rate at which we go to fallback particle definitions. We multiply this by the number of milliseconds
  42. // over sim time and add it to the fallback base. Higher numbers cause the fallbacks to occur more frequently once
  43. // you've gone past the threshold.
  44. // 50 is a very agressive fallback, but it gains up to a couple of ms of performance back on the 360.
  45. static ConVar cl_particle_sim_fallback_base_multiplier( "cl_particle_sim_fallback_base_multiplier", "50", FCVAR_NONE, "How aggressive the switch to fallbacks will be depending on how far over the cl_particle_sim_fallback_threshold_ms the sim time is. Higher numbers are more aggressive." );
  46. #else
  47. // Don't fallback as fast ont he PC since the rest of the frame doesn't take as long and we can afford to have more high
  48. // quality systems.
  49. static ConVar cl_particle_sim_fallback_threshold_ms( "cl_particle_sim_fallback_threshold_ms", "6.0", FCVAR_NONE, "Amount of simulation time that can elapse before new systems start falling back to cheaper versions" );
  50. // Don't fallback as fast on the PC since the rest of the frame doesn't take as long
  51. static ConVar cl_particle_sim_fallback_base_multiplier( "cl_particle_sim_fallback_base_multiplier", "5", FCVAR_NONE, "How aggressive the switch to fallbacks will be depending on how far over the cl_particle_sim_fallback_threshold_ms the sim time is. Higher numbers are more aggressive." );
  52. #endif
  53. #define BUCKET_SORT_EVERY_N 8 // It does a bucket sort for each material approximately every N times.
  54. #define BBOX_UPDATE_EVERY_N 8 // It does a full bbox update (checks all particles instead of every eighth one).
  55. //-----------------------------------------------------------------------------
  56. //
  57. // Particle manager implementation
  58. //
  59. //-----------------------------------------------------------------------------
  60. #define PARTICLE_SIZE 96
  61. CParticleMgr *ParticleMgr()
  62. {
  63. static CParticleMgr s_ParticleMgr;
  64. return &s_ParticleMgr;
  65. }
  66. //-----------------------------------------------------------------------------
  67. // CParticleSubTextureGroup implementation.
  68. //-----------------------------------------------------------------------------
  69. CParticleSubTextureGroup::CParticleSubTextureGroup()
  70. {
  71. m_pPageMaterial = NULL;
  72. }
  73. CParticleSubTextureGroup::~CParticleSubTextureGroup()
  74. {
  75. }
  76. //-----------------------------------------------------------------------------
  77. // CParticleSubTexture implementation.
  78. //-----------------------------------------------------------------------------
  79. CParticleSubTexture::CParticleSubTexture()
  80. {
  81. m_tCoordMins[0] = m_tCoordMins[0] = 0;
  82. m_tCoordMaxs[0] = m_tCoordMaxs[0] = 1;
  83. m_pGroup = &m_DefaultGroup;
  84. m_pMaterial = NULL;
  85. #ifdef _DEBUG
  86. m_szDebugName = NULL;
  87. #endif
  88. }
  89. //-----------------------------------------------------------------------------
  90. // CEffectMaterial.
  91. //-----------------------------------------------------------------------------
  92. CEffectMaterial::CEffectMaterial()
  93. {
  94. m_Particles.m_pNext = m_Particles.m_pPrev = &m_Particles;
  95. m_pGroup = NULL;
  96. }
  97. //-----------------------------------------------------------------------------
  98. // CParticleEffectBinding.
  99. //-----------------------------------------------------------------------------
  100. CParticleEffectBinding::CParticleEffectBinding()
  101. {
  102. m_pParticleMgr = NULL;
  103. m_pSim = NULL;
  104. m_LocalSpaceTransform.Identity();
  105. m_bLocalSpaceTransformIdentity = true;
  106. m_Flags = 0;
  107. SetAutoUpdateBBox( true );
  108. SetFirstFrameFlag( true );
  109. SetNeedsBBoxUpdate( true );
  110. SetAlwaysSimulate( true );
  111. SetEffectCameraSpace( true );
  112. SetDrawThruLeafSystem( true );
  113. SetAutoApplyLocalTransform( true );
  114. // default bbox
  115. m_Min.Init( -50, -50, -50 );
  116. m_Max.Init( 50, 50, 50 );
  117. m_LastMin = m_Min;
  118. m_LastMax = m_Max;
  119. SetParticleCullRadius( 0.0f );
  120. m_nActiveParticles = 0;
  121. m_FrameCode = 0;
  122. m_ListIndex = 0xFFFF;
  123. m_UpdateBBoxCounter = 0;
  124. memset( m_EffectMaterialHash, 0, sizeof( m_EffectMaterialHash ) );
  125. }
  126. CParticleEffectBinding::~CParticleEffectBinding()
  127. {
  128. if( m_pParticleMgr )
  129. m_pParticleMgr->RemoveEffect( this );
  130. Term();
  131. }
  132. // The is the max size of the particles for use in bounding computation
  133. void CParticleEffectBinding::SetParticleCullRadius( float flMaxParticleRadius )
  134. {
  135. if ( m_flParticleCullRadius != flMaxParticleRadius )
  136. {
  137. m_flParticleCullRadius = flMaxParticleRadius;
  138. if ( m_hRenderHandle != INVALID_CLIENT_RENDER_HANDLE )
  139. {
  140. ClientLeafSystem()->RenderableChanged( m_hRenderHandle );
  141. }
  142. }
  143. }
  144. const Vector& CParticleEffectBinding::GetRenderOrigin( void )
  145. {
  146. return m_pSim->GetSortOrigin();
  147. }
  148. const QAngle& CParticleEffectBinding::GetRenderAngles( void )
  149. {
  150. return vec3_angle;
  151. }
  152. const matrix3x4_t & CParticleEffectBinding::RenderableToWorldTransform()
  153. {
  154. static matrix3x4_t mat;
  155. SetIdentityMatrix( mat );
  156. PositionMatrix( GetRenderOrigin(), mat );
  157. return mat;
  158. }
  159. void CParticleEffectBinding::GetRenderBounds( Vector& mins, Vector& maxs )
  160. {
  161. const Vector &vSortOrigin = m_pSim->GetSortOrigin();
  162. // Convert to local space (around the sort origin).
  163. mins = m_Min - vSortOrigin;
  164. mins.x -= m_flParticleCullRadius; mins.y -= m_flParticleCullRadius; mins.z -= m_flParticleCullRadius;
  165. maxs = m_Max - vSortOrigin;
  166. maxs.x += m_flParticleCullRadius; maxs.y += m_flParticleCullRadius; maxs.z += m_flParticleCullRadius;
  167. }
  168. bool CParticleEffectBinding::ShouldDraw( void )
  169. {
  170. return GetFlag( FLAGS_DRAW_THRU_LEAF_SYSTEM ) != 0;
  171. }
  172. inline void CParticleEffectBinding::StartDrawMaterialParticles(
  173. CEffectMaterial *pMaterial,
  174. float flTimeDelta,
  175. IMesh* &pMesh,
  176. CMeshBuilder &builder,
  177. ParticleDraw &particleDraw,
  178. bool bWireframe )
  179. {
  180. CMatRenderContextPtr pRenderContext( m_pParticleMgr->m_pMaterialSystem );
  181. // Setup the ParticleDraw and bind the material.
  182. if( bWireframe )
  183. {
  184. IMaterial *pMaterial = m_pParticleMgr->m_pMaterialSystem->FindMaterial( "debug/debugparticlewireframe", TEXTURE_GROUP_OTHER );
  185. pRenderContext->Bind( pMaterial, NULL );
  186. }
  187. else
  188. {
  189. pRenderContext->Bind( pMaterial->m_pGroup->m_pPageMaterial, m_pParticleMgr );
  190. }
  191. pMesh = pRenderContext->GetDynamicMesh( true );
  192. builder.Begin( pMesh, MATERIAL_QUADS, NUM_PARTICLES_PER_BATCH * 4 );
  193. particleDraw.Init( &builder, pMaterial->m_pGroup->m_pPageMaterial, flTimeDelta );
  194. }
  195. void CParticleEffectBinding::BBoxCalcStart( Vector &bbMin, Vector &bbMax )
  196. {
  197. if ( !GetAutoUpdateBBox() )
  198. return;
  199. // We're going to fully recompute the bbox.
  200. bbMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  201. bbMax.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  202. }
  203. void CParticleEffectBinding::BBoxCalcEnd( bool bboxSet, Vector &bbMin, Vector &bbMax )
  204. {
  205. if ( !GetAutoUpdateBBox() )
  206. return;
  207. // Get the bbox into world space.
  208. Vector bbMinWorld, bbMaxWorld;
  209. if ( m_bLocalSpaceTransformIdentity )
  210. {
  211. bbMinWorld = bbMin;
  212. bbMaxWorld = bbMax;
  213. }
  214. else
  215. {
  216. TransformAABB( m_LocalSpaceTransform.As3x4(), bbMin, bbMax, bbMinWorld, bbMaxWorld );
  217. }
  218. // If there were ANY particles in the system, then we've got a valid bbox here. Otherwise,
  219. // we don't have anything, so leave m_Min and m_Max at the sort origin.
  220. if ( bboxSet )
  221. {
  222. m_Min = bbMinWorld;
  223. m_Max = bbMaxWorld;
  224. }
  225. else
  226. {
  227. m_Min = m_Max = m_pSim->GetSortOrigin();
  228. }
  229. }
  230. int CParticleEffectBinding::DrawModel( int flags, const RenderableInstance_t &instance )
  231. {
  232. VPROF_BUDGET( "CParticleEffectBinding::DrawModel", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  233. #ifndef PARTICLEPROTOTYPE_APP
  234. if ( !r_DrawParticles.GetInt() )
  235. return 0;
  236. #endif
  237. Assert( flags != 0 );
  238. // If we're in commander mode and it's trying to draw the effect,
  239. // exit out. If the effect has FLAGS_ALWAYSSIMULATE set, then it'll come back
  240. // in here and simulate at the end of the frame.
  241. // NOTE: We do not check ParticleMgr()->ShouldRenderParticleSystems()
  242. // here as a sort of hack: the SFM currently plays back Tempents, which create
  243. // old-style particle systems back during playback, which means we want
  244. // them to display always
  245. if( !GetClientMode()->ShouldDrawParticles() )
  246. return 0;
  247. //Avoid drawing particles while building depth textures. Perf win.
  248. //At the very least, we absolutely should not do refraction updates below. So if this gets removed, be sure to wrap the refract/screen texture updates.
  249. if ( flags & ( STUDIO_SHADOWDEPTHTEXTURE | STUDIO_SSAODEPTHTEXTURE ) )
  250. return 0;
  251. SetDrawn( true );
  252. // Don't do anything if there are no particles.
  253. if( !m_nActiveParticles )
  254. return 1;
  255. // Reset the transformation matrix to identity.
  256. VMatrix mTempModel, mTempView;
  257. RenderStart( mTempModel, mTempView );
  258. bool bBucketSort = random->RandomInt( 0, BUCKET_SORT_EVERY_N ) == 0;
  259. // Set frametime to zero if we've already rendered this frame.
  260. float flFrameTime = 0;
  261. if ( m_FrameCode != m_pParticleMgr->m_FrameCode )
  262. {
  263. m_FrameCode = m_pParticleMgr->m_FrameCode;
  264. flFrameTime = Helper_GetFrameTime();
  265. }
  266. // For each material, render...
  267. // This does an incremental bubble sort. It only does one pass every frame, and it will shuffle
  268. // unsorted particles one step towards where they should be.
  269. bool bWireframe = false;
  270. FOR_EACH_LL( m_Materials, iMaterial )
  271. {
  272. CEffectMaterial *pMaterial = m_Materials[iMaterial];
  273. if ( pMaterial->m_pGroup->m_pPageMaterial && pMaterial->m_pGroup->m_pPageMaterial->NeedsPowerOfTwoFrameBufferTexture() )
  274. {
  275. UpdateRefractTexture();
  276. }
  277. if ( pMaterial->m_pGroup->m_pPageMaterial && pMaterial->m_pGroup->m_pPageMaterial->NeedsFullFrameBufferTexture() )
  278. {
  279. UpdateScreenEffectTexture();
  280. }
  281. DrawMaterialParticles(
  282. bBucketSort,
  283. pMaterial,
  284. flFrameTime,
  285. bWireframe );
  286. }
  287. if ( ShouldDrawInWireFrameMode() )
  288. {
  289. bWireframe = true;
  290. FOR_EACH_LL( m_Materials, iDrawMaterial )
  291. {
  292. CEffectMaterial *pMaterial = m_Materials[iDrawMaterial];
  293. DrawMaterialParticles(
  294. bBucketSort,
  295. pMaterial,
  296. flFrameTime,
  297. bWireframe );
  298. }
  299. }
  300. if ( cl_particles_show_bbox.GetBool() )
  301. {
  302. Vector center = (m_Min + m_Max)/2;
  303. Vector mins = m_Min - center;
  304. Vector maxs = m_Max - center;
  305. int r, g;
  306. if ( m_Flags & FLAGS_AUTOUPDATEBBOX )
  307. {
  308. // red is bad, the bbox update is costly
  309. r = 255;
  310. g = 0;
  311. }
  312. else
  313. {
  314. // green, this effect presents less cpu load
  315. r = 0;
  316. g = 255;
  317. }
  318. debugoverlay->AddBoxOverlay( center, mins, maxs, QAngle( 0, 0, 0 ), r, g, 0, 16, 0 );
  319. debugoverlay->AddTextOverlayRGB( center, 0, 0, r, g, 0, 64, "%s:(%d)", m_pSim->GetEffectName(), m_nActiveParticles );
  320. }
  321. RenderEnd( mTempModel, mTempView );
  322. return 1;
  323. }
  324. PMaterialHandle CParticleEffectBinding::FindOrAddMaterial( const char *pMaterialName )
  325. {
  326. if ( !m_pParticleMgr )
  327. {
  328. return NULL;
  329. }
  330. return m_pParticleMgr->GetPMaterial( pMaterialName );
  331. }
  332. Particle* CParticleEffectBinding::AddParticle( int sizeInBytes, PMaterialHandle hMaterial )
  333. {
  334. m_pParticleMgr->RepairPMaterial( hMaterial ); //HACKHACK: Remove this when we can stop leaking handles from level to level.
  335. // We've currently clamped the particle size to PARTICLE_SIZE,
  336. // we may need to change this algorithm if we get particles with
  337. // widely varying size
  338. if ( sizeInBytes > PARTICLE_SIZE )
  339. {
  340. Assert( sizeInBytes <= PARTICLE_SIZE );
  341. return NULL;
  342. }
  343. // This is for testing - simulate it running out of memory.
  344. if ( particle_simulateoverflow.GetInt() )
  345. {
  346. if ( rand() % 10 <= 6 )
  347. return NULL;
  348. }
  349. // Allocate the puppy. We are actually allocating space for the
  350. // internals + the actual data
  351. Particle* pParticle = m_pParticleMgr->AllocParticle( PARTICLE_SIZE );
  352. if( !pParticle )
  353. return NULL;
  354. // Link it in
  355. CEffectMaterial *pEffectMat = GetEffectMaterial( hMaterial );
  356. InsertParticleAfter( pParticle, &pEffectMat->m_Particles );
  357. if ( hMaterial )
  358. pParticle->m_pSubTexture = hMaterial;
  359. else
  360. pParticle->m_pSubTexture = &m_pParticleMgr->m_DefaultInvalidSubTexture;
  361. ++m_nActiveParticles;
  362. return pParticle;
  363. }
  364. void CParticleEffectBinding::SetBBox( const Vector &bbMin, const Vector &bbMax, bool bDisableAutoUpdate )
  365. {
  366. m_Min = bbMin;
  367. m_Max = bbMax;
  368. if ( bDisableAutoUpdate )
  369. SetAutoUpdateBBox( false );
  370. }
  371. void CParticleEffectBinding::GetWorldspaceBounds( Vector *pMins, Vector *pMaxs )
  372. {
  373. *pMins = m_Min;
  374. *pMaxs = m_Max;
  375. }
  376. void CParticleEffectBinding::SetLocalSpaceTransform( const matrix3x4_t &transform )
  377. {
  378. m_LocalSpaceTransform.CopyFrom3x4( transform );
  379. if ( m_LocalSpaceTransform.IsIdentity() )
  380. {
  381. m_bLocalSpaceTransformIdentity = true;
  382. }
  383. else
  384. {
  385. m_bLocalSpaceTransformIdentity = false;
  386. }
  387. }
  388. bool CParticleEffectBinding::EnlargeBBoxToContain( const Vector &pt )
  389. {
  390. if ( m_nActiveParticles == 0 )
  391. {
  392. m_Min = m_Max = pt;
  393. return true;
  394. }
  395. bool bHasChanged = false;
  396. // check min bounds
  397. if ( pt.x < m_Min.x )
  398. { m_Min.x = pt.x; bHasChanged = true; }
  399. if ( pt.y < m_Min.y )
  400. { m_Min.y = pt.y; bHasChanged = true; }
  401. if ( pt.z < m_Min.z )
  402. { m_Min.z = pt.z; bHasChanged = true; }
  403. // check max bounds
  404. if ( pt.x > m_Max.x )
  405. { m_Max.x = pt.x; bHasChanged = true; }
  406. if ( pt.y > m_Max.y )
  407. { m_Max.y = pt.y; bHasChanged = true; }
  408. if ( pt.z > m_Max.z )
  409. { m_Max.z = pt.z; bHasChanged = true; }
  410. return bHasChanged;
  411. }
  412. void CParticleEffectBinding::DetectChanges()
  413. {
  414. // if we have no render handle, return
  415. if ( m_hRenderHandle == INVALID_CLIENT_RENDER_HANDLE )
  416. return;
  417. // if nothing changed, return
  418. if ( ( m_Min != m_LastMin ) || ( m_Max != m_LastMax ) )
  419. {
  420. // call leafsystem to update this guy
  421. ClientLeafSystem()->RenderableChanged( m_hRenderHandle );
  422. // remember last parameters
  423. m_LastMin = m_Min;
  424. m_LastMax = m_Max;
  425. }
  426. }
  427. void CParticleEffectBinding::GrowBBoxFromParticlePositions( CEffectMaterial *pMaterial, bool &bboxSet, Vector &bbMin, Vector &bbMax )
  428. {
  429. // If its bbox is manually set, don't bother updating it here.
  430. if ( !GetAutoUpdateBBox() )
  431. return;
  432. for( Particle *pCur=pMaterial->m_Particles.m_pNext; pCur != &pMaterial->m_Particles; pCur=pCur->m_pNext )
  433. {
  434. // Update bounding box
  435. VectorMin( bbMin, pCur->m_Pos, bbMin );
  436. VectorMax( bbMax, pCur->m_Pos, bbMax );
  437. bboxSet = true;
  438. }
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Simulate particles
  442. //-----------------------------------------------------------------------------
  443. void CParticleEffectBinding::SimulateParticles( float flTimeDelta )
  444. {
  445. if ( !m_pSim->ShouldSimulate() )
  446. return;
  447. if ( GetFlag( FLAGS_NEW_PARTICLE_SYSTEM ) )
  448. {
  449. CParticleSimulateIterator simulateIterator;
  450. simulateIterator.m_pEffectBinding = this;
  451. simulateIterator.m_pMaterial = NULL; //pMaterial;
  452. simulateIterator.m_flTimeDelta = flTimeDelta;
  453. m_pSim->SimulateParticles( &simulateIterator );
  454. }
  455. else
  456. {
  457. Vector bbMin(0,0,0), bbMax(0,0,0);
  458. bool bboxSet = false;
  459. // slow the expensive update operation for particle systems that use auto-update-bbox
  460. // auto update the bbox after N frames then randomly 1/N or after 2*N frames
  461. bool bFullBBoxUpdate = false;
  462. ++m_UpdateBBoxCounter;
  463. if ( ( m_UpdateBBoxCounter >= BBOX_UPDATE_EVERY_N && random->RandomInt( 0, BBOX_UPDATE_EVERY_N ) == 0 ) ||
  464. ( m_UpdateBBoxCounter >= 2*BBOX_UPDATE_EVERY_N ) )
  465. {
  466. bFullBBoxUpdate = true;
  467. // reset watchdog
  468. m_UpdateBBoxCounter = 0;
  469. }
  470. if ( bFullBBoxUpdate )
  471. {
  472. BBoxCalcStart( bbMin, bbMax );
  473. }
  474. FOR_EACH_LL( m_Materials, i )
  475. {
  476. CEffectMaterial *pMaterial = m_Materials[i];
  477. CParticleSimulateIterator simulateIterator;
  478. simulateIterator.m_pEffectBinding = this;
  479. simulateIterator.m_pMaterial = pMaterial;
  480. simulateIterator.m_flTimeDelta = flTimeDelta;
  481. m_pSim->SimulateParticles( &simulateIterator );
  482. // Update the bbox.
  483. if ( bFullBBoxUpdate )
  484. {
  485. GrowBBoxFromParticlePositions( pMaterial, bboxSet, bbMin, bbMax );
  486. }
  487. }
  488. if ( bFullBBoxUpdate )
  489. {
  490. BBoxCalcEnd( bboxSet, bbMin, bbMax );
  491. }
  492. }
  493. }
  494. void CParticleEffectBinding::SetDrawThruLeafSystem( int bDraw )
  495. {
  496. if ( bDraw )
  497. {
  498. // If SetDrawBeforeViewModel was called, then they shouldn't be telling it to draw through
  499. // the leaf system too.
  500. Assert( !( m_Flags & FLAGS_DRAW_BEFORE_VIEW_MODEL) );
  501. }
  502. SetFlag( FLAGS_DRAW_THRU_LEAF_SYSTEM, bDraw );
  503. }
  504. void CParticleEffectBinding::SetDrawBeforeViewModel( int bDraw )
  505. {
  506. // Don't draw through the leaf system if they want it to specifically draw before the view model.
  507. if ( bDraw )
  508. m_Flags &= ~FLAGS_DRAW_THRU_LEAF_SYSTEM;
  509. SetFlag( FLAGS_DRAW_BEFORE_VIEW_MODEL, bDraw );
  510. }
  511. int CParticleEffectBinding::GetNumActiveParticles()
  512. {
  513. return m_nActiveParticles;
  514. }
  515. // Build a list of all active particles
  516. int CParticleEffectBinding::GetActiveParticleList( int nCount, Particle **ppParticleList )
  517. {
  518. int nCurrCount = 0;
  519. FOR_EACH_LL( m_Materials, i )
  520. {
  521. CEffectMaterial *pMaterial = m_Materials[i];
  522. Particle *pParticle = pMaterial->m_Particles.m_pNext;
  523. for ( ; pParticle != &pMaterial->m_Particles; pParticle = pParticle->m_pNext )
  524. {
  525. ppParticleList[nCurrCount] = pParticle;
  526. if ( ++nCurrCount == nCount )
  527. return nCurrCount;
  528. }
  529. }
  530. return nCurrCount;
  531. }
  532. int CParticleEffectBinding::DrawMaterialParticles(
  533. bool bBucketSort,
  534. CEffectMaterial *pMaterial,
  535. float flTimeDelta,
  536. bool bWireframe
  537. )
  538. {
  539. // Setup everything.
  540. CMeshBuilder builder;
  541. ParticleDraw particleDraw;
  542. IMesh *pMesh = NULL;
  543. StartDrawMaterialParticles( pMaterial, flTimeDelta, pMesh, builder, particleDraw, bWireframe );
  544. if ( m_nActiveParticles > MAX_TOTAL_PARTICLES )
  545. Error( "CParticleEffectBinding::DrawMaterialParticles: too many particles (%d should be less than %d)", m_nActiveParticles, MAX_TOTAL_PARTICLES );
  546. // Simluate and render all the particles.
  547. CParticleRenderIterator renderIterator;
  548. renderIterator.m_pEffectBinding = this;
  549. renderIterator.m_pMaterial = pMaterial;
  550. renderIterator.m_pParticleDraw = &particleDraw;
  551. renderIterator.m_pMeshBuilder = &builder;
  552. renderIterator.m_pMesh = pMesh;
  553. renderIterator.m_bBucketSort = bBucketSort;
  554. renderIterator.m_pMaterialSystem = m_pParticleMgr->m_pMaterialSystem;
  555. m_pSim->RenderParticles( &renderIterator );
  556. g_nParticlesDrawn += m_nActiveParticles;
  557. if( bBucketSort )
  558. {
  559. DoBucketSort( pMaterial, renderIterator.m_zCoords, renderIterator.m_nZCoords, renderIterator.m_MinZ, renderIterator.m_MaxZ );
  560. }
  561. // Flush out any remaining particles.
  562. builder.End( false, true );
  563. return m_nActiveParticles;
  564. }
  565. void CParticleEffectBinding::RenderStart( VMatrix &tempModel, VMatrix &tempView )
  566. {
  567. if( IsEffectCameraSpace() )
  568. {
  569. CMatRenderContextPtr pRenderContext( m_pParticleMgr->m_pMaterialSystem );
  570. // Store matrices off so we can restore them in RenderEnd().
  571. pRenderContext->GetMatrix(MATERIAL_VIEW, &tempView);
  572. pRenderContext->GetMatrix(MATERIAL_MODEL, &tempModel);
  573. // We're gonna assume the model matrix was identity and blow it off
  574. // This means that the particle positions are all specified in world space
  575. // which makes bounding box computations faster.
  576. m_pParticleMgr->m_mModelView = tempView;
  577. // Force the user clip planes to use the old view matrix
  578. pRenderContext->EnableUserClipTransformOverride( true );
  579. pRenderContext->UserClipTransform( tempView );
  580. // The particle renderers want to do things in camera space
  581. pRenderContext->MatrixMode( MATERIAL_MODEL );
  582. pRenderContext->LoadIdentity();
  583. pRenderContext->MatrixMode( MATERIAL_VIEW );
  584. pRenderContext->LoadIdentity();
  585. }
  586. else
  587. {
  588. m_pParticleMgr->m_mModelView.Identity();
  589. }
  590. // Add their local space transform if they have one and they want it applied.
  591. if ( GetAutoApplyLocalTransform() && !m_bLocalSpaceTransformIdentity )
  592. {
  593. m_pParticleMgr->m_mModelView = m_pParticleMgr->m_mModelView * m_LocalSpaceTransform;
  594. }
  595. // Let the particle effect do any per-frame setup/processing here
  596. m_pSim->StartRender( m_pParticleMgr->m_mModelView );
  597. }
  598. void CParticleEffectBinding::RenderEnd( VMatrix &tempModel, VMatrix &tempView )
  599. {
  600. if( IsEffectCameraSpace() )
  601. {
  602. CMatRenderContextPtr pRenderContext( m_pParticleMgr->m_pMaterialSystem );
  603. // Make user clip planes work normally
  604. pRenderContext->EnableUserClipTransformOverride( false );
  605. // Reset the model matrix.
  606. pRenderContext->MatrixMode( MATERIAL_MODEL );
  607. pRenderContext->LoadMatrix( tempModel );
  608. // Reset the view matrix.
  609. pRenderContext->MatrixMode( MATERIAL_VIEW );
  610. pRenderContext->LoadMatrix( tempView );
  611. }
  612. }
  613. void CParticleEffectBinding::DoBucketSort( CEffectMaterial *pMaterial, float *zCoords, int nZCoords, float minZ, float maxZ )
  614. {
  615. // Do an O(N) bucket sort. This helps the sort when there are lots of particles.
  616. #define NUM_BUCKETS 32
  617. Particle buckets[NUM_BUCKETS];
  618. for( int iBucket=0; iBucket < NUM_BUCKETS; iBucket++ )
  619. {
  620. buckets[iBucket].m_pPrev = buckets[iBucket].m_pNext = &buckets[iBucket];
  621. }
  622. // Sort into buckets.
  623. int iCurParticle = 0;
  624. Particle *pNext, *pCur;
  625. for( pCur=pMaterial->m_Particles.m_pNext; pCur != &pMaterial->m_Particles; pCur=pNext )
  626. {
  627. pNext = pCur->m_pNext;
  628. if( iCurParticle >= nZCoords )
  629. break;
  630. // Remove it..
  631. UnlinkParticle( pCur );
  632. // Add it to the appropriate bucket.
  633. float flPercent;
  634. if (maxZ == minZ)
  635. flPercent = 0;
  636. else
  637. flPercent = (zCoords[iCurParticle] - minZ) / (maxZ - minZ);
  638. int iAddBucket = (int)( flPercent * (NUM_BUCKETS - 0.0001f) );
  639. iAddBucket = NUM_BUCKETS - iAddBucket - 1;
  640. Assert( iAddBucket >= 0 && iAddBucket < NUM_BUCKETS );
  641. InsertParticleAfter( pCur, &buckets[iAddBucket] );
  642. ++iCurParticle;
  643. }
  644. // Put the buckets back into the main list.
  645. for( int iReAddBucket=0; iReAddBucket < NUM_BUCKETS; iReAddBucket++ )
  646. {
  647. Particle *pListHead = &buckets[iReAddBucket];
  648. for( pCur=pListHead->m_pNext; pCur != pListHead; pCur=pNext )
  649. {
  650. pNext = pCur->m_pNext;
  651. InsertParticleAfter( pCur, &pMaterial->m_Particles );
  652. --iCurParticle;
  653. }
  654. }
  655. Assert(iCurParticle==0);
  656. }
  657. void CParticleEffectBinding::Init( CParticleMgr *pMgr, IParticleEffect *pSim )
  658. {
  659. // Must Term before reinitializing.
  660. Assert( !m_pSim && !m_pParticleMgr );
  661. m_pSim = pSim;
  662. m_pParticleMgr = pMgr;
  663. }
  664. void CParticleEffectBinding::Term()
  665. {
  666. if ( !m_pParticleMgr )
  667. return;
  668. // Free materials.
  669. FOR_EACH_LL( m_Materials, iMaterial )
  670. {
  671. CEffectMaterial *pMaterial = m_Materials[iMaterial];
  672. // Remove all particles tied to this effect.
  673. Particle *pNext = NULL;
  674. for(Particle *pCur = pMaterial->m_Particles.m_pNext; pCur != &pMaterial->m_Particles; pCur=pNext )
  675. {
  676. pNext = pCur->m_pNext;
  677. RemoveParticle( pCur );
  678. }
  679. delete pMaterial;
  680. }
  681. m_Materials.Purge();
  682. memset( m_EffectMaterialHash, 0, sizeof( m_EffectMaterialHash ) );
  683. }
  684. void CParticleEffectBinding::RemoveParticle( Particle *pParticle )
  685. {
  686. UnlinkParticle( pParticle );
  687. // Important that this is updated BEFORE NotifyDestroyParticle is called.
  688. --m_nActiveParticles;
  689. Assert( m_nActiveParticles >= 0 );
  690. // Let the effect do any necessary cleanup
  691. m_pSim->NotifyDestroyParticle(pParticle);
  692. // Remove it from the list of particles and deallocate
  693. m_pParticleMgr->FreeParticle(pParticle);
  694. }
  695. bool CParticleEffectBinding::RecalculateBoundingBox()
  696. {
  697. if ( m_nActiveParticles == 0 )
  698. {
  699. m_Max = m_Min = m_pSim->GetSortOrigin();
  700. return false;
  701. }
  702. Vector bbMin( 1e28, 1e28, 1e28 );
  703. Vector bbMax( -1e28, -1e28, -1e28 );
  704. FOR_EACH_LL( m_Materials, iMaterial )
  705. {
  706. CEffectMaterial *pMaterial = m_Materials[iMaterial];
  707. for( Particle *pCur=pMaterial->m_Particles.m_pNext; pCur != &pMaterial->m_Particles; pCur=pCur->m_pNext )
  708. {
  709. VectorMin( bbMin, pCur->m_Pos, bbMin );
  710. VectorMax( bbMax, pCur->m_Pos, bbMax );
  711. }
  712. }
  713. // Get the bbox into world space.
  714. if ( m_bLocalSpaceTransformIdentity )
  715. {
  716. m_Min = bbMin;
  717. m_Max = bbMax;
  718. }
  719. else
  720. {
  721. TransformAABB( m_LocalSpaceTransform.As3x4(), bbMin, bbMax, m_Min, m_Max );
  722. }
  723. return true;
  724. }
  725. CEffectMaterial* CParticleEffectBinding::GetEffectMaterial( CParticleSubTexture *pSubTexture )
  726. {
  727. // Hash the IMaterial pointer.
  728. unsigned long index = (((unsigned long)pSubTexture->m_pGroup) >> 6) % EFFECT_MATERIAL_HASH_SIZE;
  729. for ( CEffectMaterial *pCur=m_EffectMaterialHash[index]; pCur; pCur = pCur->m_pHashedNext )
  730. {
  731. if ( pCur->m_pGroup == pSubTexture->m_pGroup )
  732. return pCur;
  733. }
  734. CEffectMaterial *pEffectMat = new CEffectMaterial;
  735. pEffectMat->m_pGroup = pSubTexture->m_pGroup;
  736. pEffectMat->m_pHashedNext = m_EffectMaterialHash[index];
  737. m_EffectMaterialHash[index] = pEffectMat;
  738. m_Materials.AddToTail( pEffectMat );
  739. return pEffectMat;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // CParticleMgr
  743. //-----------------------------------------------------------------------------
  744. CParticleMgr::CParticleMgr()
  745. {
  746. m_nToolParticleEffectId = 0;
  747. m_bUpdatingEffects = false;
  748. m_bRenderParticleEffects = true;
  749. m_pMaterialSystem = NULL;
  750. m_pThreadPool[0] = 0;
  751. m_pThreadPool[1] = 0;
  752. memset( &m_DirectionalLight, 0, sizeof( m_DirectionalLight ) );
  753. m_FrameCode = 1;
  754. m_DefaultInvalidSubTexture.m_pGroup = &m_DefaultInvalidSubTexture.m_DefaultGroup;
  755. m_DefaultInvalidSubTexture.m_pMaterial = NULL;
  756. m_DefaultInvalidSubTexture.m_tCoordMins[0] = m_DefaultInvalidSubTexture.m_tCoordMins[1] = 0;
  757. m_DefaultInvalidSubTexture.m_tCoordMaxs[0] = m_DefaultInvalidSubTexture.m_tCoordMaxs[1] = 1;
  758. m_nCurrentParticlesAllocated = 0;
  759. SetDefLessFunc( m_effectFactories );
  760. }
  761. CParticleMgr::~CParticleMgr()
  762. {
  763. Term(false);
  764. }
  765. //-----------------------------------------------------------------------------
  766. // Initialization and shutdown
  767. //-----------------------------------------------------------------------------
  768. bool CParticleMgr::Init(unsigned long count, IMaterialSystem *pMaterials)
  769. {
  770. Term();
  771. m_pMaterialSystem = pMaterials;
  772. // Initialize the particle system
  773. bool bPrecacheParticles = IsPC() && !engine->IsCreatingXboxReslist();
  774. g_pParticleSystemMgr->Init( g_pParticleSystemQuery, bPrecacheParticles );
  775. // tell particle mgr to add the default simulation + rendering ops
  776. g_pParticleSystemMgr->AddBuiltinSimulationOperators();
  777. g_pParticleSystemMgr->AddBuiltinRenderingOperators();
  778. // Send true to load the sheets
  779. ParseParticleEffects( true );
  780. #ifdef TF_CLIENT_DLL
  781. if ( IsGameConsole() )
  782. {
  783. //m_pThreadPool[0] = CreateNewThreadPool();
  784. m_pThreadPool[1] = CreateNewThreadPool();
  785. ThreadPoolStartParams_t startParams;
  786. startParams.nThreads = 3;
  787. startParams.nStackSize = 128*1024;
  788. startParams.fDistribute = TRS_TRUE;
  789. startParams.bUseAffinityTable = true;
  790. startParams.iAffinityTable[0] = XBOX_PROCESSOR_1;
  791. startParams.iAffinityTable[1] = XBOX_PROCESSOR_3;
  792. startParams.iAffinityTable[2] = XBOX_PROCESSOR_5;
  793. //m_pThreadPool[0]->Start( startParams );
  794. startParams.nThreads = 2;
  795. startParams.iAffinityTable[1] = CommandLine()->FindParm( "-swapcores" ) ? XBOX_PROCESSOR_5 : XBOX_PROCESSOR_3;
  796. m_pThreadPool[1]->Start( startParams, "Particle" );
  797. }
  798. #endif
  799. return true;
  800. }
  801. void CParticleMgr::Term(bool bCanReferenceOtherStaticObjects)
  802. {
  803. // Free all the effects.
  804. int iNext;
  805. for ( int i = m_Effects.Head(); i != m_Effects.InvalidIndex(); i = iNext )
  806. {
  807. iNext = m_Effects.Next( i );
  808. m_Effects[i]->m_pSim->NotifyRemove();
  809. }
  810. m_Effects.Purge();
  811. m_NewEffects.Purge();
  812. for( int i = m_SubTextures.First(); i != m_SubTextures.InvalidIndex(); i = m_SubTextures.Next( i ) )
  813. {
  814. IMaterial *pMaterial = m_SubTextures[i]->m_pMaterial;
  815. if ( pMaterial )
  816. pMaterial->Release();
  817. }
  818. m_SubTextures.PurgeAndDeleteElements();
  819. for( int i = m_SubTextureGroups.Count(); --i >= 0; )
  820. {
  821. IMaterial *pMaterial = m_SubTextureGroups[i]->m_pPageMaterial;
  822. if ( pMaterial )
  823. pMaterial->Release();
  824. }
  825. m_SubTextureGroups.PurgeAndDeleteElements();
  826. if (bCanReferenceOtherStaticObjects)
  827. g_pParticleSystemMgr->UncacheAllParticleSystems();
  828. if ( m_pMaterialSystem )
  829. {
  830. m_pMaterialSystem->UncacheUnusedMaterials();
  831. }
  832. m_pMaterialSystem = NULL;
  833. if ( m_pThreadPool[0] )
  834. {
  835. m_pThreadPool[0]->Stop();
  836. DestroyThreadPool( m_pThreadPool[0] );
  837. m_pThreadPool[0] = NULL;
  838. }
  839. if ( m_pThreadPool[1] )
  840. {
  841. m_pThreadPool[1]->Stop();
  842. DestroyThreadPool( m_pThreadPool[1] );
  843. m_pThreadPool[1] = NULL;
  844. }
  845. Assert( m_nCurrentParticlesAllocated == 0 );
  846. }
  847. void CParticleMgr::LevelInit()
  848. {
  849. g_pParticleSystemMgr->SetLastSimulationTime( gpGlobals->curtime );
  850. }
  851. Particle *CParticleMgr::AllocParticle( int size )
  852. {
  853. // Enforce max particle limit.
  854. if ( m_nCurrentParticlesAllocated >= MAX_TOTAL_PARTICLES )
  855. return NULL;
  856. Particle *pRet = (Particle *)malloc( size );
  857. if ( pRet )
  858. ++m_nCurrentParticlesAllocated;
  859. return pRet;
  860. }
  861. void CParticleMgr::FreeParticle( Particle *pParticle )
  862. {
  863. Assert( m_nCurrentParticlesAllocated > 0 );
  864. if ( pParticle )
  865. --m_nCurrentParticlesAllocated;
  866. free( pParticle );
  867. }
  868. //-----------------------------------------------------------------------------
  869. // Should particle effects be rendered?
  870. //-----------------------------------------------------------------------------
  871. void CParticleMgr::RenderParticleSystems( bool bEnable )
  872. {
  873. m_bRenderParticleEffects = bEnable;
  874. }
  875. bool CParticleMgr::ShouldRenderParticleSystems() const
  876. {
  877. return m_bRenderParticleEffects;
  878. }
  879. //-----------------------------------------------------------------------------
  880. // add a class that gets notified of entity events
  881. //-----------------------------------------------------------------------------
  882. void CParticleMgr::AddEffectListener( IClientParticleListener *pListener )
  883. {
  884. int i = m_effectListeners.Find( pListener );
  885. if ( !m_effectListeners.IsValidIndex( i ) )
  886. {
  887. m_effectListeners.AddToTail( pListener );
  888. }
  889. }
  890. void CParticleMgr::RemoveEffectListener( IClientParticleListener *pListener )
  891. {
  892. int i = m_effectListeners.Find( pListener );
  893. if ( m_effectListeners.IsValidIndex( i ) )
  894. {
  895. m_effectListeners.Remove( i );
  896. }
  897. }
  898. //-----------------------------------------------------------------------------
  899. // registers effects classes, and create instances of these effects classes
  900. //-----------------------------------------------------------------------------
  901. void CParticleMgr::RegisterEffect( const char *pEffectType, CreateParticleEffectFN func )
  902. {
  903. #ifdef _DEBUG
  904. int i = m_effectFactories.Find( pEffectType );
  905. Assert( !m_effectFactories.IsValidIndex( i ) );
  906. #endif
  907. m_effectFactories.Insert( pEffectType, func );
  908. }
  909. IParticleEffect *CParticleMgr::CreateEffect( const char *pEffectType )
  910. {
  911. int i = m_effectFactories.Find( pEffectType );
  912. if ( !m_effectFactories.IsValidIndex( i ) )
  913. {
  914. Msg( "CParticleMgr::CreateEffect: factory not found for effect '%s'\n", pEffectType );
  915. return NULL;
  916. }
  917. CreateParticleEffectFN func = m_effectFactories[ i ];
  918. if ( func == NULL )
  919. {
  920. Msg( "CParticleMgr::CreateEffect: NULL factory for effect '%s'\n", pEffectType );
  921. return NULL;
  922. }
  923. return func();
  924. }
  925. //-----------------------------------------------------------------------------
  926. // Adds and removes effects from our global list
  927. //-----------------------------------------------------------------------------
  928. void CParticleMgr::AddEffect( CNewParticleEffect *pEffect )
  929. {
  930. m_NewEffects.AddToHead( pEffect );
  931. if ( pEffect->IsValid() && pEffect->m_pDef->IsDrawnThroughLeafSystem() )
  932. {
  933. #if !defined( PARTICLEPROTOTYPE_APP )
  934. RenderableTranslucencyType_t nType;
  935. if ( pEffect->IsTranslucent() )
  936. {
  937. nType = pEffect->IsTwoPass() ? RENDERABLE_IS_TWO_PASS : RENDERABLE_IS_TRANSLUCENT;
  938. }
  939. else
  940. {
  941. nType = RENDERABLE_IS_OPAQUE;
  942. }
  943. ClientLeafSystem()->CreateRenderableHandle( pEffect, pEffect->m_pDef->IsViewModelEffect(), nType, RENDERABLE_MODEL_ENTITY );
  944. ClientLeafSystem()->EnableBloatedBounds( pEffect->RenderHandle(), true );
  945. CBaseEntity *pOwner = pEffect->GetOwner();
  946. ClientLeafSystem()->RenderInFastReflections( pEffect->RenderHandle(), pOwner ? pOwner->IsRenderingInFastReflections() : false );
  947. #endif
  948. }
  949. }
  950. bool CParticleMgr::AddEffect( CParticleEffectBinding *pEffect, IParticleEffect *pSim )
  951. {
  952. #ifdef _DEBUG
  953. FOR_EACH_LL( m_Effects, i )
  954. {
  955. if( m_Effects[i]->m_pSim == pSim )
  956. {
  957. Assert( !"CParticleMgr::AddEffect: added same effect twice" );
  958. return false;
  959. }
  960. }
  961. #endif
  962. pEffect->Init( this, pSim );
  963. // Add it to the leaf system.
  964. #if !defined( PARTICLEPROTOTYPE_APP )
  965. ClientLeafSystem()->CreateRenderableHandle( pEffect, false, RENDERABLE_IS_TRANSLUCENT, RENDERABLE_MODEL_ENTITY );
  966. ClientLeafSystem()->EnableBloatedBounds( pEffect->RenderHandle(), true );
  967. #endif
  968. pEffect->m_ListIndex = m_Effects.AddToTail( pEffect );
  969. Assert( pEffect->m_ListIndex != 0xFFFF );
  970. // notify listeners
  971. int nListeners = m_effectListeners.Count();
  972. for ( int i = 0; i < nListeners; ++i )
  973. {
  974. m_effectListeners[ i ]->OnParticleEffectAdded( pSim );
  975. }
  976. return true;
  977. }
  978. void CParticleMgr::RemoveEffect( CParticleEffectBinding *pEffect )
  979. {
  980. // This prevents certain recursive situations where a NotifyRemove
  981. // call can wind up triggering another one, usually in an effect's
  982. // destructor.
  983. Assert( pEffect );
  984. if( !pEffect || pEffect->GetRemovalInProgressFlag() )
  985. return;
  986. pEffect->SetRemovalInProgressFlag();
  987. // Don't call RemoveEffect while inside an IParticleEffect's Update() function.
  988. // Return false from the Update function instead.
  989. Assert( !m_bUpdatingEffects );
  990. // notify listeners
  991. int nListeners = m_effectListeners.Count();
  992. for ( int i = 0; i < nListeners; ++i )
  993. {
  994. m_effectListeners[ i ]->OnParticleEffectRemoved( pEffect->m_pSim );
  995. }
  996. // Take it out of the leaf system.
  997. ClientLeafSystem()->RemoveRenderable( pEffect->m_hRenderHandle );
  998. int listIndex = pEffect->m_ListIndex;
  999. if ( pEffect->m_pSim )
  1000. {
  1001. pEffect->m_pSim->NotifyRemove();
  1002. m_Effects.Remove( listIndex );
  1003. }
  1004. else
  1005. {
  1006. Assert( listIndex == 0xFFFF );
  1007. }
  1008. }
  1009. void CParticleMgr::RemoveEffect( CNewParticleEffect *pEffect )
  1010. {
  1011. Assert( pEffect );
  1012. if ( !pEffect )
  1013. return;
  1014. // Don't call RemoveEffect while inside an IParticleEffect's Update() function.
  1015. // Return false from the Update function instead.
  1016. Assert( !m_bUpdatingEffects );
  1017. #if !defined( PARTICLEPROTOTYPE_APP )
  1018. // Take it out of the leaf system.
  1019. ClientLeafSystem()->RemoveRenderable( pEffect->m_hRenderHandle );
  1020. #endif
  1021. m_NewEffects.RemoveNode( pEffect );
  1022. pEffect->NotifyRemove();
  1023. }
  1024. void CParticleMgr::RemoveAllNewEffects()
  1025. {
  1026. // Remove any of the new effects that were flagged to be removed.
  1027. for( CNewParticleEffect *pNewEffect = m_NewEffects.m_pHead; pNewEffect; )
  1028. {
  1029. CNewParticleEffect *pNextEffect = pNewEffect->m_pNext;
  1030. // see it any entitiy has a particle prop pointing at this one. this loop through all
  1031. // entities shouldn't be important perf-wise because it only happens on reload
  1032. C_BaseEntityIterator iterator;
  1033. C_BaseEntity *pEnt;
  1034. while ( (pEnt = iterator.Next()) != NULL )
  1035. {
  1036. if ( pEnt->ParticleProp() )
  1037. {
  1038. pEnt->ParticleProp()->OnParticleSystemDeleted( pNewEffect );
  1039. }
  1040. }
  1041. RemoveEffect( pNewEffect );
  1042. pNewEffect = pNextEffect;
  1043. }
  1044. }
  1045. void CParticleMgr::RemoveAllEffects()
  1046. {
  1047. int iNext;
  1048. for ( int i = m_Effects.Head(); i != m_Effects.InvalidIndex(); i = iNext )
  1049. {
  1050. iNext = m_Effects.Next( i );
  1051. RemoveEffect( m_Effects[i] );
  1052. }
  1053. RemoveAllNewEffects();
  1054. for( int i = m_SubTextures.First(); i != m_SubTextures.InvalidIndex(); i = m_SubTextures.Next( i ) )
  1055. {
  1056. IMaterial *pMaterial = m_SubTextures[i]->m_pMaterial;
  1057. if ( pMaterial )
  1058. pMaterial->Release();
  1059. m_SubTextures[i]->m_pMaterial = NULL;
  1060. }
  1061. //HACKHACK: commented out because we need to keep leaking handles until every piece of code that grabs one ditches it at level end
  1062. //m_SubTextures.PurgeAndDeleteElements();
  1063. for( int i = m_SubTextureGroups.Count(); --i >= 0; )
  1064. {
  1065. IMaterial *pMaterial = m_SubTextureGroups[i]->m_pPageMaterial;
  1066. if ( pMaterial )
  1067. pMaterial->Release();
  1068. m_SubTextureGroups[i]->m_pPageMaterial = NULL;
  1069. }
  1070. //HACKHACK: commented out because we need to keep leaking handles until every piece of code that grabs one ditches it at level end
  1071. //m_SubTextureGroups.PurgeAndDeleteElements();
  1072. }
  1073. CNewParticleEffect *CParticleMgr::FirstNewEffect()
  1074. {
  1075. return m_NewEffects.m_pHead;
  1076. }
  1077. CNewParticleEffect *CParticleMgr::NextNewEffect( CNewParticleEffect *pEffect )
  1078. {
  1079. return pEffect ? pEffect->m_pNext : NULL;
  1080. }
  1081. void CParticleMgr::IncrementFrameCode()
  1082. {
  1083. VPROF( "CParticleMgr::IncrementFrameCode()" );
  1084. ++m_FrameCode;
  1085. if ( m_FrameCode == 0 )
  1086. {
  1087. // Reset all the CParticleEffectBindings..
  1088. FOR_EACH_LL( m_Effects, i )
  1089. {
  1090. m_Effects[i]->m_FrameCode = 0;
  1091. }
  1092. m_FrameCode = 1;
  1093. }
  1094. //!!new!!
  1095. }
  1096. //-----------------------------------------------------------------------------
  1097. // Main rendering loop
  1098. //-----------------------------------------------------------------------------
  1099. void CParticleMgr::Simulate( float flTimeDelta )
  1100. {
  1101. g_nParticlesDrawn = 0;
  1102. if(!m_pMaterialSystem)
  1103. {
  1104. Assert(false);
  1105. return;
  1106. }
  1107. // Update all the effects.
  1108. UpdateAllEffects( flTimeDelta );
  1109. }
  1110. bool g_bMeasureParticlePerformance;
  1111. bool g_bDisplayParticlePerformance;
  1112. static int64 g_nNumParticlesSimulated;
  1113. static int64 g_nNumUSSpentSimulatingParticles;
  1114. static double g_flStartSimTime;
  1115. int GetParticlePerformance()
  1116. {
  1117. if (! g_nNumUSSpentSimulatingParticles )
  1118. return 0;
  1119. return (1000*g_nNumParticlesSimulated) / g_nNumUSSpentSimulatingParticles;
  1120. }
  1121. void CParticleMgr::PostRender()
  1122. {
  1123. VPROF("CParticleMgr::SimulateUndrawnEffects");
  1124. // Simulate all effects that weren't drawn (if they have their 'always simulate' flag set).
  1125. FOR_EACH_LL( m_Effects, i )
  1126. {
  1127. CParticleEffectBinding *pEffect = m_Effects[i];
  1128. // Tell the effect if it was drawn or not.
  1129. pEffect->SetWasDrawnPrevFrame( pEffect->WasDrawn() );
  1130. // Now that we've rendered, clear this flag so it'll simulate next frame.
  1131. pEffect->SetFlag( CParticleEffectBinding::FLAGS_FIRST_FRAME, false );
  1132. }
  1133. }
  1134. void CParticleMgr::DrawBeforeViewModelEffects()
  1135. {
  1136. RenderableInstance_t instance;
  1137. instance.m_nAlpha = 255;
  1138. FOR_EACH_LL( m_Effects, i )
  1139. {
  1140. CParticleEffectBinding *pEffect = m_Effects[i];
  1141. if ( pEffect->GetFlag( CParticleEffectBinding::FLAGS_DRAW_BEFORE_VIEW_MODEL ) )
  1142. {
  1143. Assert( !pEffect->WasDrawn() );
  1144. pEffect->DrawModel( STUDIO_RENDER, instance );
  1145. }
  1146. }
  1147. }
  1148. void ResetParticlePerformanceCounters( void )
  1149. {
  1150. g_nNumUSSpentSimulatingParticles = 0;
  1151. g_nNumParticlesSimulated = 0;
  1152. }
  1153. void BeginSimulateParticles( void )
  1154. {
  1155. g_flStartSimTime = Plat_FloatTime();
  1156. }
  1157. static ConVar r_particle_sim_spike_threshold_ms( "r_particle_sim_spike_threshold_ms", "0" );
  1158. void EndSimulateParticles( void )
  1159. {
  1160. float flETime = Plat_FloatTime() - g_flStartSimTime;
  1161. if ( g_bMeasureParticlePerformance )
  1162. {
  1163. g_nNumUSSpentSimulatingParticles += 1.0e6 * flETime;
  1164. }
  1165. g_pParticleSystemMgr->SetLastSimulationDuration( flETime );
  1166. g_pParticleSystemMgr->CommitProfileInformation( flETime > .001 * r_particle_sim_spike_threshold_ms.GetInt() );
  1167. }
  1168. static ConVar r_threaded_particles( "r_threaded_particles", "1" );
  1169. static float s_flThreadedPSystemTimeStep;
  1170. static void PreProcessPSystem()
  1171. {
  1172. mdlcache->BeginCoarseLock();
  1173. mdlcache->BeginLock();
  1174. }
  1175. static void PostProcessPSystem()
  1176. {
  1177. mdlcache->EndLock();
  1178. mdlcache->EndCoarseLock();
  1179. }
  1180. static void ProcessPSystem( CNewParticleEffect *&pNewEffect )
  1181. {
  1182. // If this is a new effect, then update its bbox so it goes in the
  1183. // right leaves (if it has particles).
  1184. if ( pNewEffect->m_bQueuedStartEmission )
  1185. {
  1186. pNewEffect->m_bQueuedStartEmission = false;
  1187. pNewEffect->StartEmission();
  1188. }
  1189. int bFirstUpdate = pNewEffect->GetNeedsBBoxUpdate();
  1190. if ( bFirstUpdate )
  1191. {
  1192. // If the effect already disabled auto-updating of the bbox, then it should have
  1193. // set the bbox by now and we can ignore this responsibility here.
  1194. if ( !pNewEffect->GetAutoUpdateBBox() || pNewEffect->RecalculateBoundingBox() )
  1195. {
  1196. pNewEffect->SetNeedsBBoxUpdate( false );
  1197. }
  1198. }
  1199. // This flag will get set to true if the effect is drawn through the leaf system.
  1200. pNewEffect->SetDrawn( false );
  1201. if ( pNewEffect->GetFirstFrameFlag() )
  1202. {
  1203. pNewEffect->Simulate( 0.0f );
  1204. pNewEffect->SetFirstFrameFlag( false );
  1205. }
  1206. else if ( pNewEffect->ShouldSimulate() )
  1207. {
  1208. pNewEffect->Simulate( s_flThreadedPSystemTimeStep );
  1209. }
  1210. if ( pNewEffect->IsFinished() )
  1211. {
  1212. pNewEffect->SetRemoveFlag();
  1213. }
  1214. }
  1215. static void ProcessNonDrawingSystem( CParticleCollection *&pNonDrawingEffect )
  1216. {
  1217. pNonDrawingEffect->Simulate( s_flThreadedPSystemTimeStep );
  1218. }
  1219. int CParticleMgr::ComputeParticleDefScreenArea( int nInfoCount, RetireInfo_t *pInfo, float *pTotalArea, CParticleSystemDefinition* pDef,
  1220. const CViewSetup& view, const VMatrix &worldToPixels, float flFocalDist )
  1221. {
  1222. int nCollection = 0;
  1223. float flCullCost = pDef->GetCullFillCost();
  1224. float flCullRadius = pDef->GetCullRadius();
  1225. float flCullRadiusSqr = flCullRadius * flCullRadius;
  1226. *pTotalArea = 0.0f;
  1227. #ifdef DBGFLAG_ASSERT
  1228. float flMaxPixels = view.width * view.height;
  1229. #endif
  1230. CParticleCollection *pCollection = pDef->FirstCollection();
  1231. for ( ; pCollection; pCollection = pCollection->GetNextCollectionUsingSameDef() )
  1232. {
  1233. CNewParticleEffect *pEffect = static_cast< CNewParticleEffect* >( pCollection );
  1234. if ( !pEffect->ShouldPerformCullCheck() )
  1235. continue;
  1236. // Don't count parents
  1237. Assert( !pCollection->m_pParent );
  1238. Assert( nCollection < nInfoCount && pDef == pCollection->m_pDef );
  1239. pInfo[nCollection].m_flScreenArea = 0.0f;
  1240. pInfo[nCollection].m_pCollection = pCollection;
  1241. pInfo[nCollection].m_bFirstFrame = false;
  1242. Vector vecCenter, vecScreenCenter, vecCenterCam;
  1243. vecCenter = pCollection->GetControlPointAtCurrentTime( pDef->GetCullControlPoint() );
  1244. Vector3DMultiplyPositionProjective( worldToPixels, vecCenter, vecScreenCenter );
  1245. float lSqr = vecCenter.DistToSqr( view.origin );
  1246. float flProjRadius = ( lSqr > flCullRadiusSqr ) ? 0.5f * flFocalDist * flCullRadius / sqrt( lSqr - flCullRadiusSqr ) : 1.0f;
  1247. flProjRadius *= view.width;
  1248. float flMinX = MAX( view.x, vecScreenCenter.x - flProjRadius );
  1249. float flMaxX = MIN( view.x + view.width, vecScreenCenter.x + flProjRadius );
  1250. float flMinY = MAX( view.y, vecScreenCenter.y - flProjRadius );
  1251. float flMaxY = MIN( view.y + view.height, vecScreenCenter.y + flProjRadius );
  1252. // Clamp the min/max values to the screen so that particles particle systems outside of the view don't cause early retirement.
  1253. flMinX = clamp( flMinX, view.x, view.width );
  1254. flMaxX = clamp( flMaxX, view.x, view.width );
  1255. flMinY = clamp( flMinY, view.y, view.height );
  1256. flMaxY = clamp( flMaxY, view.y, view.height );
  1257. float flArea = ( flMaxX - flMinX ) * ( flMaxY - flMinY );
  1258. Assert( flArea <= flMaxPixels );
  1259. flArea *= flCullCost;
  1260. *pTotalArea += flArea;
  1261. pInfo[nCollection].m_flScreenArea = flArea;
  1262. pInfo[nCollection].m_pCollection = pCollection;
  1263. pInfo[nCollection].m_bFirstFrame = pEffect->GetFirstFrameFlag();
  1264. ++nCollection;
  1265. }
  1266. return nCollection;
  1267. }
  1268. int CParticleMgr::RetireSort( const void *p1, const void *p2 )
  1269. {
  1270. RetireInfo_t *pRetire1 = (RetireInfo_t*)p1;
  1271. RetireInfo_t *pRetire2 = (RetireInfo_t*)p2;
  1272. float flArea = pRetire1->m_flScreenArea - pRetire2->m_flScreenArea;
  1273. if ( flArea == 0.0f )
  1274. return 0;
  1275. return ( flArea > 0 ) ? -1 : 1;
  1276. }
  1277. bool CParticleMgr::RetireParticleCollections( CParticleSystemDefinition* pDef,
  1278. int nCount, RetireInfo_t *pInfo, float flScreenArea, float flMaxTotalArea )
  1279. {
  1280. bool bRetirementOccurred = false;
  1281. // Don't cull out the particle system if there's only 1 and no replacement
  1282. const char *pReplacementDef = pDef->GetCullReplacementDefinition();
  1283. if ( ( !pReplacementDef || !pReplacementDef[0] ) && ( nCount <= 1 ) )
  1284. return false;
  1285. // Quicksort the retirement info
  1286. qsort( pInfo, nCount, sizeof(RetireInfo_t), RetireSort );
  1287. for ( int i = 0; i < nCount; ++i )
  1288. {
  1289. if ( flScreenArea <= flMaxTotalArea )
  1290. break;
  1291. // We can only replace stuff that's being emitted this frame
  1292. if ( !pInfo[i].m_bFirstFrame )
  1293. continue;
  1294. CNewParticleEffect* pRetireEffect = static_cast< CNewParticleEffect* >( pInfo[i].m_pCollection );
  1295. CNewParticleEffect* pNewEffect = pRetireEffect->ReplaceWith( pReplacementDef );
  1296. if ( pNewEffect )
  1297. {
  1298. pNewEffect->Update( s_flThreadedPSystemTimeStep );
  1299. }
  1300. bRetirementOccurred = true;
  1301. flScreenArea -= pInfo[i].m_flScreenArea;
  1302. }
  1303. return bRetirementOccurred;
  1304. }
  1305. // Next, see if there are new particle systems that need early retirement
  1306. static ConVar cl_particle_retire_cost( "cl_particle_retire_cost", "0", FCVAR_CHEAT );
  1307. bool CParticleMgr::EarlyRetireParticleSystems( int nCount, CNewParticleEffect **ppEffects )
  1308. {
  1309. // NOTE: Doing a cheap and hacky estimate of worst-case fillrate
  1310. const CViewSetup *pViewSetup[ MAX_SPLITSCREEN_PLAYERS ];
  1311. VMatrix worldToScreen[ MAX_SPLITSCREEN_PLAYERS ];
  1312. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  1313. {
  1314. pViewSetup[ hh ] = view->GetPlayerViewSetup( hh );
  1315. if ( pViewSetup[ hh ]->width == 0 || pViewSetup[ hh ]->height == 0 )
  1316. return false;
  1317. }
  1318. float flMaxScreenArea = cl_particle_retire_cost.GetFloat() * 1000.0f;
  1319. if ( flMaxScreenArea == 0.0f )
  1320. return false;
  1321. int nDefCount = 0;
  1322. CParticleSystemDefinition **ppDefs = (CParticleSystemDefinition**)stackalloc( nCount * sizeof(CParticleSystemDefinition*) );
  1323. for ( int i = 0; i < nCount; ++i )
  1324. {
  1325. CParticleSystemDefinition *pDef = ppEffects[i]->m_pDef;
  1326. // Skip stuff that doesn't have a cull radius set
  1327. if ( pDef->GetCullRadius() == 0.0f )
  1328. continue;
  1329. // Only perform the cull check on creation
  1330. if ( !ppEffects[i]->GetFirstFrameFlag() )
  1331. continue;
  1332. if ( pDef->HasRetirementBeenChecked( gpGlobals->framecount ) )
  1333. continue;
  1334. pDef->MarkRetirementCheck( gpGlobals->framecount );
  1335. ppDefs[nDefCount++] = ppEffects[i]->m_pDef;
  1336. }
  1337. if ( nDefCount == 0 )
  1338. return false;
  1339. for ( int i = 0; i < nCount; ++i )
  1340. {
  1341. ppEffects[i]->MarkShouldPerformCullCheck( true );
  1342. }
  1343. float flFocalDist[ MAX_SPLITSCREEN_PLAYERS ];
  1344. FOR_EACH_VALID_SPLITSCREEN_PLAYER( ii )
  1345. {
  1346. VMatrix dummy1, dummy2, dummy3;
  1347. render->GetMatricesForView( *pViewSetup[ ii ], &dummy1, &dummy2, &dummy3, &worldToScreen[ ii ] );
  1348. flFocalDist[ ii ] = tan( DEG2RAD( pViewSetup[ ii ]->fov * 0.5f ) );
  1349. }
  1350. bool bRetiredCollections = true;
  1351. float flScreenArea;
  1352. int nSize = nCount * sizeof(RetireInfo_t);
  1353. RetireInfo_t *pInfo = (RetireInfo_t*)stackalloc( nSize );
  1354. for ( int i = 0; i < nDefCount; ++i )
  1355. {
  1356. CParticleSystemDefinition* pDef = ppDefs[i];
  1357. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  1358. {
  1359. int nActualCount = ComputeParticleDefScreenArea( nCount, pInfo, &flScreenArea, pDef, *pViewSetup[ hh ], worldToScreen[ hh ], flFocalDist[ hh ] );
  1360. if ( flScreenArea > flMaxScreenArea )
  1361. {
  1362. if ( RetireParticleCollections( pDef, nActualCount, pInfo, flScreenArea, flMaxScreenArea ) )
  1363. {
  1364. bRetiredCollections = true;
  1365. break;
  1366. }
  1367. }
  1368. }
  1369. }
  1370. for ( int i = 0; i < nCount; ++i )
  1371. {
  1372. ppEffects[i]->MarkShouldPerformCullCheck( false );
  1373. }
  1374. return bRetiredCollections;
  1375. }
  1376. static ConVar particle_sim_alt_cores( "particle_sim_alt_cores", "2" );
  1377. void CParticleMgr::BuildParticleSimList( CUtlVector< CNewParticleEffect* > &list )
  1378. {
  1379. float flNow = g_pParticleSystemMgr->GetLastSimulationTime();
  1380. for( CNewParticleEffect *pNewEffect=m_NewEffects.m_pHead; pNewEffect;
  1381. pNewEffect=pNewEffect->m_pNext )
  1382. {
  1383. if ( flNow >= pNewEffect->m_flNextSleepTime && pNewEffect->m_nActiveParticles > 0 )
  1384. {
  1385. if ( pNewEffect->GetOwner() )
  1386. {
  1387. //Not in PVS so we really want to respect NextSleepTime
  1388. if ( !g_pClientLeafSystem->IsRenderableInPVS( pNewEffect->GetOwner()->GetClientRenderable() ) )
  1389. continue;
  1390. }
  1391. }
  1392. if ( pNewEffect->GetRemoveFlag() )
  1393. continue;
  1394. if ( g_bMeasureParticlePerformance )
  1395. {
  1396. g_nNumParticlesSimulated += pNewEffect->m_nActiveParticles;
  1397. }
  1398. list.AddToTail( pNewEffect );
  1399. }
  1400. }
  1401. static ConVar r_particle_timescale( "r_particle_timescale", "1.0" );
  1402. static int CountChildParticleSystems( CParticleCollection *p )
  1403. {
  1404. int nCount = 1;
  1405. for ( CParticleCollection *pChild = p->m_Children.m_pHead; pChild; pChild = pChild->m_pNext )
  1406. {
  1407. nCount += CountChildParticleSystems( pChild );
  1408. }
  1409. return nCount;
  1410. }
  1411. static ConVar cl_particle_max_count( "cl_particle_max_count", "0" );
  1412. struct ParticleInfo_t
  1413. {
  1414. ParticleInfo_t() : m_nCount(0), m_nChildCount(0) {}
  1415. int m_nCount;
  1416. int m_nChildCount;
  1417. };
  1418. void CParticleMgr::SpewActiveParticleSystems( )
  1419. {
  1420. CUtlStringMap< ParticleInfo_t > histo;
  1421. for( CNewParticleEffect *pNewEffect=m_NewEffects.m_pHead; pNewEffect;
  1422. pNewEffect=pNewEffect->m_pNext )
  1423. {
  1424. if ( ++histo[ pNewEffect->GetName() ].m_nCount == 1 )
  1425. {
  1426. histo[ pNewEffect->GetName() ].m_nChildCount = CountChildParticleSystems( pNewEffect );
  1427. }
  1428. }
  1429. Msg( "Too many simultaneously active particle systems!\n" );
  1430. Msg( "Name\t\t\t\t\tCount\t\tChild Count Per Instance\n" );
  1431. int nCount = histo.GetNumStrings();
  1432. for ( int i = 0; i < nCount; ++i )
  1433. {
  1434. Msg( "%30s\t\t%d\t\t%d\n", histo.String(i), histo[i].m_nCount, histo[i].m_nChildCount );
  1435. }
  1436. }
  1437. void CParticleMgr::UpdateNewEffects( float flTimeDelta )
  1438. {
  1439. // #ifdef TF_CLIENT_DLL
  1440. // extern bool g_bDontMakeSkipToTimeTakeForever;
  1441. // g_bDontMakeSkipToTimeTakeForever = true;
  1442. // #endif
  1443. flTimeDelta *= r_particle_timescale.GetFloat();
  1444. VPROF_BUDGET( "CParticleMSG::UpdateNewEffects", "Particle Simulation" );
  1445. g_pParticleSystemMgr->SetLastSimulationTime( gpGlobals->curtime );
  1446. int nParticleSystemCount = 0;
  1447. int nMaxParticleCount = cl_particle_max_count.GetInt();
  1448. BeginSimulateParticles();
  1449. CUtlVector<CNewParticleEffect *> particlesToSimulate;
  1450. BuildParticleSimList( particlesToSimulate );
  1451. s_flThreadedPSystemTimeStep = flTimeDelta;
  1452. int nCount = particlesToSimulate.Count();
  1453. // first, run non-reentrant part to get CP updates from entities
  1454. for( int i=0; i<nCount; i++ )
  1455. {
  1456. // this one can call into random entity code which may not be thread-safe
  1457. particlesToSimulate[i]->Update( s_flThreadedPSystemTimeStep );
  1458. if ( nMaxParticleCount > 0 )
  1459. {
  1460. nParticleSystemCount += CountChildParticleSystems( particlesToSimulate[i] );
  1461. }
  1462. }
  1463. // See if there are new particle systems that need early retirement
  1464. // This has to happen after the first update
  1465. if ( EarlyRetireParticleSystems( nCount, particlesToSimulate.Base() ) )
  1466. {
  1467. particlesToSimulate.RemoveAll();
  1468. BuildParticleSimList( particlesToSimulate );
  1469. nCount = particlesToSimulate.Count();
  1470. }
  1471. if ( nCount )
  1472. {
  1473. UpdateDirtySpatialPartitionEntities();
  1474. if ( !r_threaded_particles.GetBool() )
  1475. {
  1476. for( int i=0; i<nCount; i++)
  1477. {
  1478. ProcessPSystem( particlesToSimulate[i] );
  1479. }
  1480. }
  1481. else
  1482. {
  1483. int nAltCore = IsGameConsole() && particle_sim_alt_cores.GetInt();
  1484. if ( !m_pThreadPool[1] || nAltCore == 0 )
  1485. {
  1486. ParallelProcess( particlesToSimulate.Base(), nCount, ProcessPSystem, PreProcessPSystem, PostProcessPSystem );
  1487. }
  1488. else
  1489. {
  1490. if ( nAltCore > 2 )
  1491. {
  1492. nAltCore = 2;
  1493. }
  1494. CParallelProcessor<CNewParticleEffect*, CFuncJobItemProcessor<CNewParticleEffect*>, 3 > processor;
  1495. processor.m_ItemProcessor.Init( ProcessPSystem, PreProcessPSystem, PostProcessPSystem );
  1496. processor.Run( particlesToSimulate.Base(), nCount, 1, INT_MAX, m_pThreadPool[nAltCore-1] );
  1497. }
  1498. }
  1499. }
  1500. // now, simulate the non-drawing ones
  1501. CUtlVectorFixedGrowable< CParticleCollection *, 128 > nonDrawingSimulateList;
  1502. for( CNonDrawingParticleSystem *i = m_NonDrawingParticleSystems.m_pHead; i; i = i->m_pNext )
  1503. {
  1504. nonDrawingSimulateList.AddToTail( i->m_pSystem );
  1505. }
  1506. if ( nonDrawingSimulateList.Count() )
  1507. {
  1508. ParallelProcess( nonDrawingSimulateList.Base(), nonDrawingSimulateList.Count(), ProcessNonDrawingSystem, PreProcessPSystem, PostProcessPSystem );
  1509. }
  1510. // now, run non-reentrant part for updating changes
  1511. for( int i=0; i<nCount; i++)
  1512. {
  1513. // this one can call into random entity code which may not be thread-safe
  1514. particlesToSimulate[i]->DetectChanges();
  1515. }
  1516. EndSimulateParticles();
  1517. if ( nMaxParticleCount > 0 && ( nParticleSystemCount >= nMaxParticleCount ) )
  1518. {
  1519. SpewActiveParticleSystems();
  1520. }
  1521. }
  1522. void CParticleMgr::UpdateAllEffects( float flTimeDelta )
  1523. {
  1524. m_bUpdatingEffects = true;
  1525. g_pParticleSystemQuery->PreSimulate();
  1526. g_pParticleSystemMgr->SetFallbackParameters( cl_particle_fallback_base.GetFloat(), cl_particle_fallback_multiplier.GetFloat(),
  1527. cl_particle_sim_fallback_base_multiplier.GetFloat(), cl_particle_sim_fallback_threshold_ms.GetFloat() );
  1528. g_pParticleSystemMgr->SetSystemLevel( GetCPULevel(), GetGPULevel() );
  1529. if( flTimeDelta > 0.1f )
  1530. flTimeDelta = 0.1f;
  1531. FOR_EACH_LL( m_Effects, iEffect )
  1532. {
  1533. CParticleEffectBinding *pEffect = m_Effects[iEffect];
  1534. // Don't update this effect if it will be removed.
  1535. if( pEffect->GetRemoveFlag() )
  1536. continue;
  1537. // If this is a new effect, then update its bbox so it goes in the
  1538. // right leaves (if it has particles).
  1539. int bFirstUpdate = pEffect->GetNeedsBBoxUpdate();
  1540. if ( bFirstUpdate )
  1541. {
  1542. // If the effect already disabled auto-updating of the bbox, then it should have
  1543. // set the bbox by now and we can ignore this responsibility here.
  1544. if ( !pEffect->GetAutoUpdateBBox() || pEffect->RecalculateBoundingBox() )
  1545. {
  1546. pEffect->SetNeedsBBoxUpdate( false );
  1547. }
  1548. }
  1549. // This flag will get set to true if the effect is drawn through the leaf system.
  1550. pEffect->SetDrawn( false );
  1551. // Update the effect.
  1552. pEffect->m_pSim->Update( flTimeDelta );
  1553. if ( pEffect->GetFirstFrameFlag() )
  1554. pEffect->SetFirstFrameFlag( false );
  1555. else
  1556. pEffect->SimulateParticles( flTimeDelta );
  1557. // Update its position in the leaf system if its bbox changed.
  1558. pEffect->DetectChanges();
  1559. }
  1560. if ( g_bMeasureParticlePerformance ) // use fixed time step
  1561. {
  1562. for( float dt=0.0f; dt <= flTimeDelta ; dt+= 0.01f )
  1563. {
  1564. UpdateNewEffects( 0.01f );
  1565. }
  1566. }
  1567. else
  1568. {
  1569. UpdateNewEffects( flTimeDelta );
  1570. }
  1571. m_bUpdatingEffects = false;
  1572. // Remove any effects that were flagged to be removed.
  1573. int iNext;
  1574. for ( int i=m_Effects.Head(); i != m_Effects.InvalidIndex(); i=iNext )
  1575. {
  1576. iNext = m_Effects.Next( i );
  1577. CParticleEffectBinding *pEffect = m_Effects[i];
  1578. if( pEffect->GetRemoveFlag() )
  1579. {
  1580. RemoveEffect( pEffect );
  1581. }
  1582. }
  1583. // Remove any of the new effects that were flagged to be removed.
  1584. for( CNewParticleEffect *pNewEffect=m_NewEffects.m_pHead; pNewEffect; )
  1585. {
  1586. CNewParticleEffect *pNextEffect = pNewEffect->m_pNext;
  1587. if ( pNewEffect->GetRemoveFlag() )
  1588. {
  1589. RemoveEffect( pNewEffect );
  1590. }
  1591. pNewEffect = pNextEffect;
  1592. }
  1593. g_pParticleSystemQuery->PostSimulate();
  1594. }
  1595. void CParticleMgr::RemoveOldParticleEffects( float flTime )
  1596. {
  1597. for( CNewParticleEffect *pNewEffect=m_NewEffects.m_pHead; pNewEffect;
  1598. pNewEffect=pNewEffect->m_pNext )
  1599. {
  1600. if ( pNewEffect->m_flCurTime > flTime )
  1601. {
  1602. pNewEffect->StopEmission( false, true, true );
  1603. }
  1604. }
  1605. }
  1606. void CParticleMgr::SetRemoveAllParticleEffects()
  1607. {
  1608. for( CNewParticleEffect *pNewEffect=m_NewEffects.m_pHead; pNewEffect;
  1609. pNewEffect=pNewEffect->m_pNext )
  1610. {
  1611. pNewEffect->StopEmission( false, true, true );
  1612. pNewEffect->SetRemoveFlag();
  1613. }
  1614. }
  1615. CParticleSubTextureGroup* CParticleMgr::FindOrAddSubTextureGroup( IMaterial *pPageMaterial )
  1616. {
  1617. for ( int i=0; i < m_SubTextureGroups.Count(); i++ )
  1618. {
  1619. if ( m_SubTextureGroups[i]->m_pPageMaterial == pPageMaterial )
  1620. return m_SubTextureGroups[i];
  1621. }
  1622. CParticleSubTextureGroup *pGroup = new CParticleSubTextureGroup;
  1623. m_SubTextureGroups.AddToTail( pGroup );
  1624. pGroup->m_pPageMaterial = pPageMaterial;
  1625. pPageMaterial->AddRef();
  1626. return pGroup;
  1627. }
  1628. PMaterialHandle CParticleMgr::GetPMaterial( const char *pMaterialName )
  1629. {
  1630. if( !m_pMaterialSystem )
  1631. {
  1632. Assert(false);
  1633. return NULL;
  1634. }
  1635. int hMat = m_SubTextures.Find( pMaterialName );
  1636. if ( hMat == m_SubTextures.InvalidIndex() )
  1637. {
  1638. IMaterial *pIMaterial = m_pMaterialSystem->FindMaterial( pMaterialName, TEXTURE_GROUP_PARTICLE );
  1639. if ( pIMaterial )
  1640. {
  1641. pIMaterial->AddRef();
  1642. hMat = m_SubTextures.Insert( pMaterialName );
  1643. CParticleSubTexture *pSubTexture = new CParticleSubTexture;
  1644. m_SubTextures[hMat] = pSubTexture;
  1645. pSubTexture->m_pMaterial = pIMaterial;
  1646. #ifdef _DEBUG
  1647. int iNameLength = V_strlen( pMaterialName ) + 1;
  1648. pSubTexture->m_szDebugName = new char [iNameLength];
  1649. memcpy( pSubTexture->m_szDebugName, pMaterialName, iNameLength );
  1650. #endif
  1651. // See if it's got a group name. If not, make a group with a special name.
  1652. IMaterial *pPageMaterial = pIMaterial->GetMaterialPage();
  1653. if ( pIMaterial->InMaterialPage() && pPageMaterial )
  1654. {
  1655. float flOffset[2], flScale[2];
  1656. pIMaterial->GetMaterialOffset( flOffset );
  1657. pIMaterial->GetMaterialScale( flScale );
  1658. pSubTexture->m_tCoordMins[0] = (0*flScale[0] + flOffset[0]) * pPageMaterial->GetMappingWidth();
  1659. pSubTexture->m_tCoordMaxs[0] = (1*flScale[0] + flOffset[0]) * pPageMaterial->GetMappingWidth();
  1660. pSubTexture->m_tCoordMins[1] = (0*flScale[1] + flOffset[1]) * pPageMaterial->GetMappingHeight();
  1661. pSubTexture->m_tCoordMaxs[1] = (1*flScale[1] + flOffset[1]) * pPageMaterial->GetMappingHeight();
  1662. pSubTexture->m_pGroup = FindOrAddSubTextureGroup( pPageMaterial );
  1663. }
  1664. else
  1665. {
  1666. // Ok, this material isn't part of a group. Give it its own subtexture group.
  1667. pSubTexture->m_pGroup = &pSubTexture->m_DefaultGroup;
  1668. pSubTexture->m_DefaultGroup.m_pPageMaterial = pIMaterial;
  1669. pPageMaterial = pIMaterial; // For tcoord scaling.
  1670. pSubTexture->m_tCoordMins[0] = pSubTexture->m_tCoordMins[1] = 0;
  1671. pSubTexture->m_tCoordMaxs[0] = pIMaterial->GetMappingWidth();
  1672. pSubTexture->m_tCoordMaxs[1] = pIMaterial->GetMappingHeight();
  1673. }
  1674. // Rescale the texture coordinates.
  1675. pSubTexture->m_tCoordMins[0] = (pSubTexture->m_tCoordMins[0] + 0.5f) / pPageMaterial->GetMappingWidth();
  1676. pSubTexture->m_tCoordMins[1] = (pSubTexture->m_tCoordMins[1] + 0.5f) / pPageMaterial->GetMappingHeight();
  1677. pSubTexture->m_tCoordMaxs[0] = (pSubTexture->m_tCoordMaxs[0] - 0.5f) / pPageMaterial->GetMappingWidth();
  1678. pSubTexture->m_tCoordMaxs[1] = (pSubTexture->m_tCoordMaxs[1] - 0.5f) / pPageMaterial->GetMappingHeight();
  1679. return pSubTexture;
  1680. }
  1681. else
  1682. {
  1683. return NULL;
  1684. }
  1685. }
  1686. else
  1687. {
  1688. RepairPMaterial( m_SubTextures[hMat] ); //HACKHACK: Remove this when we can stop leaking handles from level to level.
  1689. return m_SubTextures[hMat];
  1690. }
  1691. }
  1692. //HACKHACK: The old system would leak handles and materials until shutdown. The new system still needs to leak handles until every piece of code that grabs one ditches it at level end.
  1693. //This function takes a leaked handle from a previous level and reacquires necessary materials.
  1694. void CParticleMgr::RepairPMaterial( PMaterialHandle hMaterial )
  1695. {
  1696. if( hMaterial->m_pMaterial != NULL )
  1697. return;
  1698. const char *pMaterialName = NULL;
  1699. for( int i = m_SubTextures.First(); i != m_SubTextures.InvalidIndex(); i = m_SubTextures.Next( i ) )
  1700. {
  1701. if( m_SubTextures[i] == hMaterial )
  1702. {
  1703. pMaterialName = m_SubTextures.GetElementName( i );
  1704. break;
  1705. }
  1706. }
  1707. Assert( pMaterialName != NULL );
  1708. IMaterial *pIMaterial = m_pMaterialSystem->FindMaterial( pMaterialName, TEXTURE_GROUP_PARTICLE );
  1709. hMaterial->m_pMaterial = pIMaterial;
  1710. if ( pIMaterial != NULL )
  1711. {
  1712. pIMaterial->AddRef();
  1713. CMatRenderContextPtr pRenderContext( m_pMaterialSystem );
  1714. pRenderContext->Bind( pIMaterial, this );
  1715. IMaterial *pPageMaterial = pIMaterial->GetMaterialPage();
  1716. if ( pIMaterial->InMaterialPage() && pPageMaterial )
  1717. {
  1718. if ( hMaterial->m_pGroup->m_pPageMaterial == NULL )
  1719. {
  1720. hMaterial->m_pGroup->m_pPageMaterial = pPageMaterial;
  1721. pPageMaterial->AddRef();
  1722. }
  1723. }
  1724. else
  1725. {
  1726. hMaterial->m_pGroup->m_pPageMaterial = pIMaterial;
  1727. }
  1728. }
  1729. }
  1730. IMaterial* CParticleMgr::PMaterialToIMaterial( PMaterialHandle hMaterial )
  1731. {
  1732. if ( hMaterial )
  1733. {
  1734. RepairPMaterial( hMaterial ); //HACKHACK: Remove this when we can stop leaking handles from level to level.
  1735. return hMaterial->m_pMaterial;
  1736. }
  1737. else
  1738. return NULL;
  1739. }
  1740. void CParticleMgr::GetDirectionalLightInfo( CParticleLightInfo &info ) const
  1741. {
  1742. info = m_DirectionalLight;
  1743. }
  1744. void CParticleMgr::SetDirectionalLightInfo( const CParticleLightInfo &info )
  1745. {
  1746. m_DirectionalLight = info;
  1747. }
  1748. //---------------------------------------------------------------------------
  1749. // non-drawing effects support
  1750. //---------------------------------------------------------------------------
  1751. CNonDrawingParticleSystem *CParticleMgr::CreateNonDrawingEffect( const char *pEffectName )
  1752. {
  1753. CNonDrawingParticleSystem *pNew = new CNonDrawingParticleSystem;
  1754. pNew->m_pSystem = g_pParticleSystemMgr->CreateParticleCollection( pEffectName );
  1755. m_NonDrawingParticleSystems.AddToHead( pNew );
  1756. return pNew;
  1757. }
  1758. CNonDrawingParticleSystem::~CNonDrawingParticleSystem( void )
  1759. {
  1760. ParticleMgr()->m_NonDrawingParticleSystems.RemoveNode( this );
  1761. delete m_pSystem;
  1762. }
  1763. void CParticleMgr::SpewInfo( bool bDetail )
  1764. {
  1765. DevMsg( "Particle Effect Systems:\n" );
  1766. FOR_EACH_LL( m_Effects, i )
  1767. {
  1768. const char *pEffectName;
  1769. pEffectName = m_Effects[i]->m_pSim->GetEffectName();
  1770. DevMsg( "%3d: NumActive: %3d, AutoBBox: %3s \"%s\" \n", i, m_Effects[i]->m_nActiveParticles, m_Effects[i]->GetAutoUpdateBBox() ? "on" : "off", pEffectName );
  1771. }
  1772. }
  1773. CON_COMMAND( cl_particles_dump_effects, "" )
  1774. {
  1775. ParticleMgr()->SpewInfo( true );
  1776. }
  1777. // ------------------------------------------------------------------------------------ //
  1778. // ------------------------------------------------------------------------------------ //
  1779. float Helper_GetTime()
  1780. {
  1781. #if defined( PARTICLEPROTOTYPE_APP )
  1782. static bool bStarted = false;
  1783. static CCycleCount startTimer;
  1784. if( !bStarted )
  1785. {
  1786. bStarted = true;
  1787. startTimer.Sample();
  1788. }
  1789. CCycleCount curCount;
  1790. curCount.Sample();
  1791. CCycleCount elapsed;
  1792. CCycleCount::Sub( curCount, startTimer, elapsed );
  1793. return (float)elapsed.GetSeconds();
  1794. #else
  1795. return gpGlobals->curtime;
  1796. #endif
  1797. }
  1798. float Helper_RandomFloat( float minVal, float maxVal )
  1799. {
  1800. #if defined( PARTICLEPROTOTYPE_APP )
  1801. return Lerp( (float)rand() / VALVE_RAND_MAX, minVal, maxVal );
  1802. #else
  1803. return random->RandomFloat( minVal, maxVal );
  1804. #endif
  1805. }
  1806. int Helper_RandomInt( int minVal, int maxVal )
  1807. {
  1808. #if defined( PARTICLEPROTOTYPE_APP )
  1809. return minVal + (rand() * (maxVal - minVal)) / VALVE_RAND_MAX;
  1810. #else
  1811. return random->RandomInt( minVal, maxVal );
  1812. #endif
  1813. }
  1814. float Helper_GetFrameTime()
  1815. {
  1816. #if defined( PARTICLEPROTOTYPE_APP )
  1817. extern float g_ParticleAppFrameTime;
  1818. return g_ParticleAppFrameTime;
  1819. #else
  1820. return gpGlobals->frametime;
  1821. #endif
  1822. }