784 lines
24 KiB
C++
784 lines
24 KiB
C++
//===== Copyright © 1996-2006, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "particlemgr.h"
|
|
#include "particles_new.h"
|
|
#include "iclientmode.h"
|
|
#include "engine/ivdebugoverlay.h"
|
|
#include "particle_property.h"
|
|
#include "toolframework/itoolframework.h"
|
|
#include "toolframework_client.h"
|
|
#include "tier1/keyvalues.h"
|
|
#include "model_types.h"
|
|
#include "vprof.h"
|
|
#include "datacache/iresourceaccesscontrol.h"
|
|
#include "tier2/tier2.h"
|
|
#include "viewrender.h"
|
|
|
|
#if defined( PORTAL )
|
|
#include "c_portal_player.h"
|
|
#endif
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#ifdef PORTAL
|
|
#include "portalrender.h"
|
|
#endif
|
|
|
|
|
|
extern ConVar cl_particleeffect_aabb_buffer;
|
|
extern ConVar cl_particles_show_bbox;
|
|
ConVar cl_particles_show_controlpoints( "cl_particles_show_controlpoints", "0", FCVAR_CHEAT );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, CParticleSystemDefinition *pEffect )
|
|
{
|
|
m_hOwner = pOwner;
|
|
if ( g_pResourceAccessControl )
|
|
{
|
|
if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_PARTICLE_SYSTEM, pEffect->GetName() ) )
|
|
{
|
|
pEffect = g_pParticleSystemMgr->FindParticleSystem( "error" );
|
|
}
|
|
}
|
|
Init( pEffect );
|
|
Construct();
|
|
}
|
|
|
|
CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, const char* pEffectName )
|
|
{
|
|
m_hOwner = pOwner;
|
|
if ( g_pResourceAccessControl )
|
|
{
|
|
if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_PARTICLE_SYSTEM, pEffectName ) )
|
|
{
|
|
pEffectName = "error";
|
|
}
|
|
}
|
|
Init( pEffectName );
|
|
Construct();
|
|
}
|
|
|
|
CNewParticleEffect::CNewParticleEffect( CBaseEntity *pOwner, int nPrecacheIndex )
|
|
{
|
|
m_hOwner = pOwner;
|
|
CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
|
|
if ( g_pResourceAccessControl )
|
|
{
|
|
if ( !g_pResourceAccessControl->IsAccessAllowed( RESOURCE_PARTICLE_SYSTEM, pDef->GetName() ) )
|
|
{
|
|
// This is the error effect
|
|
pDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
|
|
}
|
|
}
|
|
|
|
Init( pDef );
|
|
Construct();
|
|
}
|
|
|
|
static ConVar cl_aggregate_particles( "cl_aggregate_particles", "1" );
|
|
|
|
CNewParticleEffect *CNewParticleEffect::CreateOrAggregate( CBaseEntity *pOwner, CParticleSystemDefinition *pDef,
|
|
Vector const &vecAggregatePosition, const char *pDebugName,
|
|
int nSplitScreenSlot )
|
|
{
|
|
if (!pDef) { return NULL; }
|
|
|
|
CNewParticleEffect *pAggregateTarget = NULL;
|
|
// see if we should aggregate
|
|
bool bCanAggregate = ( pOwner == NULL ) && ( pDef->m_flAggregateRadius > 0.0 ) && ( cl_aggregate_particles.GetInt() != 0 );
|
|
if ( bCanAggregate )
|
|
{
|
|
CParticleSystemDefinition *pDefFallback = pDef;
|
|
do
|
|
{
|
|
float flAggregateDistSqr = ( pDefFallback->m_flAggregateRadius * pDefFallback->m_flAggregateRadius ) + 0.1;
|
|
for( CParticleCollection *pSystem = pDefFallback->FirstCollection(); pSystem; pSystem = pSystem->GetNextCollectionUsingSameDef() )
|
|
{
|
|
CNewParticleEffect *pEffectCheck = static_cast<CNewParticleEffect *>( pSystem );
|
|
if ( ! pEffectCheck->m_bDisableAggregation )
|
|
{
|
|
float flDistSQ = vecAggregatePosition.DistToSqr( pEffectCheck->m_vecAggregationCenter );
|
|
if ( ( flDistSQ < flAggregateDistSqr ) &&
|
|
( pSystem->m_nMaxAllowedParticles - pSystem->m_nActiveParticles > pDefFallback->m_nAggregationMinAvailableParticles ) &&
|
|
( pEffectCheck->m_nSplitScreenUser == nSplitScreenSlot ) )
|
|
{
|
|
flAggregateDistSqr = flDistSQ;
|
|
pAggregateTarget = pEffectCheck;
|
|
}
|
|
}
|
|
}
|
|
pDefFallback = pDefFallback->GetFallbackReplacementDefinition();
|
|
} while ( pDefFallback );
|
|
}
|
|
if ( ! pAggregateTarget )
|
|
{
|
|
// we need a new one
|
|
pAggregateTarget = new CNewParticleEffect( pOwner, pDef );
|
|
pAggregateTarget->SetDrawOnlyForSplitScreenUser( nSplitScreenSlot );
|
|
pAggregateTarget->SetDynamicallyAllocated( true );
|
|
}
|
|
else
|
|
{
|
|
// just reset the old one
|
|
pAggregateTarget->Restart( RESTART_RESET_AND_MAKE_SURE_EMITS_HAPPEN );
|
|
}
|
|
if ( bCanAggregate )
|
|
{
|
|
pAggregateTarget->m_vecAggregationCenter = vecAggregatePosition;
|
|
}
|
|
pAggregateTarget->m_pDebugName = pDebugName;
|
|
pAggregateTarget->m_bDisableAggregation = false;
|
|
return pAggregateTarget;
|
|
|
|
}
|
|
|
|
void CNewParticleEffect::RemoveParticleEffect( int nPrecacheIndex )
|
|
{
|
|
CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
|
|
|
|
if ( pDef == NULL )
|
|
return;
|
|
|
|
for( CParticleCollection *pSystem = pDef->FirstCollection(); pSystem; pSystem = pSystem->GetNextCollectionUsingSameDef() )
|
|
{
|
|
CNewParticleEffect *pEffectCheck = static_cast<CNewParticleEffect *>( pSystem );
|
|
|
|
if ( pEffectCheck )
|
|
{
|
|
pEffectCheck->SetRemoveFlag();
|
|
}
|
|
}
|
|
}
|
|
|
|
CNewParticleEffect *CNewParticleEffect::CreateOrAggregate(
|
|
CBaseEntity *pOwner, const char *pParticleSystemName,
|
|
Vector const &vecAggregatePosition,
|
|
const char *pDebugName,
|
|
int nSplitScreenUser )
|
|
{
|
|
CParticleSystemDefinition *pDef = g_pParticleSystemMgr->FindParticleSystem( pParticleSystemName );
|
|
if ( !pDef )
|
|
{
|
|
Warning( "Attempted to create unknown particle system type \"%s\"!\n", pParticleSystemName );
|
|
pDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
|
|
}
|
|
return CreateOrAggregate( pOwner, pDef, vecAggregatePosition, pDebugName, nSplitScreenUser );
|
|
}
|
|
|
|
CNewParticleEffect *CNewParticleEffect::CreateOrAggregatePrecached(
|
|
CBaseEntity *pOwner, int nPrecacheIndex,
|
|
Vector const &vecAggregatePosition, const char *pDebugName,
|
|
int nSplitScreenUser )
|
|
{
|
|
CParticleSystemDefinition* pDef = g_pParticleSystemMgr->FindPrecachedParticleSystem( nPrecacheIndex );
|
|
if ( !pDef )
|
|
{
|
|
Warning( "Attempted to create unknown particle system type \"%s\"!\n", GetParticleSystemNameFromIndex( nPrecacheIndex ) );
|
|
pDef = g_pParticleSystemMgr->FindParticleSystem( "error" );
|
|
}
|
|
return CreateOrAggregate( pOwner, pDef, vecAggregatePosition, pDebugName, nSplitScreenUser );
|
|
}
|
|
|
|
|
|
void CNewParticleEffect::Construct()
|
|
{
|
|
m_vSortOrigin.Init();
|
|
|
|
m_bDontRemove = false;
|
|
m_bRemove = false;
|
|
m_bDrawn = false;
|
|
m_bNeedsBBoxUpdate = false;
|
|
m_bIsFirstFrame = true;
|
|
m_bAutoUpdateBBox = false;
|
|
m_bAllocated = true;
|
|
m_bSimulate = true;
|
|
m_bRecord = false;
|
|
m_bShouldPerformCullCheck = false;
|
|
m_bDisableAggregation = true; // will be reset when someone creates it via CreateOrAggregate
|
|
|
|
|
|
m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
|
|
m_RefCount = 0;
|
|
ParticleMgr()->AddEffect( this );
|
|
m_LastMax = Vector( -1.0e6, -1.0e6, -1.0e6 );
|
|
m_LastMin = Vector( 1.0e6, 1.0e6, 1.0e6 );
|
|
m_MinBounds = Vector( 1.0e6, 1.0e6, 1.0e6 );
|
|
m_MaxBounds = Vector( -1.0e6, -1.0e6, -1.0e6 );
|
|
m_pDebugName = NULL;
|
|
m_nSplitScreenUser = -1;
|
|
|
|
SetRenderable( this );
|
|
|
|
RecordCreation();
|
|
}
|
|
|
|
CNewParticleEffect::~CNewParticleEffect(void)
|
|
{
|
|
if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
|
|
{
|
|
static ParticleSystemDestroyedState_t state;
|
|
state.m_nParticleSystemId = GetToolParticleEffectId();
|
|
state.m_flTime = gpGlobals->curtime;
|
|
|
|
KeyValues *msg = new KeyValues( "ParticleSystem_Destroy" );
|
|
msg->SetPtr( "state", &state );
|
|
|
|
ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
|
|
m_nToolParticleEffectId = TOOLPARTICLESYSTEMID_INVALID;
|
|
}
|
|
|
|
m_bAllocated = false;
|
|
if ( m_hOwner )
|
|
{
|
|
// NOTE: This can provoke another NotifyRemove call which is why flags is set to 0
|
|
m_hOwner->ParticleProp()->OnParticleSystemDeleted( this );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Refcounting
|
|
//-----------------------------------------------------------------------------
|
|
void CNewParticleEffect::AddRef()
|
|
{
|
|
++m_RefCount;
|
|
}
|
|
|
|
void CNewParticleEffect::Release()
|
|
{
|
|
Assert( m_RefCount > 0 );
|
|
--m_RefCount;
|
|
|
|
// If all the particles are already gone, delete ourselves now.
|
|
// If there are still particles, wait for the last NotifyDestroyParticle.
|
|
if ( m_RefCount == 0 )
|
|
{
|
|
if ( m_bAllocated )
|
|
{
|
|
if ( IsFinished() )
|
|
{
|
|
SetRemoveFlag();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CNewParticleEffect::NotifyRemove()
|
|
{
|
|
if ( m_bAllocated )
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
int CNewParticleEffect::IsReleased()
|
|
{
|
|
return m_RefCount == 0;
|
|
}
|
|
|
|
|
|
void CNewParticleEffect::SetOwner( CBaseEntity *pOwner )
|
|
{
|
|
if ( m_hOwner.Get() != pOwner )
|
|
{
|
|
m_hOwner = pOwner;
|
|
ClientLeafSystem()->RenderInFastReflections( RenderHandle(), pOwner ? pOwner->IsRenderingInFastReflections() : false );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Refraction and soft particle support
|
|
//-----------------------------------------------------------------------------
|
|
int CNewParticleEffect::GetRenderFlags( void )
|
|
{
|
|
int nRet = 0;
|
|
// NOTE: Need to do this because CParticleCollection's version is non-virtual
|
|
if ( CParticleCollection::UsesPowerOfTwoFrameBufferTexture( true ) )
|
|
nRet |= ERENDERFLAGS_NEEDS_POWER_OF_TWO_FB | ERENDERFLAGS_REFRACT_ONLY_ONCE_PER_FRAME;
|
|
if ( CParticleCollection::UsesFullFrameBufferTexture( true ) )
|
|
nRet |= ERENDERFLAGS_NEEDS_FULL_FB;
|
|
return nRet;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Overrides for recording
|
|
//-----------------------------------------------------------------------------
|
|
void CNewParticleEffect::StopEmission( bool bInfiniteOnly, bool bRemoveAllParticles, bool bWakeOnStop, bool bPlayEndCap )
|
|
{
|
|
if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
|
|
{
|
|
KeyValues *msg = new KeyValues( "ParticleSystem_StopEmission" );
|
|
|
|
static ParticleSystemStopEmissionState_t state;
|
|
state.m_nParticleSystemId = GetToolParticleEffectId();
|
|
state.m_flTime = gpGlobals->curtime;
|
|
state.m_bInfiniteOnly = bInfiniteOnly;
|
|
|
|
msg->SetPtr( "state", &state );
|
|
ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
|
|
}
|
|
|
|
CParticleCollection::StopEmission( bInfiniteOnly, bRemoveAllParticles, bWakeOnStop, bPlayEndCap );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CNewParticleEffect::SetDormant( bool bDormant )
|
|
{
|
|
CParticleCollection::SetDormant( bDormant );
|
|
}
|
|
|
|
void CNewParticleEffect::SetControlPointEntity( int nWhichPoint, CBaseEntity *pEntity )
|
|
{
|
|
if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
|
|
{
|
|
static ParticleSystemSetControlPointObjectState_t state;
|
|
state.m_nParticleSystemId = GetToolParticleEffectId();
|
|
state.m_flTime = gpGlobals->curtime;
|
|
state.m_nControlPoint = nWhichPoint;
|
|
state.m_nObject = pEntity ? pEntity->entindex() : -1;
|
|
|
|
KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointObject" );
|
|
msg->SetPtr( "state", &state );
|
|
ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
|
|
}
|
|
|
|
if ( pEntity )
|
|
{
|
|
CParticleCollection::SetControlPointObject( nWhichPoint, &m_hControlPointOwners[ nWhichPoint ] );
|
|
m_hControlPointOwners[ nWhichPoint ] = pEntity;
|
|
}
|
|
else
|
|
CParticleCollection::SetControlPointObject( nWhichPoint, NULL );
|
|
}
|
|
|
|
|
|
void CNewParticleEffect::SetControlPoint( int nWhichPoint, const Vector &v )
|
|
{
|
|
if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
|
|
{
|
|
static ParticleSystemSetControlPointPositionState_t state;
|
|
state.m_nParticleSystemId = GetToolParticleEffectId();
|
|
state.m_flTime = gpGlobals->curtime;
|
|
state.m_nControlPoint = nWhichPoint;
|
|
state.m_vecPosition = v;
|
|
|
|
KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointPosition" );
|
|
msg->SetPtr( "state", &state );
|
|
ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
|
|
}
|
|
|
|
CParticleCollection::SetControlPoint( nWhichPoint, v );
|
|
}
|
|
|
|
void CNewParticleEffect::SetToolRecording( bool bRecord )
|
|
{
|
|
if ( bRecord == m_bRecord )
|
|
return;
|
|
|
|
m_bRecord = bRecord;
|
|
|
|
if ( m_bRecord )
|
|
{
|
|
RecordCreation();
|
|
}
|
|
}
|
|
|
|
void CNewParticleEffect::RecordCreation()
|
|
{
|
|
if ( IsValid() && clienttools->IsInRecordingMode() )
|
|
{
|
|
m_bRecord = true;
|
|
|
|
int nId = AllocateToolParticleEffectId();
|
|
|
|
static ParticleSystemCreatedState_t state;
|
|
state.m_nParticleSystemId = nId;
|
|
state.m_flTime = gpGlobals->curtime;
|
|
state.m_pName = m_pDef->GetName();
|
|
state.m_nOwner = m_hOwner ? m_hOwner->entindex() : -1;
|
|
|
|
KeyValues *msg = new KeyValues( "ParticleSystem_Create" );
|
|
msg->SetPtr( "state", &state );
|
|
ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
|
|
}
|
|
}
|
|
|
|
void CNewParticleEffect::RecordControlPointOrientation( int nWhichPoint )
|
|
{
|
|
if ( m_bRecord && m_nToolParticleEffectId != TOOLPARTICLESYSTEMID_INVALID && clienttools->IsInRecordingMode() )
|
|
{
|
|
// FIXME: Make a more direct way of getting
|
|
QAngle angles;
|
|
VectorAngles( ControlPoint( nWhichPoint ).m_ForwardVector, ControlPoint( nWhichPoint ).m_UpVector, angles );
|
|
|
|
static ParticleSystemSetControlPointOrientationState_t state;
|
|
state.m_nParticleSystemId = GetToolParticleEffectId();
|
|
state.m_flTime = gpGlobals->curtime;
|
|
state.m_nControlPoint = nWhichPoint;
|
|
AngleQuaternion( angles, state.m_qOrientation );
|
|
|
|
KeyValues *msg = new KeyValues( "ParticleSystem_SetControlPointOrientation" );
|
|
msg->SetPtr( "state", &state );
|
|
ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
|
|
}
|
|
}
|
|
|
|
void CNewParticleEffect::SetControlPointOrientation( int nWhichPoint,
|
|
const Vector &forward, const Vector &right, const Vector &up )
|
|
{
|
|
CParticleCollection::SetControlPointOrientation( nWhichPoint, forward, right, up );
|
|
RecordControlPointOrientation( nWhichPoint );
|
|
}
|
|
|
|
void CNewParticleEffect::SetControlPointOrientation( int nWhichPoint, const Quaternion &q )
|
|
{
|
|
CParticleCollection::SetControlPointOrientation( nWhichPoint, q );
|
|
RecordControlPointOrientation( nWhichPoint );
|
|
}
|
|
|
|
void CNewParticleEffect::SetControlPointForwardVector( int nWhichPoint, const Vector &v )
|
|
{
|
|
CParticleCollection::SetControlPointForwardVector( nWhichPoint, v );
|
|
RecordControlPointOrientation( nWhichPoint );
|
|
}
|
|
|
|
void CNewParticleEffect::SetControlPointUpVector( int nWhichPoint, const Vector &v )
|
|
{
|
|
CParticleCollection::SetControlPointUpVector( nWhichPoint, v );
|
|
RecordControlPointOrientation( nWhichPoint );
|
|
}
|
|
|
|
void CNewParticleEffect::SetControlPointRightVector( int nWhichPoint, const Vector &v )
|
|
{
|
|
CParticleCollection::SetControlPointRightVector( nWhichPoint, v );
|
|
RecordControlPointOrientation( nWhichPoint );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Called when the particle effect is about to update
|
|
//-----------------------------------------------------------------------------
|
|
void CNewParticleEffect::Update( float flTimeDelta )
|
|
{
|
|
if ( m_hOwner )
|
|
{
|
|
m_hOwner->ParticleProp()->OnParticleSystemUpdated( this, flTimeDelta );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Bounding box
|
|
//-----------------------------------------------------------------------------
|
|
CNewParticleEffect* CNewParticleEffect::ReplaceWith( const char *pParticleSystemName )
|
|
{
|
|
StopEmission( false, true, true );
|
|
if ( !pParticleSystemName || !pParticleSystemName[0] )
|
|
return NULL;
|
|
|
|
CNewParticleEffect *pNewEffect = CNewParticleEffect::Create( GetOwner(), pParticleSystemName, pParticleSystemName );
|
|
if ( !pNewEffect )
|
|
return NULL;
|
|
|
|
// Copy over the control point data
|
|
for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
|
|
{
|
|
if ( !ReadsControlPoint( i ) )
|
|
continue;
|
|
|
|
Vector vecForward, vecRight, vecUp;
|
|
pNewEffect->SetControlPoint( i, GetControlPointAtCurrentTime( i ) );
|
|
GetControlPointOrientationAtCurrentTime( i, &vecForward, &vecRight, &vecUp );
|
|
pNewEffect->SetControlPointOrientation( i, vecForward, vecRight, vecUp );
|
|
pNewEffect->SetControlPointParent( i, GetControlPointParent( i ) );
|
|
}
|
|
|
|
if ( m_hOwner )
|
|
{
|
|
m_hOwner->ParticleProp()->ReplaceParticleEffect( this, pNewEffect );
|
|
}
|
|
|
|
// fixup any other references to the old system, to point to the new system
|
|
while( m_References.m_pHead )
|
|
{
|
|
// this will remove the reference from m_References
|
|
m_References.m_pHead->Set( pNewEffect );
|
|
}
|
|
|
|
// At this point any references should have been redirected,
|
|
// but we may still be running with some stray particles, so we
|
|
// might not be flagged for removal - force the issue!
|
|
Assert( m_RefCount == 0 );
|
|
SetRemoveFlag();
|
|
|
|
return pNewEffect;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Bounding box
|
|
//-----------------------------------------------------------------------------
|
|
void CNewParticleEffect::SetParticleCullRadius( float radius )
|
|
{
|
|
}
|
|
|
|
bool CNewParticleEffect::RecalculateBoundingBox()
|
|
{
|
|
BloatBoundsUsingControlPoint();
|
|
if ( !m_bBoundsValid )
|
|
{
|
|
m_MaxBounds = m_MinBounds = GetRenderOrigin();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CNewParticleEffect::GetRenderBounds( Vector& mins, Vector& maxs )
|
|
{
|
|
if ( !m_bBoundsValid )
|
|
{
|
|
mins = vec3_origin;
|
|
maxs = mins;
|
|
return;
|
|
}
|
|
VectorSubtract( m_MinBounds, GetRenderOrigin(), mins );
|
|
VectorSubtract( m_MaxBounds, GetRenderOrigin(), maxs );
|
|
}
|
|
|
|
void CNewParticleEffect::DetectChanges()
|
|
{
|
|
// if we have no render handle, return
|
|
if ( m_hRenderHandle == INVALID_CLIENT_RENDER_HANDLE )
|
|
return;
|
|
|
|
// Turn off rendering if the bounds aren't valid
|
|
g_pClientLeafSystem->EnableRendering( m_hRenderHandle, m_bBoundsValid );
|
|
|
|
if ( !m_bBoundsValid )
|
|
{
|
|
m_LastMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
|
|
m_LastMin.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
|
|
return;
|
|
}
|
|
|
|
if ( m_MinBounds != m_LastMin || m_MaxBounds != m_LastMax )
|
|
{
|
|
// call leafsystem to update this guy
|
|
ClientLeafSystem()->RenderableChanged( m_hRenderHandle );
|
|
|
|
// remember last parameters
|
|
m_LastMin = m_MinBounds;
|
|
m_LastMax = m_MaxBounds;
|
|
}
|
|
}
|
|
|
|
extern ConVar r_DrawParticles;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Rendering
|
|
//-----------------------------------------------------------------------------
|
|
int CNewParticleEffect::DrawModel( int flags, const RenderableInstance_t &instance )
|
|
{
|
|
VPROF_BUDGET( "CNewParticleEffect::DrawModel", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
|
|
if ( r_DrawParticles.GetBool() == false )
|
|
return 0;
|
|
|
|
if ( !GetClientMode()->ShouldDrawParticles() || !ParticleMgr()->ShouldRenderParticleSystems() )
|
|
return 0;
|
|
|
|
if ( flags & ( STUDIO_SHADOWDEPTHTEXTURE | STUDIO_SSAODEPTHTEXTURE ) )
|
|
return 0;
|
|
|
|
if ( m_hOwner && m_hOwner->IsDormant() )
|
|
return 0;
|
|
|
|
int nViewRecursionLevel = 0;
|
|
#ifdef PORTAL
|
|
nViewRecursionLevel = g_pPortalRender->GetViewRecursionLevel();
|
|
if ( m_pDef->GetMaxRecursionDepth() < nViewRecursionLevel )
|
|
{
|
|
//DevMsg( "---Aborted at Particle Portal Recursion Level : %d - Max : %d\n", g_pPortalRender->GetViewRecursionLevel(), m_pDef->GetMaxRecursionDepth() );
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
// do distance cull check here. We do it here instead of in particles so we can easily only do
|
|
// it for root objects, not bothering to cull children individually
|
|
CMatRenderContextPtr pRenderContext( materials );
|
|
if ( !m_pDef->IsScreenSpaceEffect() && !m_pDef->IsViewModelEffect() )
|
|
{
|
|
Vector vecCamera;
|
|
pRenderContext->GetWorldSpaceCameraPosition( &vecCamera );
|
|
if ( ( CalcSqrDistanceToAABB( m_MinBounds, m_MaxBounds, vecCamera ) > ( m_pDef->m_flMaxDrawDistance * m_pDef->m_flMaxDrawDistance ) ) )
|
|
return 0;
|
|
}
|
|
|
|
if ( m_pDef->IsViewModelEffect() )
|
|
{
|
|
C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
|
|
if ( !pPlayer || !pPlayer->IsAlive() )
|
|
return 0;
|
|
|
|
C_BaseAnimating *pRenderedWeaponModel = pPlayer->GetRenderedWeaponModel();
|
|
if ( !pRenderedWeaponModel || !pRenderedWeaponModel->IsViewModel() )
|
|
return 0;
|
|
}
|
|
|
|
if ( ( flags & STUDIO_TRANSPARENCY ) || !IsBatchable() || !m_pDef->IsDrawnThroughLeafSystem() )
|
|
{
|
|
C_BaseEntity *pCameraObject = GetSplitScreenViewPlayer();
|
|
if( ((C_BasePlayer *)pCameraObject)->GetViewEntity() )
|
|
{
|
|
pCameraObject = ((C_BasePlayer *)pCameraObject)->GetViewEntity();
|
|
}
|
|
|
|
C_BaseEntity *pSkipRenderObject = ( m_pDef->m_nSkipRenderControlPoint != -1 ) ? GetControlPointEntity( m_pDef->m_nSkipRenderControlPoint ).Get() : nullptr;
|
|
C_BaseEntity *pAllowRenderObject = ( m_pDef->m_nAllowRenderControlPoint != -1 ) ? GetControlPointEntity( m_pDef->m_nAllowRenderControlPoint ).Get() : nullptr;
|
|
if ( pSkipRenderObject && ( pSkipRenderObject->IsBaseCombatWeapon() || ( pSkipRenderObject->GetBaseAnimating() && pSkipRenderObject->GetBaseAnimating()->IsViewModel() ) ) )
|
|
pSkipRenderObject = pSkipRenderObject->GetOwnerEntity();
|
|
if ( pAllowRenderObject && ( pAllowRenderObject->IsBaseCombatWeapon() || ( pAllowRenderObject->GetBaseAnimating() && pAllowRenderObject->GetBaseAnimating()->IsViewModel() ) ) )
|
|
pAllowRenderObject = pAllowRenderObject->GetOwnerEntity();
|
|
|
|
// apply logic that lets you skip rendering a system if the camera is attached to its entity
|
|
if ( pCameraObject )
|
|
{
|
|
if ( pCameraObject == pSkipRenderObject )
|
|
{
|
|
#if defined( PORTAL )
|
|
if ( pSkipRenderObject->IsPlayer() )
|
|
{
|
|
if ( ((C_Portal_Player *)pSkipRenderObject)->ShouldSkipRenderingViewpointPlayerForThisView() )
|
|
return 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if ( nViewRecursionLevel == 0 )
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ( pAllowRenderObject && (pCameraObject != pAllowRenderObject) )
|
|
return 0;
|
|
}
|
|
|
|
Vector4D vecDiffuseModulation( 1.0f, 1.0f, 1.0f, 1.0f ); //instance.m_nAlpha / 255.0f );
|
|
pRenderContext->MatrixMode( MATERIAL_MODEL );
|
|
pRenderContext->PushMatrix();
|
|
pRenderContext->LoadIdentity();
|
|
Render( nViewRecursionLevel, pRenderContext, vecDiffuseModulation, ( flags & STUDIO_TRANSPARENCY ) ? IsTwoPass() : false, pCameraObject );
|
|
pRenderContext->MatrixMode( MATERIAL_MODEL );
|
|
pRenderContext->PopMatrix();
|
|
}
|
|
else
|
|
{
|
|
g_pParticleSystemMgr->AddToRenderCache( this );
|
|
}
|
|
|
|
if ( cl_particles_show_bbox.GetBool() )
|
|
{
|
|
Vector center = GetRenderOrigin();
|
|
Vector mins = m_MinBounds - center;
|
|
Vector maxs = m_MaxBounds - center;
|
|
|
|
int r, g;
|
|
if ( GetAutoUpdateBBox() )
|
|
{
|
|
// red is bad, the bbox update is costly
|
|
r = 255;
|
|
g = 0;
|
|
}
|
|
else
|
|
{
|
|
// green, this effect presents less cpu load
|
|
r = 0;
|
|
g = 255;
|
|
}
|
|
Vector vecCurCPPos = GetControlPointAtCurrentTime( 0 );
|
|
debugoverlay->AddBoxOverlay( center, mins, maxs, QAngle( 0, 0, 0 ), r, g, 0, 16, 0 );
|
|
debugoverlay->AddTextOverlayRGB( center, 0, 0, r, g, 0, 64, "%s:(%d)", GetEffectName(),
|
|
m_nActiveParticles );
|
|
}
|
|
if ( cl_particles_show_controlpoints.GetBool() )
|
|
{
|
|
for ( int i = 0; i < MAX_PARTICLE_CONTROL_POINTS; ++i )
|
|
{
|
|
if ( ReadsControlPoint( i ) )
|
|
{
|
|
Vector vecPos = GetControlPointAtCurrentTime( i );
|
|
Vector vecForward, vecRight, vecUp;
|
|
GetControlPointOrientationAtCurrentTime( i, &vecForward, &vecRight, &vecUp );
|
|
NDebugOverlay::Line( vecPos, vecPos + ( vecForward * 4.0f ), 255, 0, 0, true, 0.05f );
|
|
NDebugOverlay::Line( vecPos, vecPos + ( vecRight * 4.0f ), 0, 255, 0, true, 0.05f );
|
|
NDebugOverlay::Line( vecPos, vecPos + ( vecUp * 4.0f ), 0, 0, 255, true, 0.05f );
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void CNewParticleEffect::SetDrawOnlyForSplitScreenUser( int nSlot )
|
|
{
|
|
if ( nSlot != m_nSplitScreenUser )
|
|
{
|
|
m_nSplitScreenUser = nSlot;
|
|
if ( IsSplitScreenSupported() && ( m_hRenderHandle != INVALID_CLIENT_RENDER_HANDLE ) )
|
|
{
|
|
g_pClientLeafSystem->EnableSplitscreenRendering( m_hRenderHandle, ComputeSplitscreenRenderingFlags( this ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CNewParticleEffect::ShouldDrawForSplitScreenUser( int nSlot )
|
|
{
|
|
if ( m_nSplitScreenUser == -1 )
|
|
return true;
|
|
return m_nSplitScreenUser == nSlot;
|
|
}
|
|
|
|
bool CNewParticleEffect::SetupBones( matrix3x4a_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
|
|
{
|
|
matrix3x4a_t mat;
|
|
mat.Init( Vector( 0, -1, 0), Vector( 1, 0, 0), Vector( 0, 0, 1 ), vec3_origin );
|
|
MatrixMultiply( m_DrawModelMatrix, mat, pBoneToWorldOut[0] );
|
|
return true;
|
|
}
|
|
|
|
|
|
static void DumpParticleStats_f( void )
|
|
{
|
|
g_pParticleSystemMgr->DumpProfileInformation();
|
|
}
|
|
|
|
static ConCommand cl_dump_particle_stats( "cl_dump_particle_stats", DumpParticleStats_f, "dump particle profiling info to particle_profile.csv") ;
|
|
|
|
CON_COMMAND( cl_particles_dumplist, "Dump all new particles, optional name substring." )
|
|
{
|
|
if ( args.ArgC() == 2 )
|
|
{
|
|
g_pParticleSystemMgr->DumpParticleList( args.Arg(1) );
|
|
}
|
|
else
|
|
{
|
|
g_pParticleSystemMgr->DumpParticleList( NULL );
|
|
}
|
|
}
|