Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

632 lines
16 KiB

// ClientInferno.cpp
// Render client-side Inferno effects
// Author: Michael Booth, February 2005
// Copyright (c) 2005 Turtle Rock Studios, Inc. - All Rights Reserved
#include "cbase.h"
#include "igamesystem.h"
#include "hud_macros.h"
#include "view.h"
#include "enginesprite.h"
#include "precache_register.h"
#include "iefx.h"
#include "dlight.h"
#include "tier0/vprof.h"
#include "debugoverlay_shared.h"
#include "basecsgrenade_projectile.h"
#include "clientinferno.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
PRECACHE_REGISTER_BEGIN( GLOBAL, InfernoMaterials )
PRECACHE( MATERIAL, "sprites/white" )
PRECACHE_REGISTER_END()
ConVar InfernoDlightSpacing( "inferno_dlight_spacing", "200", FCVAR_CHEAT, "Inferno dlights are at least this far apart" );
ConVar InfernoDlights( "inferno_dlights", "30", 0, "Min FPS at which molotov dlights will be created" );
//ConVar InfernoParticles( "inferno_particles", "molotov_groundfire", FCVAR_REPLICATED | FCVAR_CHEAT );
ConVar InfernoFire( "inferno_fire", "2" );
enum FireMaskType
{
OLD_FIRE_MASK = 1,
NEW_FIRE_MASK = 2
};
IMPLEMENT_CLIENTCLASS_DT( C_Inferno, DT_Inferno, CInferno )
RecvPropArray3( RECVINFO_ARRAY( m_fireXDelta ), RecvPropInt( RECVINFO(m_fireXDelta[0] ) ) ),
RecvPropArray3( RECVINFO_ARRAY( m_fireYDelta ), RecvPropInt( RECVINFO(m_fireYDelta[0] ) ) ),
RecvPropArray3( RECVINFO_ARRAY( m_fireZDelta ), RecvPropInt( RECVINFO(m_fireZDelta[0] ) ) ),
RecvPropArray3( RECVINFO_ARRAY( m_bFireIsBurning ), RecvPropBool( RECVINFO(m_bFireIsBurning[0] ) ) ),
//RecvPropArray3( RECVINFO_ARRAY( m_BurnNormal ), RecvPropVector( RECVINFO(m_BurnNormal[0] ) ) ),
RecvPropInt( RECVINFO( m_fireCount ) ),
END_RECV_TABLE()
//-----------------------------------------------------------------------------------------------
C_Inferno::C_Inferno()
{
m_maxFireHalfWidth = 30.0f;
m_maxFireHeight = 80.0f;
m_burnParticleEffect = NULL;
}
//-----------------------------------------------------------------------------------------------
C_Inferno::~C_Inferno()
{
if ( m_burnParticleEffect.IsValid() )
{
m_burnParticleEffect->StopEmission();
}
}
//-----------------------------------------------------------------------------------------------
void C_Inferno::Spawn( void )
{
BaseClass::Spawn();
m_fireCount = 0;
m_lastFireCount = 0;
m_drawableCount = 0;
m_burnParticleEffect = NULL;
m_minBounds = Vector( 0, 0, 0 );
m_maxBounds = Vector( 0, 0, 0 );
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
//-----------------------------------------------------------------------------------------------
/**
* Monitor changes and recompute render bounds
*/
void C_Inferno::ClientThink()
{
VPROF_BUDGET( "C_Inferno::ClientThink", "Magic" );
bool bIsAttachedToMovingObject = (GetMoveParent() != NULL) ? true : false;
if (true || m_lastFireCount != m_fireCount || bIsAttachedToMovingObject )
{
SynchronizeDrawables();
m_lastFireCount = m_fireCount;
}
bool bDidRecomputeBounds = false;
// update Drawables
for( int i=0; i<m_drawableCount; ++i )
{
Drawable *draw = &m_drawable[i];
switch( draw->m_state )
{
case STARTING:
{
float growRate = draw->m_maxSize/2.0f;
draw->m_size = growRate * (gpGlobals->realtime - draw->m_stateTimestamp);
if (draw->m_size > draw->m_maxSize)
{
draw->m_size = draw->m_maxSize;
draw->SetState( BURNING );
}
break;
}
case GOING_OUT:
{
float dieRate = draw->m_maxSize/2.0f;
draw->m_size = draw->m_maxSize - dieRate * (gpGlobals->realtime - draw->m_stateTimestamp);
if (draw->m_size <= 0.0f)
{
draw->SetState( FIRE_OUT );
// render bounds changed
RecomputeBounds();
bDidRecomputeBounds = true;
if ( GetRenderHandle() != INVALID_CLIENT_RENDER_HANDLE )
{
ClientLeafSystem()->RenderableChanged( GetRenderHandle() );
}
}
break;
}
}
}
if( bIsAttachedToMovingObject && !bDidRecomputeBounds )
{
RecomputeBounds();
}
UpdateParticles();
}
//--------------------------------------------------------------------------------------------------------
void C_Inferno::OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect )
{
if ( FStrEq( pszParticleName, GetParticleEffectName() ) )
{
m_burnParticleEffect = pNewParticleEffect;
}
}
//--------------------------------------------------------------------------------------------------------
void C_Inferno::OnParticleEffectDeleted( CNewParticleEffect *pParticleEffect )
{
if ( m_burnParticleEffect == pParticleEffect )
{
m_burnParticleEffect = NULL;
}
}
//--------------------------------------------------------------------------------------------------------
void C_Inferno::UpdateParticles( void )
{
if ( m_drawableCount > 0 && (InfernoFire.GetInt() & NEW_FIRE_MASK) != 0 )
{
if ( !m_burnParticleEffect.IsValid() )
{
MDLCACHE_CRITICAL_SECTION();
m_burnParticleEffect = ParticleProp()->Create( GetParticleEffectName(), PATTACH_ABSORIGIN_FOLLOW );
/*
DevMsg( "inferno @ %f %f %f / %f %f %f\n",
GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z,
GetAbsAngles()[PITCH], GetAbsAngles()[YAW], GetAbsAngles()[ROLL] );
NDebugOverlay::Cross3D( GetAbsOrigin(), 5, 255, 0, 0, false, 30.0f );
NDebugOverlay::Cross3D( GetAbsOrigin(), 5, 128, 0, 0, true, 30.0f );
*/
}
else
{
for( int i=0; i<m_drawableCount && i<MAX_PARTICLE_CONTROL_POINTS; ++i )
{
Drawable *draw = &m_drawable[i];
// TODO: need a way to disable a control point!!!
if ( draw->m_state >= FIRE_OUT )
{
Vector vecCenter = draw->m_pos;
//VectorLerp( m_minBounds, m_maxBounds, 0.5f, vecCenter );
draw->m_pos = vecCenter + Vector( 0, 0, -9999 );
draw->m_size = 0;
// this sucks
if ( i != 0 )
m_burnParticleEffect->SetControlPoint( i, vec3_invalid );
//engine->Con_NPrintf( i + 10, "0 0 0" );
//NDebugOverlay::Cross3D( draw->m_pos, 5, 0, 0, 255, false, 5.1f );
}
else
{
//NDebugOverlay::Cross3D( draw->m_pos, 5, 0, 0, 255, false, 0.1f );
//NDebugOverlay::Line( GetAbsOrigin(), draw->m_pos, 0, 255, 0, true, 0.1f );
m_burnParticleEffect->SetControlPointEntity( i, NULL );
m_burnParticleEffect->SetControlPoint( i, draw->m_pos );
// FIXME - Set orientation to burn normal once we have per particle normals.
//m_burnParticleEffect->SetControlPointOrientation( i, Orientation );
if ( i % 2 == 0 )
{
//Elight, for perf reasons only for every other fire
dlight_t *el = effects->CL_AllocElight( draw->m_dlightIndex );
el->origin = draw->m_pos;
el->origin[2] += 64;
el->color.r = 254;
el->color.g = 100;
el->color.b = 10;
el->radius = random->RandomFloat(60, 120);
el->die = gpGlobals->curtime + random->RandomFloat( 0.01, 0.025 );
el->color.exponent = 5;
}
}
}
SetNextClientThink( 0.1f );
m_burnParticleEffect->SetNeedsBBoxUpdate( true );
Vector vecCenter = GetRenderOrigin();
m_burnParticleEffect->SetSortOrigin( vecCenter );
//NDebugOverlay::Cross3D( vecCenter, 5, 255, 0, 255, false, 0.5f );
Vector vecMin, vecMax;
GetRenderBounds( vecMin, vecMax );
//NDebugOverlay::Box( vecCenter, vecMin, vecMax, 255, 0, 255, 255, 0.5 );
}
}
else
{
if ( m_burnParticleEffect.IsValid() )
{
m_burnParticleEffect->StopEmission();
// m_burnParticleEffect->SetRemoveFlag();
}
}
}
//-----------------------------------------------------------------------------------------------
void C_Inferno::GetRenderBounds( Vector& mins, Vector& maxs )
{
if (m_drawableCount)
{
mins = m_minBounds - GetRenderOrigin();
maxs = m_maxBounds - GetRenderOrigin();
}
else
{
mins = Vector( 0, 0, 0 );
maxs = Vector( 0, 0, 0 );
}
}
//-----------------------------------------------------------------------------------------------
/**
* Returns the bounds as an AABB in worldspace
*/
void C_Inferno::GetRenderBoundsWorldspace( Vector& mins, Vector& maxs )
{
if (m_drawableCount)
{
mins = m_minBounds;
maxs = m_maxBounds;
}
else
{
mins = Vector( 0, 0, 0 );
maxs = Vector( 0, 0, 0 );
}
}
//-----------------------------------------------------------------------------------------------
void C_Inferno::RecomputeBounds( void )
{
m_minBounds = GetAbsOrigin() + Vector( 64.9f, 64.9f, 64.9f );
m_maxBounds = GetAbsOrigin() + Vector( -64.9f, -64.9f, -64.9f );
for( int i=0; i<m_drawableCount; ++i )
{
Drawable *draw = &m_drawable[i];
if (draw->m_state == FIRE_OUT)
continue;
if (draw->m_pos.x - m_maxFireHalfWidth < m_minBounds.x)
m_minBounds.x = draw->m_pos.x - m_maxFireHalfWidth;
if (draw->m_pos.x + m_maxFireHalfWidth > m_maxBounds.x)
m_maxBounds.x = draw->m_pos.x + m_maxFireHalfWidth;
if (draw->m_pos.y - m_maxFireHalfWidth < m_minBounds.y)
m_minBounds.y = draw->m_pos.y - m_maxFireHalfWidth;
if (draw->m_pos.y + m_maxFireHalfWidth > m_maxBounds.y)
m_maxBounds.y = draw->m_pos.y + m_maxFireHalfWidth;
if (draw->m_pos.z < m_minBounds.z)
m_minBounds.z = draw->m_pos.z;
if (draw->m_pos.z + m_maxFireHeight > m_maxBounds.z)
m_maxBounds.z = draw->m_pos.z + m_maxFireHeight;
}
}
//-----------------------------------------------------------------------------------------------
/**
* Given a position, return the fire there
*/
C_Inferno::Drawable *C_Inferno::GetDrawable( const Vector &pos )
{
for( int i=0; i<m_drawableCount; ++i )
{
const float equalTolerance = 12.0f;
if (VectorsAreEqual( m_drawable[i].m_pos, pos, equalTolerance ))
{
m_drawable[ i ].m_pos = pos;
return &m_drawable[i];
}
}
return NULL;
}
//-----------------------------------------------------------------------------------------------
/**
* Compare m_fireX etc to m_drawable and update states
*/
void C_Inferno::SynchronizeDrawables( void )
{
VPROF_BUDGET( "C_Inferno::SynchronizeDrawables", "Magic" );
int i;
// mark all fires that are "burning" as "unknown" - active ones will be reset
for( i=0; i<m_drawableCount; ++i )
{
if (m_drawable[i].m_state == BURNING)
{
m_drawable[i].m_state = UNKNOWN;
}
}
Vector vInfernoOrigin = GetAbsOrigin();
for( i=0; i<m_fireCount; ++i )
{
Vector firePos = vInfernoOrigin;
Vector vecFlamePos = Vector( m_fireXDelta[ i ], m_fireYDelta[ i ], m_fireZDelta[ i ] );
firePos.x += vecFlamePos.x;
firePos.y += vecFlamePos.y;
firePos.z += vecFlamePos.z;
Vector fireNormal = m_BurnNormal[i];
Drawable *info = GetDrawable( firePos );
if ( m_bFireIsBurning[i] == false )
{
if ( info && info->m_state != FIRE_OUT )
{
info->m_state = FIRE_OUT;
// render bounds changed
RecomputeBounds();
if ( GetRenderHandle() != INVALID_CLIENT_RENDER_HANDLE )
{
ClientLeafSystem()->RenderableChanged( GetRenderHandle() );
}
}
continue;
}
else if ( info )
{
// existing fire continues to burn
if (info->m_state == UNKNOWN)
{
info->m_state = BURNING;
}
}
else if (m_drawableCount < MAX_INFERNO_FIRES)
{
// new fire
info = &m_drawable[ m_drawableCount ];
info->SetState( STARTING );
info->m_pos = firePos;
info->m_normal = fireNormal;
info->m_frame = 0;
info->m_framerate = random->RandomFloat( 0.04f, 0.06f );
info->m_mirror = (random->RandomInt( 0, 100 ) < 50);
info->m_size = 0.0f;
info->m_maxSize = random->RandomFloat( 70.0f, 90.0f );
bool closeDlight = false;
for ( int i=0; i<m_drawableCount; ++i )
{
if ( m_drawable[i].m_state != FIRE_OUT )
{
if ( m_drawable[i].m_dlightIndex > 0 )
{
if ( m_drawable[i].m_pos.DistToSqr( firePos ) < InfernoDlightSpacing.GetFloat() * InfernoDlightSpacing.GetFloat() )
{
closeDlight = true;
break;
}
}
}
}
if ( closeDlight )
{
info->m_dlightIndex = 0;
}
else
{
info->m_dlightIndex = LIGHT_INDEX_TE_DYNAMIC + index + m_drawableCount;
}
// render bounds changed
RecomputeBounds();
if ( GetRenderHandle() != INVALID_CLIENT_RENDER_HANDLE )
{
ClientLeafSystem()->RenderableChanged( GetRenderHandle() );
}
++m_drawableCount;
}
}
// any fires still in the UNKNOWN state are now GOING_OUT
for( i=0; i<m_drawableCount; ++i )
{
if (m_drawable[i].m_state == UNKNOWN)
{
m_drawable[i].SetState( GOING_OUT );
}
}
}
//-----------------------------------------------------------------------------------------------
int C_Inferno::DrawModel( int flags, const RenderableInstance_t &instance )
{
VPROF_BUDGET( "C_Inferno::DrawModel", "DeadRun" );
IMaterial *material;
CEngineSprite *sprite;
const model_t *model;
if ( (InfernoFire.GetInt() & OLD_FIRE_MASK) == 0 )
return 0;
model = modelinfo->GetModel( modelinfo->GetModelIndex( "sprites/fire1.vmt" ) );
if (model == NULL)
return 0;
sprite = (CEngineSprite *)modelinfo->GetModelExtraData( model );
if (sprite == NULL)
return 0;
material = sprite->GetMaterial( kRenderTransAdd );
if (material == NULL)
return 0;
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->Bind( material );
IMesh *pMesh = pRenderContext->GetDynamicMesh();
if ( pMesh )
{
// draw the actual flames
for( int i=0; i<m_drawableCount; ++i )
{
int frame = (int)(gpGlobals->realtime/m_drawable[i].m_framerate) % sprite->GetNumFrames();
sprite->SetFrame( kRenderTransAdd, frame );
DrawFire( &m_drawable[i], pMesh );
}
}
return 0;
}
//-----------------------------------------------------------------------------------------------
/**
* Render an individual fire sprite
*/
void C_Inferno::DrawFire( C_Inferno::Drawable *fire, IMesh *mesh )
{
const float halfWidth = fire->m_size/3.0f;
//unsigned char color[4] = { 255,255,255,255 };
unsigned char color[4] = { 150,150,150,255 };
CMeshBuilder meshBuilder;
meshBuilder.Begin( mesh, MATERIAL_QUADS, 1 );
const Vector &right = (fire->m_mirror) ? -CurrentViewRight() : CurrentViewRight();
Vector up( 0.0f, 0.0f, 1.0f );
Vector top = fire->m_pos + up * fire->m_size;
const Vector &bottom = fire->m_pos;
Vector pos = top + right * halfWidth;
meshBuilder.Position3fv( pos.Base() );
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, 1, 0 );
meshBuilder.AdvanceVertex();
pos = bottom + right * halfWidth;
meshBuilder.Position3fv( pos.Base() );
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, 1, 1 );
meshBuilder.AdvanceVertex();
pos = bottom - right * halfWidth;
meshBuilder.Position3fv( pos.Base() );
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, 0, 1 );
meshBuilder.AdvanceVertex();
pos = top - right * halfWidth;
meshBuilder.Position3fv( pos.Base() );
meshBuilder.Color4ubv( color );
meshBuilder.TexCoord2f( 0, 0, 0 );
meshBuilder.AdvanceVertex();
meshBuilder.End();
mesh->Draw();
if ( fire->m_dlightIndex > 0 && InfernoDlights.GetFloat() >= 1 )
{
static float lastRealTime = -1.0f;
float realFrameTime = gpGlobals->realtime - lastRealTime;
if ( realFrameTime > 2 )
{
realFrameTime = -1.0f;
}
if ( realFrameTime > 0 )
{
static float AverageFPS = -1;
static int high = -1;
static int low = -1;
int nFps = -1;
const float NewWeight = 0.1f;
float NewFrame = 1.0f / realFrameTime;
if ( AverageFPS < 0.0f )
{
AverageFPS = NewFrame;
high = (int)AverageFPS;
low = (int)AverageFPS;
}
else
{
AverageFPS *= ( 1.0f - NewWeight ) ;
AverageFPS += ( ( NewFrame ) * NewWeight );
}
int NewFrameInt = (int)NewFrame;
if( NewFrameInt < low ) low = NewFrameInt;
if( NewFrameInt > high ) high = NewFrameInt;
nFps = static_cast<int>( AverageFPS );
if ( nFps < InfernoDlights.GetFloat() )
{
fire->m_dlightIndex = 0;
lastRealTime = gpGlobals->realtime;
return;
}
}
lastRealTime = gpGlobals->realtime;
// These are the dlight params from the Ep1 fire glows, with a slightly larger flicker
// (radius delta is larger, starting from 250 instead of 400).
float scale = fire->m_size / fire->m_maxSize * 1.5f;
dlight_t *el = effects->CL_AllocElight( fire->m_dlightIndex );
el->origin = bottom;
el->origin[2] += 16.0f * scale;
el->color.r = 254;
el->color.g = 100;
el->color.b = 10;
el->radius = random->RandomFloat(50, 131) * scale;
el->die = gpGlobals->curtime + 0.1f;
el->color.exponent = 5;
/*
dlight_t *dl = effects->CL_AllocDlight ( fire->m_dlightIndex );
dl->origin = bottom;
dl->origin[2] += 16.0f * scale;
dl->color.r = 254;
dl->color.g = 174;
dl->color.b = 10;
dl->radius = random->RandomFloat(350,431) * scale;
dl->die = gpGlobals->curtime + 0.1f;
*/
}
}
IMPLEMENT_CLIENTCLASS_DT( C_FireCrackerBlast, DT_FireCrackerBlast, CFireCrackerBlast )
END_RECV_TABLE()