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.

783 lines
24 KiB

  1. //===== Copyright � 1996-2006, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "particlemgr.h"
  9. #include "particles_new.h"
  10. #include "iclientmode.h"
  11. #include "engine/ivdebugoverlay.h"
  12. #include "particle_property.h"
  13. #include "toolframework/itoolframework.h"
  14. #include "toolframework_client.h"
  15. #include "tier1/keyvalues.h"
  16. #include "model_types.h"
  17. #include "vprof.h"
  18. #include "datacache/iresourceaccesscontrol.h"
  19. #include "tier2/tier2.h"
  20. #include "viewrender.h"
  21. #if defined( PORTAL )
  22. #include "c_portal_player.h"
  23. #endif
  24. // NOTE: This has to be the last file included!
  25. #include "tier0/memdbgon.h"
  26. #ifdef PORTAL
  27. #include "portalrender.h"
  28. #endif
  29. extern ConVar cl_particleeffect_aabb_buffer;
  30. extern ConVar cl_particles_show_bbox;
  31. ConVar cl_particles_show_controlpoints( "cl_particles_show_controlpoints", "0", FCVAR_CHEAT );
  32. //-----------------------------------------------------------------------------
  33. // Constructor, destructor
  34. //-----------------------------------------------------------------------------
  35. CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, CParticleSystemDefinition *pEffect )
  36. {
  37. m_hOwner = pOwner;
  38. if ( g_pResourceAccessControl )
  39. {
  40. if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_PARTICLE_SYSTEM, pEffect->GetName() ) )
  41. {
  42. pEffect = g_pParticleSystemMgr->FindParticleSystem( "error" );
  43. }
  44. }
  45. Init( pEffect );
  46. Construct();
  47. }
  48. CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, const char* pEffectName )
  49. {
  50. m_hOwner = pOwner;
  51. if ( g_pResourceAccessControl )
  52. {
  53. if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_PARTICLE_SYSTEM, pEffectName ) )
  54. {
  55. pEffectName = "error";
  56. }
  57. }
  58. Init( pEffectName );
  59. Construct();
  60. }
  61. CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, int nPrecacheIndex )
  62. {
  63. m_hOwner = pOwner;
  64. CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
  65. if ( g_pResourceAccessControl )
  66. {
  67. if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_PARTICLE_SYSTEM, pDef->GetName() ) )
  68. {
  69. // This is the error effect
  70. pDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
  71. }
  72. }
  73. Init( pDef );
  74. Construct();
  75. }
  76. static ConVar cl_aggregate_particles( "cl_aggregate_particles", "1" );
  77. CNewParticleEffect *CNewParticleEffect::CreateOrAggregate( CBaseEntity *pOwner, CParticleSystemDefinition *pDef,
  78. Vector const &vecAggregatePosition, const char *pDebugName,
  79. int nSplitScreenSlot )
  80. {
  81. if (!pDef) { return NULL; }
  82. CNewParticleEffect *pAggregateTarget = NULL;
  83. // see if we should aggregate
  84. bool bCanAggregate = ( pOwner == NULL ) && ( pDef->m_flAggregateRadius > 0.0 ) && ( cl_aggregate_particles.GetInt() != 0 );
  85. if ( bCanAggregate )
  86. {
  87. CParticleSystemDefinition *pDefFallback = pDef;
  88. do
  89. {
  90. float flAggregateDistSqr = ( pDefFallback->m_flAggregateRadius * pDefFallback->m_flAggregateRadius ) + 0.1;
  91. for( CParticleCollection *pSystem = pDefFallback->FirstCollection(); pSystem; pSystem = pSystem->GetNextCollectionUsingSameDef() )
  92. {
  93. CNewParticleEffect *pEffectCheck = static_cast<CNewParticleEffect *>( pSystem );
  94. if ( ! pEffectCheck->m_bDisableAggregation )
  95. {
  96. float flDistSQ = vecAggregatePosition.DistToSqr( pEffectCheck->m_vecAggregationCenter );
  97. if ( ( flDistSQ < flAggregateDistSqr ) &&
  98. ( pSystem->m_nMaxAllowedParticles - pSystem->m_nActiveParticles > pDefFallback->m_nAggregationMinAvailableParticles ) &&
  99. ( pEffectCheck->m_nSplitScreenUser == nSplitScreenSlot ) )
  100. {
  101. flAggregateDistSqr = flDistSQ;
  102. pAggregateTarget = pEffectCheck;
  103. }
  104. }
  105. }
  106. pDefFallback = pDefFallback->GetFallbackReplacementDefinition();
  107. } while ( pDefFallback );
  108. }
  109. if ( ! pAggregateTarget )
  110. {
  111. // we need a new one
  112. pAggregateTarget = new CNewParticleEffect( pOwner, pDef );
  113. pAggregateTarget->SetDrawOnlyForSplitScreenUser( nSplitScreenSlot );
  114. pAggregateTarget->SetDynamicallyAllocated( true );
  115. }
  116. else
  117. {
  118. // just reset the old one
  119. pAggregateTarget->Restart( RESTART_RESET_AND_MAKE_SURE_EMITS_HAPPEN );
  120. }
  121. if ( bCanAggregate )
  122. {
  123. pAggregateTarget->m_vecAggregationCenter = vecAggregatePosition;
  124. }
  125. pAggregateTarget->m_pDebugName = pDebugName;
  126. pAggregateTarget->m_bDisableAggregation = false;
  127. return pAggregateTarget;
  128. }
  129. void CNewParticleEffect::RemoveParticleEffect( int nPrecacheIndex )
  130. {
  131. CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
  132. if ( pDef == NULL )
  133. return;
  134. for( CParticleCollection *pSystem = pDef->FirstCollection(); pSystem; pSystem = pSystem->GetNextCollectionUsingSameDef() )
  135. {
  136. CNewParticleEffect *pEffectCheck = static_cast<CNewParticleEffect *>( pSystem );
  137. if ( pEffectCheck )
  138. {
  139. pEffectCheck->SetRemoveFlag();
  140. }
  141. }
  142. }
  143. CNewParticleEffect *CNewParticleEffect::CreateOrAggregate(
  144. CBaseEntity *pOwner, const char *pParticleSystemName,
  145. Vector const &vecAggregatePosition,
  146. const char *pDebugName,
  147. int nSplitScreenUser )
  148. {
  149. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName );
  150. if ( !pDef )
  151. {
  152. Warning( "Attempted to create unknown particle system type \"%s\"!\n", pParticleSystemName );
  153. pDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
  154. }
  155. return CreateOrAggregate( pOwner, pDef, vecAggregatePosition, pDebugName, nSplitScreenUser );
  156. }
  157. CNewParticleEffect *CNewParticleEffect::CreateOrAggregatePrecached(
  158. CBaseEntity *pOwner, int nPrecacheIndex,
  159. Vector const &vecAggregatePosition, const char *pDebugName,
  160. int nSplitScreenUser )
  161. {
  162. CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
  163. if ( !pDef )
  164. {
  165. Warning( "Attempted to create unknown particle system type \"%s\"!\n", GetParticleSystemNameFromIndex( nPrecacheIndex ) );
  166. pDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
  167. }
  168. return CreateOrAggregate( pOwner, pDef, vecAggregatePosition, pDebugName, nSplitScreenUser );
  169. }
  170. void CNewParticleEffect::Construct()
  171. {
  172. m_vSortOrigin.Init();
  173. m_bDontRemove = false;
  174. m_bRemove = false;
  175. m_bDrawn = false;
  176. m_bNeedsBBoxUpdate = false;
  177. m_bIsFirstFrame = true;
  178. m_bAutoUpdateBBox = false;
  179. m_bAllocated = true;
  180. m_bSimulate = true;
  181. m_bRecord = false;
  182. m_bShouldPerformCullCheck = false;
  183. m_bDisableAggregation = true; // will be reset when someone creates it via CreateOrAggregate
  184. m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
  185. m_RefCount = 0;
  186. ParticleMgr()->AddEffect( this );
  187. m_LastMax = Vector( -1.0e6, -1.0e6, -1.0e6 );
  188. m_LastMin = Vector( 1.0e6, 1.0e6, 1.0e6 );
  189. m_MinBounds = Vector( 1.0e6, 1.0e6, 1.0e6 );
  190. m_MaxBounds = Vector( -1.0e6, -1.0e6, -1.0e6 );
  191. m_pDebugName = NULL;
  192. m_nSplitScreenUser = -1;
  193. SetRenderable( this );
  194. RecordCreation();
  195. }
  196. CNewParticleEffect::~CNewParticleEffect(void)
  197. {
  198. if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  199. {
  200. static ParticleSystemDestroyedState_t state;
  201. state.m_nParticleSystemId = GetToolParticleEffectId();
  202. state.m_flTime = gpGlobals->curtime;
  203. KeyValues *msg = new KeyValues( "ParticleSystem_Destroy" );
  204. msg->SetPtr( "state", &state );
  205. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  206. m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
  207. }
  208. m_bAllocated = false;
  209. if ( m_hOwner )
  210. {
  211. // NOTE: This can provoke another NotifyRemove call which is why flags is set to 0
  212. m_hOwner->ParticleProp()->OnParticleSystemDeleted( this );
  213. }
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Refcounting
  217. //-----------------------------------------------------------------------------
  218. void CNewParticleEffect::AddRef()
  219. {
  220. ++m_RefCount;
  221. }
  222. void CNewParticleEffect::Release()
  223. {
  224. Assert( m_RefCount > 0 );
  225. --m_RefCount;
  226. // If all the particles are already gone, delete ourselves now.
  227. // If there are still particles, wait for the last NotifyDestroyParticle.
  228. if ( m_RefCount == 0 )
  229. {
  230. if ( m_bAllocated )
  231. {
  232. if ( IsFinished() )
  233. {
  234. SetRemoveFlag();
  235. }
  236. }
  237. }
  238. }
  239. void CNewParticleEffect::NotifyRemove()
  240. {
  241. if ( m_bAllocated )
  242. {
  243. delete this;
  244. }
  245. }
  246. int CNewParticleEffect::IsReleased()
  247. {
  248. return m_RefCount == 0;
  249. }
  250. void CNewParticleEffect::SetOwner( CBaseEntity *pOwner )
  251. {
  252. if ( m_hOwner.Get() != pOwner )
  253. {
  254. m_hOwner = pOwner;
  255. ClientLeafSystem()->RenderInFastReflections( RenderHandle(), pOwner ? pOwner->IsRenderingInFastReflections() : false );
  256. }
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Refraction and soft particle support
  260. //-----------------------------------------------------------------------------
  261. int CNewParticleEffect::GetRenderFlags( void )
  262. {
  263. int nRet = 0;
  264. // NOTE: Need to do this because CParticleCollection's version is non-virtual
  265. if ( CParticleCollection::UsesPowerOfTwoFrameBufferTexture( true ) )
  266. nRet |= ERENDERFLAGS_NEEDS_POWER_OF_TWO_FB | ERENDERFLAGS_REFRACT_ONLY_ONCE_PER_FRAME;
  267. if ( CParticleCollection::UsesFullFrameBufferTexture( true ) )
  268. nRet |= ERENDERFLAGS_NEEDS_FULL_FB;
  269. return nRet;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Overrides for recording
  273. //-----------------------------------------------------------------------------
  274. void CNewParticleEffect::StopEmission( bool bInfiniteOnly, bool bRemoveAllParticles, bool bWakeOnStop, bool bPlayEndCap )
  275. {
  276. if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  277. {
  278. KeyValues *msg = new KeyValues( "ParticleSystem_StopEmission" );
  279. static ParticleSystemStopEmissionState_t state;
  280. state.m_nParticleSystemId = GetToolParticleEffectId();
  281. state.m_flTime = gpGlobals->curtime;
  282. state.m_bInfiniteOnly = bInfiniteOnly;
  283. msg->SetPtr( "state", &state );
  284. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  285. }
  286. CParticleCollection::StopEmission( bInfiniteOnly, bRemoveAllParticles, bWakeOnStop, bPlayEndCap );
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. //-----------------------------------------------------------------------------
  291. void CNewParticleEffect::SetDormant( bool bDormant )
  292. {
  293. CParticleCollection::SetDormant( bDormant );
  294. }
  295. void CNewParticleEffect::SetControlPointEntity( int nWhichPoint, CBaseEntity *pEntity )
  296. {
  297. if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  298. {
  299. static ParticleSystemSetControlPointObjectState_t state;
  300. state.m_nParticleSystemId = GetToolParticleEffectId();
  301. state.m_flTime = gpGlobals->curtime;
  302. state.m_nControlPoint = nWhichPoint;
  303. state.m_nObject = pEntity ? pEntity->entindex() : -1;
  304. KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointObject" );
  305. msg->SetPtr( "state", &state );
  306. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  307. }
  308. if ( pEntity )
  309. {
  310. CParticleCollection::SetControlPointObject( nWhichPoint, &m_hControlPointOwners[ nWhichPoint ] );
  311. m_hControlPointOwners[ nWhichPoint ] = pEntity;
  312. }
  313. else
  314. CParticleCollection::SetControlPointObject( nWhichPoint, NULL );
  315. }
  316. void CNewParticleEffect::SetControlPoint( int nWhichPoint, const Vector &v )
  317. {
  318. if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  319. {
  320. static ParticleSystemSetControlPointPositionState_t state;
  321. state.m_nParticleSystemId = GetToolParticleEffectId();
  322. state.m_flTime = gpGlobals->curtime;
  323. state.m_nControlPoint = nWhichPoint;
  324. state.m_vecPosition = v;
  325. KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointPosition" );
  326. msg->SetPtr( "state", &state );
  327. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  328. }
  329. CParticleCollection::SetControlPoint( nWhichPoint, v );
  330. }
  331. void CNewParticleEffect::SetToolRecording( bool bRecord )
  332. {
  333. if ( bRecord == m_bRecord )
  334. return;
  335. m_bRecord = bRecord;
  336. if ( m_bRecord )
  337. {
  338. RecordCreation();
  339. }
  340. }
  341. void CNewParticleEffect::RecordCreation()
  342. {
  343. if ( IsValid() && clienttools->IsInRecordingMode() )
  344. {
  345. m_bRecord = true;
  346. int nId = AllocateToolParticleEffectId();
  347. static ParticleSystemCreatedState_t state;
  348. state.m_nParticleSystemId = nId;
  349. state.m_flTime = gpGlobals->curtime;
  350. state.m_pName = m_pDef->GetName();
  351. state.m_nOwner = m_hOwner ? m_hOwner->entindex() : -1;
  352. KeyValues *msg = new KeyValues( "ParticleSystem_Create" );
  353. msg->SetPtr( "state", &state );
  354. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  355. }
  356. }
  357. void CNewParticleEffect::RecordControlPointOrientation( int nWhichPoint )
  358. {
  359. if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  360. {
  361. // FIXME: Make a more direct way of getting
  362. QAngle angles;
  363. VectorAngles( ControlPoint( nWhichPoint ).m_ForwardVector, ControlPoint( nWhichPoint ).m_UpVector, angles );
  364. static ParticleSystemSetControlPointOrientationState_t state;
  365. state.m_nParticleSystemId = GetToolParticleEffectId();
  366. state.m_flTime = gpGlobals->curtime;
  367. state.m_nControlPoint = nWhichPoint;
  368. AngleQuaternion( angles, state.m_qOrientation );
  369. KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointOrientation" );
  370. msg->SetPtr( "state", &state );
  371. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  372. }
  373. }
  374. void CNewParticleEffect::SetControlPointOrientation( int nWhichPoint,
  375. const Vector &forward, const Vector &right, const Vector &up )
  376. {
  377. CParticleCollection::SetControlPointOrientation( nWhichPoint, forward, right, up );
  378. RecordControlPointOrientation( nWhichPoint );
  379. }
  380. void CNewParticleEffect::SetControlPointOrientation( int nWhichPoint, const Quaternion &q )
  381. {
  382. CParticleCollection::SetControlPointOrientation( nWhichPoint, q );
  383. RecordControlPointOrientation( nWhichPoint );
  384. }
  385. void CNewParticleEffect::SetControlPointForwardVector( int nWhichPoint, const Vector &v )
  386. {
  387. CParticleCollection::SetControlPointForwardVector( nWhichPoint, v );
  388. RecordControlPointOrientation( nWhichPoint );
  389. }
  390. void CNewParticleEffect::SetControlPointUpVector( int nWhichPoint, const Vector &v )
  391. {
  392. CParticleCollection::SetControlPointUpVector( nWhichPoint, v );
  393. RecordControlPointOrientation( nWhichPoint );
  394. }
  395. void CNewParticleEffect::SetControlPointRightVector( int nWhichPoint, const Vector &v )
  396. {
  397. CParticleCollection::SetControlPointRightVector( nWhichPoint, v );
  398. RecordControlPointOrientation( nWhichPoint );
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Called when the particle effect is about to update
  402. //-----------------------------------------------------------------------------
  403. void CNewParticleEffect::Update( float flTimeDelta )
  404. {
  405. if ( m_hOwner )
  406. {
  407. m_hOwner->ParticleProp()->OnParticleSystemUpdated( this, flTimeDelta );
  408. }
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Bounding box
  412. //-----------------------------------------------------------------------------
  413. CNewParticleEffect* CNewParticleEffect::ReplaceWith( const char *pParticleSystemName )
  414. {
  415. StopEmission( false, true, true );
  416. if ( !pParticleSystemName || !pParticleSystemName[0] )
  417. return NULL;
  418. CNewParticleEffect *pNewEffect = CNewParticleEffect::Create( GetOwner(), pParticleSystemName, pParticleSystemName );
  419. if ( !pNewEffect )
  420. return NULL;
  421. // Copy over the control point data
  422. for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
  423. {
  424. if ( !ReadsControlPoint( i ) )
  425. continue;
  426. Vector vecForward, vecRight, vecUp;
  427. pNewEffect->SetControlPoint( i, GetControlPointAtCurrentTime( i ) );
  428. GetControlPointOrientationAtCurrentTime( i, &vecForward, &vecRight, &vecUp );
  429. pNewEffect->SetControlPointOrientation( i, vecForward, vecRight, vecUp );
  430. pNewEffect->SetControlPointParent( i, GetControlPointParent( i ) );
  431. }
  432. if ( m_hOwner )
  433. {
  434. m_hOwner->ParticleProp()->ReplaceParticleEffect( this, pNewEffect );
  435. }
  436. // fixup any other references to the old system, to point to the new system
  437. while( m_References.m_pHead )
  438. {
  439. // this will remove the reference from m_References
  440. m_References.m_pHead->Set( pNewEffect );
  441. }
  442. // At this point any references should have been redirected,
  443. // but we may still be running with some stray particles, so we
  444. // might not be flagged for removal - force the issue!
  445. Assert( m_RefCount == 0 );
  446. SetRemoveFlag();
  447. return pNewEffect;
  448. }
  449. //-----------------------------------------------------------------------------
  450. // Bounding box
  451. //-----------------------------------------------------------------------------
  452. void CNewParticleEffect::SetParticleCullRadius( float radius )
  453. {
  454. }
  455. bool CNewParticleEffect::RecalculateBoundingBox()
  456. {
  457. BloatBoundsUsingControlPoint();
  458. if ( !m_bBoundsValid )
  459. {
  460. m_MaxBounds = m_MinBounds = GetRenderOrigin();
  461. return false;
  462. }
  463. return true;
  464. }
  465. void CNewParticleEffect::GetRenderBounds( Vector& mins, Vector& maxs )
  466. {
  467. if ( !m_bBoundsValid )
  468. {
  469. mins = vec3_origin;
  470. maxs = mins;
  471. return;
  472. }
  473. VectorSubtract( m_MinBounds, GetRenderOrigin(), mins );
  474. VectorSubtract( m_MaxBounds, GetRenderOrigin(), maxs );
  475. }
  476. void CNewParticleEffect::DetectChanges()
  477. {
  478. // if we have no render handle, return
  479. if ( m_hRenderHandle == INVALID_CLIENT_RENDER_HANDLE )
  480. return;
  481. // Turn off rendering if the bounds aren't valid
  482. g_pClientLeafSystem->EnableRendering( m_hRenderHandle, m_bBoundsValid );
  483. if ( !m_bBoundsValid )
  484. {
  485. m_LastMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  486. m_LastMin.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  487. return;
  488. }
  489. if ( m_MinBounds != m_LastMin || m_MaxBounds != m_LastMax )
  490. {
  491. // call leafsystem to update this guy
  492. ClientLeafSystem()->RenderableChanged( m_hRenderHandle );
  493. // remember last parameters
  494. m_LastMin = m_MinBounds;
  495. m_LastMax = m_MaxBounds;
  496. }
  497. }
  498. extern ConVar r_DrawParticles;
  499. //-----------------------------------------------------------------------------
  500. // Rendering
  501. //-----------------------------------------------------------------------------
  502. int CNewParticleEffect::DrawModel( int flags, const RenderableInstance_t &instance )
  503. {
  504. VPROF_BUDGET( "CNewParticleEffect::DrawModel", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  505. if ( r_DrawParticles.GetBool() == false )
  506. return 0;
  507. if ( !GetClientMode()->ShouldDrawParticles() || !ParticleMgr()->ShouldRenderParticleSystems() )
  508. return 0;
  509. if ( flags & ( STUDIO_SHADOWDEPTHTEXTURE | STUDIO_SSAODEPTHTEXTURE ) )
  510. return 0;
  511. if ( m_hOwner && m_hOwner->IsDormant() )
  512. return 0;
  513. int nViewRecursionLevel = 0;
  514. #ifdef PORTAL
  515. nViewRecursionLevel = g_pPortalRender->GetViewRecursionLevel();
  516. if ( m_pDef->GetMaxRecursionDepth() < nViewRecursionLevel )
  517. {
  518. //DevMsg( "---Aborted at Particle Portal Recursion Level : %d - Max : %d\n", g_pPortalRender->GetViewRecursionLevel(), m_pDef->GetMaxRecursionDepth() );
  519. return 0;
  520. }
  521. #endif
  522. // do distance cull check here. We do it here instead of in particles so we can easily only do
  523. // it for root objects, not bothering to cull children individually
  524. CMatRenderContextPtr pRenderContext( materials );
  525. if ( !m_pDef->IsScreenSpaceEffect() && !m_pDef->IsViewModelEffect() )
  526. {
  527. Vector vecCamera;
  528. pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
  529. if ( ( CalcSqrDistanceToAABB( m_MinBounds, m_MaxBounds, vecCamera ) > ( m_pDef->m_flMaxDrawDistance * m_pDef->m_flMaxDrawDistance ) ) )
  530. return 0;
  531. }
  532. if ( m_pDef->IsViewModelEffect() )
  533. {
  534. C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
  535. if ( !pPlayer || !pPlayer->IsAlive() )
  536. return 0;
  537. C_BaseAnimating *pRenderedWeaponModel = pPlayer->GetRenderedWeaponModel();
  538. if ( !pRenderedWeaponModel || !pRenderedWeaponModel->IsViewModel() )
  539. return 0;
  540. }
  541. if ( ( flags & STUDIO_TRANSPARENCY ) || !IsBatchable() || !m_pDef->IsDrawnThroughLeafSystem() )
  542. {
  543. C_BaseEntity *pCameraObject = GetSplitScreenViewPlayer();
  544. if( ((C_BasePlayer *)pCameraObject)->GetViewEntity() )
  545. {
  546. pCameraObject = ((C_BasePlayer *)pCameraObject)->GetViewEntity();
  547. }
  548. C_BaseEntity *pSkipRenderObject = ( m_pDef->m_nSkipRenderControlPoint != -1 ) ? GetControlPointEntity( m_pDef->m_nSkipRenderControlPoint ).Get() : nullptr;
  549. C_BaseEntity *pAllowRenderObject = ( m_pDef->m_nAllowRenderControlPoint != -1 ) ? GetControlPointEntity( m_pDef->m_nAllowRenderControlPoint ).Get() : nullptr;
  550. if ( pSkipRenderObject && ( pSkipRenderObject->IsBaseCombatWeapon() || ( pSkipRenderObject->GetBaseAnimating() && pSkipRenderObject->GetBaseAnimating()->IsViewModel() ) ) )
  551. pSkipRenderObject = pSkipRenderObject->GetOwnerEntity();
  552. if ( pAllowRenderObject && ( pAllowRenderObject->IsBaseCombatWeapon() || ( pAllowRenderObject->GetBaseAnimating() && pAllowRenderObject->GetBaseAnimating()->IsViewModel() ) ) )
  553. pAllowRenderObject = pAllowRenderObject->GetOwnerEntity();
  554. // apply logic that lets you skip rendering a system if the camera is attached to its entity
  555. if ( pCameraObject )
  556. {
  557. if ( pCameraObject == pSkipRenderObject )
  558. {
  559. #if defined( PORTAL )
  560. if ( pSkipRenderObject->IsPlayer() )
  561. {
  562. if ( ((C_Portal_Player *)pSkipRenderObject)->ShouldSkipRenderingViewpointPlayerForThisView() )
  563. return 0;
  564. }
  565. else
  566. #endif
  567. {
  568. if ( nViewRecursionLevel == 0 )
  569. return 0;
  570. }
  571. }
  572. if ( pAllowRenderObject && (pCameraObject != pAllowRenderObject) )
  573. return 0;
  574. }
  575. Vector4D vecDiffuseModulation( 1.0f, 1.0f, 1.0f, 1.0f ); //instance.m_nAlpha / 255.0f );
  576. pRenderContext->MatrixMode( MATERIAL_MODEL );
  577. pRenderContext->PushMatrix();
  578. pRenderContext->LoadIdentity();
  579. Render( nViewRecursionLevel, pRenderContext, vecDiffuseModulation, ( flags & STUDIO_TRANSPARENCY ) ? IsTwoPass() : false, pCameraObject );
  580. pRenderContext->MatrixMode( MATERIAL_MODEL );
  581. pRenderContext->PopMatrix();
  582. }
  583. else
  584. {
  585. g_pParticleSystemMgr->AddToRenderCache( this );
  586. }
  587. if ( cl_particles_show_bbox.GetBool() )
  588. {
  589. Vector center = GetRenderOrigin();
  590. Vector mins = m_MinBounds - center;
  591. Vector maxs = m_MaxBounds - center;
  592. int r, g;
  593. if ( GetAutoUpdateBBox() )
  594. {
  595. // red is bad, the bbox update is costly
  596. r = 255;
  597. g = 0;
  598. }
  599. else
  600. {
  601. // green, this effect presents less cpu load
  602. r = 0;
  603. g = 255;
  604. }
  605. Vector vecCurCPPos = GetControlPointAtCurrentTime( 0 );
  606. debugoverlay->AddBoxOverlay( center, mins, maxs, QAngle( 0, 0, 0 ), r, g, 0, 16, 0 );
  607. debugoverlay->AddTextOverlayRGB( center, 0, 0, r, g, 0, 64, "%s:(%d)", GetEffectName(),
  608. m_nActiveParticles );
  609. }
  610. if ( cl_particles_show_controlpoints.GetBool() )
  611. {
  612. for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
  613. {
  614. if ( ReadsControlPoint( i ) )
  615. {
  616. Vector vecPos = GetControlPointAtCurrentTime( i );
  617. Vector vecForward, vecRight, vecUp;
  618. GetControlPointOrientationAtCurrentTime( i, &vecForward, &vecRight, &vecUp );
  619. NDebugOverlay::Line( vecPos, vecPos + ( vecForward * 4.0f ), 255, 0, 0, true, 0.05f );
  620. NDebugOverlay::Line( vecPos, vecPos + ( vecRight * 4.0f ), 0, 255, 0, true, 0.05f );
  621. NDebugOverlay::Line( vecPos, vecPos + ( vecUp * 4.0f ), 0, 0, 255, true, 0.05f );
  622. }
  623. }
  624. }
  625. return 1;
  626. }
  627. void CNewParticleEffect::SetDrawOnlyForSplitScreenUser( int nSlot )
  628. {
  629. if ( nSlot != m_nSplitScreenUser )
  630. {
  631. m_nSplitScreenUser = nSlot;
  632. if ( IsSplitScreenSupported() && ( m_hRenderHandle != INVALID_CLIENT_RENDER_HANDLE ) )
  633. {
  634. g_pClientLeafSystem->EnableSplitscreenRendering( m_hRenderHandle, ComputeSplitscreenRenderingFlags( this ) );
  635. }
  636. }
  637. }
  638. bool CNewParticleEffect::ShouldDrawForSplitScreenUser( int nSlot )
  639. {
  640. if ( m_nSplitScreenUser == -1 )
  641. return true;
  642. return m_nSplitScreenUser == nSlot;
  643. }
  644. bool CNewParticleEffect::SetupBones( matrix3x4a_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
  645. {
  646. matrix3x4a_t mat;
  647. mat.Init( Vector( 0, -1, 0), Vector( 1, 0, 0), Vector( 0, 0, 1 ), vec3_origin );
  648. MatrixMultiply( m_DrawModelMatrix, mat, pBoneToWorldOut[0] );
  649. return true;
  650. }
  651. static void DumpParticleStats_f( void )
  652. {
  653. g_pParticleSystemMgr->DumpProfileInformation();
  654. }
  655. static ConCommand cl_dump_particle_stats( "cl_dump_particle_stats", DumpParticleStats_f, "dump particle profiling info to particle_profile.csv") ;
  656. CON_COMMAND( cl_particles_dumplist, "Dump all new particles, optional name substring." )
  657. {
  658. if ( args.ArgC() == 2 )
  659. {
  660. g_pParticleSystemMgr->DumpParticleList( args.Arg(1) );
  661. }
  662. else
  663. {
  664. g_pParticleSystemMgr->DumpParticleList( NULL );
  665. }
  666. }