//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //===========================================================================// #include "cbase.h" #include "c_baseanimating.h" #include "particlemgr.h" #include "materialsystem/imaterialvar.h" #include "cl_animevent.h" #include "particle_util.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- // An entity which emits other entities at points //----------------------------------------------------------------------------- class C_EnvParticleScript : public C_BaseAnimating, public IParticleEffect { public: DECLARE_CLASS( C_EnvParticleScript, C_BaseAnimating ); DECLARE_CLIENTCLASS(); C_EnvParticleScript(); // IParticleEffect overrides. public: virtual bool ShouldSimulate() const { return m_bSimulate; } virtual void SetShouldSimulate( bool bSim ) { m_bSimulate = bSim; } virtual void RenderParticles( CParticleRenderIterator *pIterator ); virtual void SimulateParticles( CParticleSimulateIterator *pIterator ); virtual const Vector &GetSortOrigin(); // C_BaseAnimating overrides public: // NOTE: Ths enclosed particle effect binding will do all the drawing // But we have to return true, unlike other particle systems, for the animation events to work virtual bool ShouldDraw() { return true; } virtual int DrawModel( int flags ) { return 0; } virtual int GetFxBlend( void ) { return 0; } virtual void FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ); virtual void OnPreDataChanged( DataUpdateType_t updateType ); virtual void OnDataChanged( DataUpdateType_t updateType ); private: // Creates, destroys particles attached to an attachment void CreateParticle( const char *pAttachmentName, const char *pSpriteName ); void DestroyAllParticles( const char *pAttachmentName ); void DestroyAllParticles( ); private: struct ParticleScriptParticle_t : public Particle { int m_nAttachment; float m_flSize; }; CParticleEffectBinding m_ParticleEffect; float m_flMaxParticleSize; int m_nOldSequence; float m_flSequenceScale; bool m_bSimulate; }; REGISTER_EFFECT( C_EnvParticleScript ); //----------------------------------------------------------------------------- // Datatable //----------------------------------------------------------------------------- IMPLEMENT_CLIENTCLASS_DT( C_EnvParticleScript, DT_EnvParticleScript, CEnvParticleScript ) RecvPropFloat( RECVINFO(m_flSequenceScale) ), END_RECV_TABLE() //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- C_EnvParticleScript::C_EnvParticleScript() { m_flMaxParticleSize = 0.0f; m_bSimulate = true; } //----------------------------------------------------------------------------- // Check for changed sequence numbers //----------------------------------------------------------------------------- void C_EnvParticleScript::OnPreDataChanged( DataUpdateType_t updateType ) { BaseClass::OnPreDataChanged( updateType ); m_nOldSequence = GetSequence(); } //----------------------------------------------------------------------------- // Starts up the particle system //----------------------------------------------------------------------------- void C_EnvParticleScript::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); if(updateType == DATA_UPDATE_CREATED) { ParticleMgr()->AddEffect( &m_ParticleEffect, this ); } if ( m_nOldSequence != GetSequence() ) { DestroyAllParticles(); } } //----------------------------------------------------------------------------- // Creates, destroys particles attached to an attachment //----------------------------------------------------------------------------- void C_EnvParticleScript::CreateParticle( const char *pAttachmentName, const char *pSpriteName ) { // Find the attachment int nAttachment = LookupAttachment( pAttachmentName ); if ( nAttachment <= 0 ) return; // Get the sprite materials PMaterialHandle hMat = m_ParticleEffect.FindOrAddMaterial( pSpriteName ); ParticleScriptParticle_t *pParticle = (ParticleScriptParticle_t*)m_ParticleEffect.AddParticle(sizeof(ParticleScriptParticle_t), hMat); if ( pParticle == NULL ) return; // Get the sprite size from the material's materialvars bool bFound = false; IMaterialVar *pMaterialVar = NULL; IMaterial *pMaterial = ParticleMgr()->PMaterialToIMaterial( hMat ); if ( pMaterial ) { pMaterialVar = pMaterial->FindVar( "$spritesize", &bFound, false ); } if ( bFound ) { pParticle->m_flSize = pMaterialVar->GetFloatValue(); } else { pParticle->m_flSize = 100.0f; } // Make sure the particle cull size reflects our particles if ( pParticle->m_flSize > m_flMaxParticleSize ) { m_flMaxParticleSize = pParticle->m_flSize; m_ParticleEffect.SetParticleCullRadius( m_flMaxParticleSize ); } // Place the particle on the attachment specified pParticle->m_nAttachment = nAttachment; QAngle vecAngles; GetAttachment( nAttachment, pParticle->m_Pos, vecAngles ); if ( m_flSequenceScale != 1.0f ) { pParticle->m_Pos -= GetAbsOrigin(); pParticle->m_Pos *= m_flSequenceScale; pParticle->m_Pos += GetAbsOrigin(); } } void C_EnvParticleScript::DestroyAllParticles( const char *pAttachmentName ) { int nAttachment = LookupAttachment( pAttachmentName ); if ( nAttachment <= 0 ) return; int nCount = m_ParticleEffect.GetNumActiveParticles(); Particle** ppParticles = (Particle**)stackalloc( nCount * sizeof(Particle*) ); int nActualCount = m_ParticleEffect.GetActiveParticleList( nCount, ppParticles ); Assert( nActualCount == nCount ); for ( int i = 0; i < nActualCount; ++i ) { ParticleScriptParticle_t *pParticle = (ParticleScriptParticle_t*)ppParticles[i]; if ( pParticle->m_nAttachment == nAttachment ) { // Mark for deletion pParticle->m_nAttachment = -1; } } } void C_EnvParticleScript::DestroyAllParticles( ) { int nCount = m_ParticleEffect.GetNumActiveParticles(); Particle** ppParticles = (Particle**)stackalloc( nCount * sizeof(Particle*) ); int nActualCount = m_ParticleEffect.GetActiveParticleList( nCount, ppParticles ); Assert( nActualCount == nCount ); for ( int i = 0; i < nActualCount; ++i ) { ParticleScriptParticle_t *pParticle = (ParticleScriptParticle_t*)ppParticles[i]; // Mark for deletion pParticle->m_nAttachment = -1; } } //----------------------------------------------------------------------------- // The animation events will create particles on the attachment points //----------------------------------------------------------------------------- void C_EnvParticleScript::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options ) { // Handle events to create + destroy particles switch( event ) { case CL_EVENT_SPRITEGROUP_CREATE: { char pAttachmentName[256]; char pSpriteName[256]; int nArgs = sscanf( options, "%255s %255s", pAttachmentName, pSpriteName ); if ( nArgs == 2 ) { CreateParticle( pAttachmentName, pSpriteName ); } } return; case CL_EVENT_SPRITEGROUP_DESTROY: { char pAttachmentName[256]; int nArgs = sscanf( options, "%255s", pAttachmentName ); if ( nArgs == 1 ) { DestroyAllParticles( pAttachmentName ); } } return; } // Fall back BaseClass::FireEvent( origin, angles, event, options ); } //----------------------------------------------------------------------------- // Simulate the particles //----------------------------------------------------------------------------- void C_EnvParticleScript::RenderParticles( CParticleRenderIterator *pIterator ) { const ParticleScriptParticle_t* pParticle = (const ParticleScriptParticle_t*)pIterator->GetFirst(); while ( pParticle ) { Vector vecRenderPos; TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, vecRenderPos ); float sortKey = vecRenderPos.z; Vector color( 1, 1, 1 ); RenderParticle_ColorSize( pIterator->GetParticleDraw(), vecRenderPos, color, 1.0f, pParticle->m_flSize ); pParticle = (const ParticleScriptParticle_t*)pIterator->GetNext( sortKey ); } } void C_EnvParticleScript::SimulateParticles( CParticleSimulateIterator *pIterator ) { ParticleScriptParticle_t* pParticle = (ParticleScriptParticle_t*)pIterator->GetFirst(); while ( pParticle ) { // Here's how we retire particles if ( pParticle->m_nAttachment == -1 ) { pIterator->RemoveParticle( pParticle ); } else { // Move the particle to the attachment point QAngle vecAngles; GetAttachment( pParticle->m_nAttachment, pParticle->m_Pos, vecAngles ); if ( m_flSequenceScale != 1.0f ) { pParticle->m_Pos -= GetAbsOrigin(); pParticle->m_Pos *= m_flSequenceScale; pParticle->m_Pos += GetAbsOrigin(); } } pParticle = (ParticleScriptParticle_t*)pIterator->GetNext(); } } const Vector &C_EnvParticleScript::GetSortOrigin() { return GetAbsOrigin(); }