Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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

5 years ago
//========= 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();
}