halflife-thewastes-sdk/cl_dll/ParticleBase.cpp
2023-09-06 03:25:39 +03:00

525 lines
No EOL
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
}
}