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.
475 lines
13 KiB
475 lines
13 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "particlemgr.h" |
|
#include "particle_prototype.h" |
|
#include "particle_util.h" |
|
#include "surfinfo.h" |
|
#include "baseparticleentity.h" |
|
#include "materialsystem/imaterialsystemhardwareconfig.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// ------------------------------------------------------------------------- // |
|
// Definitions |
|
// ------------------------------------------------------------------------- // |
|
|
|
#define NUM_AR2_EXPLOSION_PARTICLES 70 |
|
#define AR2_DUST_RADIUS 240 // 340 |
|
#define AR2_DUST_LIFETIME 4 |
|
#define AR2_DUST_LIFETIME_DELTA 6 |
|
#define AR2_DUST_SPEED 10000 |
|
#define AR2_DUST_STARTSIZE 8 |
|
#define AR2_DUST_ENDSIZE 32 |
|
#define AR2_DUST_ALPHA 0.5f |
|
#define AR2_DUST_FADE_IN_TIME 0.25f |
|
|
|
static Vector g_AR2DustColor1(0.35, 0.345, 0.33 ); |
|
static Vector g_AR2DustColor2(0.75, 0.75, 0.7); |
|
|
|
|
|
// ------------------------------------------------------------------------- // |
|
// Classes |
|
// ------------------------------------------------------------------------- // |
|
|
|
class C_AR2Explosion : public C_BaseParticleEntity, public IPrototypeAppEffect |
|
{ |
|
public: |
|
DECLARE_CLASS( C_AR2Explosion, C_BaseParticleEntity ); |
|
DECLARE_CLIENTCLASS(); |
|
|
|
C_AR2Explosion(); |
|
~C_AR2Explosion(); |
|
|
|
private: |
|
|
|
class AR2ExplosionParticle : public StandardParticle_t |
|
{ |
|
public: |
|
float m_Dist; |
|
Vector m_Start; |
|
float m_Roll; |
|
float m_RollSpeed; |
|
float m_Dwell; |
|
}; |
|
|
|
// C_BaseEntity. |
|
public: |
|
virtual void OnDataChanged(DataUpdateType_t updateType); |
|
|
|
// IPrototypeAppEffect. |
|
public: |
|
virtual void Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs); |
|
|
|
// IParticleEffect. |
|
public: |
|
virtual void Update(float fTimeDelta); |
|
|
|
virtual void RenderParticles( CParticleRenderIterator *pIterator ); |
|
virtual void SimulateParticles( CParticleSimulateIterator *pIterator ); |
|
|
|
|
|
public: |
|
CParticleMgr *m_pParticleMgr; |
|
PMaterialHandle m_MaterialHandle; |
|
|
|
private: |
|
|
|
char m_szMaterialName[255]; |
|
|
|
C_AR2Explosion( const C_AR2Explosion & ); |
|
}; |
|
|
|
// Expose to the particle app. |
|
EXPOSE_PROTOTYPE_EFFECT(AR2Explosion, C_AR2Explosion); |
|
|
|
IMPLEMENT_CLIENTCLASS_DT(C_AR2Explosion, DT_AR2Explosion, AR2Explosion) |
|
RecvPropString( RECVINFO( m_szMaterialName ) ), |
|
END_RECV_TABLE() |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------- // |
|
// Helpers. |
|
// ------------------------------------------------------------------------- // |
|
|
|
// Given a line segment from vStart to vEnd |
|
// and a list of convex polygons in pSurfInfos and nSurfInfos, |
|
// fill in the list of which polygons the segment intersects. |
|
// Returns the number of intersected surfaces. |
|
static int IntersectSegmentWithSurfInfos( |
|
const Vector &vStart, |
|
const Vector &vEnd, |
|
SurfInfo *pSurfInfos, |
|
const int nSurfInfos, |
|
SurfInfo ** pIntersections, |
|
Vector *pIntersectionPositions, |
|
int nMaxIntersections) |
|
{ |
|
if(nMaxIntersections == 0) |
|
return 0; |
|
|
|
int nIntersections = 0; |
|
for(int i=0; i < nSurfInfos; i++) |
|
{ |
|
SurfInfo *pSurf = &pSurfInfos[i]; |
|
|
|
// Does it intersect the plane? |
|
float dot1 = pSurf->m_Plane.DistTo(vStart); |
|
float dot2 = pSurf->m_Plane.DistTo(vEnd); |
|
if((dot1 > 0) != (dot2 > 0)) |
|
{ |
|
float t = dot1 / (dot1 - dot2); |
|
Vector vIntersection = vStart + (vEnd - vStart) * t; |
|
|
|
// If the intersection is behind any edge plane, then it's not inside the polygon. |
|
unsigned long iEdge; |
|
for(iEdge=0; iEdge < pSurf->m_nVerts; iEdge++) |
|
{ |
|
VPlane edgePlane; |
|
edgePlane.m_Normal = pSurf->m_Plane.m_Normal.Cross(pSurf->m_Verts[iEdge] - pSurf->m_Verts[(iEdge+1)%pSurf->m_nVerts]); |
|
VectorNormalize( edgePlane.m_Normal ); |
|
edgePlane.m_Dist = edgePlane.m_Normal.Dot(pSurf->m_Verts[iEdge]); |
|
|
|
if(edgePlane.DistTo(vIntersection) < 0.0f) |
|
break; |
|
} |
|
|
|
if(iEdge == pSurf->m_nVerts) |
|
{ |
|
pIntersections[nIntersections] = pSurf; |
|
pIntersectionPositions[nIntersections] = vIntersection; |
|
++nIntersections; |
|
if(nIntersections >= nMaxIntersections) |
|
break; |
|
} |
|
} |
|
} |
|
|
|
return nIntersections; |
|
} |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------- // |
|
// C_AR2Explosion |
|
// ------------------------------------------------------------------------- // |
|
C_AR2Explosion::C_AR2Explosion() |
|
{ |
|
m_pParticleMgr = NULL; |
|
m_MaterialHandle = INVALID_MATERIAL_HANDLE; |
|
} |
|
|
|
|
|
C_AR2Explosion::~C_AR2Explosion() |
|
{ |
|
} |
|
|
|
|
|
void C_AR2Explosion::OnDataChanged(DataUpdateType_t updateType) |
|
{ |
|
C_BaseEntity::OnDataChanged(updateType); |
|
|
|
if(updateType == DATA_UPDATE_CREATED) |
|
{ |
|
Start(ParticleMgr(), NULL); |
|
} |
|
} |
|
|
|
static ConVar mat_reduceparticles( "mat_reduceparticles", "0" ); |
|
|
|
void C_AR2Explosion::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs) |
|
{ |
|
m_pParticleMgr = pParticleMgr; |
|
if(!pParticleMgr->AddEffect(&m_ParticleEffect, this)) |
|
return; |
|
|
|
if (!m_szMaterialName[0]) |
|
{ |
|
Q_strncpy(m_szMaterialName, "particle/particle_noisesphere", sizeof( m_szMaterialName ) ); |
|
} |
|
|
|
m_MaterialHandle = m_ParticleEffect.FindOrAddMaterial(m_szMaterialName); |
|
|
|
// Precalculate stuff for the particle spawning.. |
|
#define NUM_DUSTEMITTER_SURFINFOS 128 |
|
SurfInfo surfInfos[NUM_DUSTEMITTER_SURFINFOS]; |
|
int nSurfInfos; |
|
|
|
// Center of explosion. |
|
Vector vCenter = GetAbsOrigin(); // HACKHACK.. when the engine bug is fixed, use origin. |
|
|
|
if ( IsXbox() ) |
|
{ |
|
m_ParticleEffect.SetBBox( vCenter-Vector(300,300,300), vCenter+Vector(300,300,300) ); |
|
} |
|
|
|
#ifdef PARTICLEPROTOTYPE_APP |
|
float surfSize = 10000; |
|
nSurfInfos = 1; |
|
surfInfos[0].m_Verts[0].Init(-surfSize,-surfSize,0); |
|
surfInfos[0].m_Verts[1].Init(-surfSize,surfSize,0); |
|
surfInfos[0].m_Verts[2].Init(surfSize, surfSize,0); |
|
surfInfos[0].m_Verts[3].Init(surfSize,-surfSize,0); |
|
surfInfos[0].m_nVerts = 4; |
|
surfInfos[0].m_Plane.m_Normal.Init(0,0,1); |
|
surfInfos[0].m_Plane.m_Dist = -3; |
|
#else |
|
{ |
|
nSurfInfos = 0; |
|
C_BaseEntity *ent = cl_entitylist->GetEnt( 0 ); |
|
if ( ent ) |
|
{ |
|
nSurfInfos = engine->GetIntersectingSurfaces( |
|
ent->GetModel(), |
|
vCenter, |
|
AR2_DUST_RADIUS, |
|
true, |
|
surfInfos, |
|
NUM_DUSTEMITTER_SURFINFOS); |
|
} |
|
} |
|
#endif |
|
|
|
int nParticles = 0; |
|
|
|
int iParticlesToSpawn = NUM_AR2_EXPLOSION_PARTICLES; |
|
|
|
// In DX7, much fewer particles |
|
if ( g_pMaterialSystemHardwareConfig->GetDXSupportLevel() < 80 ) |
|
{ |
|
iParticlesToSpawn *= 0.25; |
|
} |
|
else if ( mat_reduceparticles.GetBool() ) |
|
{ |
|
iParticlesToSpawn *= 0.025; |
|
} |
|
|
|
if( nSurfInfos > 0 ) |
|
{ |
|
// For nParticles*N, generate a ray and cast it out. If it hits anything, spawn a particle there. |
|
int nTestsPerParticle=3; |
|
for(int i=0; i < iParticlesToSpawn; i++) |
|
{ |
|
for(int iTest=0; iTest < nTestsPerParticle; iTest++) |
|
{ |
|
Vector randVec = RandomVector(-1,1); |
|
VectorNormalize( randVec ); |
|
Vector startPos = vCenter + randVec * AR2_DUST_RADIUS; |
|
|
|
randVec = RandomVector(-1,1); |
|
VectorNormalize( randVec ); |
|
Vector endPos = vCenter + randVec * AR2_DUST_RADIUS; |
|
|
|
#define MAX_SURFINFO_INTERSECTIONS 4 |
|
SurfInfo *pIntersected[MAX_SURFINFO_INTERSECTIONS]; |
|
Vector vIntersections[MAX_SURFINFO_INTERSECTIONS]; |
|
int nIntersections; |
|
nIntersections = IntersectSegmentWithSurfInfos( |
|
startPos, |
|
endPos, |
|
surfInfos, |
|
nSurfInfos, |
|
pIntersected, |
|
vIntersections, |
|
MAX_SURFINFO_INTERSECTIONS); |
|
|
|
if(nIntersections) |
|
{ |
|
int iIntersection = rand() % nIntersections; |
|
|
|
Vector velocity; |
|
//velocity.Init(-1.0f + ((float)rand()/VALVE_RAND_MAX) * 2.0f, -1.0f + ((float)rand()/VALVE_RAND_MAX) * 2.0f, -1.0f + ((float)rand()/VALVE_RAND_MAX) * 2.0f); |
|
//velocity = velocity * FRand(m_MinSpeed, m_MaxSpeed); |
|
Vector direction = (vIntersections[iIntersection] - vCenter ); |
|
float dist = VectorNormalize( direction ); |
|
if(dist > AR2_DUST_RADIUS) |
|
dist = AR2_DUST_RADIUS; |
|
|
|
static float power = 2.0f; |
|
float falloffMul = pow(1.0f - dist / AR2_DUST_RADIUS, power); |
|
|
|
Vector reflection = direction - 2 * DotProduct( direction, pIntersected[iIntersection]->m_Plane.m_Normal ) * pIntersected[iIntersection]->m_Plane.m_Normal; |
|
VectorNormalize( reflection ); |
|
|
|
velocity = reflection * AR2_DUST_SPEED * falloffMul; |
|
// velocity = velocity + (vIntersections[iIntersection] - vCenter) * falloffMul; |
|
|
|
|
|
/* |
|
debugoverlay->AddLineOverlay( vIntersections[iIntersection], |
|
vIntersections[iIntersection] + reflection * 64, |
|
128, 128, 255, false, 15.0 ); |
|
*/ |
|
#if 1 |
|
AR2ExplosionParticle *pParticle = |
|
(AR2ExplosionParticle*)m_ParticleEffect.AddParticle( sizeof(AR2ExplosionParticle), m_MaterialHandle ); |
|
|
|
if(pParticle) |
|
{ |
|
pParticle->m_Pos = vIntersections[iIntersection]; |
|
pParticle->m_Start = pParticle->m_Pos; |
|
pParticle->m_Dist = 8.0; |
|
pParticle->m_Velocity = velocity; |
|
// sound == 13031.496062992125984251968503937ips |
|
pParticle->m_Lifetime = -dist / 13031.5f - 0.1; |
|
pParticle->m_Roll = FRand( 0, M_PI * 2 ); |
|
pParticle->m_RollSpeed = FRand( -1, 1 ) * 0.4; |
|
pParticle->m_Dwell = AR2_DUST_LIFETIME + random->RandomFloat( 0, AR2_DUST_LIFETIME_DELTA ); |
|
nParticles++; |
|
break; |
|
} |
|
#endif |
|
} |
|
} |
|
} |
|
} |
|
|
|
// build interior smoke particles |
|
for(int i=nParticles; i < iParticlesToSpawn; i++) |
|
{ |
|
Vector randVec = RandomVector(-1,1); |
|
VectorNormalize( randVec ); |
|
Vector endPos = vCenter + randVec * AR2_DUST_RADIUS / 4.0; |
|
|
|
Vector direction = (endPos - vCenter ); |
|
float dist = VectorNormalize( direction ) + random->RandomFloat( 0, AR2_DUST_RADIUS / 4.0 ); |
|
if(dist > AR2_DUST_RADIUS) |
|
dist = AR2_DUST_RADIUS; |
|
|
|
static float power = 2.0f; |
|
float falloffMul = pow(1.0f - dist / (AR2_DUST_RADIUS / 2), power); |
|
|
|
Vector velocity = direction * AR2_DUST_SPEED * falloffMul; |
|
AR2ExplosionParticle *pParticle = |
|
(AR2ExplosionParticle*)m_ParticleEffect.AddParticle( sizeof(AR2ExplosionParticle), m_MaterialHandle ); |
|
|
|
if(pParticle) |
|
{ |
|
pParticle->m_Pos = endPos; |
|
pParticle->m_Start = pParticle->m_Pos; |
|
pParticle->m_Dist = 8.0; |
|
pParticle->m_Velocity = velocity; |
|
// sound == 13031.496062992125984251968503937ips |
|
pParticle->m_Lifetime = -dist / 13031.5f - 0.1; |
|
pParticle->m_Roll = FRand( 0, M_PI * 2 ); |
|
pParticle->m_RollSpeed = FRand( -1, 1 ) * 4.0; |
|
pParticle->m_Dwell = 0.5 * (AR2_DUST_LIFETIME + random->RandomFloat( 0, AR2_DUST_LIFETIME_DELTA )); |
|
} |
|
} |
|
} |
|
|
|
|
|
void C_AR2Explosion::Update(float fTimeDelta) |
|
{ |
|
if(!m_pParticleMgr) |
|
return; |
|
} |
|
|
|
|
|
void C_AR2Explosion::SimulateParticles( CParticleSimulateIterator *pIterator ) |
|
{ |
|
float dt = pIterator->GetTimeDelta(); |
|
|
|
AR2ExplosionParticle *pParticle = (AR2ExplosionParticle*)pIterator->GetFirst(); |
|
while ( pParticle ) |
|
{ |
|
if (dt > 0.05) |
|
dt = 0.05; // yuck, air resistance function craps out at less then 20fps |
|
|
|
// Update its lifetime. |
|
pParticle->m_Lifetime += dt; // pDraw->GetTimeDelta(); |
|
if(pParticle->m_Lifetime > pParticle->m_Dwell) |
|
{ |
|
// faded to nothing.... |
|
pIterator->RemoveParticle( pParticle ); |
|
} |
|
else |
|
{ |
|
// Spin the thing |
|
pParticle->m_Roll += pParticle->m_RollSpeed * pIterator->GetTimeDelta(); |
|
|
|
// delayed? |
|
if ( pParticle->m_Lifetime >= 0.0f ) |
|
{ |
|
// Move it (this comes after rendering to make it clear that moving the particle here won't change |
|
// its rendering for this frame since m_TransformedPos has already been set). |
|
pParticle->m_Pos = pParticle->m_Pos + pParticle->m_Velocity * dt; |
|
|
|
// keep track of distance traveled |
|
pParticle->m_Dist = pParticle->m_Dist + pParticle->m_Velocity.Length() * dt; |
|
|
|
// Dampen velocity. |
|
float dist = pParticle->m_Velocity.Length() * dt; |
|
float r = dist * dist; |
|
// FIXME: this is a really screwy air-resistance function.... |
|
pParticle->m_Velocity = pParticle->m_Velocity * (100 / (100 + r )); |
|
|
|
// dampen roll |
|
static float dtime; |
|
static float decay; |
|
if (dtime != dt) |
|
{ |
|
dtime = dt; |
|
decay = ExponentialDecay( 0.3, 1.0, dtime ); |
|
} |
|
if (fabs(pParticle->m_RollSpeed) > 0.2) |
|
pParticle->m_RollSpeed = pParticle->m_RollSpeed * decay; |
|
} |
|
} |
|
|
|
pParticle = (AR2ExplosionParticle*)pIterator->GetNext(); |
|
} |
|
} |
|
|
|
|
|
void C_AR2Explosion::RenderParticles( CParticleRenderIterator *pIterator ) |
|
{ |
|
const AR2ExplosionParticle *pParticle = (const AR2ExplosionParticle *)pIterator->GetFirst(); |
|
while ( pParticle ) |
|
{ |
|
float sortKey = 0; |
|
if ( pParticle->m_Lifetime >= 0.0f ) |
|
{ |
|
// Draw. |
|
float lifetimePercent = ( pParticle->m_Lifetime - AR2_DUST_FADE_IN_TIME ) / pParticle->m_Dwell; |
|
|
|
// FIXME: base color should be a dirty version of the material color |
|
Vector color = g_AR2DustColor1 * (1.0 - lifetimePercent) + g_AR2DustColor2 * lifetimePercent; |
|
|
|
Vector tPos; |
|
TransformParticle(m_pParticleMgr->GetModelView(), pParticle->m_Pos, tPos); |
|
sortKey = tPos.z; |
|
|
|
float alpha; |
|
|
|
if ( pParticle->m_Lifetime < AR2_DUST_FADE_IN_TIME ) |
|
{ |
|
alpha = AR2_DUST_ALPHA * ( pParticle->m_Lifetime / AR2_DUST_FADE_IN_TIME ); |
|
} |
|
else |
|
{ |
|
alpha = AR2_DUST_ALPHA * ( 1.0f - lifetimePercent ); |
|
} |
|
|
|
alpha *= GetAlphaDistanceFade( tPos, IsXbox() ? 100 : 50, IsXbox() ? 200 : 150 ); |
|
|
|
RenderParticle_ColorSizeAngle( |
|
pIterator->GetParticleDraw(), |
|
tPos, |
|
color, |
|
alpha, |
|
pParticle->m_Dist, // size based on how far it's traveled |
|
pParticle->m_Roll); |
|
} |
|
|
|
pParticle = (const AR2ExplosionParticle *)pIterator->GetNext( sortKey ); |
|
} |
|
} |
|
|
|
|
|
|