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.

695 lines
23 KiB

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