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

609 lines
17 KiB

  1. //========= Copyright 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 "input.h"
  19. extern ConVar cl_particleeffect_aabb_buffer;
  20. //extern ConVar cl_particle_show_bbox;
  21. //extern ConVar cl_particle_show_bbox_cost;
  22. extern bool g_cl_particle_show_bbox;
  23. extern int g_cl_particle_show_bbox_cost;
  24. //-----------------------------------------------------------------------------
  25. // Constructor, destructor
  26. //-----------------------------------------------------------------------------
  27. CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, CParticleSystemDefinition *pEffect )
  28. {
  29. m_hOwner = pOwner;
  30. Init( pEffect );
  31. Construct();
  32. }
  33. CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, const char* pEffectName )
  34. {
  35. m_hOwner = pOwner;
  36. Init( pEffectName );
  37. Construct();
  38. }
  39. void CNewParticleEffect::Construct()
  40. {
  41. m_vSortOrigin.Init();
  42. m_bDontRemove = false;
  43. m_bRemove = false;
  44. m_bDrawn = false;
  45. m_bNeedsBBoxUpdate = false;
  46. m_bIsFirstFrame = true;
  47. m_bAutoUpdateBBox = false;
  48. m_bAllocated = true;
  49. m_bSimulate = true;
  50. m_bShouldPerformCullCheck = false;
  51. m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
  52. m_RefCount = 0;
  53. ParticleMgr()->AddEffect( this );
  54. m_LastMax = Vector( -1.0e6, -1.0e6, -1.0e6 );
  55. m_LastMin = Vector( 1.0e6, 1.0e6, 1.0e6 );
  56. m_MinBounds = Vector( 1.0e6, 1.0e6, 1.0e6 );
  57. m_MaxBounds = Vector( -1.0e6, -1.0e6, -1.0e6 );
  58. m_pDebugName = NULL;
  59. m_bViewModelEffect = m_pDef ? m_pDef->IsViewModelEffect() : false;
  60. if ( IsValid() && clienttools->IsInRecordingMode() )
  61. {
  62. int nId = AllocateToolParticleEffectId();
  63. static ParticleSystemCreatedState_t state;
  64. state.m_nParticleSystemId = nId;
  65. state.m_flTime = gpGlobals->curtime;
  66. state.m_pName = GetName();
  67. state.m_nOwner = m_hOwner.Get() ? m_hOwner->entindex() : -1;
  68. KeyValues *msg = new KeyValues( "ParticleSystem_Create" );
  69. msg->SetPtr( "state", &state );
  70. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  71. }
  72. }
  73. CNewParticleEffect::~CNewParticleEffect(void)
  74. {
  75. if ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  76. {
  77. static ParticleSystemDestroyedState_t state;
  78. state.m_nParticleSystemId = gpGlobals->curtime;
  79. state.m_flTime = gpGlobals->curtime;
  80. KeyValues *msg = new KeyValues( "ParticleSystem_Destroy" );
  81. msg->SetPtr( "state", &state );
  82. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  83. m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
  84. }
  85. m_bAllocated = false;
  86. if ( m_hOwner )
  87. {
  88. // NOTE: This can provoke another NotifyRemove call which is why flags is set to 0
  89. m_hOwner->ParticleProp()->OnParticleSystemDeleted( this );
  90. }
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Refcounting
  94. //-----------------------------------------------------------------------------
  95. void CNewParticleEffect::AddRef()
  96. {
  97. ++m_RefCount;
  98. }
  99. void CNewParticleEffect::Release()
  100. {
  101. Assert( m_RefCount > 0 );
  102. --m_RefCount;
  103. // If all the particles are already gone, delete ourselves now.
  104. // If there are still particles, wait for the last NotifyDestroyParticle.
  105. if ( m_RefCount == 0 )
  106. {
  107. if ( m_bAllocated )
  108. {
  109. if ( IsFinished() )
  110. {
  111. SetRemoveFlag();
  112. }
  113. }
  114. }
  115. }
  116. void CNewParticleEffect::NotifyRemove()
  117. {
  118. if ( m_bAllocated )
  119. {
  120. delete this;
  121. }
  122. }
  123. int CNewParticleEffect::IsReleased()
  124. {
  125. return m_RefCount == 0;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Refraction and soft particle support
  129. //-----------------------------------------------------------------------------
  130. bool CNewParticleEffect::UsesPowerOfTwoFrameBufferTexture( void )
  131. {
  132. // NOTE: Need to do this because CParticleCollection's version is non-virtual
  133. return CParticleCollection::UsesPowerOfTwoFrameBufferTexture( true );
  134. }
  135. bool CNewParticleEffect::UsesFullFrameBufferTexture( void )
  136. {
  137. // NOTE: Need to do this because CParticleCollection's version is non-virtual
  138. return CParticleCollection::UsesFullFrameBufferTexture( true );
  139. }
  140. bool CNewParticleEffect::IsTwoPass( void )
  141. {
  142. // NOTE: Need to do this because CParticleCollection's version is non-virtual
  143. return CParticleCollection::IsTwoPass();
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Overrides for recording
  147. //-----------------------------------------------------------------------------
  148. void CNewParticleEffect::StopEmission( bool bInfiniteOnly, bool bRemoveAllParticles, bool bWakeOnStop )
  149. {
  150. if ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  151. {
  152. KeyValues *msg = new KeyValues( "ParticleSystem_StopEmission" );
  153. static ParticleSystemStopEmissionState_t state;
  154. state.m_nParticleSystemId = GetToolParticleEffectId();
  155. state.m_flTime = gpGlobals->curtime;
  156. state.m_bInfiniteOnly = bInfiniteOnly;
  157. msg->SetPtr( "state", &state );
  158. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  159. }
  160. CParticleCollection::StopEmission( bInfiniteOnly, bRemoveAllParticles, bWakeOnStop );
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose:
  164. //-----------------------------------------------------------------------------
  165. void CNewParticleEffect::SetDormant( bool bDormant )
  166. {
  167. CParticleCollection::SetDormant( bDormant );
  168. }
  169. void CNewParticleEffect::SetControlPointEntity( int nWhichPoint, CBaseEntity *pEntity )
  170. {
  171. if ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  172. {
  173. static ParticleSystemSetControlPointObjectState_t state;
  174. state.m_nParticleSystemId = GetToolParticleEffectId();
  175. state.m_flTime = gpGlobals->curtime;
  176. state.m_nControlPoint = nWhichPoint;
  177. state.m_nObject = pEntity ? pEntity->entindex() : -1;
  178. KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointObject" );
  179. msg->SetPtr( "state", &state );
  180. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  181. }
  182. if ( pEntity )
  183. {
  184. CParticleCollection::SetControlPointObject( nWhichPoint, &m_hControlPointOwners[ nWhichPoint ] );
  185. m_hControlPointOwners[ nWhichPoint ] = pEntity;
  186. }
  187. else
  188. CParticleCollection::SetControlPointObject( nWhichPoint, NULL );
  189. }
  190. void CNewParticleEffect::SetControlPoint( int nWhichPoint, const Vector &v )
  191. {
  192. if ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  193. {
  194. static ParticleSystemSetControlPointPositionState_t state;
  195. state.m_nParticleSystemId = GetToolParticleEffectId();
  196. state.m_flTime = gpGlobals->curtime;
  197. state.m_nControlPoint = nWhichPoint;
  198. state.m_vecPosition = v;
  199. KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointPosition" );
  200. msg->SetPtr( "state", &state );
  201. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  202. }
  203. CParticleCollection::SetControlPoint( nWhichPoint, v );
  204. }
  205. void CNewParticleEffect::RecordControlPointOrientation( int nWhichPoint )
  206. {
  207. if ( m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
  208. {
  209. // FIXME: Make a more direct way of getting
  210. QAngle angles;
  211. VectorAngles( m_ControlPoints[nWhichPoint].m_ForwardVector, m_ControlPoints[nWhichPoint].m_UpVector, angles );
  212. static ParticleSystemSetControlPointOrientationState_t state;
  213. state.m_nParticleSystemId = GetToolParticleEffectId();
  214. state.m_flTime = gpGlobals->curtime;
  215. state.m_nControlPoint = nWhichPoint;
  216. AngleQuaternion( angles, state.m_qOrientation );
  217. KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointOrientation" );
  218. msg->SetPtr( "state", &state );
  219. ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
  220. }
  221. }
  222. void CNewParticleEffect::SetControlPointOrientation( int nWhichPoint,
  223. const Vector &forward, const Vector &right, const Vector &up )
  224. {
  225. CParticleCollection::SetControlPointOrientation( nWhichPoint, forward, right, up );
  226. RecordControlPointOrientation( nWhichPoint );
  227. }
  228. void CNewParticleEffect::SetControlPointOrientation( int nWhichPoint, const Quaternion &q )
  229. {
  230. CParticleCollection::SetControlPointOrientation( nWhichPoint, q );
  231. RecordControlPointOrientation( nWhichPoint );
  232. }
  233. void CNewParticleEffect::SetControlPointForwardVector( int nWhichPoint, const Vector &v )
  234. {
  235. CParticleCollection::SetControlPointForwardVector( nWhichPoint, v );
  236. RecordControlPointOrientation( nWhichPoint );
  237. }
  238. void CNewParticleEffect::SetControlPointUpVector( int nWhichPoint, const Vector &v )
  239. {
  240. CParticleCollection::SetControlPointUpVector( nWhichPoint, v );
  241. RecordControlPointOrientation( nWhichPoint );
  242. }
  243. void CNewParticleEffect::SetControlPointRightVector( int nWhichPoint, const Vector &v )
  244. {
  245. CParticleCollection::SetControlPointRightVector( nWhichPoint, v );
  246. RecordControlPointOrientation( nWhichPoint );
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Called when the particle effect is about to update
  250. //-----------------------------------------------------------------------------
  251. void CNewParticleEffect::Update( float flTimeDelta )
  252. {
  253. if ( m_hOwner )
  254. {
  255. m_hOwner->ParticleProp()->OnParticleSystemUpdated( this, flTimeDelta );
  256. }
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Bounding box
  260. //-----------------------------------------------------------------------------
  261. CNewParticleEffect* CNewParticleEffect::ReplaceWith( const char *pParticleSystemName )
  262. {
  263. StopEmission( false, true, true );
  264. if ( !pParticleSystemName || !pParticleSystemName[0] )
  265. return NULL;
  266. CSmartPtr< CNewParticleEffect > pNewEffect = CNewParticleEffect::Create( GetOwner(), pParticleSystemName, pParticleSystemName );
  267. if ( !pNewEffect->IsValid() )
  268. return pNewEffect.GetObject();
  269. // Copy over the control point data
  270. for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
  271. {
  272. if ( !ReadsControlPoint( i ) )
  273. continue;
  274. Vector vecForward, vecRight, vecUp;
  275. pNewEffect->SetControlPoint( i, GetControlPointAtCurrentTime( i ) );
  276. GetControlPointOrientationAtCurrentTime( i, &vecForward, &vecRight, &vecUp );
  277. pNewEffect->SetControlPointOrientation( i, vecForward, vecRight, vecUp );
  278. pNewEffect->SetControlPointParent( i, GetControlPointParent( i ) );
  279. }
  280. if ( !m_hOwner )
  281. return pNewEffect.GetObject();
  282. m_hOwner->ParticleProp()->ReplaceParticleEffect( this, pNewEffect.GetObject() );
  283. return pNewEffect.GetObject();
  284. }
  285. //-----------------------------------------------------------------------------
  286. // Bounding box
  287. //-----------------------------------------------------------------------------
  288. void CNewParticleEffect::SetParticleCullRadius( float radius )
  289. {
  290. }
  291. bool CNewParticleEffect::RecalculateBoundingBox()
  292. {
  293. BloatBoundsUsingControlPoint();
  294. if ( m_MaxBounds.x < m_MinBounds.x )
  295. {
  296. m_MaxBounds = m_MinBounds = GetSortOrigin();
  297. return false;
  298. }
  299. return true;
  300. }
  301. void CNewParticleEffect::GetRenderBounds( Vector& mins, Vector& maxs )
  302. {
  303. VectorSubtract( m_MinBounds, GetRenderOrigin(), mins );
  304. VectorSubtract( m_MaxBounds, GetRenderOrigin(), maxs );
  305. }
  306. void CNewParticleEffect::DetectChanges()
  307. {
  308. // if we have no render handle, return
  309. if ( m_hRenderHandle == INVALID_CLIENT_RENDER_HANDLE )
  310. return;
  311. float flBuffer = cl_particleeffect_aabb_buffer.GetFloat();
  312. float flExtraBuffer = flBuffer * 1.3f;
  313. // if nothing changed, return
  314. if ( m_MinBounds.x < m_LastMin.x ||
  315. m_MinBounds.y < m_LastMin.y ||
  316. m_MinBounds.z < m_LastMin.z ||
  317. m_MinBounds.x > (m_LastMin.x + flExtraBuffer) ||
  318. m_MinBounds.y > (m_LastMin.y + flExtraBuffer) ||
  319. m_MinBounds.z > (m_LastMin.z + flExtraBuffer) ||
  320. m_MaxBounds.x > m_LastMax.x ||
  321. m_MaxBounds.y > m_LastMax.y ||
  322. m_MaxBounds.z > m_LastMax.z ||
  323. m_MaxBounds.x < (m_LastMax.x - flExtraBuffer) ||
  324. m_MaxBounds.y < (m_LastMax.y - flExtraBuffer) ||
  325. m_MaxBounds.z < (m_LastMax.z - flExtraBuffer)
  326. )
  327. {
  328. // call leafsystem to updated this guy
  329. ClientLeafSystem()->RenderableChanged( m_hRenderHandle );
  330. // remember last parameters
  331. // Add some padding in here so we don't reinsert it into the leaf system if it just changes a tiny amount.
  332. m_LastMin = m_MinBounds - Vector( flBuffer, flBuffer, flBuffer );
  333. m_LastMax = m_MaxBounds + Vector( flBuffer, flBuffer, flBuffer );
  334. }
  335. }
  336. extern ConVar r_DrawParticles;
  337. void CNewParticleEffect::DebugDrawBbox ( bool bCulled )
  338. {
  339. int nParticlesShowBboxCost = g_cl_particle_show_bbox_cost;
  340. bool bShowCheapSystems = false;
  341. if ( nParticlesShowBboxCost < 0 )
  342. {
  343. nParticlesShowBboxCost = -nParticlesShowBboxCost;
  344. bShowCheapSystems = true;
  345. }
  346. Vector center = GetRenderOrigin();
  347. Vector mins = m_MinBounds - center;
  348. Vector maxs = m_MaxBounds - center;
  349. int r, g, b;
  350. bool bDraw = true;
  351. if ( bCulled )
  352. {
  353. r = 64;
  354. g = 64;
  355. b = 64;
  356. }
  357. else if ( nParticlesShowBboxCost > 0 )
  358. {
  359. float fAmount = (float)m_nActiveParticles / (float)nParticlesShowBboxCost;
  360. if ( fAmount < 0.5f )
  361. {
  362. if ( bShowCheapSystems )
  363. {
  364. r = 0;
  365. g = 255;
  366. b = 0;
  367. }
  368. else
  369. {
  370. // Prevent the screen getting spammed with low-count particles which aren't that expensive.
  371. bDraw = false;
  372. r = 0;
  373. g = 0;
  374. b = 0;
  375. }
  376. }
  377. else if ( fAmount < 1.0f )
  378. {
  379. // green 0.5-1.0 blue
  380. int nBlend = (int)( 512.0f * ( fAmount - 0.5f ) );
  381. nBlend = MIN ( 255, MAX ( 0, nBlend ) );
  382. r = 0;
  383. g = 255 - nBlend;
  384. b = nBlend;
  385. }
  386. else if ( fAmount < 2.0f )
  387. {
  388. // blue 1.0-2.0 red
  389. int nBlend = (int)( 256.0f * ( fAmount - 1.0f ) );
  390. nBlend = MIN ( 255, MAX ( 0, nBlend ) );
  391. r = nBlend;
  392. g = 0;
  393. b = 255 - nBlend;
  394. }
  395. else
  396. {
  397. r = 255;
  398. g = 0;
  399. b = 0;
  400. }
  401. }
  402. else
  403. {
  404. if ( GetAutoUpdateBBox() )
  405. {
  406. // red is bad, the bbox update is costly
  407. r = 255;
  408. g = 0;
  409. b = 0;
  410. }
  411. else
  412. {
  413. // green, this effect presents less cpu load
  414. r = 0;
  415. g = 255;
  416. b = 0;
  417. }
  418. }
  419. if ( bDraw )
  420. {
  421. if ( debugoverlay )
  422. {
  423. debugoverlay->AddBoxOverlay( center, mins, maxs, QAngle( 0, 0, 0 ), r, g, b, 16, 0 );
  424. debugoverlay->AddTextOverlayRGB( center, 0, 0, r, g, b, 64, "%s:(%d)", GetEffectName(), m_nActiveParticles );
  425. }
  426. }
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Rendering
  430. //-----------------------------------------------------------------------------
  431. int CNewParticleEffect::DrawModel( int flags )
  432. {
  433. VPROF_BUDGET( "CNewParticleEffect::DrawModel", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  434. if ( r_DrawParticles.GetBool() == false )
  435. return 0;
  436. if ( !g_pClientMode->ShouldDrawParticles() || !ParticleMgr()->ShouldRenderParticleSystems() )
  437. return 0;
  438. if ( ( flags & ( STUDIO_SHADOWDEPTHTEXTURE | STUDIO_SSAODEPTHTEXTURE ) ) != 0 )
  439. {
  440. return 0;
  441. }
  442. // do distance cull check here. We do it here instead of in particles so we can easily only do
  443. // it for root objects, not bothering to cull children individually
  444. CMatRenderContextPtr pRenderContext( materials );
  445. Vector vecCamera;
  446. pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
  447. if ( CalcSqrDistanceToAABB( m_MinBounds, m_MaxBounds, vecCamera ) > ( m_pDef->m_flMaxDrawDistance * m_pDef->m_flMaxDrawDistance ) )
  448. {
  449. if ( !IsRetail() && ( g_cl_particle_show_bbox || ( g_cl_particle_show_bbox_cost != 0 ) ) )
  450. {
  451. DebugDrawBbox ( true );
  452. }
  453. // Still need to make sure we set this or they won't follow their attachemnt points.
  454. m_flNextSleepTime = Max ( m_flNextSleepTime, ( g_pParticleSystemMgr->GetLastSimulationTime() + m_pDef->m_flNoDrawTimeToGoToSleep ));
  455. return 0;
  456. }
  457. if ( flags & STUDIO_TRANSPARENCY )
  458. {
  459. int viewentity = render->GetViewEntity();
  460. C_BaseEntity *pCameraObject = cl_entitylist->GetEnt( viewentity );
  461. // apply logic that lets you skip rendering a system if the camera is attached to its entity
  462. if ( pCameraObject &&
  463. ( m_pDef->m_nSkipRenderControlPoint != -1 ) &&
  464. ( m_pDef->m_nSkipRenderControlPoint <= m_nHighestCP ) )
  465. {
  466. C_BaseEntity *pEntity = (EHANDLE)GetControlPointEntity( m_pDef->m_nSkipRenderControlPoint );
  467. if ( pEntity )
  468. {
  469. // If we're in thirdperson, we still see it
  470. if ( !input->CAM_IsThirdPerson() )
  471. {
  472. if ( pEntity == pCameraObject )
  473. return 0;
  474. C_BaseEntity *pRootMove = pEntity->GetRootMoveParent();
  475. if ( pRootMove == pCameraObject )
  476. return 0;
  477. // If we're spectating in-eyes of the camera object, we don't see it
  478. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  479. if ( pPlayer == pCameraObject )
  480. {
  481. C_BaseEntity *pObTarget = pPlayer->GetObserverTarget();
  482. if ( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && (pObTarget == pEntity || pRootMove == pObTarget ) )
  483. return 0;
  484. }
  485. }
  486. }
  487. }
  488. pRenderContext->MatrixMode( MATERIAL_MODEL );
  489. pRenderContext->PushMatrix();
  490. pRenderContext->LoadIdentity();
  491. Render( pRenderContext, IsTwoPass(), pCameraObject );
  492. pRenderContext->MatrixMode( MATERIAL_MODEL );
  493. pRenderContext->PopMatrix();
  494. }
  495. else
  496. {
  497. g_pParticleSystemMgr->AddToRenderCache( this );
  498. }
  499. if ( !IsRetail() )
  500. {
  501. CParticleMgr *pMgr = ParticleMgr();
  502. if ( pMgr->m_bStatsRunning )
  503. {
  504. pMgr->StatsNewParticleEffectDrawn ( this );
  505. }
  506. if ( g_cl_particle_show_bbox || ( g_cl_particle_show_bbox_cost != 0 ) )
  507. {
  508. DebugDrawBbox ( false );
  509. }
  510. }
  511. return 1;
  512. }
  513. static void DumpParticleStats_f( void )
  514. {
  515. g_pParticleSystemMgr->DumpProfileInformation();
  516. }
  517. static ConCommand cl_dump_particle_stats( "cl_dump_particle_stats", DumpParticleStats_f, "dump particle profiling info to particle_profile.csv") ;