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.
556 lines
15 KiB
556 lines
15 KiB
//========= 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(); |
|
} |