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
695 lines
23 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "particle_property.h" |
|
#include "utlvector.h" |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
#include "c_baseentity.h" |
|
#include "c_baseanimating.h" |
|
#include "recvproxy.h" |
|
#include "particles_new.h" |
|
#include "engine/ivdebugoverlay.h" |
|
#include "bone_setup.h" |
|
#else |
|
|
|
#include "baseentity.h" |
|
#include "baseanimating.h" |
|
#include "sendproxy.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#ifdef STAGING_ONLY |
|
#ifdef TF_CLIENT_DLL |
|
extern ConVar tf_unusual_effect_offset; |
|
#endif |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Save/load |
|
//----------------------------------------------------------------------------- |
|
BEGIN_DATADESC_NO_BASE( CParticleProperty ) |
|
// DEFINE_FIELD( m_pOuter, FIELD_CLASSPTR ), |
|
END_DATADESC() |
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Prediction |
|
//----------------------------------------------------------------------------- |
|
BEGIN_PREDICTION_DATA_NO_BASE( CParticleProperty ) |
|
//DEFINE_PRED_FIELD( m_vecMins, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ), |
|
END_PREDICTION_DATA() |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Networking |
|
//----------------------------------------------------------------------------- |
|
BEGIN_NETWORK_TABLE_NOBASE( CParticleProperty, DT_ParticleProperty ) |
|
#ifdef CLIENT_DLL |
|
//RecvPropVector( RECVINFO(m_vecMins), 0, RecvProxy_OBBMins ), |
|
#else |
|
//SendPropVector( SENDINFO(m_vecMins), 0, SPROP_NOSCALE), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Constructor, destructor |
|
//----------------------------------------------------------------------------- |
|
CParticleProperty::CParticleProperty() |
|
{ |
|
Init( NULL ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CParticleProperty::~CParticleProperty() |
|
{ |
|
// We're being removed. Call StopEmission() on any particle system |
|
// that has an unlimited number of particles to emit. |
|
StopEmission( NULL, false, true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Initialization |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::Init( CBaseEntity *pEntity ) |
|
{ |
|
m_pOuter = pEntity; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CParticleProperty::GetParticleAttachment( C_BaseEntity *pEntity, const char *pszAttachmentName, const char *pszParticleName ) |
|
{ |
|
Assert( pEntity && pEntity->GetBaseAnimating() ); |
|
if ( !pEntity || !pEntity->GetBaseAnimating() ) |
|
return INVALID_PARTICLE_ATTACHMENT; |
|
|
|
// Find the attachment point index |
|
int iAttachment = pEntity->GetBaseAnimating()->LookupAttachment( pszAttachmentName ); |
|
if ( iAttachment == INVALID_PARTICLE_ATTACHMENT ) |
|
{ |
|
Warning("Model '%s' doesn't have attachment '%s' to attach particle system '%s' to.\n", STRING(pEntity->GetBaseAnimating()->GetModelName()), pszAttachmentName, pszParticleName ); |
|
} |
|
|
|
return iAttachment; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create a new particle system and attach it to our owner |
|
//----------------------------------------------------------------------------- |
|
CNewParticleEffect *CParticleProperty::Create( const char *pszParticleName, ParticleAttachment_t iAttachType, const char *pszAttachmentName ) |
|
{ |
|
int iAttachment = GetParticleAttachment( GetOuter(), pszAttachmentName, pszParticleName ); |
|
if ( iAttachment == INVALID_PARTICLE_ATTACHMENT ) |
|
return NULL; |
|
|
|
// Create the system |
|
return Create( pszParticleName, iAttachType, iAttachment ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create a new particle system and attach it to our owner |
|
//----------------------------------------------------------------------------- |
|
static ConVar cl_particle_batch_mode( "cl_particle_batch_mode", "1" ); |
|
CNewParticleEffect *CParticleProperty::Create( const char *pszParticleName, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset ) |
|
{ |
|
if ( GameRules() ) |
|
{ |
|
pszParticleName = GameRules()->TranslateEffectForVisionFilter( "particles", pszParticleName ); |
|
} |
|
|
|
int nBatchMode = cl_particle_batch_mode.GetInt(); |
|
CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszParticleName ); |
|
bool bRequestedBatch = ( nBatchMode == 2 ) || ( ( nBatchMode == 1 ) && pDef && pDef->ShouldBatch() ); |
|
if ( ( iAttachType == PATTACH_CUSTOMORIGIN ) && bRequestedBatch ) |
|
{ |
|
int iIndex = FindEffect( pszParticleName ); |
|
if ( iIndex >= 0 ) |
|
{ |
|
CNewParticleEffect *pEffect = m_ParticleEffects[iIndex].pParticleEffect.GetObject(); |
|
pEffect->Restart(); |
|
return pEffect; |
|
} |
|
} |
|
|
|
if ( !pDef ) |
|
{ |
|
AssertMsg( 0, "Attempting to create unknown particle system" ); |
|
Warning( "Attempting to create unknown particle system '%s' \n", pszParticleName ); |
|
return NULL; |
|
} |
|
|
|
int iIndex = m_ParticleEffects.AddToTail(); |
|
ParticleEffectList_t *newEffect = &m_ParticleEffects[iIndex]; |
|
newEffect->pParticleEffect = CNewParticleEffect::Create( m_pOuter, pDef ); |
|
|
|
if ( !newEffect->pParticleEffect->IsValid() ) |
|
{ |
|
// Caused by trying to spawn an unregistered particle effect. Remove it. |
|
ParticleMgr()->RemoveEffect( newEffect->pParticleEffect.GetObject() ); |
|
return NULL; |
|
} |
|
|
|
AddControlPoint( iIndex, 0, GetOuter(), iAttachType, iAttachmentPoint, vecOriginOffset ); |
|
|
|
if ( m_pOuter ) |
|
{ |
|
m_pOuter->OnNewParticleEffect( pszParticleName, newEffect->pParticleEffect.GetObject() ); |
|
} |
|
|
|
return newEffect->pParticleEffect.GetObject(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::AddControlPoint( CNewParticleEffect *pEffect, int iPoint, C_BaseEntity *pEntity, ParticleAttachment_t iAttachType, const char *pszAttachmentName, Vector vecOriginOffset ) |
|
{ |
|
int iAttachment = INVALID_PARTICLE_ATTACHMENT; |
|
if ( pszAttachmentName ) |
|
{ |
|
iAttachment = GetParticleAttachment( pEntity, pszAttachmentName, pEffect->GetEffectName() ); |
|
} |
|
|
|
for ( int i = 0; i < m_ParticleEffects.Count(); i++ ) |
|
{ |
|
if ( m_ParticleEffects[i].pParticleEffect == pEffect ) |
|
{ |
|
AddControlPoint( i, iPoint, pEntity, iAttachType, iAttachment, vecOriginOffset ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::AddControlPoint( int iEffectIndex, int iPoint, C_BaseEntity *pEntity, ParticleAttachment_t iAttachType, int iAttachmentPoint, Vector vecOriginOffset ) |
|
{ |
|
Assert( iEffectIndex >= 0 && iEffectIndex < m_ParticleEffects.Count() ); |
|
ParticleEffectList_t *pEffect = &m_ParticleEffects[iEffectIndex]; |
|
Assert( pEffect->pControlPoints.Count() < MAX_PARTICLE_CONTROL_POINTS ); |
|
|
|
// If the control point is already used, override it |
|
ParticleControlPoint_t *pNewPoint = NULL; |
|
int iIndex = iPoint; |
|
FOR_EACH_VEC( pEffect->pControlPoints, i ) |
|
{ |
|
if ( pEffect->pControlPoints[i].iControlPoint == iPoint ) |
|
{ |
|
pNewPoint = &pEffect->pControlPoints[i]; |
|
} |
|
} |
|
|
|
if ( !pNewPoint ) |
|
{ |
|
iIndex = pEffect->pControlPoints.AddToTail(); |
|
pNewPoint = &pEffect->pControlPoints[iIndex]; |
|
} |
|
|
|
pNewPoint->iControlPoint = iPoint; |
|
pNewPoint->hEntity = pEntity; |
|
pNewPoint->iAttachType = iAttachType; |
|
pNewPoint->iAttachmentPoint = iAttachmentPoint; |
|
pNewPoint->vecOriginOffset = vecOriginOffset; |
|
|
|
UpdateParticleEffect( pEffect, true, iIndex ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used to replace a particle effect with a different one; attaches the control point updating to the new one |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::ReplaceParticleEffect( CNewParticleEffect *pOldEffect, CNewParticleEffect *pNewEffect ) |
|
{ |
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
if ( pOldEffect != m_ParticleEffects[i].pParticleEffect.GetObject() ) |
|
continue; |
|
|
|
m_ParticleEffects[i].pParticleEffect = pNewEffect; |
|
UpdateParticleEffect( &m_ParticleEffects[i], true ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the parent of a given control point to the index of some other |
|
// control point. |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::SetControlPointParent( int iEffectIndex, int whichControlPoint, int parentIdx ) |
|
{ |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stop effects from emitting more particles. If no effect is |
|
// specified, all effects attached to this entity are stopped. |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::StopEmission( CNewParticleEffect *pEffect, bool bWakeOnStop, bool bDestroyAsleepSystems ) |
|
{ |
|
// If we return from dormancy and are then told to stop emitting, |
|
// we should have died while dormant. Remove ourselves immediately. |
|
bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount); |
|
|
|
if ( pEffect ) |
|
{ |
|
if ( FindEffect( pEffect ) != -1 ) |
|
{ |
|
pEffect->StopEmission( false, bRemoveInstantly, bWakeOnStop ); |
|
} |
|
} |
|
else |
|
{ |
|
// Stop all effects |
|
float flNow = g_pParticleSystemMgr->GetLastSimulationTime(); |
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = nCount-1; i >= 0; i-- ) |
|
{ |
|
CNewParticleEffect *pTmp = m_ParticleEffects[i].pParticleEffect.GetObject(); |
|
bool bRemoveSystem = bRemoveInstantly || ( bDestroyAsleepSystems && ( flNow >= pTmp->m_flNextSleepTime ) ); |
|
if ( bRemoveSystem ) |
|
{ |
|
m_ParticleEffects.Remove( i ); |
|
pTmp->SetOwner( NULL ); |
|
} |
|
pTmp->StopEmission( false, bRemoveSystem, !bRemoveSystem && bWakeOnStop ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Remove effects immediately, including all current particles. If no |
|
// effect is specified, all effects attached to this entity are removed. |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::StopEmissionAndDestroyImmediately( CNewParticleEffect *pEffect ) |
|
{ |
|
if ( pEffect ) |
|
{ |
|
int iIndex = FindEffect( pEffect ); |
|
//Assert( iIndex != -1 ); |
|
if ( iIndex != -1 ) |
|
{ |
|
m_ParticleEffects.Remove( iIndex ); |
|
|
|
// Clear the owner so it doesn't try to call back to us on deletion |
|
pEffect->SetOwner( NULL ); |
|
pEffect->StopEmission( false, true ); |
|
} |
|
} |
|
else |
|
{ |
|
// Immediately destroy all effects |
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = nCount-1; i >= 0; i-- ) |
|
{ |
|
CNewParticleEffect *pTmp = m_ParticleEffects[i].pParticleEffect.GetObject(); |
|
m_ParticleEffects.Remove( i ); |
|
|
|
// Clear the owner so it doesn't try to call back to us on deletion |
|
pTmp->SetOwner( NULL ); |
|
pTmp->StopEmission( false, true ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stop all effects that have a control point associated with the given |
|
// entity. |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::StopParticlesInvolving( CBaseEntity *pEntity ) |
|
{ |
|
Assert( pEntity ); |
|
|
|
EHANDLE entHandle(pEntity); |
|
|
|
// If we return from dormancy and are then told to stop emitting, |
|
// we should have died while dormant. Remove ourselves immediately. |
|
bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount); |
|
|
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
// for each effect... |
|
ParticleEffectList_t &part = m_ParticleEffects[i]; |
|
// look through all the control points to see if any mention the given object |
|
int cpCount = part.pControlPoints.Count(); |
|
for (int j = 0; j < cpCount ; ++j ) |
|
{ |
|
// if any control points respond to the given handle... |
|
if (part.pControlPoints[j].hEntity == entHandle) |
|
{ |
|
part.pParticleEffect->StopEmission( false, bRemoveInstantly ); |
|
part.pControlPoints.Remove( j ); |
|
break; // break out of the inner loop (to where it says BREAK TO HERE) |
|
} |
|
} |
|
// BREAK TO HERE |
|
} |
|
} |
|
|
|
//g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stop all effects that were created using the given definition |
|
// name. |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::StopParticlesNamed( const char *pszEffectName, bool bForceRemoveInstantly /* =false */, bool bInverse /*= false*/ ) |
|
{ |
|
CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszEffectName ); |
|
AssertMsg1(pDef, "Could not find particle definition %s", pszEffectName ); |
|
if (!pDef) |
|
return; |
|
|
|
|
|
// If we return from dormancy and are then told to stop emitting, |
|
// we should have died while dormant. Remove ourselves immediately. |
|
bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount); |
|
// force remove particles instantly if caller specified |
|
bRemoveInstantly |= bForceRemoveInstantly; |
|
|
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
// for each effect... |
|
CNewParticleEffect *pParticleEffect = m_ParticleEffects[i].pParticleEffect.GetObject(); |
|
bool bMatches = pParticleEffect->m_pDef() == pDef; |
|
if ( bMatches == !bInverse ) |
|
{ |
|
pParticleEffect->StopEmission( false, bRemoveInstantly ); |
|
} |
|
} |
|
} |
|
|
|
|
|
void CParticleProperty::StopParticlesWithNameAndAttachment( const char *pszEffectName, int iAttachmentPoint, bool bForceRemoveInstantly /* =false */ ) |
|
{ |
|
CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pszEffectName ); |
|
AssertMsg1(pDef, "Could not find particle definition %s", pszEffectName ); |
|
if (!pDef) |
|
return; |
|
|
|
|
|
// If we return from dormancy and are then told to stop emitting, |
|
// we should have died while dormant. Remove ourselves immediately. |
|
bool bRemoveInstantly = (m_iDormancyChangedAtFrame == gpGlobals->framecount); |
|
// force remove particles instantly if caller specified |
|
bRemoveInstantly |= bForceRemoveInstantly; |
|
|
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
// for each effect... |
|
ParticleEffectList_t *pParticleEffectList = &m_ParticleEffects[i]; |
|
CNewParticleEffect *pParticleEffect = pParticleEffectList->pParticleEffect.GetObject(); |
|
if (pParticleEffect->m_pDef() == pDef) |
|
{ |
|
int nControlPointCount = pParticleEffectList->pControlPoints.Count(); |
|
for ( int j = 0; j < nControlPointCount; ++j ) |
|
{ |
|
if ( pParticleEffectList->pControlPoints[j].iAttachmentPoint == iAttachmentPoint ) |
|
{ |
|
pParticleEffect->StopEmission( false, bRemoveInstantly ); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::OnParticleSystemUpdated( CNewParticleEffect *pEffect, float flTimeDelta ) |
|
{ |
|
int iIndex = FindEffect( pEffect ); |
|
Assert( iIndex != -1 ); |
|
if ( iIndex == -1 ) |
|
return; |
|
|
|
// Enable FP exceptions here when FP_EXCEPTIONS_ENABLED is defined, |
|
// to help track down bad math. |
|
FPExceptionEnabler enableExceptions; |
|
|
|
UpdateParticleEffect( &m_ParticleEffects[iIndex] ); |
|
|
|
/* |
|
// Display the bounding box of the particle effect |
|
Vector vecMins, vecMaxs; |
|
pEffect->GetRenderBounds( vecMins, vecMaxs ); |
|
debugoverlay->AddBoxOverlay( pEffect->GetRenderOrigin(), vecMins, vecMaxs, QAngle( 0, 0, 0 ), 0, 255, 255, 0, 0 ); |
|
*/ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::OnParticleSystemDeleted( CNewParticleEffect *pEffect ) |
|
{ |
|
int iIndex = FindEffect( pEffect ); |
|
if ( iIndex == -1 ) |
|
return; |
|
|
|
m_ParticleEffects[iIndex].pParticleEffect.MarkDeleted(); |
|
m_ParticleEffects.Remove( iIndex ); |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
//----------------------------------------------------------------------------- |
|
// Purpose: The entity we're attached to has change dormancy state on our client |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::OwnerSetDormantTo( bool bDormant ) |
|
{ |
|
m_iDormancyChangedAtFrame = gpGlobals->framecount; |
|
|
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = 0; i < nCount; i++ ) |
|
{ |
|
//m_ParticleEffects[i].pParticleEffect->SetShouldSimulate( !bDormant ); |
|
m_ParticleEffects[i].pParticleEffect->SetDormant( bDormant ); |
|
} |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CParticleProperty::FindEffect( CNewParticleEffect *pEffect ) |
|
{ |
|
for ( int i = 0; i < m_ParticleEffects.Count(); i++ ) |
|
{ |
|
if ( m_ParticleEffects[i].pParticleEffect == pEffect ) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
int CParticleProperty::FindEffect( const char *pEffectName, int nStart /*= 0*/ ) |
|
{ |
|
for ( int i = nStart; i < m_ParticleEffects.Count(); i++ ) |
|
{ |
|
if ( !Q_stricmp( m_ParticleEffects[i].pParticleEffect->GetName(), pEffectName ) ) |
|
return i; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::UpdateParticleEffect( ParticleEffectList_t *pEffect, bool bInitializing, int iOnlyThisControlPoint ) |
|
{ |
|
if ( iOnlyThisControlPoint != -1 ) |
|
{ |
|
UpdateControlPoint( pEffect, iOnlyThisControlPoint, bInitializing ); |
|
return; |
|
} |
|
|
|
// Loop through our control points and update them all |
|
for ( int i = 0; i < pEffect->pControlPoints.Count(); i++ ) |
|
{ |
|
UpdateControlPoint( pEffect, i, bInitializing ); |
|
} |
|
} |
|
|
|
extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::UpdateControlPoint( ParticleEffectList_t *pEffect, int iPoint, bool bInitializing ) |
|
{ |
|
ParticleControlPoint_t *pPoint = &pEffect->pControlPoints[iPoint]; |
|
|
|
if ( !pPoint->hEntity.Get() ) |
|
{ |
|
if ( pPoint->iAttachType == PATTACH_WORLDORIGIN && bInitializing ) |
|
{ |
|
pEffect->pParticleEffect->SetControlPointOrientation( pPoint->iControlPoint, Vector(1,0,0), Vector(0,1,0), Vector(0,0,1) ); |
|
pEffect->pParticleEffect->SetControlPoint( pPoint->iControlPoint, pPoint->vecOriginOffset ); |
|
pEffect->pParticleEffect->SetSortOrigin( pPoint->vecOriginOffset ); |
|
} |
|
|
|
pEffect->pParticleEffect->SetControlPointEntity( pPoint->iControlPoint, NULL ); |
|
return; |
|
} |
|
|
|
// Only update non-follow particles when we're initializing, |
|
// unless we're parented to something, in which case we should always update |
|
if ( !bInitializing && !pPoint->hEntity->GetMoveParent() && (pPoint->iAttachType == PATTACH_ABSORIGIN || pPoint->iAttachType == PATTACH_POINT ) ) |
|
return; |
|
|
|
if ( pPoint->iAttachType == PATTACH_CUSTOMORIGIN ) |
|
return; |
|
|
|
Vector vecOrigin, vecForward, vecRight, vecUp; |
|
|
|
float flOffset = 0.0f; |
|
bool bUsingHeadOrigin = false; |
|
|
|
#ifdef TF_CLIENT_DLL |
|
|
|
CBaseEntity *pWearable = (CBaseEntity*) pPoint->hEntity.Get(); |
|
if ( pWearable && GetAttribInterface( pWearable ) && !pWearable->IsPlayer() ) |
|
{ |
|
C_BaseAnimating *pAnimating = pPoint->hEntity->GetBaseAnimating(); |
|
if ( pAnimating ) |
|
{ |
|
int bUseHeadOrigin = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pAnimating, bUseHeadOrigin, particle_effect_use_head_origin ); |
|
if ( bUseHeadOrigin > 0 ) |
|
{ |
|
int iBone = Studio_BoneIndexByName( pAnimating->GetModelPtr(), "bip_head" ); |
|
if ( iBone < 0 ) |
|
{ |
|
iBone = Studio_BoneIndexByName( pAnimating->GetModelPtr(), "prp_helmet" ); |
|
if ( iBone < 0 ) |
|
{ |
|
iBone = Studio_BoneIndexByName( pAnimating->GetModelPtr(), "prp_hat" ); |
|
} |
|
} |
|
if ( iBone < 0 ) |
|
{ |
|
iBone = 0; |
|
} |
|
|
|
bUsingHeadOrigin = true; |
|
const matrix3x4_t headBone = pAnimating->GetBone( iBone ); |
|
MatrixVectors( headBone, &vecForward, &vecRight, &vecUp ); |
|
MatrixPosition( headBone, vecOrigin ); |
|
|
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pAnimating, flOffset, particle_effect_vertical_offset ); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
if ( !bUsingHeadOrigin ) |
|
{ |
|
switch ( pPoint->iAttachType ) |
|
{ |
|
case PATTACH_POINT: |
|
case PATTACH_POINT_FOLLOW: |
|
{ |
|
C_BaseAnimating *pAnimating = pPoint->hEntity->GetBaseAnimating(); |
|
|
|
Assert( pAnimating ); |
|
if ( pAnimating ) |
|
{ |
|
matrix3x4_t attachmentToWorld; |
|
|
|
if ( !pAnimating->GetAttachment( pPoint->iAttachmentPoint, attachmentToWorld ) ) |
|
{ |
|
// try C_BaseAnimating if attach point is not on the weapon |
|
if ( !pAnimating->C_BaseAnimating::GetAttachment( pPoint->iAttachmentPoint, attachmentToWorld ) ) |
|
{ |
|
Warning( "Cannot update control point %d for effect '%s'.\n", pPoint->iAttachmentPoint, pEffect->pParticleEffect->GetEffectName() ); |
|
// Remove the effect cause this warning means something is orphaned |
|
StopParticlesNamed( pEffect->pParticleEffect->GetEffectName() ); |
|
return; |
|
} |
|
} |
|
|
|
VMatrix vMat(attachmentToWorld); |
|
MatrixTranslate( vMat, pPoint->vecOriginOffset ); |
|
MatrixVectors( vMat.As3x4(), &vecForward, &vecRight, &vecUp ); |
|
MatrixPosition( vMat.As3x4(), vecOrigin ); |
|
|
|
if ( pEffect->pParticleEffect->GetIsViewModelEffect() ) |
|
{ |
|
FormatViewModelAttachment( vecOrigin, true ); |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case PATTACH_ABSORIGIN: |
|
case PATTACH_ABSORIGIN_FOLLOW: |
|
default: |
|
{ |
|
vecOrigin = pPoint->hEntity->GetAbsOrigin() + pPoint->vecOriginOffset; |
|
pPoint->hEntity->GetVectors( &vecForward, &vecRight, &vecUp ); |
|
} |
|
break; |
|
|
|
case PATTACH_ROOTBONE_FOLLOW: |
|
{ |
|
C_BaseAnimating *pAnimating = pPoint->hEntity->GetBaseAnimating(); |
|
|
|
Assert( pAnimating ); |
|
if ( pAnimating ) |
|
{ |
|
matrix3x4_t rootBone; |
|
if ( pAnimating->GetRootBone( rootBone ) ) |
|
{ |
|
MatrixVectors( rootBone, &vecForward, &vecRight, &vecUp ); |
|
MatrixPosition( rootBone, vecOrigin ); |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
Vector vecForcedOriginOffset( 0, 0, flOffset ); |
|
pEffect->pParticleEffect->SetControlPointOrientation( pPoint->iControlPoint, vecForward, vecRight, vecUp ); |
|
pEffect->pParticleEffect->SetControlPointEntity( pPoint->iControlPoint, pPoint->hEntity ); |
|
pEffect->pParticleEffect->SetControlPoint( pPoint->iControlPoint, vecOrigin + vecForcedOriginOffset ); |
|
pEffect->pParticleEffect->SetSortOrigin( vecOrigin + vecForcedOriginOffset); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Output all active effects |
|
//----------------------------------------------------------------------------- |
|
void CParticleProperty::DebugPrintEffects( void ) |
|
{ |
|
int nCount = m_ParticleEffects.Count(); |
|
for ( int i = 0; i < nCount; ++i ) |
|
{ |
|
// for each effect... |
|
CNewParticleEffect *pParticleEffect = m_ParticleEffects[i].pParticleEffect.GetObject(); |
|
|
|
if ( !pParticleEffect ) |
|
continue; |
|
|
|
Msg( "(%d) EffectName \"%s\" Dormant? %s Emission Stopped? %s \n", |
|
i, |
|
pParticleEffect->GetEffectName(), |
|
( pParticleEffect->m_bDormant ) ? "yes" : "no", |
|
( pParticleEffect->m_bEmissionStopped ) ? "yes" : "no" ); |
|
} |
|
}
|
|
|