//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "baseparticleentity.h" #include "entityparticletrail_shared.h" #include "particlemgr.h" #include "particle_util.h" #include "particles_simple.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // Entity particle trail, client-side implementation //----------------------------------------------------------------------------- class C_EntityParticleTrail : public C_BaseParticleEntity { public: DECLARE_CLIENTCLASS(); DECLARE_CLASS( C_EntityParticleTrail, C_BaseParticleEntity ); C_EntityParticleTrail( ); ~C_EntityParticleTrail( ); // C_BaseEntity virtual void OnDataChanged( DataUpdateType_t updateType ); // IParticleEffect void Update( float fTimeDelta ); virtual void RenderParticles( CParticleRenderIterator *pIterator ); virtual void SimulateParticles( CParticleSimulateIterator *pIterator ); private: C_EntityParticleTrail( const C_EntityParticleTrail & ); // not defined, not accessible void Start( ); void AddParticle( float flInitialDeltaTime, const Vector &vecMins, const Vector &vecMaxs, const matrix3x4_t &boxToWorld ); int m_iMaterialName; EntityParticleTrailInfo_t m_Info; EHANDLE m_hConstraintEntity; PMaterialHandle m_hMaterial; TimedEvent m_teParticleSpawn; }; //----------------------------------------------------------------------------- // Networking //----------------------------------------------------------------------------- IMPLEMENT_CLIENTCLASS_DT( C_EntityParticleTrail, DT_EntityParticleTrail, CEntityParticleTrail ) RecvPropInt(RECVINFO(m_iMaterialName)), RecvPropDataTable( RECVINFO_DT( m_Info ), 0, &REFERENCE_RECV_TABLE(DT_EntityParticleTrailInfo) ), RecvPropEHandle(RECVINFO(m_hConstraintEntity)), END_RECV_TABLE() //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- C_EntityParticleTrail::C_EntityParticleTrail( void ) { } C_EntityParticleTrail::~C_EntityParticleTrail() { ParticleMgr()->RemoveEffect( &m_ParticleEffect ); } //----------------------------------------------------------------------------- // On data changed //----------------------------------------------------------------------------- void C_EntityParticleTrail::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); if ( updateType == DATA_UPDATE_CREATED ) { Start( ); } } //----------------------------------------------------------------------------- // Purpose: // Input : *pParticleMgr - // *pArgs - //----------------------------------------------------------------------------- void C_EntityParticleTrail::Start( ) { if( ParticleMgr()->AddEffect( &m_ParticleEffect, this ) == false ) return; const char *pMaterialName = GetMaterialNameFromIndex( m_iMaterialName ); if ( !pMaterialName ) return; m_hMaterial = ParticleMgr()->GetPMaterial( pMaterialName ); m_teParticleSpawn.Init( 150 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_EntityParticleTrail::AddParticle( float flInitialDeltaTime, const Vector &vecMins, const Vector &vecMaxs, const matrix3x4_t &boxToWorld ) { // Select a random point somewhere in the hitboxes of the entity. Vector vecLocalPosition, vecWorldPosition; vecLocalPosition.x = Lerp( random->RandomFloat( 0.0f, 1.0f ), vecMins.x, vecMaxs.x ); vecLocalPosition.y = Lerp( random->RandomFloat( 0.0f, 1.0f ), vecMins.y, vecMaxs.y ); vecLocalPosition.z = Lerp( random->RandomFloat( 0.0f, 1.0f ), vecMins.z, vecMaxs.z ); VectorTransform( vecLocalPosition, boxToWorld, vecWorldPosition ); // Don't emit the particle unless it's inside the model if ( m_hConstraintEntity.Get() ) { Ray_t ray; trace_t tr; ray.Init( vecWorldPosition, vecWorldPosition ); enginetrace->ClipRayToEntity( ray, MASK_ALL, m_hConstraintEntity, &tr ); if ( !tr.startsolid ) return; } // Make a new particle SimpleParticle *pParticle = (SimpleParticle *)m_ParticleEffect.AddParticle( sizeof(SimpleParticle), m_hMaterial ); if ( pParticle == NULL ) return; pParticle->m_Pos = vecWorldPosition; pParticle->m_flRoll = Helper_RandomInt( 0, 360 ); pParticle->m_flRollDelta = Helper_RandomFloat( -2.0f, 2.0f ); pParticle->m_flLifetime = flInitialDeltaTime; pParticle->m_flDieTime = m_Info.m_flLifetime; pParticle->m_uchColor[0] = 64; pParticle->m_uchColor[1] = 140; pParticle->m_uchColor[2] = 225; pParticle->m_uchStartAlpha = Helper_RandomInt( 64, 64 ); pParticle->m_uchEndAlpha = 0; pParticle->m_uchStartSize = m_Info.m_flStartSize; pParticle->m_uchEndSize = m_Info.m_flEndSize; pParticle->m_vecVelocity = vec3_origin; VectorMA( pParticle->m_Pos, flInitialDeltaTime, pParticle->m_vecVelocity, pParticle->m_Pos ); } //----------------------------------------------------------------------------- // Purpose: // Input : fTimeDelta - //----------------------------------------------------------------------------- void C_EntityParticleTrail::Update( float fTimeDelta ) { float tempDelta = fTimeDelta; studiohdr_t *pStudioHdr; mstudiohitboxset_t *set; matrix3x4_t *hitboxbones[MAXSTUDIOBONES]; C_BaseEntity *pMoveParent = GetMoveParent(); if ( !pMoveParent ) return; C_BaseAnimating *pAnimating = pMoveParent->GetBaseAnimating(); if (!pAnimating) goto trailNoHitboxes; if ( !pAnimating->HitboxToWorldTransforms( hitboxbones ) ) goto trailNoHitboxes; pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() ); if (!pStudioHdr) goto trailNoHitboxes; set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() ); if ( !set ) goto trailNoHitboxes; //Add new particles while ( m_teParticleSpawn.NextEvent( tempDelta ) ) { int nHitbox = random->RandomInt( 0, set->numhitboxes - 1 ); mstudiobbox_t *pBox = set->pHitbox(nHitbox); AddParticle( tempDelta, pBox->bbmin, pBox->bbmax, *hitboxbones[pBox->bone] ); } return; trailNoHitboxes: while ( m_teParticleSpawn.NextEvent( tempDelta ) ) { AddParticle( tempDelta, pMoveParent->CollisionProp()->OBBMins(), pMoveParent->CollisionProp()->OBBMaxs(), pMoveParent->EntityToWorldTransform() ); } } inline void C_EntityParticleTrail::RenderParticles( CParticleRenderIterator *pIterator ) { const SimpleParticle *pParticle = (const SimpleParticle*)pIterator->GetFirst(); while ( pParticle ) { float t = pParticle->m_flLifetime / pParticle->m_flDieTime; // Render Vector tPos; TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos ); float sortKey = tPos.z; Vector color = Vector( pParticle->m_uchColor[0] / 255.0f, pParticle->m_uchColor[1] / 255.0f, pParticle->m_uchColor[2] / 255.0f ); float alpha = Lerp( t, pParticle->m_uchStartAlpha / 255.0f, pParticle->m_uchEndAlpha / 255.0f ); float flSize = Lerp( t, pParticle->m_uchStartSize, pParticle->m_uchEndSize ); // Render it RenderParticle_ColorSize( pIterator->GetParticleDraw(), tPos, color, alpha, flSize ); pParticle = (const SimpleParticle*)pIterator->GetNext( sortKey ); } } inline void C_EntityParticleTrail::SimulateParticles( CParticleSimulateIterator *pIterator ) { SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst(); while ( pParticle ) { // Update position float flTimeDelta = pIterator->GetTimeDelta(); pParticle->m_Pos += pParticle->m_vecVelocity * flTimeDelta; // NOTE: I'm overloading "die time" to be the actual start time. pParticle->m_flLifetime += flTimeDelta; if ( pParticle->m_flLifetime >= pParticle->m_flDieTime ) pIterator->RemoveParticle( pParticle ); pParticle = (SimpleParticle*)pIterator->GetNext(); } }