summaryrefslogtreecommitdiff
path: root/game/client/hl2/c_ar2_explosion.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/hl2/c_ar2_explosion.cpp')
-rw-r--r--game/client/hl2/c_ar2_explosion.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/game/client/hl2/c_ar2_explosion.cpp b/game/client/hl2/c_ar2_explosion.cpp
new file mode 100644
index 0000000..e60343e
--- /dev/null
+++ b/game/client/hl2/c_ar2_explosion.cpp
@@ -0,0 +1,475 @@
+//========= 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 );
+ }
+}
+
+