|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A panel that display particle systems
//
//=============================================================================//
#include "cbase.h"
#include <KeyValues.h>
#include <vgui/IScheme.h>
#include <vgui/ISurface.h>
#include <vgui_controls/EditablePanel.h>
#include "vgui/IVGui.h"
#include "tf_particlepanel.h"
#include "matsys_controls/matsyscontrols.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "tier2/renderutils.h"
#include "renderparm.h"
using namespace vgui;
CTFParticlePanel::ParticleEffect_t::ParticleEffect_t() : m_bLoop( true ) , m_pParticleSystem( NULL ) , m_flLastTime( FLT_MAX ) , m_ParticleSystemName( NULL ) , m_bStartActivated( true ) , m_flScale( 1.f ) , m_flEndTime( FLT_MAX ) , m_nXPos( 0 ) , m_nYPos( 0 ) , m_Angles( 0.f, 0.f, 0.f ) , m_pParent( NULL ) , m_bForceStopped( false ) , m_bAutoDelete( false ) , m_bStarted( false ) {}
DECLARE_BUILD_FACTORY( CTFParticlePanel ); //-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CTFParticlePanel::CTFParticlePanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) { m_Camera.m_flZNear = 3.0f; m_Camera.m_flZFar = 16384.0f * 1.73205080757f; m_Camera.m_flFOV = 30.0f; m_Camera.m_origin = Vector(0,0,0); m_Camera.m_angles = QAngle(0,0,0);
m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" ); m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true ); }
CTFParticlePanel::~CTFParticlePanel() { m_pLightmapTexture.Shutdown(); m_DefaultEnvCubemap.Shutdown(); m_vecParticleEffects.PurgeAndDeleteElements(); }
void CTFParticlePanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData );
KeyValues *pKVParticleEffects = inResourceData->FindKey( "ParticleEffects" ); if ( pKVParticleEffects ) { FOR_EACH_SUBKEY( pKVParticleEffects, pKVEffect ) { m_vecParticleEffects[ m_vecParticleEffects.AddToTail() ] = new ParticleEffect_t(); ParticleEffect_t* pEffect = m_vecParticleEffects.Tail();
// get the position
int alignScreenWide = GetWide(), alignScreenTall = GetTall(); // screen dimensions used for pinning in splitscreen
int x, y; GetPos(x, y); const char *xstr = pKVEffect->GetString( "particle_xpos", NULL ); const char *ystr = pKVEffect->GetString( "particle_ypos", NULL );
if (xstr) { bool bRightAlign = false; bool bCenterAlign = false; // look for alignment flags
if (xstr[0] == 'r' || xstr[0] == 'R') { bRightAlign = true; xstr++; } else if (xstr[0] == 'c' || xstr[0] == 'C') { bCenterAlign = true; xstr++; }
// get the value
x = atoi(xstr); // scale the x up to our screen co-ords
if ( IsProportional() ) { x = scheme()->GetProportionalScaledValueEx(GetScheme(), x); } // now correct the alignment
if ( bRightAlign ) { x = alignScreenWide - x; } else if ( bCenterAlign ) { x = (alignScreenWide / 2) + x; } }
if (ystr) { bool bBottomAlign = false; bool bCenterAlign = false; // look for alignment flags
if (ystr[0] == 'r' || ystr[0] == 'R') { bBottomAlign = true; ystr++; } else if (ystr[0] == 'c' || ystr[0] == 'C') { bCenterAlign = true; ystr++; } y = atoi(ystr); if (IsProportional()) { // scale the y up to our screen co-ords
y = scheme()->GetProportionalScaledValueEx(GetScheme(), y); } // now correct the alignment
if ( bBottomAlign ) { y = alignScreenTall - y; } else if ( bCenterAlign ) { y = (alignScreenTall / 2) + y; } }
pEffect->m_nXPos = x; pEffect->m_nYPos = y;
pEffect->m_flScale = pKVEffect->GetFloat( "particle_scale", 1.f ); // Scale the scale factor the same way we do the XY position coordinates
if( IsProportional() ) { int wide, tall; surface()->GetScreenSize( wide, tall );
int proH, proW; surface()->GetProportionalBase( proW, proH ); double scale = (double)tall / (double)proH; pEffect->m_flScale *= scale; }
pEffect->m_pParent = this; pEffect->m_bLoop = pKVEffect->GetBool( "loop", true ); pEffect->m_bStartActivated = pKVEffect->GetBool( "start_activated", true ); pEffect->SetParticleSystem( pKVEffect->GetString( "particleName" ) );
// Read angles for the particle system
{ float x1,y1,z1; const char* pszAngles = pKVEffect->GetString( "angles" ); if( *pszAngles ) { if( pEffect->m_pParticleSystem && sscanf( pszAngles, "%f %f %f", &x1, &y1, &z1 ) == 3 ) { pEffect->m_Angles = QAngle( x1, y1, z1 ); Quaternion q; AngleQuaternion( pEffect->m_Angles , q ); pEffect->m_pParticleSystem->SetControlPointOrientation( 0, q ); } } }
pEffect->SetControlPointValue( 0, Vector(0,0,0) ); // Read all control point values
const char* pszControlPoint = NULL; int nControlPointNumber = 0; do { pszControlPoint = pKVEffect->GetString( VarArgs("control_point%d", nControlPointNumber), "" ); if ( *pszControlPoint ) { float x2,y2,z2; if (sscanf(pszControlPoint, "%f %f %f", &x2, &y2, &z2 ) == 3) { pEffect->SetControlPointValue( nControlPointNumber, Vector( x2, y2, z2 ) ); } }
++nControlPointNumber; } while( *pszControlPoint ); } } }
//-----------------------------------------------------------------------------
// Scheme
//-----------------------------------------------------------------------------
void CTFParticlePanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme );
SetMouseInputEnabled( false ); SetKeyBoardInputEnabled( false ); }
void CTFParticlePanel::OnCommand( const char *command ) { if ( !Q_strnicmp( command, "start", ARRAYSIZE("start") - 1 ) ) { // Check if they specified a particular one to turn on
const char* pszNum = command + ARRAYSIZE( "start" ) - 1; int nIndex = m_vecParticleEffects.InvalidIndex();
if( pszNum && pszNum[0] ) { nIndex = atoi(pszNum); Assert( nIndex >= 0 && nIndex < m_vecParticleEffects.Count() ); }
FOR_EACH_VEC( m_vecParticleEffects, i ) { if ( nIndex != m_vecParticleEffects.InvalidIndex() && i != nIndex ) continue;
ParticleEffect_t* pEffect = m_vecParticleEffects[ i ];
if( !pEffect->m_pParticleSystem ) { pEffect->SetParticleSystem( pEffect->m_ParticleSystemName ); }
if( !pEffect->m_pParticleSystem ) continue; pEffect->StartupParticleCollection(); pEffect->m_pParticleSystem->StartEmission(); pEffect->m_bForceStopped = false; } } else if ( !Q_strnicmp( command, "stop", ARRAYSIZE("stop") - 1 ) ) { // Check if they specified a specific one to turn off
const char* pszNum = command + ARRAYSIZE( "start" ) - 1; if( pszNum && pszNum[0] ) { int iIndex = atoi(pszNum); Assert( iIndex >= 0 && iIndex < m_vecParticleEffects.Count() );
ParticleEffect_t* pEffect = m_vecParticleEffects[iIndex]; if( pEffect->m_pParticleSystem ) { pEffect->m_pParticleSystem->StopEmission(); pEffect->m_bForceStopped = true; } } else { // Turn them ALL off
FOR_EACH_VEC( m_vecParticleEffects, i ) { ParticleEffect_t* pEffect = m_vecParticleEffects[ i ]; if( pEffect->m_pParticleSystem ) { pEffect->m_pParticleSystem->StopEmission(); pEffect->m_bForceStopped = true; } } } } }
void CTFParticlePanel::FireParticleEffect( const char *pszName, int xPos, int yPos, float flScale, bool bLoop, float flEndTime ) { m_vecParticleEffects[ m_vecParticleEffects.AddToTail() ] = new ParticleEffect_t(); ParticleEffect_t* pEffect = m_vecParticleEffects.Tail();
int iParentAbsX, iParentAbsY; vgui::ipanel()->GetAbsPos( GetParent()->GetVPanel(), iParentAbsX, iParentAbsY );
pEffect->m_pParent = this; pEffect->m_nXPos = xPos - iParentAbsX; pEffect->m_nYPos = yPos - iParentAbsY; pEffect->m_flScale = flScale; pEffect->m_bLoop = bLoop; pEffect->m_bAutoDelete = true; // This will get automatically deleted once it stops
pEffect->m_bStartActivated = true; pEffect->m_flEndTime = gpGlobals->curtime + flEndTime;
if( IsProportional() ) { int wide, tall; surface()->GetScreenSize( wide, tall );
int proH, proW; surface()->GetProportionalBase( proW, proH ); double scale = (double)tall / (double)proH; pEffect->m_flScale *= scale; }
for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) { pEffect->SetControlPointValue( i, Vector( 0, 0, 10.0f * i ) ); } pEffect->SetParticleSystem( pszName ); }
static bool IsValidHierarchy( CParticleCollection *pCollection ) { if ( !pCollection->IsValid() ) return false;
for( CParticleCollection *pChild = pCollection->m_Children.m_pHead; pChild; pChild = pChild->m_pNext ) { if ( !IsValidHierarchy( pChild ) ) return false; } return true; }
//-----------------------------------------------------------------------------
// Simulate the particle system
//-----------------------------------------------------------------------------
void CTFParticlePanel::OnTick() { BaseClass::OnTick(); float flTime = engine->Time();
bool bAnyActive = false; // Update all particles
FOR_EACH_VEC_BACK( m_vecParticleEffects, i ) { bAnyActive |= m_vecParticleEffects[i]->Update( flTime ); // If this effect is done and should auto-delete, then now is when we delete
if( m_vecParticleEffects[i]->m_pParticleSystem == NULL && m_vecParticleEffects[i]->m_bAutoDelete ) { delete m_vecParticleEffects[i]; m_vecParticleEffects.FastRemove( i ); } }
if ( !bAnyActive ) { vgui::ivgui()->RemoveTickSignal( GetVPanel() ); } }
void CTFParticlePanel::Paint() { // This needs calling to reset various counters.
g_pParticleSystemMgr->SetLastSimulationTime( gpGlobals->curtime );
// No particles? Do nothing.
if( m_vecParticleEffects.Count() == 0 ) return;
int screenW, screenH; vgui::surface()->GetScreenSize( screenW, screenH );
vgui::MatSystemSurface()->Begin3DPaint( 0, 0, screenW, screenH, false );
VMatrix view, projection; ComputeViewMatrix( &view, m_Camera ); ComputeProjectionMatrix( &projection, m_Camera, screenW, screenH );
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->CullMode( MATERIAL_CULLMODE_CCW ); pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, false );
pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->LoadIdentity( );
pRenderContext->MatrixMode( MATERIAL_VIEW ); pRenderContext->LoadMatrix( view );
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->LoadMatrix( projection );
int iXOffset, iYOffset; vgui::ipanel()->GetAbsPos( GetVPanel(), iXOffset, iYOffset ); if ( iXOffset > 0 ) iXOffset = 0; if ( iYOffset > 0 ) iYOffset = 0;
float flXScale = 1.f; if ( GetWide() > screenW ) flXScale = (float)screenW / GetWide(); float flYScale = 1.f; if ( GetTall() > screenH ) flYScale = (float)screenH / GetTall();
FOR_EACH_VEC( m_vecParticleEffects, i ) { m_vecParticleEffects[i]->Paint( pRenderContext, iXOffset, iYOffset, flXScale, flYScale, screenW, screenH ); }
pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
vgui::MatSystemSurface()->End3DPaint(); }
bool CTFParticlePanel::ParticleEffect_t::Update( float flTime ) { if ( !m_pParticleSystem || !m_bStarted ) return false;
if ( m_flLastTime == FLT_MAX ) { m_flLastTime = flTime; }
float flDt = flTime - m_flLastTime; m_flLastTime = flTime;
Quaternion q; AngleQuaternion( m_Angles, q );
for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i ) { if ( !m_pParticleSystem->ReadsControlPoint( i ) ) continue;
m_pParticleSystem->SetControlPoint( i, m_pControlPointValue[i] ); m_pParticleSystem->SetControlPointOrientation( i, q ); m_pParticleSystem->SetControlPointParent( i, i ); }
// Restart the particle system if it's finished
bool bIsInvalid = !IsValidHierarchy( m_pParticleSystem );
if ( !bIsInvalid ) { m_pParticleSystem->Simulate( flDt, false ); }
// Past our end time?
bool bEnd = gpGlobals->curtime >= m_flEndTime;
if ( m_pParticleSystem->IsFinished() || bIsInvalid || bEnd ) { delete m_pParticleSystem; m_pParticleSystem = NULL;
// Loop if we're supposed to
if ( m_bLoop && m_ParticleSystemName.Length() && !m_bForceStopped ) { m_pParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( m_ParticleSystemName ); }
if ( bIsInvalid && m_pParent ) { m_pParent->PostActionSignal( new KeyValues( "ParticleSystemReconstructed" ) ); } m_flLastTime = FLT_MAX; }
return m_pParticleSystem != NULL; }
//-----------------------------------------------------------------------------
// Startup, shutdown particle collection
//-----------------------------------------------------------------------------
void CTFParticlePanel::ParticleEffect_t::StartupParticleCollection() { if ( m_pParticleSystem && m_pParent ) { vgui::ivgui()->AddTickSignal( m_pParent->GetVPanel(), 0 ); } m_flLastTime = FLT_MAX; m_bStarted = true; }
void CTFParticlePanel::ParticleEffect_t::ShutdownParticleCollection() { if ( m_pParticleSystem && m_pParent ) { delete m_pParticleSystem; m_pParticleSystem = NULL; } m_bStarted = false; }
//-----------------------------------------------------------------------------
// Set the particle system to draw
//-----------------------------------------------------------------------------
void CTFParticlePanel::ParticleEffect_t::SetParticleSystem( const char* pszParticleSystemName ) { ShutdownParticleCollection();
if( !g_pParticleSystemMgr->IsParticleSystemDefined( pszParticleSystemName ) ) { AssertMsg1( false, "%s is not a valid particle system name", pszParticleSystemName ); return; } m_pParticleSystem = g_pParticleSystemMgr->CreateParticleCollection( pszParticleSystemName ); m_ParticleSystemName = pszParticleSystemName;
m_pParent->PostActionSignal( new KeyValues( "ParticleSystemReconstructed" ) ); if( m_bStartActivated ) { StartupParticleCollection(); } }
void CTFParticlePanel::ParticleEffect_t::Paint( CMatRenderContextPtr& pRenderContext, int iXOffset, int iYOffset, float flXScale, float flYScale, int screenW, int screenH ) { if ( !m_pParticleSystem || !m_bStarted ) return;
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity(); pRenderContext->Ortho( 0, 0, screenW, screenH, -9999, 9999 );
pRenderContext->Translate( flXScale * ( m_nXPos + iXOffset ), screenH - flYScale * ( m_nYPos + iYOffset ), 0.f ); pRenderContext->Scale( m_flScale, m_flScale, m_flScale );
// Render Particles
pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity( );
m_pParticleSystem->Render( pRenderContext );
pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); pRenderContext->PopMatrix(); }
|