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.

768 lines
25 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "particle_property.h"
  8. #include "utlvector.h"
  9. #ifdef CLIENT_DLL
  10. #include "c_baseentity.h"
  11. #include "c_baseanimating.h"
  12. #include "recvproxy.h"
  13. #include "particles_new.h"
  14. #include "engine/ivdebugoverlay.h"
  15. #else
  16. #include "baseentity.h"
  17. #include "baseanimating.h"
  18. #include "sendproxy.h"
  19. #endif
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. //-----------------------------------------------------------------------------
  23. // Save/load
  24. //-----------------------------------------------------------------------------
  25. BEGIN_DATADESC_NO_BASE( CParticleProperty )
  26. // DEFINE_FIELD( m_pOuter, FIELD_CLASSPTR ),
  27. END_DATADESC()
  28. #ifdef CLIENT_DLL
  29. //-----------------------------------------------------------------------------
  30. // Prediction
  31. //-----------------------------------------------------------------------------
  32. BEGIN_PREDICTION_DATA_NO_BASE( CParticleProperty )
  33. //DEFINE_PRED_FIELD( m_vecMins, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
  34. END_PREDICTION_DATA()
  35. #endif
  36. //-----------------------------------------------------------------------------
  37. // Networking
  38. //-----------------------------------------------------------------------------
  39. BEGIN_NETWORK_TABLE_NOBASE( CParticleProperty, DT_ParticleProperty )
  40. #ifdef CLIENT_DLL
  41. //RecvPropVector( RECVINFO(m_vecMins), 0, RecvProxy_OBBMins ),
  42. #else
  43. //SendPropVector( SENDINFO(m_vecMins), 0, SPROP_NOSCALE),
  44. #endif
  45. END_NETWORK_TABLE()
  46. //-----------------------------------------------------------------------------
  47. // Constructor, destructor
  48. //-----------------------------------------------------------------------------
  49. CParticleProperty::CParticleProperty()
  50. {
  51. Init( NULL );
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose:
  55. //-----------------------------------------------------------------------------
  56. CParticleProperty::~CParticleProperty()
  57. {
  58. // We're being removed. Call StopEmission() on any particle system
  59. // that has an unlimited number of particles to emit.
  60. StopEmission( NULL, false, true );
  61. }
  62. //-----------------------------------------------------------------------------
  63. // Initialization
  64. //-----------------------------------------------------------------------------
  65. void CParticleProperty::Init( CBaseEntity *pEntity )
  66. {
  67. m_pOuter = pEntity;
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose:
  71. //-----------------------------------------------------------------------------
  72. int CParticleProperty::GetParticleAttachment( C_BaseEntity *pEntity, const char *pszAttachmentName, const char *pszParticleName )
  73. {
  74. Assert( pEntity && pEntity->GetBaseAnimating() );
  75. if ( !pEntity || !pEntity->GetBaseAnimating() )
  76. return -1;
  77. // Find the attachment point index
  78. int iAttachment = pEntity->GetBaseAnimating()->LookupAttachment( pszAttachmentName );
  79. if ( iAttachment == -1 )
  80. {
  81. Warning("Model '%s' doesn't have attachment '%s' to attach particle system '%s' to.\n", STRING(pEntity->GetBaseAnimating()->GetModelName()), pszAttachmentName, pszParticleName );
  82. }
  83. return iAttachment;
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: Get's a list of all renderables used for this particle property
  87. //-----------------------------------------------------------------------------
  88. int CParticleProperty::GetAllParticleEffectRenderables( IClientRenderable **pOutput, int iMaxOutput )
  89. {
  90. if( iMaxOutput == 0 )
  91. return 0;
  92. int iReturnedRenderables = 0;
  93. int iParticleEffectListCount = m_ParticleEffects.Count();
  94. for( int i = 0; i != iParticleEffectListCount; ++i )
  95. {
  96. if( m_ParticleEffects[i].pParticleEffect.IsValid() )
  97. {
  98. pOutput[iReturnedRenderables++] = m_ParticleEffects[i].pParticleEffect.GetObject();
  99. if( iReturnedRenderables == iMaxOutput )
  100. break;
  101. }
  102. }
  103. return iReturnedRenderables;
  104. }
  105. //-----------------------------------------------------------------------------
  106. // Purpose: Create a new particle system and attach it to our owner
  107. //-----------------------------------------------------------------------------
  108. CNewParticleEffect *CParticleProperty::Create( const char *pszParticleName, ParticleAttachment_t iAttachType, const char *pszAttachmentName )
  109. {
  110. int iAttachment = GetParticleAttachment( GetOuter(), pszAttachmentName, pszParticleName );
  111. if ( iAttachment == -1 )
  112. return NULL;
  113. // Create the system
  114. return Create( pszParticleName, iAttachType, iAttachment );
  115. }
  116. //-----------------------------------------------------------------------------
  117. // Purpose: Create a new particle system and attach it to our owner
  118. //-----------------------------------------------------------------------------
  119. static ConVar cl_particle_batch_mode( "cl_particle_batch_mode", "1" );
  120. CNewParticleEffect *CParticleProperty::Create( CParticleSystemDefinition *pDef, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset, matrix3x4_t *matOffset )
  121. {
  122. int nBatchMode = cl_particle_batch_mode.GetInt();
  123. bool bRequestedBatch = ( nBatchMode == 2 ) || ( ( nBatchMode == 1 ) && pDef && pDef->ShouldBatch() );
  124. if ( ( iAttachType == PATTACH_CUSTOMORIGIN ) && bRequestedBatch )
  125. {
  126. int iIndex = FindEffect( pDef->GetName() );
  127. if ( iIndex >= 0 )
  128. {
  129. CNewParticleEffect *pEffect = m_ParticleEffects[iIndex].pParticleEffect.GetObject();
  130. pEffect->Restart();
  131. return pEffect;
  132. }
  133. }
  134. int iIndex = m_ParticleEffects.AddToTail();
  135. ParticleEffectList_t *newEffect = &m_ParticleEffects[iIndex];
  136. newEffect->pParticleEffect = CNewParticleEffect::Create( m_pOuter, pDef, pDef->GetName() );
  137. if ( !newEffect->pParticleEffect->IsValid() )
  138. {
  139. // Caused by trying to spawn an unregistered particle effect. Remove it.
  140. ParticleMgr()->RemoveEffect( newEffect->pParticleEffect.GetObject() );
  141. return NULL;
  142. }
  143. AddControlPoint( iIndex, 0, GetOuter(), iAttachType, iAttachmentPoint, vecOriginOffset, matOffset );
  144. if ( m_pOuter )
  145. {
  146. m_pOuter->OnNewParticleEffect( pDef->GetName(), newEffect->pParticleEffect.GetObject() );
  147. }
  148. return newEffect->pParticleEffect.GetObject();
  149. }
  150. CNewParticleEffect *CParticleProperty::CreatePrecached( int nPrecacheIndex, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset, matrix3x4_t *matOffset )
  151. {
  152. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
  153. if ( !pDef )
  154. {
  155. AssertMsg( 0, "Attempting to create unknown particle system" );
  156. return NULL;
  157. }
  158. return Create( pDef, iAttachType, iAttachmentPoint, vecOriginOffset, matOffset );
  159. }
  160. CNewParticleEffect *CParticleProperty::Create( const char *pszParticleName, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset, matrix3x4_t *matOffset )
  161. {
  162. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszParticleName );
  163. if ( !pDef )
  164. {
  165. // AssertMsg( 0, "Attempting to create unknown particle system" );
  166. Warning( "Attempting to create unknown particle system '%s' \n", pszParticleName );
  167. return NULL;
  168. }
  169. return Create( pDef, iAttachType, iAttachmentPoint, vecOriginOffset, matOffset );
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. //-----------------------------------------------------------------------------
  174. void CParticleProperty::AddControlPoint( CNewParticleEffect *pEffect, int iPoint, C_BaseEntity *pEntity, ParticleAttachment_t iAttachType, const char *pszAttachmentName, Vector vecOriginOffset, matrix3x4_t *matOffset )
  175. {
  176. Assert( pEffect );
  177. if ( pEffect )
  178. {
  179. int iAttachment = -1;
  180. if ( pszAttachmentName )
  181. {
  182. iAttachment = GetParticleAttachment( pEntity, pszAttachmentName, pEffect->GetEffectName() );
  183. }
  184. for ( int i = 0; i < m_ParticleEffects.Count(); i++ )
  185. {
  186. if ( m_ParticleEffects[i].pParticleEffect == pEffect )
  187. {
  188. AddControlPoint( i, iPoint, pEntity, iAttachType, iAttachment, vecOriginOffset, matOffset );
  189. }
  190. }
  191. }
  192. else
  193. {
  194. DevWarning( "Attempted to add control point to NULL particle effect!\n" );
  195. }
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Purpose:
  199. //-----------------------------------------------------------------------------
  200. void CParticleProperty::AddControlPoint( int iEffectIndex, int iPoint, C_BaseEntity *pEntity, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset, matrix3x4_t *matOffset )
  201. {
  202. Assert( iEffectIndex >= 0 && iEffectIndex < m_ParticleEffects.Count() );
  203. ParticleEffectList_t *pEffect = &m_ParticleEffects[iEffectIndex];
  204. Assert( pEffect->pControlPoints.Count() < MAX_PARTICLE_CONTROL_POINTS );
  205. int iIndex = pEffect->pControlPoints.AddToTail();
  206. ParticleControlPoint_t *pNewPoint = &pEffect->pControlPoints[iIndex];
  207. pNewPoint->iControlPoint = iPoint;
  208. pNewPoint->hEntity = pEntity;
  209. pNewPoint->iAttachType = iAttachType;
  210. pNewPoint->iAttachmentPoint = iAttachmentPoint;
  211. pNewPoint->vecOriginOffset = vecOriginOffset;
  212. if ( matOffset )
  213. pNewPoint->matOffset = *matOffset;
  214. else
  215. pNewPoint->matOffset.Init( Vector(1,0,0), Vector(0,1,0), Vector(0,0,1), Vector(0,0,0) );
  216. UpdateParticleEffect( pEffect, true, iIndex );
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Used to replace a particle effect with a different one; attaches the control point updating to the new one
  220. //-----------------------------------------------------------------------------
  221. void CParticleProperty::ReplaceParticleEffect( CNewParticleEffect *pOldEffect, CNewParticleEffect *pNewEffect )
  222. {
  223. int nCount = m_ParticleEffects.Count();
  224. for ( int i = 0; i < nCount; ++i )
  225. {
  226. if ( pOldEffect != m_ParticleEffects[i].pParticleEffect.GetObject() )
  227. continue;
  228. m_ParticleEffects[i].pParticleEffect = pNewEffect;
  229. UpdateParticleEffect( &m_ParticleEffects[i], true );
  230. }
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: Set the parent of a given control point to the index of some other
  234. // control point.
  235. //-----------------------------------------------------------------------------
  236. void CParticleProperty::SetControlPointParent( int iEffectIndex, int whichControlPoint, int parentIdx )
  237. {
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Stop effects from emitting more particles. If no effect is
  241. // specified, all effects attached to this entity are stopped.
  242. //-----------------------------------------------------------------------------
  243. void CParticleProperty::StopEmission( CNewParticleEffect *pEffect, bool bWakeOnStop, bool bDestroyAsleepSystems, bool bForceRemoveInstantly, bool bPlayEndCap )
  244. {
  245. // If we return from dormancy and are then told to stop emitting,
  246. // we should have died while dormant. Remove ourselves immediately.
  247. bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount);
  248. // force remove particles instantly if caller specified
  249. bRemoveInstantly |= bForceRemoveInstantly;
  250. if ( pEffect )
  251. {
  252. if ( FindEffect( pEffect ) != -1 )
  253. {
  254. pEffect->StopEmission( false, bRemoveInstantly, bWakeOnStop, bPlayEndCap );
  255. }
  256. }
  257. else
  258. {
  259. // Stop all effects
  260. float flNow = g_pParticleSystemMgr->GetLastSimulationTime();
  261. int nCount = m_ParticleEffects.Count();
  262. for ( int i = nCount-1; i >= 0; i-- )
  263. {
  264. CNewParticleEffect *pTmp = m_ParticleEffects[i].pParticleEffect.GetObject();
  265. bool bRemoveSystem = bRemoveInstantly || ( bDestroyAsleepSystems && ( flNow >= pTmp->m_flNextSleepTime ) );
  266. if ( bRemoveSystem )
  267. {
  268. m_ParticleEffects.Remove( i );
  269. pTmp->SetOwner( NULL );
  270. }
  271. pTmp->StopEmission( false, bRemoveSystem, !bRemoveSystem && bWakeOnStop, bPlayEndCap );
  272. }
  273. }
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Remove effects immediately, including all current particles. If no
  277. // effect is specified, all effects attached to this entity are removed.
  278. //-----------------------------------------------------------------------------
  279. void CParticleProperty::StopEmissionAndDestroyImmediately( CNewParticleEffect *pEffect )
  280. {
  281. if ( pEffect )
  282. {
  283. int iIndex = FindEffect( pEffect );
  284. Assert( iIndex != -1 );
  285. if ( iIndex != -1 )
  286. {
  287. m_ParticleEffects.Remove( iIndex );
  288. // Clear the owner so it doesn't try to call back to us on deletion
  289. pEffect->SetOwner( NULL );
  290. pEffect->StopEmission( false, true );
  291. }
  292. }
  293. else
  294. {
  295. // Immediately destroy all effects
  296. int nCount = m_ParticleEffects.Count();
  297. for ( int i = nCount-1; i >= 0; i-- )
  298. {
  299. CNewParticleEffect *pTmp = m_ParticleEffects[i].pParticleEffect.GetObject();
  300. m_ParticleEffects.Remove( i );
  301. // Clear the owner so it doesn't try to call back to us on deletion
  302. pTmp->SetOwner( NULL );
  303. pTmp->StopEmission( false, true );
  304. }
  305. }
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose: Stop all effects that have a control point associated with the given
  309. // entity.
  310. //-----------------------------------------------------------------------------
  311. void CParticleProperty::StopParticlesInvolving( CBaseEntity *pEntity, bool bForceRemoveInstantly /* =false */ )
  312. {
  313. Assert( pEntity );
  314. EHANDLE entHandle = pEntity;
  315. // If we return from dormancy and are then told to stop emitting,
  316. // we should have died while dormant. Remove ourselves immediately.
  317. bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount);
  318. // force remove particles instantly if caller specified
  319. bRemoveInstantly |= bForceRemoveInstantly;
  320. int nCount = m_ParticleEffects.Count();
  321. for ( int i = 0; i < nCount; ++i )
  322. {
  323. // for each effect...
  324. ParticleEffectList_t &part = m_ParticleEffects[i];
  325. // look through all the control points to see if any mention the given object
  326. int cpCount = part.pControlPoints.Count();
  327. for (int j = 0; j < cpCount ; ++j )
  328. {
  329. // if any control points respond to the given handle...
  330. if (part.pControlPoints[j].hEntity == entHandle)
  331. {
  332. part.pParticleEffect->StopEmission( false, bRemoveInstantly );
  333. break; // break out of the inner loop (to where it says BREAK TO HERE)
  334. }
  335. }
  336. // BREAK TO HERE
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose: Stop all effects that were created using the given definition
  341. // name.
  342. //-----------------------------------------------------------------------------
  343. void CParticleProperty::StopParticlesNamed( const char *pszEffectName, bool bForceRemoveInstantly /* =false */, int nSplitScreenPlayerSlot /*= -1*/ )
  344. {
  345. CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszEffectName );
  346. AssertMsg1(pDef, "Could not find particle definition %s", pszEffectName );
  347. if (!pDef)
  348. return;
  349. // If we return from dormancy and are then told to stop emitting,
  350. // we should have died while dormant. Remove ourselves immediately.
  351. bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount);
  352. // force remove particles instantly if caller specified
  353. bRemoveInstantly |= bForceRemoveInstantly;
  354. int nCount = m_ParticleEffects.Count();
  355. for ( int i = 0; i < nCount; ++i )
  356. {
  357. // for each effect...
  358. CNewParticleEffect *pParticleEffect = m_ParticleEffects[i].pParticleEffect.GetObject();
  359. if (pParticleEffect->m_pDef() == pDef)
  360. {
  361. if ( nSplitScreenPlayerSlot != -1 )
  362. {
  363. if ( !pParticleEffect->ShouldDrawForSplitScreenUser( nSplitScreenPlayerSlot ) )
  364. continue;
  365. }
  366. pParticleEffect->StopEmission( false, bRemoveInstantly );
  367. }
  368. }
  369. }
  370. //-----------------------------------------------------------------------------
  371. // Purpose:
  372. //-----------------------------------------------------------------------------
  373. void CParticleProperty::OnParticleSystemUpdated( CNewParticleEffect *pEffect, float flTimeDelta )
  374. {
  375. int iIndex = FindEffect( pEffect );
  376. Assert( iIndex != -1 );
  377. if ( iIndex == -1 )
  378. return;
  379. UpdateParticleEffect( &m_ParticleEffects[iIndex] );
  380. /*
  381. // Display the bounding box of the particle effect
  382. Vector vecMins, vecMaxs;
  383. pEffect->GetRenderBounds( vecMins, vecMaxs );
  384. debugoverlay->AddBoxOverlay( pEffect->GetRenderOrigin(), vecMins, vecMaxs, QAngle( 0, 0, 0 ), 0, 255, 255, 0, 0 );
  385. */
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Purpose:
  389. //-----------------------------------------------------------------------------
  390. void CParticleProperty::OnParticleSystemDeleted( CNewParticleEffect *pEffect )
  391. {
  392. int iIndex = FindEffect( pEffect );
  393. if ( iIndex == -1 )
  394. return;
  395. if ( m_pOuter )
  396. {
  397. m_pOuter->OnParticleEffectDeleted( pEffect );
  398. }
  399. m_ParticleEffects[iIndex].pParticleEffect.MarkDeleted();
  400. m_ParticleEffects.Remove( iIndex );
  401. }
  402. #ifdef CLIENT_DLL
  403. //-----------------------------------------------------------------------------
  404. // Purpose: The entity we're attached to has change dormancy state on our client
  405. //-----------------------------------------------------------------------------
  406. void CParticleProperty::OwnerSetDormantTo( bool bDormant )
  407. {
  408. m_iDormancyChangedAtFrame = gpGlobals->framecount;
  409. int nCount = m_ParticleEffects.Count();
  410. for ( int i = 0; i < nCount; i++ )
  411. {
  412. //m_ParticleEffects[i].pParticleEffect->SetShouldSimulate( !bDormant );
  413. m_ParticleEffects[i].pParticleEffect->SetDormant( bDormant );
  414. }
  415. }
  416. #endif
  417. //-----------------------------------------------------------------------------
  418. // Purpose:
  419. //-----------------------------------------------------------------------------
  420. int CParticleProperty::FindEffect( CNewParticleEffect *pEffect )
  421. {
  422. for ( int i = 0; i < m_ParticleEffects.Count(); i++ )
  423. {
  424. if ( m_ParticleEffects[i].pParticleEffect == pEffect )
  425. return i;
  426. }
  427. return -1;
  428. }
  429. int CParticleProperty::FindEffect( const char *pEffectName )
  430. {
  431. for ( int i = 0; i < m_ParticleEffects.Count(); i++ )
  432. {
  433. if ( !Q_stricmp( m_ParticleEffects[i].pParticleEffect->GetName(), pEffectName ) )
  434. return i;
  435. }
  436. return -1;
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Purpose:
  440. //-----------------------------------------------------------------------------
  441. void CParticleProperty::UpdateParticleEffect( ParticleEffectList_t *pEffect, bool bInitializing, int iOnlyThisControlPoint )
  442. {
  443. if ( iOnlyThisControlPoint != -1 )
  444. {
  445. UpdateControlPoint( pEffect, iOnlyThisControlPoint, bInitializing );
  446. return;
  447. }
  448. // Loop through our control points and update them all
  449. for ( int i = 0; i < pEffect->pControlPoints.Count(); i++ )
  450. {
  451. UpdateControlPoint( pEffect, i, bInitializing );
  452. }
  453. }
  454. extern void FormatViewModelAttachment( C_BasePlayer *pPlayer, Vector &vOrigin, bool bInverse );
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. //-----------------------------------------------------------------------------
  458. void CParticleProperty::UpdateControlPoint( ParticleEffectList_t *pEffect, int iPoint, bool bInitializing )
  459. {
  460. ParticleControlPoint_t *pPoint = &pEffect->pControlPoints[iPoint];
  461. if ( pEffect->pParticleEffect->m_pDef->IsScreenSpaceEffect() && iPoint == 0 )
  462. {
  463. pEffect->pParticleEffect->SetControlPointOrientation( pPoint->iControlPoint, Vector(1,0,0), Vector(0,1,0), Vector(0,0,1) );
  464. pEffect->pParticleEffect->SetControlPoint( pPoint->iControlPoint, vec3_origin );
  465. return;
  466. }
  467. if ( !pPoint->hEntity.Get() )
  468. {
  469. if ( pPoint->iAttachType == PATTACH_WORLDORIGIN && bInitializing )
  470. {
  471. pEffect->pParticleEffect->SetControlPointOrientation( pPoint->iControlPoint, Vector(1,0,0), Vector(0,1,0), Vector(0,0,1) );
  472. pEffect->pParticleEffect->SetControlPoint( pPoint->iControlPoint, pPoint->vecOriginOffset );
  473. pEffect->pParticleEffect->SetSortOrigin( pPoint->vecOriginOffset );
  474. }
  475. pEffect->pParticleEffect->SetControlPointEntity( pPoint->iControlPoint, NULL );
  476. return;
  477. }
  478. // Only update non-follow particles when we're initializing,
  479. if ( !bInitializing && (pPoint->iAttachType == PATTACH_ABSORIGIN || pPoint->iAttachType == PATTACH_POINT ) )
  480. return;
  481. if ( pPoint->iAttachType == PATTACH_CUSTOMORIGIN )
  482. return;
  483. Vector vecOrigin, vecForward, vecRight, vecUp;
  484. switch ( pPoint->iAttachType )
  485. {
  486. case PATTACH_POINT:
  487. case PATTACH_POINT_FOLLOW:
  488. {
  489. C_BaseAnimating *pAnimating = pPoint->hEntity->GetBaseAnimating();
  490. bool bValid = false;
  491. Assert( pAnimating );
  492. if ( pAnimating )
  493. {
  494. matrix3x4_t attachmentToWorld;
  495. if ( pAnimating->IsViewModel() )
  496. {
  497. C_BasePlayer *pPlayer = ToBasePlayer( ((C_BaseViewModel *)pAnimating)->GetOwner() );
  498. ACTIVE_SPLITSCREEN_PLAYER_GUARD( C_BasePlayer::GetSplitScreenSlotForPlayer( pPlayer ) );
  499. if ( pAnimating->GetAttachment( pPoint->iAttachmentPoint, attachmentToWorld ) )
  500. {
  501. bValid = true;
  502. MatrixVectors( attachmentToWorld, &vecForward, &vecRight, &vecUp );
  503. MatrixPosition( attachmentToWorld, vecOrigin );
  504. #ifndef PORTAL2
  505. // This is breaking in Portal
  506. if ( pEffect->pParticleEffect->m_pDef->IsViewModelEffect() )
  507. {
  508. FormatViewModelAttachment( pPlayer, vecOrigin, true );
  509. }
  510. #endif
  511. }
  512. }
  513. else
  514. {
  515. // HACK_GETLOCALPLAYER_GUARD( "CParticleProperty::UpdateControlPoint" );
  516. if ( pAnimating->GetAttachment( pPoint->iAttachmentPoint, attachmentToWorld ) )
  517. {
  518. bValid = true;
  519. MatrixVectors( attachmentToWorld, &vecForward, &vecRight, &vecUp );
  520. #ifdef _DEBUG
  521. float flTests[3] = {vecForward.Dot( vecRight ), vecRight.Dot( vecUp ), vecUp.Dot( vecForward )};
  522. static float s_flMaxTest = 0.001f;
  523. Assert( fabs( flTests[0] ) + fabs( flTests[1] ) + fabs( flTests[2] ) < s_flMaxTest );
  524. #endif
  525. MatrixPosition( attachmentToWorld, vecOrigin );
  526. if ( pEffect->pParticleEffect->m_pDef->IsViewModelEffect() )
  527. {
  528. HACK_GETLOCALPLAYER_GUARD( "CParticleProperty::UpdateControlPoint" );
  529. FormatViewModelAttachment( NULL, vecOrigin, true );
  530. }
  531. }
  532. }
  533. }
  534. if ( !bValid )
  535. {
  536. static bool bWarned = false;
  537. if ( !bWarned )
  538. {
  539. bWarned = true;
  540. DevWarning( "Attempted to attach particle effect %s to an unknown attachment on entity %s\n",
  541. pEffect->pParticleEffect->m_pDef->GetName(), pAnimating ? pAnimating->GetClassname() : "(null)" );
  542. }
  543. // FIXME: what's the fallback? is it ok to kill the effect if we don't know where to attach it?
  544. pEffect->pParticleEffect->StopEmission();
  545. }
  546. if ( !bValid )
  547. {
  548. AssertOnce( 0 );
  549. return;
  550. }
  551. }
  552. break;
  553. case PATTACH_ABSORIGIN:
  554. case PATTACH_ABSORIGIN_FOLLOW:
  555. default:
  556. {
  557. vecOrigin = pPoint->hEntity->GetAbsOrigin() + pPoint->vecOriginOffset;
  558. pPoint->hEntity->GetVectors( &vecForward, &vecRight, &vecUp );
  559. }
  560. break;
  561. case PATTACH_EYES_FOLLOW:
  562. {
  563. C_BaseEntity *pEnt = pPoint->hEntity;
  564. if ( !pEnt->IsPlayer() )
  565. return;
  566. C_BasePlayer *pPlayer = assert_cast< C_BasePlayer* >( pEnt );
  567. bool bValid = false;
  568. Assert( pPlayer );
  569. if ( pPlayer )
  570. {
  571. bValid = true;
  572. vecOrigin = pPlayer->EyePosition() + pPoint->vecOriginOffset;
  573. pPlayer->EyeVectors( &vecForward, &vecRight, &vecUp );
  574. }
  575. if ( !bValid )
  576. {
  577. AssertOnce( 0 );
  578. return;
  579. }
  580. }
  581. break;
  582. case PATTACH_OVERHEAD_FOLLOW:
  583. {
  584. Vector vecMins, vecMaxs = vec3_origin;
  585. const model_t *mod = pPoint->hEntity->GetModel();
  586. if ( mod )
  587. {
  588. modelinfo->GetModelBounds( mod, vecMins, vecMaxs );
  589. }
  590. vecOrigin = pPoint->hEntity->GetAbsOrigin() + pPoint->vecOriginOffset;
  591. vecOrigin.z += vecMaxs.z;
  592. pPoint->hEntity->GetVectors( &vecForward, &vecRight, &vecUp );
  593. }
  594. break;
  595. case PATTACH_CUSTOMORIGIN_FOLLOW:
  596. {
  597. matrix3x4_t mat;
  598. MatrixMultiply( pPoint->hEntity->RenderableToWorldTransform(), pPoint->matOffset, mat );
  599. Vector vecForward, vecRight, vecUp;
  600. MatrixVectors( mat, &vecForward, &vecRight, &vecUp );
  601. MatrixPosition( mat, vecOrigin );
  602. pEffect->pParticleEffect->SetControlPointEntity( pPoint->iControlPoint, pPoint->hEntity );
  603. pEffect->pParticleEffect->SetControlPoint( pPoint->iControlPoint, vecOrigin );
  604. pEffect->pParticleEffect->SetSortOrigin( vecOrigin );
  605. pEffect->pParticleEffect->SetControlPointOrientation( pPoint->iControlPoint, vecForward, vecRight, vecUp );
  606. return;
  607. }
  608. break;
  609. case PATTACH_ROOTBONE_FOLLOW:
  610. {
  611. C_BaseAnimating *pAnimating = pPoint->hEntity->GetBaseAnimating();
  612. Assert( pAnimating );
  613. if ( pAnimating )
  614. {
  615. matrix3x4_t rootBone;
  616. if ( pAnimating->GetRootBone( rootBone ) )
  617. {
  618. MatrixVectors( rootBone, &vecForward, &vecRight, &vecUp );
  619. MatrixPosition( rootBone, vecOrigin );
  620. }
  621. }
  622. }
  623. break;
  624. }
  625. pEffect->pParticleEffect->SetControlPointOrientation( pPoint->iControlPoint, vecForward, vecRight, vecUp );
  626. pEffect->pParticleEffect->SetControlPointEntity( pPoint->iControlPoint, pPoint->hEntity );
  627. pEffect->pParticleEffect->SetControlPoint( pPoint->iControlPoint, vecOrigin );
  628. pEffect->pParticleEffect->SetSortOrigin( vecOrigin );
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Purpose: Output all active effects
  632. //-----------------------------------------------------------------------------
  633. void CParticleProperty::DebugPrintEffects( void )
  634. {
  635. int nCount = m_ParticleEffects.Count();
  636. for ( int i = 0; i < nCount; ++i )
  637. {
  638. // for each effect...
  639. CNewParticleEffect *pParticleEffect = m_ParticleEffects[i].pParticleEffect.GetObject();
  640. if ( !pParticleEffect )
  641. continue;
  642. Msg( "(%d) EffectName \"%s\" Dormant? %s Emission Stopped? %s \n",
  643. i,
  644. pParticleEffect->GetEffectName(),
  645. ( pParticleEffect->m_bDormant ) ? "yes" : "no",
  646. ( pParticleEffect->m_bEmissionStopped ) ? "yes" : "no" );
  647. }
  648. }
  649. bool CParticleProperty::IsValidEffect( const CNewParticleEffect *pEffect )
  650. {
  651. if( pEffect == NULL )
  652. return false;
  653. int nCount = m_ParticleEffects.Count();
  654. for ( int i = 0; i < nCount; ++i )
  655. {
  656. if( pEffect == m_ParticleEffects[i].pParticleEffect.GetObject() )
  657. return true;
  658. }
  659. return false;
  660. }