525 lines
14 KiB
C++
525 lines
14 KiB
C++
/***
|
|
*
|
|
* Copyright (C) 2002 The Wastes Project, All Rights Reserved.
|
|
*
|
|
* This product contains software technology from Valve Software, LLC,
|
|
* Copyright © 1996-2001, Valve LLC, All rights reserved.
|
|
*
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
* The Wastes Project. All other use, distribution, or modification is prohibited
|
|
* without written permission from The Wastes Project.
|
|
*
|
|
***/
|
|
#include <string.h>
|
|
|
|
#include "hud.h"
|
|
#include "wrect.h"
|
|
#include "cl_util.h"
|
|
#include "tri.h"
|
|
#include "pm_defs.h"
|
|
#include "event_api.h"
|
|
#include "ParticleBase.h"
|
|
|
|
CParticleSystemManager g_ParticleSystemManager;
|
|
|
|
void HUD_GetLastOrg( float *org );
|
|
|
|
//==================================================
|
|
// CSpriteEmitter
|
|
//==================================================
|
|
CSpriteEmitter::CSpriteEmitter()
|
|
{
|
|
memset( m_szName, 0, sizeof( m_szName ) );
|
|
memset( m_Origin, 0, sizeof( m_Origin ) );
|
|
memset( m_Acceleration, 0, sizeof( m_Acceleration ) );
|
|
memset( m_StartOriginOffset, 0, sizeof( m_StartOriginOffset ) );
|
|
memset( m_StartOriginRange, 0, sizeof( m_StartOriginRange ) );
|
|
memset( m_StartVelocity, 0, sizeof( m_StartVelocity ) );
|
|
memset( m_MinVelocity, 0, sizeof( m_MinVelocity ) );
|
|
memset( m_MaxVelocity, 0, sizeof( m_MaxVelocity ) );
|
|
memset( m_flParticleLifetime, 0, sizeof( m_flParticleLifetime ) );
|
|
memset( m_flStartScaleRange, 0, sizeof( m_flStartScaleRange ) );
|
|
memset( m_flGrowScaleRange, 0, sizeof( m_flGrowScaleRange ) );
|
|
|
|
m_iNumSprites = 0;
|
|
memset( m_iSpriteHandles, 0, sizeof( m_iSpriteHandles ) );
|
|
|
|
m_iMaxParticles = 1;
|
|
m_flParticlesPerSecond = 0.0f;
|
|
m_iRenderMode = PRM_Normal;
|
|
m_iCoordSystem = PCS_Standard;
|
|
m_flInactiveTime = 0.0f;
|
|
|
|
m_flLastParticleTime = 0.0f;
|
|
m_pParticles = 0;
|
|
m_iNumParticles = 0;
|
|
m_flInactiveStart = -1.0f;
|
|
}
|
|
|
|
CSpriteEmitter::CSpriteEmitter( const CSpriteEmitter &Cpy )
|
|
{
|
|
strcpy( m_szName, Cpy.m_szName );
|
|
|
|
VectorCopy( Cpy.m_Origin, m_Origin );
|
|
VectorCopy( Cpy.m_Acceleration, m_Acceleration );
|
|
VectorCopy( Cpy.m_StartOriginOffset, m_StartOriginOffset );
|
|
VectorCopy( Cpy.m_StartOriginRange[0], m_StartOriginRange[0] );
|
|
VectorCopy( Cpy.m_StartOriginRange[1], m_StartOriginRange[1] );
|
|
VectorCopy( Cpy.m_StartVelocity[0], m_StartVelocity[0] );
|
|
VectorCopy( Cpy.m_StartVelocity[1], m_StartVelocity[1] );
|
|
VectorCopy( Cpy.m_MinVelocity, m_MinVelocity );
|
|
VectorCopy( Cpy.m_MaxVelocity, m_MaxVelocity );
|
|
|
|
memcpy( m_flParticleLifetime, Cpy.m_flParticleLifetime, sizeof( m_flParticleLifetime ) );
|
|
memcpy( m_flStartScaleRange, Cpy.m_flStartScaleRange, sizeof( m_flStartScaleRange ) );
|
|
memcpy( m_flGrowScaleRange, Cpy.m_flGrowScaleRange, sizeof( m_flGrowScaleRange ) );
|
|
|
|
m_iNumSprites = Cpy.m_iNumSprites;
|
|
memcpy( m_iSpriteHandles, Cpy.m_iSpriteHandles, sizeof( m_iSpriteHandles ) );
|
|
|
|
m_iMaxParticles = Cpy.m_iMaxParticles;
|
|
m_flParticlesPerSecond = Cpy.m_flParticlesPerSecond;
|
|
m_iRenderMode = Cpy.m_iRenderMode;
|
|
m_iCoordSystem = Cpy.m_iCoordSystem;
|
|
m_flInactiveTime = Cpy.m_flInactiveTime;
|
|
|
|
// These are unique to each emitter
|
|
m_flLastParticleTime = 0.0f;
|
|
m_pParticles = 0;
|
|
m_iNumParticles = 0;
|
|
m_flInactiveStart = -1.0f;
|
|
}
|
|
|
|
CSpriteEmitter::~CSpriteEmitter()
|
|
{
|
|
ClearList( m_pParticles );
|
|
}
|
|
|
|
void CSpriteEmitter::Render()
|
|
{
|
|
if( m_flInactiveTime > 0.0f )
|
|
{
|
|
psvec3_t origin, angles, forward, right, up;
|
|
int idx = gEngfuncs.GetLocalPlayer()->index;
|
|
pmtrace_t tr;
|
|
|
|
// See if we shouldn't render the emitter
|
|
gEngfuncs.GetViewAngles((float*)&angles);
|
|
HUD_GetLastOrg((float*)&origin);
|
|
AngleVectors(angles,forward,right,up);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction( false, false );
|
|
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers( idx - 1 );
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull( 1 );
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace( origin, m_Origin, PM_WORLD_ONLY|PM_GLASS_IGNORE, -1, &tr );
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
if( tr.fraction != 1.0f )
|
|
{
|
|
if( m_flInactiveStart == -1.0f )
|
|
m_flInactiveStart = gHUD.m_flTime;
|
|
else if( gHUD.m_flTime >= m_flInactiveStart + m_flInactiveTime )
|
|
return;
|
|
}
|
|
else
|
|
m_flInactiveStart = -1.0f;
|
|
}
|
|
|
|
CParticle *pCurPcl = m_pParticles;
|
|
while( pCurPcl )
|
|
{
|
|
const struct model_s *spritemodel;
|
|
psvec3_t angles,forward,right,up;
|
|
psvec3_t origin;
|
|
|
|
spritemodel = gEngfuncs.GetSpritePointer( m_iSpriteHandles[ pCurPcl->m_iModelIndex ] );
|
|
gEngfuncs.GetViewAngles( angles );
|
|
AngleVectors( angles, forward, right, up );
|
|
|
|
if( m_iCoordSystem == PCS_Relative )
|
|
{
|
|
VectorAdd( m_Origin, pCurPcl->m_Origin, origin );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( pCurPcl->m_Origin, origin );
|
|
}
|
|
|
|
R_Billboard( spritemodel, 0, m_iRenderMode,
|
|
1.0f, 1.0f, 1.0f, 1.0f,
|
|
origin, up, right, pCurPcl->m_flScale );
|
|
|
|
pCurPcl = pCurPcl->m_pNext;
|
|
}
|
|
|
|
if( gHUD.m_flTime - m_flLastParticleTime >= 1/m_flParticlesPerSecond )
|
|
{
|
|
m_flLastParticleTime = gHUD.m_flTime;
|
|
AddParticle();
|
|
}
|
|
}
|
|
|
|
void CSpriteEmitter::Update( double flFrametime )
|
|
{
|
|
// Dont update if we dont have to
|
|
// if( m_flInactiveStart != -1.0f && gHUD.m_flTime >= m_flInactiveStart + m_flInactiveTime )
|
|
// return;
|
|
|
|
CParticle *pCurPcl = m_pParticles;
|
|
while( pCurPcl )
|
|
{
|
|
VectorMA( pCurPcl->m_Origin, flFrametime, pCurPcl->m_Velocity, pCurPcl->m_Origin );
|
|
VectorAdd( pCurPcl->m_Velocity, m_Acceleration, pCurPcl->m_Velocity );
|
|
|
|
for( int i = 0;i < 3;i++ )
|
|
{
|
|
if( m_MinVelocity[i] != 0 && pCurPcl->m_Velocity[i] < m_MinVelocity[i])
|
|
pCurPcl->m_Velocity[i] = m_MinVelocity[i];
|
|
else if( m_MaxVelocity[i] != 0 && pCurPcl->m_Velocity[i] > m_MaxVelocity[i] )
|
|
pCurPcl->m_Velocity[i] = m_MaxVelocity[i];
|
|
}
|
|
|
|
pCurPcl->m_flScale += flFrametime * pCurPcl->m_flGrowScale;
|
|
pCurPcl = pCurPcl->m_pNext;
|
|
}
|
|
|
|
// See if we should delete any particles
|
|
// TODO: Clean this shit up will ya ? :)
|
|
pCurPcl = m_pParticles;
|
|
while( pCurPcl != NULL )
|
|
{
|
|
if( pCurPcl->m_flLifetime - gHUD.m_flTime <= 0.0f )
|
|
{
|
|
CParticle *pPrevPcl = m_pParticles;
|
|
|
|
if( pPrevPcl == pCurPcl )
|
|
{
|
|
m_pParticles = pCurPcl->m_pNext;
|
|
delete pCurPcl;
|
|
pCurPcl = m_pParticles;
|
|
}
|
|
else
|
|
{
|
|
while( pPrevPcl->m_pNext != NULL )
|
|
{
|
|
if( pPrevPcl->m_pNext == pCurPcl )
|
|
{
|
|
pPrevPcl->m_pNext = pCurPcl->m_pNext;
|
|
delete pCurPcl;
|
|
m_iNumParticles--;
|
|
break;
|
|
}
|
|
|
|
pPrevPcl = pPrevPcl->m_pNext;
|
|
}
|
|
|
|
pCurPcl = pPrevPcl->m_pNext;
|
|
}
|
|
}
|
|
else
|
|
pCurPcl = pCurPcl->m_pNext;
|
|
}
|
|
}
|
|
|
|
void CSpriteEmitter::AddParticle()
|
|
{
|
|
// Delete oldest particle if we are at max
|
|
if( m_iNumParticles >= m_iMaxParticles && m_pParticles )
|
|
{
|
|
CParticle *pDel = m_pParticles;
|
|
m_pParticles = m_pParticles->m_pNext;
|
|
delete pDel;
|
|
}
|
|
|
|
CParticle *pNewPcl = new CParticle;
|
|
|
|
// Origin
|
|
psvec3_t RangedOrigin;
|
|
RangedOrigin[0] = gEngfuncs.pfnRandomFloat( m_StartOriginRange[0][0], m_StartOriginRange[1][0] );
|
|
RangedOrigin[1] = gEngfuncs.pfnRandomFloat( m_StartOriginRange[0][1], m_StartOriginRange[1][1] );
|
|
RangedOrigin[2] = gEngfuncs.pfnRandomFloat( m_StartOriginRange[0][2], m_StartOriginRange[1][2] );
|
|
|
|
if( m_iCoordSystem == PCS_Relative )
|
|
{
|
|
VectorCopy( RangedOrigin, pNewPcl->m_Origin );
|
|
}
|
|
else
|
|
{
|
|
VectorAdd( m_Origin, RangedOrigin, pNewPcl->m_Origin );
|
|
}
|
|
|
|
VectorAdd( pNewPcl->m_Origin, m_StartOriginOffset, pNewPcl->m_Origin );
|
|
|
|
// Velocity
|
|
VectorSet( pNewPcl->m_Velocity,
|
|
gEngfuncs.pfnRandomFloat( m_StartVelocity[0][0], m_StartVelocity[1][0] ),
|
|
gEngfuncs.pfnRandomFloat( m_StartVelocity[0][1], m_StartVelocity[1][1] ),
|
|
gEngfuncs.pfnRandomFloat( m_StartVelocity[0][2], m_StartVelocity[1][2] ) );
|
|
|
|
pNewPcl->m_flLifetime = gHUD.m_flTime + gEngfuncs.pfnRandomFloat( m_flParticleLifetime[0], m_flParticleLifetime[1] );
|
|
pNewPcl->m_flScale = gEngfuncs.pfnRandomFloat( m_flStartScaleRange[0], m_flStartScaleRange[1] );
|
|
pNewPcl->m_flGrowScale = gEngfuncs.pfnRandomFloat( m_flGrowScaleRange[0], m_flGrowScaleRange[1] );
|
|
|
|
pNewPcl->m_iModelIndex = gEngfuncs.pfnRandomLong( 0, m_iNumSprites-1 );
|
|
|
|
if( m_pParticles )
|
|
m_pParticles->AddParticle( pNewPcl );
|
|
else
|
|
m_pParticles = pNewPcl;
|
|
|
|
m_iNumParticles++;
|
|
}
|
|
|
|
void CSpriteEmitter::ClearList( CParticle *pList )
|
|
{
|
|
CParticle *pCurPcl = pList;
|
|
while( pCurPcl )
|
|
{
|
|
CParticle *pNextPcl = pCurPcl->m_pNext;
|
|
delete pCurPcl;
|
|
pCurPcl = pNextPcl;
|
|
}
|
|
}
|
|
|
|
//==================================================
|
|
// CParticleSystem
|
|
//==================================================
|
|
CParticleSystem::CParticleSystem()
|
|
{
|
|
memset( m_szName, 0, sizeof( m_szName ) );
|
|
memset( m_Origin, 0, sizeof( m_Origin ) );
|
|
}
|
|
|
|
CParticleSystem::CParticleSystem( const CParticleSystem &Cpy )
|
|
{
|
|
strcpy( m_szName, Cpy.m_szName );
|
|
VectorCopy( Cpy.m_Origin, m_Origin );
|
|
}
|
|
|
|
CParticleSystem::~CParticleSystem()
|
|
{
|
|
}
|
|
|
|
void CParticleSystem::Render( int Transparent )
|
|
{
|
|
if( m_Emitters.size() )
|
|
{
|
|
list<CSpriteEmitter>::iterator iter;
|
|
|
|
for( iter = m_Emitters.begin(); iter != m_Emitters.end(); iter++ )
|
|
{
|
|
if( Transparent && iter->IsTransparent() )
|
|
iter->Render();
|
|
else if( !Transparent && !iter->IsTransparent() )
|
|
iter->Render();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CParticleSystem::Update( double flFrametime )
|
|
{
|
|
if( m_Emitters.size() )
|
|
{
|
|
list<CSpriteEmitter>::iterator iter;
|
|
|
|
for( iter = m_Emitters.begin(); iter != m_Emitters.end(); iter++ )
|
|
{
|
|
VectorCopy( m_Origin, iter->m_Origin );
|
|
iter->Update( flFrametime );
|
|
}
|
|
}
|
|
}
|
|
|
|
//==================================================
|
|
// CParticleSystemManager
|
|
//==================================================
|
|
CParticleSystemManager::CParticleSystemManager()
|
|
{
|
|
memset( m_szCurrentLevel, 0, sizeof( m_szCurrentLevel ) );
|
|
}
|
|
|
|
CParticleSystemManager::~CParticleSystemManager()
|
|
{
|
|
}
|
|
|
|
void CParticleSystemManager::RenderNormal()
|
|
{
|
|
if( m_ActiveSystems.size() )
|
|
{
|
|
list<CParticleSystem>::iterator iter;
|
|
|
|
for( iter = m_ActiveSystems.begin(); iter != m_ActiveSystems.end(); iter++ )
|
|
iter->Render( 0 );
|
|
}
|
|
}
|
|
|
|
void CParticleSystemManager::RenderTransparent()
|
|
{
|
|
if( m_ActiveSystems.size() )
|
|
{
|
|
list<CParticleSystem>::iterator iter;
|
|
|
|
for( iter = m_ActiveSystems.begin(); iter != m_ActiveSystems.end(); iter++ )
|
|
iter->Render( 1 );
|
|
}
|
|
}
|
|
|
|
void CParticleSystemManager::Update( double flFrametime )
|
|
{
|
|
if( m_ActiveSystems.size() )
|
|
{
|
|
list<CParticleSystem>::iterator iter;
|
|
|
|
for( iter = m_ActiveSystems.begin(); iter != m_ActiveSystems.end(); iter++ )
|
|
iter->Update( flFrametime );
|
|
}
|
|
}
|
|
|
|
void CParticleSystemManager::AddParticleSystem( char *pszName, psvec3_t Origin )
|
|
{
|
|
gEngfuncs.Con_Printf( "Adding particle system \"%s\" at %f %f %f\n", pszName, Origin[0],
|
|
Origin[1], Origin[2] );
|
|
|
|
const CParticleSystem *pNewPclSystem = GetParticleSystem( pszName );
|
|
if( pNewPclSystem != NULL )
|
|
{
|
|
m_ActiveSystems.push_back( *pNewPclSystem );
|
|
return;
|
|
}
|
|
|
|
gEngfuncs.Con_Printf( "Particle system \"%s\" not found!\n", pszName );
|
|
}
|
|
|
|
const CParticleSystem *CParticleSystemManager::GetParticleSystem( char *pszName )
|
|
{
|
|
if( m_ReferenceSystems.size() )
|
|
{
|
|
list<CParticleSystem>::iterator iter;
|
|
|
|
for( iter = m_ReferenceSystems.begin(); iter != m_ReferenceSystems.end(); iter++ )
|
|
{
|
|
if( stricmp( iter->m_szName, pszName ) == 0 )
|
|
return &*iter;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const CSpriteEmitter *CParticleSystemManager::GetEmitter( char *pszName )
|
|
{
|
|
if( m_ReferenceEmitters.size() )
|
|
{
|
|
list<CSpriteEmitter>::iterator iter;
|
|
|
|
for( iter = m_ReferenceEmitters.begin(); iter != m_ReferenceEmitters.end(); iter++ )
|
|
{
|
|
if( stricmp( iter->m_szName, pszName ) == 0 )
|
|
return &*iter;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CParticleSystemManager::CheckMap()
|
|
{
|
|
// Maps dont match, time to reinitialize emitters!
|
|
if( stricmp( m_szCurrentLevel, gEngfuncs.pfnGetLevelName() ) != 0 )
|
|
{
|
|
strcpy( m_szCurrentLevel, gEngfuncs.pfnGetLevelName() );
|
|
|
|
m_ActiveSystems.clear();
|
|
m_ReferenceEmitters.clear();
|
|
m_ReferenceSystems.clear();
|
|
|
|
// Test values
|
|
#if 1
|
|
CSpriteEmitter newEmitter;
|
|
|
|
strcpy( newEmitter.m_szName, "test.sewer_ventsmoke_emitter" );
|
|
VectorSet( newEmitter.m_StartVelocity[0], 50, 0, -50.0f );
|
|
VectorSet( newEmitter.m_StartVelocity[1], 100, 0, -100.0f );
|
|
newEmitter.m_flParticlesPerSecond = 10.0f;
|
|
newEmitter.m_flParticleLifetime[0] = 5.0f;
|
|
newEmitter.m_flParticleLifetime[1] = 6.0f;
|
|
|
|
VectorSet( newEmitter.m_StartOriginOffset, 0, 0, 0 );
|
|
VectorSet( newEmitter.m_StartOriginRange[0], -10, -10, -10 );
|
|
VectorSet( newEmitter.m_StartOriginRange[1], 10, 10, 10 );
|
|
|
|
newEmitter.m_flStartScaleRange[0] = 10.0f;
|
|
newEmitter.m_flStartScaleRange[1] = 15.0f;
|
|
newEmitter.m_flGrowScaleRange[0] = 1.0f;
|
|
newEmitter.m_flGrowScaleRange[1] = 2.0f;
|
|
|
|
newEmitter.m_iRenderMode = PRM_TransAdd;
|
|
newEmitter.m_iCoordSystem = PCS_Standard;
|
|
|
|
newEmitter.m_iMaxParticles = 256;
|
|
|
|
newEmitter.m_iNumSprites = 1;
|
|
newEmitter.m_iSpriteHandles[0] = gEngfuncs.pfnSPR_Load( "sprites/circle.spr" );
|
|
|
|
newEmitter.m_flInactiveTime = 0.5f;
|
|
|
|
m_ReferenceEmitters.push_back( newEmitter );
|
|
|
|
CParticleSystem newSystem;
|
|
strcpy( newSystem.m_szName, "test.sewer_ventsmoke" );
|
|
newSystem.m_Emitters.push_back( newEmitter );
|
|
m_ReferenceSystems.push_back( newSystem );
|
|
|
|
// test.sewer_lightsmoke
|
|
// test.sewer_ventsmoke
|
|
|
|
/* CSpriteEmitter newEmitter;
|
|
|
|
strcpy( newEmitter.m_szName, "test.testemitter" );
|
|
VectorSet( newEmitter.m_Origin, 0, 0, 50.0f );
|
|
newEmitter.m_Origin[2] -= 25.0f;
|
|
VectorSet( newEmitter.m_StartVelocity[0], 0, 0, -500.0f );
|
|
VectorSet( newEmitter.m_StartVelocity[1], 0, 0, -750.0f );
|
|
newEmitter.m_flParticlesPerSecond = 100.0f;
|
|
newEmitter.m_flParticleLifetime[0] = 4.0f;
|
|
newEmitter.m_flParticleLifetime[1] = 5.0f;
|
|
|
|
VectorSet( newEmitter.m_StartOriginOffset, 0, 0, 480 );
|
|
VectorSet( newEmitter.m_StartOriginRange[0], -500.0f, -500.0f, 0 );
|
|
VectorSet( newEmitter.m_StartOriginRange[1], 500.0f, 500.0f, 0 );
|
|
|
|
VectorSet( newEmitter.m_MinVelocity, 0, 0, 0 );
|
|
VectorSet( newEmitter.m_MaxVelocity, 0, 0, 0 );
|
|
|
|
newEmitter.m_flStartScaleRange[0] = 10.0;
|
|
newEmitter.m_flStartScaleRange[1] = 15.0;
|
|
newEmitter.m_flGrowScaleRange[0] = 0.0;
|
|
newEmitter.m_flGrowScaleRange[1] = 0.0;
|
|
|
|
newEmitter.m_iRenderMode = PRM_TransAdd;
|
|
newEmitter.m_iCoordSystem = PCS_Standard;
|
|
|
|
newEmitter.m_iMaxParticles = 1024;
|
|
|
|
newEmitter.m_iNumSprites = 1;
|
|
newEmitter.m_iSpriteHandles[0] = gEngfuncs.pfnSPR_Load( "sprites/circle.spr" );
|
|
|
|
newEmitter.m_flInactiveTime = 1.0f;
|
|
|
|
m_ReferenceEmitters.push_back( newEmitter );
|
|
|
|
CParticleSystem newSystem;
|
|
|
|
strcpy( newSystem.m_szName, "test.testsystem" );
|
|
VectorCopy( newEmitter.m_Origin, newSystem.m_Origin );
|
|
|
|
// newSystem.m_pEmitters.push_back( new CSpriteEmitter( g_ParticleSystemManager.GetEmitter( "test.testemitter" ) ) );
|
|
|
|
m_ActiveSystems.push_back( newSystem );
|
|
m_ReferenceSystems.push_back( newSystem );*/
|
|
#endif
|
|
}
|
|
} |