From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/client/c_effects.cpp | 4442 +++++++++++++++++++------------------- 1 file changed, 2221 insertions(+), 2221 deletions(-) (limited to 'mp/src/game/client/c_effects.cpp') diff --git a/mp/src/game/client/c_effects.cpp b/mp/src/game/client/c_effects.cpp index 03ac01f8..14a90a4c 100644 --- a/mp/src/game/client/c_effects.cpp +++ b/mp/src/game/client/c_effects.cpp @@ -1,2221 +1,2221 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// -#include "cbase.h" -#include "c_tracer.h" -#include "view.h" -#include "initializer.h" -#include "particles_simple.h" -#include "env_wind_shared.h" -#include "engine/IEngineTrace.h" -#include "engine/ivmodelinfo.h" -#include "precipitation_shared.h" -#include "fx_water.h" -#include "c_world.h" -#include "iviewrender.h" -#include "engine/ivdebugoverlay.h" -#include "clienteffectprecachesystem.h" -#include "collisionutils.h" -#include "tier0/vprof.h" -#include "viewrender.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include "tier0/memdbgon.h" - -ConVar cl_winddir ( "cl_winddir", "0", FCVAR_CHEAT, "Weather effects wind direction angle" ); -ConVar cl_windspeed ( "cl_windspeed", "0", FCVAR_CHEAT, "Weather effects wind speed scalar" ); - -Vector g_vSplashColor( 0.5, 0.5, 0.5 ); -float g_flSplashScale = 0.15; -float g_flSplashLifetime = 0.5f; -float g_flSplashAlpha = 0.3f; -ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // N% chance of a rain particle making a splash. - - -float GUST_INTERVAL_MIN = 1; -float GUST_INTERVAL_MAX = 2; - -float GUST_LIFETIME_MIN = 1; -float GUST_LIFETIME_MAX = 3; - -float MIN_SCREENSPACE_RAIN_WIDTH = 1; - -#ifndef _XBOX -ConVar r_RainHack( "r_RainHack", "0", FCVAR_CHEAT ); -ConVar r_RainRadius( "r_RainRadius", "1500", FCVAR_CHEAT ); -ConVar r_RainSideVel( "r_RainSideVel", "130", FCVAR_CHEAT, "How much sideways velocity rain gets." ); - -ConVar r_RainSimulate( "r_RainSimulate", "1", FCVAR_CHEAT, "Enable/disable rain simulation." ); -ConVar r_DrawRain( "r_DrawRain", "1", FCVAR_CHEAT, "Enable/disable rain rendering." ); -ConVar r_RainProfile( "r_RainProfile", "0", FCVAR_CHEAT, "Enable/disable rain profiling." ); - - -//Precahce the effects -CLIENTEFFECT_REGISTER_BEGIN( PrecachePrecipitation ) -CLIENTEFFECT_MATERIAL( "particle/rain" ) -CLIENTEFFECT_MATERIAL( "particle/snow" ) -CLIENTEFFECT_REGISTER_END() - -//----------------------------------------------------------------------------- -// Precipitation particle type -//----------------------------------------------------------------------------- - -class CPrecipitationParticle -{ -public: - Vector m_Pos; - Vector m_Velocity; - float m_SpawnTime; // Note: Tweak with this to change lifetime - float m_Mass; - float m_Ramp; - - float m_flCurLifetime; - float m_flMaxLifetime; -}; - - -class CClient_Precipitation; -static CUtlVector g_Precipitations; - -//=========== -// Snow fall -//=========== -class CSnowFallManager; -static CSnowFallManager *s_pSnowFallMgr = NULL; -bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ); -void SnowFallManagerDestroy( void ); - -class AshDebrisEffect : public CSimpleEmitter -{ -public: - AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} - - static AshDebrisEffect* Create( const char *pDebugName ); - - virtual float UpdateAlpha( const SimpleParticle *pParticle ); - virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta ); - -private: - AshDebrisEffect( const AshDebrisEffect & ); -}; - -//----------------------------------------------------------------------------- -// Precipitation base entity -//----------------------------------------------------------------------------- - -class CClient_Precipitation : public C_BaseEntity -{ -class CPrecipitationEffect; -friend class CClient_Precipitation::CPrecipitationEffect; - -public: - DECLARE_CLASS( CClient_Precipitation, C_BaseEntity ); - DECLARE_CLIENTCLASS(); - - CClient_Precipitation(); - virtual ~CClient_Precipitation(); - - // Inherited from C_BaseEntity - virtual void Precache( ); - - void Render(); - -private: - - // Creates a single particle - CPrecipitationParticle* CreateParticle(); - - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual void ClientThink(); - - void Simulate( float dt ); - - // Renders the particle - void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ); - - void CreateWaterSplashes(); - - // Emits the actual particles - void EmitParticles( float fTimeDelta ); - - // Computes where we're gonna emit - bool ComputeEmissionArea( Vector& origin, Vector2D& size ); - - // Gets the tracer width and speed - float GetWidth() const; - float GetLength() const; - float GetSpeed() const; - - // Gets the remaining lifetime of the particle - float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const; - - // Computes the wind vector - static void ComputeWindVector( ); - - // simulation methods - bool SimulateRain( CPrecipitationParticle* pParticle, float dt ); - bool SimulateSnow( CPrecipitationParticle* pParticle, float dt ); - - void CreateAshParticle( void ); - void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); - - // Information helpful in creating and rendering particles - IMaterial *m_MatHandle; // material used - - float m_Color[4]; // precip color - float m_Lifetime; // Precip lifetime - float m_InitialRamp; // Initial ramp value - float m_Speed; // Precip speed - float m_Width; // Tracer width - float m_Remainder; // particles we should render next time - PrecipitationType_t m_nPrecipType; // Precip type - float m_flHalfScreenWidth; // Precalculated each frame. - - float m_flDensity; - - // Some state used in rendering and simulation - // Used to modify the rain density and wind from the console - static ConVar s_raindensity; - static ConVar s_rainwidth; - static ConVar s_rainlength; - static ConVar s_rainspeed; - - static Vector s_WindVector; // Stores the wind speed vector - - CUtlLinkedList m_Particles; - CUtlVector m_Splashes; - - CSmartPtr m_pAshEmitter; - TimedEvent m_tAshParticleTimer; - TimedEvent m_tAshParticleTraceTimer; - bool m_bActiveAshEmitter; - Vector m_vAshSpawnOrigin; - - int m_iAshCount; - -private: - CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible -}; - - -// Just receive the normal data table stuff -IMPLEMENT_CLIENTCLASS_DT(CClient_Precipitation, DT_Precipitation, CPrecipitation) - RecvPropInt( RECVINFO( m_nPrecipType ) ) -END_RECV_TABLE() - -static ConVar r_SnowEnable( "r_SnowEnable", "1", FCVAR_CHEAT, "Snow Enable" ); -static ConVar r_SnowParticles( "r_SnowParticles", "500", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowInsideRadius( "r_SnowInsideRadius", "256", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowOutsideRadius( "r_SnowOutsideRadius", "1024", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowSpeedScale( "r_SnowSpeedScale", "1", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowPosScale( "r_SnowPosScale", "1", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowFallSpeed( "r_SnowFallSpeed", "1.5", FCVAR_CHEAT, "Snow fall speed scale." ); -static ConVar r_SnowWindScale( "r_SnowWindScale", "0.0035", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowDebugBox( "r_SnowDebugBox", "0", FCVAR_CHEAT, "Snow Debug Boxes." ); -static ConVar r_SnowZoomOffset( "r_SnowZoomOffset", "384.0f", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowZoomRadius( "r_SnowZoomRadius", "512.0f", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowStartAlpha( "r_SnowStartAlpha", "25", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowEndAlpha( "r_SnowEndAlpha", "255", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowColorRed( "r_SnowColorRed", "150", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowColorGreen( "r_SnowColorGreen", "175", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowColorBlue( "r_SnowColorBlue", "200", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowStartSize( "r_SnowStartSize", "1", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowEndSize( "r_SnowEndSize", "0", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowRayLength( "r_SnowRayLength", "8192.0f", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowRayRadius( "r_SnowRayRadius", "256", FCVAR_CHEAT, "Snow." ); -static ConVar r_SnowRayEnable( "r_SnowRayEnable", "1", FCVAR_CHEAT, "Snow." ); - -void DrawPrecipitation() -{ - for ( int i=0; i < g_Precipitations.Count(); i++ ) - { - g_Precipitations[i]->Render(); - } -} - - -//----------------------------------------------------------------------------- -// determines if a weather particle has hit something other than air -//----------------------------------------------------------------------------- -static bool IsInAir( const Vector& position ) -{ - int contents = enginetrace->GetPointContents( position ); - return (contents & CONTENTS_SOLID) == 0; -} - - -//----------------------------------------------------------------------------- -// Globals -//----------------------------------------------------------------------------- - -ConVar CClient_Precipitation::s_raindensity( "r_raindensity","0.001", FCVAR_CHEAT); -ConVar CClient_Precipitation::s_rainwidth( "r_rainwidth", "0.5", FCVAR_CHEAT ); -ConVar CClient_Precipitation::s_rainlength( "r_rainlength", "0.1f", FCVAR_CHEAT ); -ConVar CClient_Precipitation::s_rainspeed( "r_rainspeed", "600.0f", FCVAR_CHEAT ); -ConVar r_rainalpha( "r_rainalpha", "0.4", FCVAR_CHEAT ); -ConVar r_rainalphapow( "r_rainalphapow", "0.8", FCVAR_CHEAT ); - - -Vector CClient_Precipitation::s_WindVector; // Stores the wind speed vector - - -void CClient_Precipitation::OnDataChanged( DataUpdateType_t updateType ) -{ - // Simulate every frame. - if ( updateType == DATA_UPDATE_CREATED ) - { - SetNextClientThink( CLIENT_THINK_ALWAYS ); - if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) - { - SnowFallManagerCreate( this ); - } - } - - m_flDensity = RemapVal( m_clrRender->a, 0, 255, 0, 0.001 ); - - BaseClass::OnDataChanged( updateType ); -} - - -void CClient_Precipitation::ClientThink() -{ - Simulate( gpGlobals->frametime ); -} - - -//----------------------------------------------------------------------------- -// -// Utility methods for the various simulation functions -// -//----------------------------------------------------------------------------- -inline bool CClient_Precipitation::SimulateRain( CPrecipitationParticle* pParticle, float dt ) -{ - if (GetRemainingLifetime( pParticle ) < 0.0f) - return false; - - Vector vOldPos = pParticle->m_Pos; - - // Update position - VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity, - pParticle->m_Pos ); - - // wind blows rain around - for ( int i = 0 ; i < 2 ; i++ ) - { - if ( pParticle->m_Velocity[i] < s_WindVector[i] ) - { - pParticle->m_Velocity[i] += ( 5 / pParticle->m_Mass ); - - // clamp - if ( pParticle->m_Velocity[i] > s_WindVector[i] ) - pParticle->m_Velocity[i] = s_WindVector[i]; - } - else if (pParticle->m_Velocity[i] > s_WindVector[i] ) - { - pParticle->m_Velocity[i] -= ( 5 / pParticle->m_Mass ); - - // clamp. - if ( pParticle->m_Velocity[i] < s_WindVector[i] ) - pParticle->m_Velocity[i] = s_WindVector[i]; - } - } - - // No longer in the air? punt. - if ( !IsInAir( pParticle->m_Pos ) ) - { - // Possibly make a splash if we hit a water surface and it's in front of the view. - if ( m_Splashes.Count() < 20 ) - { - if ( RandomInt( 0, 100 ) < r_RainSplashPercentage.GetInt() ) - { - trace_t trace; - UTIL_TraceLine(vOldPos, pParticle->m_Pos, MASK_WATER, NULL, COLLISION_GROUP_NONE, &trace); - if( trace.fraction < 1 ) - { - m_Splashes.AddToTail( trace.endpos ); - } - } - } - - // Tell the framework it's time to remove the particle from the list - return false; - } - - // We still want this particle - return true; -} - - -inline bool CClient_Precipitation::SimulateSnow( CPrecipitationParticle* pParticle, float dt ) -{ - if ( IsInAir( pParticle->m_Pos ) ) - { - // Update position - VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity, - pParticle->m_Pos ); - - // wind blows rain around - for ( int i = 0 ; i < 2 ; i++ ) - { - if ( pParticle->m_Velocity[i] < s_WindVector[i] ) - { - pParticle->m_Velocity[i] += ( 5.0f / pParticle->m_Mass ); - - // accelerating flakes get a trail - pParticle->m_Ramp = 0.5f; - - // clamp - if ( pParticle->m_Velocity[i] > s_WindVector[i] ) - pParticle->m_Velocity[i] = s_WindVector[i]; - } - else if (pParticle->m_Velocity[i] > s_WindVector[i] ) - { - pParticle->m_Velocity[i] -= ( 5.0f / pParticle->m_Mass ); - - // accelerating flakes get a trail - pParticle->m_Ramp = 0.5f; - - // clamp. - if ( pParticle->m_Velocity[i] < s_WindVector[i] ) - pParticle->m_Velocity[i] = s_WindVector[i]; - } - } - - return true; - } - - - // Kill the particle immediately! - return false; -} - - -void CClient_Precipitation::Simulate( float dt ) -{ - // NOTE: When client-side prechaching works, we need to remove this - Precache(); - - m_flHalfScreenWidth = (float)ScreenWidth() / 2; - - // Our sim methods needs dt and wind vector - if ( dt ) - { - ComputeWindVector( ); - } - - if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) - { - CreateAshParticle(); - return; - } - - // The snow fall manager handles the simulation. - if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) - return; - - // calculate the max amount of time it will take this flake to fall. - // This works if we assume the wind doesn't have a z component - if ( r_RainHack.GetInt() ) - m_Lifetime = (GetClientWorldEntity()->m_WorldMaxs[2] - GetClientWorldEntity()->m_WorldMins[2]) / m_Speed; - else - m_Lifetime = (WorldAlignMaxs()[2] - WorldAlignMins()[2]) / m_Speed; - - - if ( !r_RainSimulate.GetInt() ) - return; - - CFastTimer timer; - timer.Start(); - - // Emit new particles - EmitParticles( dt ); - - // Simulate all the particles. - int iNext; - if ( m_nPrecipType == PRECIPITATION_TYPE_RAIN ) - { - for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext ) - { - iNext = m_Particles.Next( i ); - if ( !SimulateRain( &m_Particles[i], dt ) ) - m_Particles.Remove( i ); - } - } - else if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW ) - { - for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext ) - { - iNext = m_Particles.Next( i ); - if ( !SimulateSnow( &m_Particles[i], dt ) ) - m_Particles.Remove( i ); - } - } - - if ( r_RainProfile.GetInt() ) - { - timer.End(); - engine->Con_NPrintf( 15, "Rain simulation: %du (%d tracers)", timer.GetDuration().GetMicroseconds(), m_Particles.Count() ); - } -} - - -//----------------------------------------------------------------------------- -// tracer rendering -//----------------------------------------------------------------------------- - -inline void CClient_Precipitation::RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ) -{ - float scale; - Vector start, delta; - - if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) - return; - - if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) - return; - - - // make streaks 0.1 seconds long, but prevent from going past end - float lifetimeRemaining = GetRemainingLifetime( pParticle ); - if (lifetimeRemaining >= GetLength()) - scale = GetLength() * pParticle->m_Ramp; - else - scale = lifetimeRemaining * pParticle->m_Ramp; - - // NOTE: We need to do everything in screen space - Vector3DMultiplyPosition( CurrentWorldToViewMatrix(), pParticle->m_Pos, start ); - if ( start.z > -1 ) - return; - - Vector3DMultiply( CurrentWorldToViewMatrix(), pParticle->m_Velocity, delta ); - - // give a spiraling pattern to snow particles - if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW ) - { - Vector spiral, camSpiral; - float s, c; - - if ( pParticle->m_Mass > 1.0f ) - { - SinCos( gpGlobals->curtime * M_PI * (1+pParticle->m_Mass * 0.1f) + - pParticle->m_Mass * 5.0f, &s , &c ); - - // only spiral particles with a mass > 1, so some fall straight down - spiral[0] = 28 * c; - spiral[1] = 28 * s; - spiral[2] = 0.0f; - - Vector3DMultiply( CurrentWorldToViewMatrix(), spiral, camSpiral ); - - // X and Y are measured in world space; need to convert to camera space - VectorAdd( start, camSpiral, start ); - VectorAdd( delta, camSpiral, delta ); - } - - // shrink the trails on spiraling flakes. - pParticle->m_Ramp = 0.3f; - } - - delta[0] *= scale; - delta[1] *= scale; - delta[2] *= scale; - - // See c_tracer.* for this method - float flAlpha = r_rainalpha.GetFloat(); - float flWidth = GetWidth(); - - float flScreenSpaceWidth = flWidth * m_flHalfScreenWidth / -start.z; - if ( flScreenSpaceWidth < MIN_SCREENSPACE_RAIN_WIDTH ) - { - // Make the rain tracer at least the min size, but fade its alpha the smaller it gets. - flAlpha *= flScreenSpaceWidth / MIN_SCREENSPACE_RAIN_WIDTH; - flWidth = MIN_SCREENSPACE_RAIN_WIDTH * -start.z / m_flHalfScreenWidth; - } - flAlpha = pow( flAlpha, r_rainalphapow.GetFloat() ); - - float flColor[4] = { 1, 1, 1, flAlpha }; - Tracer_Draw( &mb, start, delta, flWidth, flColor, 1 ); -} - - -void CClient_Precipitation::CreateWaterSplashes() -{ - for ( int i=0; i < m_Splashes.Count(); i++ ) - { - Vector vSplash = m_Splashes[i]; - - if ( CurrentViewForward().Dot( vSplash - CurrentViewOrigin() ) > 1 ) - { - FX_WaterRipple( vSplash, g_flSplashScale, &g_vSplashColor, g_flSplashLifetime, g_flSplashAlpha ); - } - } - m_Splashes.Purge(); -} - - -void CClient_Precipitation::Render() -{ - if ( !r_DrawRain.GetInt() ) - return; - - // Don't render in monitors or in reflections or refractions. - if ( CurrentViewID() == VIEW_MONITOR ) - return; - - if ( view->GetDrawFlags() & (DF_RENDER_REFLECTION | DF_RENDER_REFRACTION) ) - return; - - if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) - return; - - if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) - return; - - // Create any queued up water splashes. - CreateWaterSplashes(); - - - CFastTimer timer; - timer.Start(); - - CMatRenderContextPtr pRenderContext( materials ); - - // We want to do our calculations in view space. - VMatrix tempView; - pRenderContext->GetMatrix( MATERIAL_VIEW, &tempView ); - pRenderContext->MatrixMode( MATERIAL_VIEW ); - pRenderContext->LoadIdentity(); - - // Force the user clip planes to use the old view matrix - pRenderContext->EnableUserClipTransformOverride( true ); - pRenderContext->UserClipTransform( tempView ); - - // Draw all the rain tracers. - pRenderContext->Bind( m_MatHandle ); - IMesh *pMesh = pRenderContext->GetDynamicMesh(); - if ( pMesh ) - { - CMeshBuilder mb; - mb.Begin( pMesh, MATERIAL_QUADS, m_Particles.Count() ); - - for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=m_Particles.Next( i ) ) - { - CPrecipitationParticle *p = &m_Particles[i]; - RenderParticle( p, mb ); - } - - mb.End( false, true ); - } - - pRenderContext->EnableUserClipTransformOverride( false ); - pRenderContext->MatrixMode( MATERIAL_VIEW ); - pRenderContext->LoadMatrix( tempView ); - - if ( r_RainProfile.GetInt() ) - { - timer.End(); - engine->Con_NPrintf( 16, "Rain render : %du", timer.GetDuration().GetMicroseconds() ); - } -} - - -//----------------------------------------------------------------------------- -// Constructor, destructor -//----------------------------------------------------------------------------- - -CClient_Precipitation::CClient_Precipitation() : m_Remainder(0.0f) -{ - m_nPrecipType = PRECIPITATION_TYPE_RAIN; - m_MatHandle = INVALID_MATERIAL_HANDLE; - m_flHalfScreenWidth = 1; - - g_Precipitations.AddToTail( this ); -} - -CClient_Precipitation::~CClient_Precipitation() -{ - g_Precipitations.FindAndRemove( this ); - SnowFallManagerDestroy(); -} - -//----------------------------------------------------------------------------- -// Precache data -//----------------------------------------------------------------------------- - -#define SNOW_SPEED 80.0f -#define RAIN_SPEED 425.0f - -#define RAIN_TRACER_WIDTH 0.35f -#define SNOW_TRACER_WIDTH 0.7f - -void CClient_Precipitation::Precache( ) -{ - if ( !m_MatHandle ) - { - // Compute precipitation emission speed - switch( m_nPrecipType ) - { - case PRECIPITATION_TYPE_SNOW: - m_Speed = SNOW_SPEED; - m_MatHandle = materials->FindMaterial( "particle/snow", TEXTURE_GROUP_CLIENT_EFFECTS ); - m_InitialRamp = 0.6f; - m_Width = SNOW_TRACER_WIDTH; - break; - - case PRECIPITATION_TYPE_RAIN: - Assert( m_nPrecipType == PRECIPITATION_TYPE_RAIN ); - m_Speed = RAIN_SPEED; - m_MatHandle = materials->FindMaterial( "particle/rain", TEXTURE_GROUP_CLIENT_EFFECTS ); - m_InitialRamp = 1.0f; - m_Color[3] = 1.0f; // make translucent - m_Width = RAIN_TRACER_WIDTH; - break; - default: - m_InitialRamp = 1.0f; - m_Color[3] = 1.0f; // make translucent - break; - } - - // Store off the color - m_Color[0] = 1.0f; - m_Color[1] = 1.0f; - m_Color[2] = 1.0f; - } -} - - -//----------------------------------------------------------------------------- -// Gets the tracer width and speed -//----------------------------------------------------------------------------- - -inline float CClient_Precipitation::GetWidth() const -{ -// return m_Width; - return s_rainwidth.GetFloat(); -} - -inline float CClient_Precipitation::GetLength() const -{ -// return m_Length; - return s_rainlength.GetFloat(); -} - -inline float CClient_Precipitation::GetSpeed() const -{ -// return m_Speed; - return s_rainspeed.GetFloat(); -} - - -//----------------------------------------------------------------------------- -// Gets the remaining lifetime of the particle -//----------------------------------------------------------------------------- - -inline float CClient_Precipitation::GetRemainingLifetime( CPrecipitationParticle* pParticle ) const -{ - float timeSinceSpawn = gpGlobals->curtime - pParticle->m_SpawnTime; - return m_Lifetime - timeSinceSpawn; -} - -//----------------------------------------------------------------------------- -// Creates a particle -//----------------------------------------------------------------------------- - -inline CPrecipitationParticle* CClient_Precipitation::CreateParticle() -{ - int i = m_Particles.AddToTail(); - CPrecipitationParticle* pParticle = &m_Particles[i]; - - pParticle->m_SpawnTime = gpGlobals->curtime; - pParticle->m_Ramp = m_InitialRamp; - - return pParticle; -} - - -//----------------------------------------------------------------------------- -// Compute the emission area -//----------------------------------------------------------------------------- - -bool CClient_Precipitation::ComputeEmissionArea( Vector& origin, Vector2D& size ) -{ - // FIXME: Compute the precipitation area based on computational power - float emissionSize = r_RainRadius.GetFloat(); // size of box to emit particles in - - Vector vMins = WorldAlignMins(); - Vector vMaxs = WorldAlignMaxs(); - if ( r_RainHack.GetInt() ) - { - vMins = GetClientWorldEntity()->m_WorldMins; - vMaxs = GetClientWorldEntity()->m_WorldMaxs; - } - - // calculate a volume around the player to snow in. Intersect this big magic - // box around the player with the volume of the current environmental ent. - C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); - if ( !pPlayer ) - return false; - - // Determine how much time it'll take a falling particle to hit the player - float emissionHeight = MIN( vMaxs[2], pPlayer->GetAbsOrigin()[2] + 512 ); - float distToFall = emissionHeight - pPlayer->GetAbsOrigin()[2]; - float fallTime = distToFall / GetSpeed(); - - // Based on the windspeed, figure out the center point of the emission - Vector2D center; - center[0] = pPlayer->GetAbsOrigin()[0] - fallTime * s_WindVector[0]; - center[1] = pPlayer->GetAbsOrigin()[1] - fallTime * s_WindVector[1]; - - Vector2D lobound, hibound; - lobound[0] = center[0] - emissionSize * 0.5f; - lobound[1] = center[1] - emissionSize * 0.5f; - hibound[0] = lobound[0] + emissionSize; - hibound[1] = lobound[1] + emissionSize; - - // Cull non-intersecting. - if ( ( vMaxs[0] < lobound[0] ) || ( vMaxs[1] < lobound[1] ) || - ( vMins[0] > hibound[0] ) || ( vMins[1] > hibound[1] ) ) - return false; - - origin[0] = MAX( vMins[0], lobound[0] ); - origin[1] = MAX( vMins[1], lobound[1] ); - origin[2] = emissionHeight; - - hibound[0] = MIN( vMaxs[0], hibound[0] ); - hibound[1] = MIN( vMaxs[1], hibound[1] ); - - size[0] = hibound[0] - origin[0]; - size[1] = hibound[1] - origin[1]; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pDebugName - -// Output : AshDebrisEffect* -//----------------------------------------------------------------------------- -AshDebrisEffect* AshDebrisEffect::Create( const char *pDebugName ) -{ - return new AshDebrisEffect( pDebugName ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pParticle - -// timeDelta - -// Output : float -//----------------------------------------------------------------------------- -float AshDebrisEffect::UpdateAlpha( const SimpleParticle *pParticle ) -{ - return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) ); -} - -#define ASH_PARTICLE_NOISE 0x4 - -float AshDebrisEffect::UpdateRoll( SimpleParticle *pParticle, float timeDelta ) -{ - float flRoll = CSimpleEmitter::UpdateRoll(pParticle, timeDelta ); - - if ( pParticle->m_iFlags & ASH_PARTICLE_NOISE ) - { - Vector vTempEntVel = pParticle->m_vecVelocity; - float fastFreq = gpGlobals->curtime * 1.5; - - float s, c; - SinCos( fastFreq, &s, &c ); - - pParticle->m_Pos = ( pParticle->m_Pos + Vector( - vTempEntVel[0] * timeDelta * s, - vTempEntVel[1] * timeDelta * s, 0 ) ); - } - - return flRoll; -} - -void CClient_Precipitation::CreateAshParticle( void ) -{ - // Make sure the emitter is setup - if ( m_pAshEmitter == NULL ) - { - if ( ( m_pAshEmitter = AshDebrisEffect::Create( "ashtray" ) ) == NULL ) - return; - - m_tAshParticleTimer.Init( 192 ); - m_tAshParticleTraceTimer.Init( 15 ); - m_bActiveAshEmitter = false; - m_iAshCount = 0; - } - - C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); - - if ( pPlayer == NULL ) - return; - - Vector vForward; - pPlayer->GetVectors( &vForward, NULL, NULL ); - vForward.z = 0.0f; - - float curTime = gpGlobals->frametime; - - Vector vPushOrigin; - - Vector absmins = WorldAlignMins(); - Vector absmaxs = WorldAlignMaxs(); - - //15 Traces a second. - while ( m_tAshParticleTraceTimer.NextEvent( curTime ) ) - { - trace_t tr; - - Vector vTraceStart = pPlayer->EyePosition(); - Vector vTraceEnd = pPlayer->EyePosition() + vForward * MAX_TRACE_LENGTH; - - UTIL_TraceLine( vTraceStart, vTraceEnd, MASK_SHOT_HULL & (~CONTENTS_GRATE), pPlayer, COLLISION_GROUP_NONE, &tr ); - - //debugoverlay->AddLineOverlay( vTraceStart, tr.endpos, 255, 0, 0, 0, 0.2 ); - - if ( tr.fraction != 1.0f ) - { - trace_t tr2; - - UTIL_TraceModel( vTraceStart, tr.endpos, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), this, COLLISION_GROUP_NONE, &tr2 ); - - if ( tr2.m_pEnt == this ) - { - m_bActiveAshEmitter = true; - - if ( tr2.startsolid == false ) - { - m_vAshSpawnOrigin = tr2.endpos + vForward * 256; - } - else - { - m_vAshSpawnOrigin = vTraceStart; - } - } - else - { - m_bActiveAshEmitter = false; - } - } - } - - if ( m_bActiveAshEmitter == false ) - return; - - Vector vecVelocity = pPlayer->GetAbsVelocity(); - - - float flVelocity = VectorNormalize( vecVelocity ); - Vector offset = m_vAshSpawnOrigin; - - m_pAshEmitter->SetSortOrigin( offset ); - - PMaterialHandle hMaterial[4]; - hMaterial[0] = ParticleMgr()->GetPMaterial( "effects/fleck_ash1" ); - hMaterial[1] = ParticleMgr()->GetPMaterial( "effects/fleck_ash2" ); - hMaterial[2] = ParticleMgr()->GetPMaterial( "effects/fleck_ash3" ); - hMaterial[3] = ParticleMgr()->GetPMaterial( "effects/ember_swirling001" ); - - SimpleParticle *pParticle; - - Vector vSpawnOrigin = vec3_origin; - - if ( flVelocity > 0 ) - { - vSpawnOrigin = ( vForward * 256 ) + ( vecVelocity * ( flVelocity * 2 ) ); - } - - // Add as many particles as we need - while ( m_tAshParticleTimer.NextEvent( curTime ) ) - { - int iRandomAltitude = RandomInt( 0, 128 ); - - offset = m_vAshSpawnOrigin + vSpawnOrigin + RandomVector( -256, 256 ); - offset.z = m_vAshSpawnOrigin.z + iRandomAltitude; - - if ( offset[0] > absmaxs[0] - || offset[1] > absmaxs[1] - || offset[2] > absmaxs[2] - || offset[0] < absmins[0] - || offset[1] < absmins[1] - || offset[2] < absmins[2] ) - continue; - - m_iAshCount++; - - bool bEmberTime = false; - - if ( m_iAshCount >= 250 ) - { - bEmberTime = true; - m_iAshCount = 0; - } - - int iRandom = random->RandomInt(0,2); - - if ( bEmberTime == true ) - { - offset = m_vAshSpawnOrigin + (vForward * 256) + RandomVector( -128, 128 ); - offset.z = pPlayer->EyePosition().z + RandomFloat( -16, 64 ); - - iRandom = 3; - } - - pParticle = (SimpleParticle *) m_pAshEmitter->AddParticle( sizeof(SimpleParticle), hMaterial[iRandom], offset ); - - if (pParticle == NULL) - continue; - - pParticle->m_flLifetime = 0.0f; - pParticle->m_flDieTime = RemapVal( iRandomAltitude, 0, 128, 4, 8 ); - - if ( bEmberTime == true ) - { - Vector vGoal = pPlayer->EyePosition() + RandomVector( -64, 64 ); - Vector vDir = vGoal - offset; - VectorNormalize( vDir ); - - pParticle->m_vecVelocity = vDir * 75; - pParticle->m_flDieTime = 2.5f; - } - else - { - pParticle->m_vecVelocity = Vector( RandomFloat( -20.0f, 20.0f ), RandomFloat( -20.0f, 20.0f ), RandomFloat( -10, -15 ) ); - } - - float color = random->RandomInt( 125, 225 ); - pParticle->m_uchColor[0] = color; - pParticle->m_uchColor[1] = color; - pParticle->m_uchColor[2] = color; - - pParticle->m_uchStartSize = 1; - pParticle->m_uchEndSize = 1; - - pParticle->m_uchStartAlpha = 255; - - pParticle->m_flRoll = random->RandomInt( 0, 360 ); - pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f ); - - pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN; - - if ( random->RandomInt( 0, 10 ) <= 1 ) - { - pParticle->m_iFlags |= ASH_PARTICLE_NOISE; - } - } - } - -void CClient_Precipitation::CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ) -{ - // Create the particle - CPrecipitationParticle* p = CreateParticle(); - if (!p) - return; - - VectorCopy( vVelocity, p->m_Velocity ); - p->m_Pos = vSpawnPosition; - - p->m_Velocity[ 0 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt()); - p->m_Velocity[ 1 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt()); - - p->m_Mass = random->RandomFloat( 0.5, 1.5 ); -} - -//----------------------------------------------------------------------------- -// emit the precipitation particles -//----------------------------------------------------------------------------- - -void CClient_Precipitation::EmitParticles( float fTimeDelta ) -{ - Vector2D size; - Vector vel, org; - - C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); - if ( !pPlayer ) - return; - Vector vPlayerCenter = pPlayer->WorldSpaceCenter(); - - // Compute where to emit - if (!ComputeEmissionArea( org, size )) - return; - - // clamp this to prevent creating a bunch of rain or snow at one time. - if( fTimeDelta > 0.075f ) - fTimeDelta = 0.075f; - - // FIXME: Compute the precipitation density based on computational power - float density = m_flDensity; - - if (density > 0.01f) - density = 0.01f; - - // Compute number of particles to emit based on precip density and emission area and dt - float fParticles = size[0] * size[1] * density * fTimeDelta + m_Remainder; - int cParticles = (int)fParticles; - m_Remainder = fParticles - cParticles; - - // calculate the max amount of time it will take this flake to fall. - // This works if we assume the wind doesn't have a z component - VectorCopy( s_WindVector, vel ); - vel[2] -= GetSpeed(); - - // Emit all the particles - for ( int i = 0 ; i < cParticles ; i++ ) - { - Vector vParticlePos = org; - vParticlePos[ 0 ] += size[ 0 ] * random->RandomFloat(0, 1); - vParticlePos[ 1 ] += size[ 1 ] * random->RandomFloat(0, 1); - - // Figure out where the particle should lie in Z by tracing a line from the player's height up to the - // desired height and making sure it doesn't hit a wall. - Vector vPlayerHeight = vParticlePos; - vPlayerHeight.z = vPlayerCenter.z; - - trace_t trace; - UTIL_TraceLine( vPlayerHeight, vParticlePos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace ); - if ( trace.fraction < 1 ) - { - // If we hit a brush, then don't spawn the particle. - if ( trace.surface.flags & SURF_SKY ) - { - vParticlePos = trace.endpos; - } - else - { - continue; - } - } - - CreateRainOrSnowParticle( vParticlePos, vel ); - } -} - - -//----------------------------------------------------------------------------- -// Computes the wind vector -//----------------------------------------------------------------------------- - -void CClient_Precipitation::ComputeWindVector( ) -{ - // Compute the wind direction - QAngle windangle( 0, cl_winddir.GetFloat(), 0 ); // used to turn wind yaw direction into a vector - - // Randomize the wind angle and speed slightly to get us a little variation - windangle[1] = windangle[1] + random->RandomFloat( -10, 10 ); - float windspeed = cl_windspeed.GetFloat() * (1.0 + random->RandomFloat( -0.2, 0.2 )); - - AngleVectors( windangle, &s_WindVector ); - VectorScale( s_WindVector, windspeed, s_WindVector ); -} - - -CHandle g_pPrecipHackEnt; - -class CPrecipHack : public CAutoGameSystemPerFrame -{ -public: - CPrecipHack( char const *name ) : CAutoGameSystemPerFrame( name ) - { - m_bLevelInitted = false; - } - - virtual void LevelInitPostEntity() - { - if ( r_RainHack.GetInt() ) - { - CClient_Precipitation *pPrecipHackEnt = new CClient_Precipitation; - pPrecipHackEnt->InitializeAsClientEntity( NULL, RENDER_GROUP_TRANSLUCENT_ENTITY ); - g_pPrecipHackEnt = pPrecipHackEnt; - } - m_bLevelInitted = true; - } - - virtual void LevelShutdownPreEntity() - { - if ( r_RainHack.GetInt() && g_pPrecipHackEnt ) - { - g_pPrecipHackEnt->Release(); - } - m_bLevelInitted = false; - } - - virtual void Update( float frametime ) - { - // Handle changes to the cvar at runtime. - if ( m_bLevelInitted ) - { - if ( r_RainHack.GetInt() && !g_pPrecipHackEnt ) - LevelInitPostEntity(); - else if ( !r_RainHack.GetInt() && g_pPrecipHackEnt ) - LevelShutdownPreEntity(); - } - } - - bool m_bLevelInitted; -}; -CPrecipHack g_PrecipHack( "CPrecipHack" ); - -#else - -void DrawPrecipitation() -{ -} - -#endif // _XBOX - -//----------------------------------------------------------------------------- -// EnvWind - global wind info -//----------------------------------------------------------------------------- -class C_EnvWind : public C_BaseEntity -{ -public: - C_EnvWind(); - - DECLARE_CLIENTCLASS(); - DECLARE_CLASS( C_EnvWind, C_BaseEntity ); - - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual bool ShouldDraw( void ) { return false; } - - virtual void ClientThink( ); - -private: - C_EnvWind( const C_EnvWind & ); - - CEnvWindShared m_EnvWindShared; -}; - -// Receive datatables -BEGIN_RECV_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared) - RecvPropInt (RECVINFO(m_iMinWind)), - RecvPropInt (RECVINFO(m_iMaxWind)), - RecvPropInt (RECVINFO(m_iMinGust)), - RecvPropInt (RECVINFO(m_iMaxGust)), - RecvPropFloat (RECVINFO(m_flMinGustDelay)), - RecvPropFloat (RECVINFO(m_flMaxGustDelay)), - RecvPropInt (RECVINFO(m_iGustDirChange)), - RecvPropInt (RECVINFO(m_iWindSeed)), - RecvPropInt (RECVINFO(m_iInitialWindDir)), - RecvPropFloat (RECVINFO(m_flInitialWindSpeed)), - RecvPropFloat (RECVINFO(m_flStartTime)), - RecvPropFloat (RECVINFO(m_flGustDuration)), -// RecvPropInt (RECVINFO(m_iszGustSound)), -END_RECV_TABLE() - -IMPLEMENT_CLIENTCLASS_DT( C_EnvWind, DT_EnvWind, CEnvWind ) - RecvPropDataTable(RECVINFO_DT(m_EnvWindShared), 0, &REFERENCE_RECV_TABLE(DT_EnvWindShared)), -END_RECV_TABLE() - - -C_EnvWind::C_EnvWind() -{ -} - -//----------------------------------------------------------------------------- -// Post data update! -//----------------------------------------------------------------------------- -void C_EnvWind::OnDataChanged( DataUpdateType_t updateType ) -{ - // Whenever we get an update, reset the entire state. - // Note that the fields have already been stored by the datatables, - // but there's still work to be done in the init block - m_EnvWindShared.Init( entindex(), m_EnvWindShared.m_iWindSeed, - m_EnvWindShared.m_flStartTime, m_EnvWindShared.m_iInitialWindDir, - m_EnvWindShared.m_flInitialWindSpeed ); - - SetNextClientThink(0.0f); - - BaseClass::OnDataChanged( updateType ); -} - -void C_EnvWind::ClientThink( ) -{ - // Update the wind speed - float flNextThink = m_EnvWindShared.WindThink( gpGlobals->curtime ); - SetNextClientThink(flNextThink); -} - - - -//================================================== -// EmberParticle -//================================================== - -class CEmberEmitter : public CSimpleEmitter -{ -public: - CEmberEmitter( const char *pDebugName ); - static CSmartPtr Create( const char *pDebugName ); - virtual void UpdateVelocity( SimpleParticle *pParticle, float timeDelta ); - virtual Vector UpdateColor( const SimpleParticle *pParticle ); - -private: - CEmberEmitter( const CEmberEmitter & ); -}; - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : fTimeDelta - -// Output : Vector -//----------------------------------------------------------------------------- -CEmberEmitter::CEmberEmitter( const char *pDebugName ) : CSimpleEmitter( pDebugName ) -{ -} - - -CSmartPtr CEmberEmitter::Create( const char *pDebugName ) -{ - return new CEmberEmitter( pDebugName ); -} - - -void CEmberEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) -{ - float speed = VectorNormalize( pParticle->m_vecVelocity ); - Vector offset; - - speed -= ( 1.0f * timeDelta ); - - offset.Random( -0.025f, 0.025f ); - offset[2] = 0.0f; - - pParticle->m_vecVelocity += offset; - VectorNormalize( pParticle->m_vecVelocity ); - - pParticle->m_vecVelocity *= speed; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pParticle - -// timeDelta - -//----------------------------------------------------------------------------- -Vector CEmberEmitter::UpdateColor( const SimpleParticle *pParticle ) -{ - Vector color; - float ramp = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime ); - - color[0] = ( (float) pParticle->m_uchColor[0] * ramp ) / 255.0f; - color[1] = ( (float) pParticle->m_uchColor[1] * ramp ) / 255.0f; - color[2] = ( (float) pParticle->m_uchColor[2] * ramp ) / 255.0f; - - return color; -} - -//================================================== -// C_Embers -//================================================== - -class C_Embers : public C_BaseEntity -{ -public: - DECLARE_CLIENTCLASS(); - DECLARE_CLASS( C_Embers, C_BaseEntity ); - - C_Embers(); - ~C_Embers(); - - void Start( void ); - - virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual bool ShouldDraw( void ); - virtual void AddEntity( void ); - - //Server-side - int m_nDensity; - int m_nLifetime; - int m_nSpeed; - bool m_bEmit; - -protected: - - void SpawnEmber( void ); - - PMaterialHandle m_hMaterial; - TimedEvent m_tParticleSpawn; - CSmartPtr m_pEmitter; - -}; - -//Receive datatable -IMPLEMENT_CLIENTCLASS_DT( C_Embers, DT_Embers, CEmbers ) - RecvPropInt( RECVINFO( m_nDensity ) ), - RecvPropInt( RECVINFO( m_nLifetime ) ), - RecvPropInt( RECVINFO( m_nSpeed ) ), - RecvPropInt( RECVINFO( m_bEmit ) ), -END_RECV_TABLE() - -//----------------------------------------------------------------------------- -// Purpose: -// Input : bnewentity - -//----------------------------------------------------------------------------- -C_Embers::C_Embers() -{ - m_pEmitter = CEmberEmitter::Create( "C_Embers" ); -} - -C_Embers::~C_Embers() -{ -} - -void C_Embers::OnDataChanged( DataUpdateType_t updateType ) -{ - BaseClass::OnDataChanged( updateType ); - - if ( updateType == DATA_UPDATE_CREATED ) - { - m_pEmitter->SetSortOrigin( GetAbsOrigin() ); - - Start(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool C_Embers::ShouldDraw() -{ - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_Embers::Start( void ) -{ - //Various setup info - m_tParticleSpawn.Init( m_nDensity ); - - m_hMaterial = m_pEmitter->GetPMaterial( "particle/fire" ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_Embers::AddEntity( void ) -{ - if ( m_bEmit == false ) - return; - - float tempDelta = gpGlobals->frametime; - - while( m_tParticleSpawn.NextEvent( tempDelta ) ) - { - SpawnEmber(); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void C_Embers::SpawnEmber( void ) -{ - Vector offset, mins, maxs; - - modelinfo->GetModelBounds( GetModel(), mins, maxs ); - - //Setup our spawn position - offset[0] = random->RandomFloat( mins[0], maxs[0] ); - offset[1] = random->RandomFloat( mins[1], maxs[1] ); - offset[2] = random->RandomFloat( mins[2], maxs[2] ); - - //Spawn the particle - SimpleParticle *sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof( SimpleParticle ), m_hMaterial, offset ); - - if (sParticle == NULL) - return; - - float cScale = random->RandomFloat( 0.75f, 1.0f ); - - //Set it up - sParticle->m_flLifetime = 0.0f; - sParticle->m_flDieTime = m_nLifetime; - - sParticle->m_uchColor[0] = m_clrRender->r * cScale; - sParticle->m_uchColor[1] = m_clrRender->g * cScale; - sParticle->m_uchColor[2] = m_clrRender->b * cScale; - sParticle->m_uchStartAlpha = 255; - sParticle->m_uchEndAlpha = 0; - sParticle->m_uchStartSize = 1; - sParticle->m_uchEndSize = 0; - sParticle->m_flRollDelta = 0; - sParticle->m_flRoll = 0; - - //Set the velocity - Vector velocity; - - AngleVectors( GetAbsAngles(), &velocity ); - - sParticle->m_vecVelocity = velocity * m_nSpeed; - - sParticle->m_vecVelocity[0] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) ); - sParticle->m_vecVelocity[1] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) ); - sParticle->m_vecVelocity[2] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) ); - - UpdateVisibility(); -} - -//----------------------------------------------------------------------------- -// Quadratic spline beam effect -//----------------------------------------------------------------------------- -#include "beamdraw.h" - -class C_QuadraticBeam : public C_BaseEntity -{ -public: - DECLARE_CLIENTCLASS(); - DECLARE_CLASS( C_QuadraticBeam, C_BaseEntity ); - - //virtual void OnDataChanged( DataUpdateType_t updateType ); - virtual bool ShouldDraw( void ) { return true; } - virtual int DrawModel( int ); - - virtual void GetRenderBounds( Vector& mins, Vector& maxs ) - { - ClearBounds( mins, maxs ); - AddPointToBounds( vec3_origin, mins, maxs ); - AddPointToBounds( m_targetPosition, mins, maxs ); - AddPointToBounds( m_controlPosition, mins, maxs ); - mins -= GetRenderOrigin(); - maxs -= GetRenderOrigin(); - } - -protected: - - Vector m_targetPosition; - Vector m_controlPosition; - float m_scrollRate; - float m_flWidth; -}; - -//Receive datatable -IMPLEMENT_CLIENTCLASS_DT( C_QuadraticBeam, DT_QuadraticBeam, CEnvQuadraticBeam ) - RecvPropVector( RECVINFO(m_targetPosition) ), - RecvPropVector( RECVINFO(m_controlPosition) ), - RecvPropFloat( RECVINFO(m_scrollRate) ), - RecvPropFloat( RECVINFO(m_flWidth) ), -END_RECV_TABLE() - -Vector Color32ToVector( const color32 &color ) -{ - return Vector( color.r * (1.0/255.0f), color.g * (1.0/255.0f), color.b * (1.0/255.0f) ); -} - -int C_QuadraticBeam::DrawModel( int ) -{ - Draw_SetSpriteTexture( GetModel(), 0, GetRenderMode() ); - Vector color = Color32ToVector( GetRenderColor() ); - DrawBeamQuadratic( GetRenderOrigin(), m_controlPosition, m_targetPosition, m_flWidth, color, gpGlobals->curtime*m_scrollRate ); - return 1; -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -class SnowFallEffect : public CSimpleEmitter -{ -public: - - SnowFallEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} - static SnowFallEffect* Create( const char *pDebugName ) - { - return new SnowFallEffect( pDebugName ); - } - - void UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) - { - float flSpeed = VectorNormalize( pParticle->m_vecVelocity ); - flSpeed -= timeDelta; - - pParticle->m_vecVelocity.x += RandomFloat( -0.025f, 0.025f ); - pParticle->m_vecVelocity.y += RandomFloat( -0.025f, 0.025f ); - VectorNormalize( pParticle->m_vecVelocity ); - - pParticle->m_vecVelocity *= flSpeed; - - Vector vecWindVelocity; - GetWindspeedAtTime( gpGlobals->curtime, vecWindVelocity ); - pParticle->m_vecVelocity += ( vecWindVelocity * r_SnowWindScale.GetFloat() ); - } - - void SimulateParticles( CParticleSimulateIterator *pIterator ) - { - float timeDelta = pIterator->GetTimeDelta(); - - SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst(); - while ( pParticle ) - { - //Update velocity - UpdateVelocity( pParticle, timeDelta ); - pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta; - - //Should this particle die? - pParticle->m_flLifetime += timeDelta; - UpdateRoll( pParticle, timeDelta ); - - if ( pParticle->m_flLifetime >= pParticle->m_flDieTime ) - { - pIterator->RemoveParticle( pParticle ); - } - else if ( !IsInAir( pParticle->m_Pos ) ) - { - pIterator->RemoveParticle( pParticle ); - } - - pParticle = (SimpleParticle*)pIterator->GetNext(); - } - } - - int GetParticleCount( void ) - { - return GetBinding().GetNumActiveParticles(); - } - - void SetBounds( const Vector &vecMin, const Vector &vecMax ) - { - GetBinding().SetBBox( vecMin, vecMax, true ); - } - - bool IsTransparent( void ) { return false; } - -private: - - SnowFallEffect( const SnowFallEffect & ); -}; - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -class CSnowFallManager : public C_BaseEntity -{ -public: - - CSnowFallManager(); - ~CSnowFallManager(); - - bool CreateEmitter( void ); - - void SpawnClientEntity( void ); - void ClientThink(); - - void AddSnowFallEntity( CClient_Precipitation *pSnowEntity ); - - // Snow Effect - enum - { - SNOWFALL_NONE = 0, - SNOWFALL_AROUND_PLAYER, - SNOWFALL_IN_ENTITY, - }; - - bool IsTransparent( void ) { return false; } - -private: - - bool CreateSnowFallEmitter( void ); - void CreateSnowFall( void ); - void CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ); - void CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale ); - void CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ); - void CreateSnowParticlesSphere( float flRadius ); - void CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward ); - void CreateSnowFallParticle( const Vector &vecParticleSpawn, int iBBox ); - - int StandingInSnowVolume( Vector &vecPoint ); - void FindSnowVolumes( Vector &vecCenter, float flRadius, Vector &vecEyePos, Vector &vecForward ); - - void UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax ); - -private: - - enum { MAX_SNOW_PARTICLES = 500 }; - enum { MAX_SNOW_LIST = 32 }; - - TimedEvent m_tSnowFallParticleTimer; - TimedEvent m_tSnowFallParticleTraceTimer; - - int m_iSnowFallArea; - CSmartPtr m_pSnowFallEmitter; - Vector m_vecSnowFallEmitOrigin; - float m_flSnowRadius; - - Vector m_vecMin; - Vector m_vecMax; - - int m_nActiveSnowCount; - int m_aActiveSnow[MAX_SNOW_LIST]; - - bool m_bRayParticles; - - struct SnowFall_t - { - PMaterialHandle m_hMaterial; - CClient_Precipitation *m_pEntity; - SnowFallEffect *m_pEffect; - Vector m_vecMin; - Vector m_vecMax; - }; - - CUtlVector m_aSnow; -}; - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CSnowFallManager::CSnowFallManager( void ) -{ - m_iSnowFallArea = SNOWFALL_NONE; - m_pSnowFallEmitter = NULL; - m_vecSnowFallEmitOrigin.Init(); - m_flSnowRadius = 0.0f; - m_vecMin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); - m_vecMax.Init( FLT_MIN, FLT_MIN, FLT_MIN ); - m_nActiveSnowCount = 0; - m_aSnow.Purge(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -CSnowFallManager::~CSnowFallManager( void ) -{ - m_aSnow.Purge(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CSnowFallManager::CreateEmitter( void ) -{ - return CreateSnowFallEmitter(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSnowFallManager::SpawnClientEntity( void ) -{ - m_tSnowFallParticleTimer.Init( 500 ); - m_tSnowFallParticleTraceTimer.Init( 6 ); - m_iSnowFallArea = SNOWFALL_NONE; - - // Have the Snow Fall Manager think for all the snow fall entities. - SetNextClientThink( CLIENT_THINK_ALWAYS ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CSnowFallManager::CreateSnowFallEmitter( void ) -{ - if ( ( m_pSnowFallEmitter = SnowFallEffect::Create( "snowfall" ) ) == NULL ) - return false; - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSnowFallManager::ClientThink( void ) -{ - if ( !r_SnowEnable.GetBool() ) - return; - - // Make sure we have a snow fall emitter. - if ( !m_pSnowFallEmitter ) - { - if ( !CreateSnowFallEmitter() ) - return; - } - - CreateSnowFall(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pSnowEntity - -//----------------------------------------------------------------------------- -void CSnowFallManager::AddSnowFallEntity( CClient_Precipitation *pSnowEntity ) -{ - if ( !pSnowEntity ) - return; - - int nSnowCount = m_aSnow.Count(); - int iSnow = 0; - for ( iSnow = 0; iSnow < nSnowCount; ++iSnow ) - { - if ( m_aSnow[iSnow].m_pEntity == pSnowEntity ) - break; - } - - if ( iSnow != nSnowCount ) - return; - - iSnow = m_aSnow.AddToTail(); - m_aSnow[iSnow].m_pEntity = pSnowEntity; - m_aSnow[iSnow].m_pEffect = SnowFallEffect::Create( "snowfall" ); - m_aSnow[iSnow].m_hMaterial = ParticleMgr()->GetPMaterial( "particle/snow" ); - - VectorCopy( pSnowEntity->WorldAlignMins(), m_aSnow[iSnow].m_vecMin ); - VectorCopy( pSnowEntity->WorldAlignMaxs(), m_aSnow[iSnow].m_vecMax ); - - UpdateBounds( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSnowFallManager::UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax ) -{ - int iAxis = 0; - for ( iAxis = 0; iAxis < 3; ++iAxis ) - { - if ( vecSnowMin[iAxis] < m_vecMin[iAxis] ) - { - m_vecMin[iAxis] = vecSnowMin[iAxis]; - } - - if ( vecSnowMax[iAxis] > m_vecMax[iAxis] ) - { - m_vecMax[iAxis] = vecSnowMax[iAxis]; - } - } - - Assert( m_pSnowFallEmitter ); - m_pSnowFallEmitter->SetBounds( m_vecMin, m_vecMax ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecPoint - -// Output : int -//----------------------------------------------------------------------------- -int CSnowFallManager::StandingInSnowVolume( Vector &vecPoint ) -{ - trace_t traceSnow; - - int nSnowCount = m_aSnow.Count(); - int iSnow = 0; - for ( iSnow = 0; iSnow < nSnowCount; ++iSnow ) - { - UTIL_TraceModel( vecPoint, vecPoint, vec3_origin, vec3_origin, static_cast( m_aSnow[iSnow].m_pEntity ), COLLISION_GROUP_NONE, &traceSnow ); - if ( traceSnow.startsolid ) - return iSnow; - } - - return -1; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecCenter - -// flRadius - -//----------------------------------------------------------------------------- -void CSnowFallManager::FindSnowVolumes( Vector &vecCenter, float flRadius, Vector &vecEyePos, Vector &vecForward ) -{ - // Reset. - m_nActiveSnowCount = 0; - m_bRayParticles = false; - - int nSnowCount = m_aSnow.Count(); - int iSnow = 0; - for ( iSnow = 0; iSnow < nSnowCount; ++iSnow ) - { - // Check to see if the volume is in the PVS. - bool bInPVS = g_pClientLeafSystem->IsRenderableInPVS( m_aSnow[iSnow].m_pEntity->GetClientRenderable() ); - if ( !bInPVS ) - continue; - - // Check to see if a snow volume is inside the given radius. - if ( IsBoxIntersectingSphere( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, vecCenter, flRadius ) ) - { - m_aActiveSnow[m_nActiveSnowCount] = iSnow; - ++m_nActiveSnowCount; - if ( m_nActiveSnowCount >= MAX_SNOW_LIST ) - { - DevWarning( 1, "Max Active Snow Volume Count!\n" ); - break; - } - } - // Check to see if a snow volume is outside of the sphere radius, but is along line-of-sight. - else - { - CBaseTrace trace; - Vector vecNewForward; - vecNewForward = vecForward * r_SnowRayLength.GetFloat(); - vecNewForward.z = 0.0f; - IntersectRayWithBox( vecEyePos, vecNewForward, m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, 0.325f, &trace ); - if ( trace.fraction < 1.0f ) - { - m_aActiveSnow[m_nActiveSnowCount] = iSnow; - ++m_nActiveSnowCount; - if ( m_nActiveSnowCount >= MAX_SNOW_LIST ) - { - DevWarning( 1, "Max Active Snow Volume Count!\n" ); - break; - } - - m_bRayParticles = true; - } - } - } - - // Debugging code! -#ifdef _DEBUG - if ( r_SnowDebugBox.GetFloat() != 0.0f ) - { - for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow ) - { - Vector vecCenter, vecMin, vecMax; - vecCenter = ( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax ) * 0.5; - vecMin = m_aSnow[iSnow].m_vecMin - vecCenter; - vecMax = m_aSnow[iSnow].m_vecMax - vecCenter; - debugoverlay->AddBoxOverlay( vecCenter, vecMin, vecMax, QAngle( 0, 0, 0 ), 200, 0, 0, 25, r_SnowDebugBox.GetFloat() ); - } - } -#endif -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CSnowFallManager::CreateSnowFall( void ) -{ -#if 1 - VPROF_BUDGET( "SnowFall", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); -#endif - - // Check to see if we have a local player before starting the snow around a local player. - C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); - if ( pPlayer == NULL ) - return; - - // Get the current frame time. - float flCurrentTime = gpGlobals->frametime; - - // Get the players data to determine where the snow emitter should reside. - VectorCopy( pPlayer->EyePosition(), m_vecSnowFallEmitOrigin ); - Vector vecForward; - pPlayer->GetVectors( &vecForward, NULL, NULL ); - vecForward.z = 0.0f; - Vector vecVelocity = pPlayer->GetAbsVelocity(); - float flSpeed = VectorNormalize( vecVelocity ); - m_vecSnowFallEmitOrigin += ( vecForward * ( 64.0f + ( flSpeed * 0.4f * r_SnowPosScale.GetFloat() ) ) ); - m_vecSnowFallEmitOrigin += ( vecVelocity * ( flSpeed * 1.25f * r_SnowSpeedScale.GetFloat() ) ); - - // Check to see if the player is zoomed. - bool bZoomed = ( pPlayer->GetFOV() != pPlayer->GetDefaultFOV() ); - float flZoomScale = 1.0f; - if ( bZoomed ) - { - flZoomScale = pPlayer->GetDefaultFOV() / pPlayer->GetFOV(); - flZoomScale *= 0.5f; - } - - // Time to test for a snow volume yet? (Only do this 6 times a second!) - if ( m_tSnowFallParticleTraceTimer.NextEvent( flCurrentTime ) ) - { - // Reset the active snow emitter. - m_iSnowFallArea = SNOWFALL_NONE; - - // Set the trace start and the emit origin. - Vector vecTraceStart; - VectorCopy( pPlayer->EyePosition(), vecTraceStart ); - - int iSnowVolume = StandingInSnowVolume( vecTraceStart ); - if ( iSnowVolume != -1 ) - { - m_flSnowRadius = r_SnowInsideRadius.GetFloat() + ( flSpeed * 0.5f ); - m_iSnowFallArea = SNOWFALL_AROUND_PLAYER; - } - else - { - m_flSnowRadius = r_SnowOutsideRadius.GetFloat(); - } - - float flRadius = m_flSnowRadius; - if ( bZoomed ) - { - if ( m_iSnowFallArea == SNOWFALL_AROUND_PLAYER ) - { - flRadius = r_SnowOutsideRadius.GetFloat() * flZoomScale; - } - else - { - flRadius *= flZoomScale; - } - } - - Vector vecEyePos = pPlayer->EyePosition(); - FindSnowVolumes( m_vecSnowFallEmitOrigin, flRadius, vecEyePos, vecForward ); - if ( m_nActiveSnowCount != 0 && m_iSnowFallArea != SNOWFALL_AROUND_PLAYER ) - { - // We found an active snow emitter. - m_iSnowFallArea = SNOWFALL_IN_ENTITY; - - } - } - - if ( m_iSnowFallArea == SNOWFALL_NONE ) - return; - - // Set the origin in the snow emitter. - m_pSnowFallEmitter->SetSortOrigin( m_vecSnowFallEmitOrigin ); - - // Create snow fall particles. - CreateSnowFallParticles( flCurrentTime, m_flSnowRadius, pPlayer->EyePosition(), vecForward, flZoomScale ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : flCurrentTime - -// flRadius - -// &vecEyePos - -// &vecForward - -// flZoomScale - -//----------------------------------------------------------------------------- -void CSnowFallManager::CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ) - { - // Outside of a snow volume. - if ( m_iSnowFallArea == SNOWFALL_IN_ENTITY ) - { - CreateOutsideVolumeSnowParticles( flCurrentTime, flRadius, flZoomScale ); - } - // Inside of a snow volume. - else - { - CreateInsideVolumeSnowParticles( flCurrentTime, flRadius, vecEyePos, vecForward, flZoomScale ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : flCurrentTime - -// flRadius - -// flZoomScale - -//----------------------------------------------------------------------------- -void CSnowFallManager::CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale ) -{ - Vector vecParticleSpawn; - - // Outside of a snow volume. - int iSnow = 0; - float flRadiusScaled = flRadius * flZoomScale; - float flRadius2 = flRadiusScaled * flRadiusScaled; - - // Add as many particles as we need - while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) ) - { - // Check for a max particle count. - if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() ) - continue; - - vecParticleSpawn.x = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ); - vecParticleSpawn.y = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ); - vecParticleSpawn.z = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ); - - float flDistance2 = ( m_vecSnowFallEmitOrigin - vecParticleSpawn ).LengthSqr(); - if ( flDistance2 < flRadius2 ) - { - CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] ); - } - - iSnow = ( iSnow + 1 ) % m_nActiveSnowCount; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : flCurrentTime - -// flRadius - -// &vecEyePos - -// &vecForward - -// flZoomScale - -//----------------------------------------------------------------------------- -void CSnowFallManager::CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ) -{ - Vector vecParticleSpawn; - - // Check/Setup for zoom. - bool bZoomed = ( flZoomScale > 1.0f ); - float flZoomRadius = 0.0f; - Vector vecZoomEmitOrigin; - if ( bZoomed ) - { - vecZoomEmitOrigin = m_vecSnowFallEmitOrigin + ( vecForward * ( r_SnowZoomOffset.GetFloat() * flZoomScale ) ); - flZoomRadius = flRadius * flZoomScale; - } - - int iIndex = 0; - - // Add as many particles as we need - while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) ) - { - // Check for a max particle count. - if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() ) - continue; - - // Create particle inside of sphere. - if ( iIndex > 0 ) - { - CreateSnowParticlesSphere( flZoomRadius ); - CreateSnowParticlesRay( flZoomRadius, vecEyePos, vecForward ); - } - else - { - CreateSnowParticlesSphere( flRadius ); - CreateSnowParticlesRay( flRadius, vecEyePos, vecForward ); - } - - // Increment if zoomed. - if ( bZoomed ) - { - iIndex = ( iIndex + 1 ) % 3; - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : flRadius - -//----------------------------------------------------------------------------- -void CSnowFallManager::CreateSnowParticlesSphere( float flRadius ) -{ - Vector vecParticleSpawn; - - vecParticleSpawn.x = m_vecSnowFallEmitOrigin.x + RandomFloat( -flRadius, flRadius ); - vecParticleSpawn.y = m_vecSnowFallEmitOrigin.y + RandomFloat( -flRadius, flRadius ); - vecParticleSpawn.z = m_vecSnowFallEmitOrigin.z + RandomFloat( -flRadius, flRadius ); - - int iSnow = 0; - for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow ) - { - if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) ) - continue; - if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) ) - continue; - if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) ) - continue; - - break; - } - - if ( iSnow == m_nActiveSnowCount ) - return; - - CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : &vecEyePos - -// &vecForward - -//----------------------------------------------------------------------------- -void CSnowFallManager::CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward ) -{ - // Check to see if we should create particles along line-of-sight. - if ( !m_bRayParticles && r_SnowRayEnable.GetBool() ) - return; - - Vector vecParticleSpawn; - - // Create a particle down the player's view beyond the radius. - float flRayRadius = r_SnowRayRadius.GetFloat(); - - Vector vecNewForward; - vecNewForward = vecForward * RandomFloat( flRadius, r_SnowRayLength.GetFloat() ); - - vecParticleSpawn.x = vecEyePos.x + vecNewForward.x; - vecParticleSpawn.y = vecEyePos.y + vecNewForward.y; - vecParticleSpawn.z = vecEyePos.z + RandomFloat( 72, flRayRadius ); - vecParticleSpawn.x += RandomFloat( -flRayRadius, flRayRadius ); - vecParticleSpawn.y += RandomFloat( -flRayRadius, flRayRadius ); - - int iSnow = 0; - for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow ) - { - if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) ) - continue; - if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) ) - continue; - if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) ) - continue; - - break; - } - - if ( iSnow == m_nActiveSnowCount ) - return; - - CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] ); -} - -void CSnowFallManager::CreateSnowFallParticle( const Vector &vecParticleSpawn, int iSnow ) -{ - SimpleParticle *pParticle = ( SimpleParticle* )m_pSnowFallEmitter->AddParticle( sizeof( SimpleParticle ), m_aSnow[iSnow].m_hMaterial, vecParticleSpawn ); - if ( pParticle == NULL ) - return; - - pParticle->m_flLifetime = 0.0f; - pParticle->m_vecVelocity = Vector( RandomFloat( -5.0f, 5.0f ), RandomFloat( -5.0f, 5.0f ), ( RandomFloat( -25, -35 ) * r_SnowFallSpeed.GetFloat() ) ); - pParticle->m_flDieTime = fabs( ( vecParticleSpawn.z - m_aSnow[iSnow].m_vecMin.z ) / ( pParticle->m_vecVelocity.z - 0.1 ) ); - - // Probably want to put the color in the snow entity. -// pParticle->m_uchColor[0] = 150;//color; -// pParticle->m_uchColor[1] = 175;//color; -// pParticle->m_uchColor[2] = 200;//color; - pParticle->m_uchColor[0] = r_SnowColorRed.GetInt(); - pParticle->m_uchColor[1] = r_SnowColorGreen.GetInt(); - pParticle->m_uchColor[2] = r_SnowColorBlue.GetInt(); - - pParticle->m_uchStartSize = r_SnowStartSize.GetInt(); - pParticle->m_uchEndSize = r_SnowEndSize.GetInt(); - -// pParticle->m_uchStartAlpha = 255; - pParticle->m_uchStartAlpha = r_SnowStartAlpha.GetInt(); - pParticle->m_uchEndAlpha = r_SnowEndAlpha.GetInt(); - - pParticle->m_flRoll = random->RandomInt( 0, 360 ); - pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f ); - - pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ) -{ - if ( !s_pSnowFallMgr ) - { - s_pSnowFallMgr = new CSnowFallManager(); - s_pSnowFallMgr->CreateEmitter(); - s_pSnowFallMgr->InitializeAsClientEntity( NULL, RENDER_GROUP_OTHER ); - if ( !s_pSnowFallMgr ) - return false; - } - - s_pSnowFallMgr->AddSnowFallEntity( pSnowEntity ); - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void SnowFallManagerDestroy( void ) -{ - if ( s_pSnowFallMgr ) - { - delete s_pSnowFallMgr; - s_pSnowFallMgr = NULL; - } -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#include "cbase.h" +#include "c_tracer.h" +#include "view.h" +#include "initializer.h" +#include "particles_simple.h" +#include "env_wind_shared.h" +#include "engine/IEngineTrace.h" +#include "engine/ivmodelinfo.h" +#include "precipitation_shared.h" +#include "fx_water.h" +#include "c_world.h" +#include "iviewrender.h" +#include "engine/ivdebugoverlay.h" +#include "clienteffectprecachesystem.h" +#include "collisionutils.h" +#include "tier0/vprof.h" +#include "viewrender.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar cl_winddir ( "cl_winddir", "0", FCVAR_CHEAT, "Weather effects wind direction angle" ); +ConVar cl_windspeed ( "cl_windspeed", "0", FCVAR_CHEAT, "Weather effects wind speed scalar" ); + +Vector g_vSplashColor( 0.5, 0.5, 0.5 ); +float g_flSplashScale = 0.15; +float g_flSplashLifetime = 0.5f; +float g_flSplashAlpha = 0.3f; +ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // N% chance of a rain particle making a splash. + + +float GUST_INTERVAL_MIN = 1; +float GUST_INTERVAL_MAX = 2; + +float GUST_LIFETIME_MIN = 1; +float GUST_LIFETIME_MAX = 3; + +float MIN_SCREENSPACE_RAIN_WIDTH = 1; + +#ifndef _XBOX +ConVar r_RainHack( "r_RainHack", "0", FCVAR_CHEAT ); +ConVar r_RainRadius( "r_RainRadius", "1500", FCVAR_CHEAT ); +ConVar r_RainSideVel( "r_RainSideVel", "130", FCVAR_CHEAT, "How much sideways velocity rain gets." ); + +ConVar r_RainSimulate( "r_RainSimulate", "1", FCVAR_CHEAT, "Enable/disable rain simulation." ); +ConVar r_DrawRain( "r_DrawRain", "1", FCVAR_CHEAT, "Enable/disable rain rendering." ); +ConVar r_RainProfile( "r_RainProfile", "0", FCVAR_CHEAT, "Enable/disable rain profiling." ); + + +//Precahce the effects +CLIENTEFFECT_REGISTER_BEGIN( PrecachePrecipitation ) +CLIENTEFFECT_MATERIAL( "particle/rain" ) +CLIENTEFFECT_MATERIAL( "particle/snow" ) +CLIENTEFFECT_REGISTER_END() + +//----------------------------------------------------------------------------- +// Precipitation particle type +//----------------------------------------------------------------------------- + +class CPrecipitationParticle +{ +public: + Vector m_Pos; + Vector m_Velocity; + float m_SpawnTime; // Note: Tweak with this to change lifetime + float m_Mass; + float m_Ramp; + + float m_flCurLifetime; + float m_flMaxLifetime; +}; + + +class CClient_Precipitation; +static CUtlVector g_Precipitations; + +//=========== +// Snow fall +//=========== +class CSnowFallManager; +static CSnowFallManager *s_pSnowFallMgr = NULL; +bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ); +void SnowFallManagerDestroy( void ); + +class AshDebrisEffect : public CSimpleEmitter +{ +public: + AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} + + static AshDebrisEffect* Create( const char *pDebugName ); + + virtual float UpdateAlpha( const SimpleParticle *pParticle ); + virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta ); + +private: + AshDebrisEffect( const AshDebrisEffect & ); +}; + +//----------------------------------------------------------------------------- +// Precipitation base entity +//----------------------------------------------------------------------------- + +class CClient_Precipitation : public C_BaseEntity +{ +class CPrecipitationEffect; +friend class CClient_Precipitation::CPrecipitationEffect; + +public: + DECLARE_CLASS( CClient_Precipitation, C_BaseEntity ); + DECLARE_CLIENTCLASS(); + + CClient_Precipitation(); + virtual ~CClient_Precipitation(); + + // Inherited from C_BaseEntity + virtual void Precache( ); + + void Render(); + +private: + + // Creates a single particle + CPrecipitationParticle* CreateParticle(); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual void ClientThink(); + + void Simulate( float dt ); + + // Renders the particle + void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ); + + void CreateWaterSplashes(); + + // Emits the actual particles + void EmitParticles( float fTimeDelta ); + + // Computes where we're gonna emit + bool ComputeEmissionArea( Vector& origin, Vector2D& size ); + + // Gets the tracer width and speed + float GetWidth() const; + float GetLength() const; + float GetSpeed() const; + + // Gets the remaining lifetime of the particle + float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const; + + // Computes the wind vector + static void ComputeWindVector( ); + + // simulation methods + bool SimulateRain( CPrecipitationParticle* pParticle, float dt ); + bool SimulateSnow( CPrecipitationParticle* pParticle, float dt ); + + void CreateAshParticle( void ); + void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ); + + // Information helpful in creating and rendering particles + IMaterial *m_MatHandle; // material used + + float m_Color[4]; // precip color + float m_Lifetime; // Precip lifetime + float m_InitialRamp; // Initial ramp value + float m_Speed; // Precip speed + float m_Width; // Tracer width + float m_Remainder; // particles we should render next time + PrecipitationType_t m_nPrecipType; // Precip type + float m_flHalfScreenWidth; // Precalculated each frame. + + float m_flDensity; + + // Some state used in rendering and simulation + // Used to modify the rain density and wind from the console + static ConVar s_raindensity; + static ConVar s_rainwidth; + static ConVar s_rainlength; + static ConVar s_rainspeed; + + static Vector s_WindVector; // Stores the wind speed vector + + CUtlLinkedList m_Particles; + CUtlVector m_Splashes; + + CSmartPtr m_pAshEmitter; + TimedEvent m_tAshParticleTimer; + TimedEvent m_tAshParticleTraceTimer; + bool m_bActiveAshEmitter; + Vector m_vAshSpawnOrigin; + + int m_iAshCount; + +private: + CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible +}; + + +// Just receive the normal data table stuff +IMPLEMENT_CLIENTCLASS_DT(CClient_Precipitation, DT_Precipitation, CPrecipitation) + RecvPropInt( RECVINFO( m_nPrecipType ) ) +END_RECV_TABLE() + +static ConVar r_SnowEnable( "r_SnowEnable", "1", FCVAR_CHEAT, "Snow Enable" ); +static ConVar r_SnowParticles( "r_SnowParticles", "500", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowInsideRadius( "r_SnowInsideRadius", "256", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowOutsideRadius( "r_SnowOutsideRadius", "1024", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowSpeedScale( "r_SnowSpeedScale", "1", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowPosScale( "r_SnowPosScale", "1", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowFallSpeed( "r_SnowFallSpeed", "1.5", FCVAR_CHEAT, "Snow fall speed scale." ); +static ConVar r_SnowWindScale( "r_SnowWindScale", "0.0035", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowDebugBox( "r_SnowDebugBox", "0", FCVAR_CHEAT, "Snow Debug Boxes." ); +static ConVar r_SnowZoomOffset( "r_SnowZoomOffset", "384.0f", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowZoomRadius( "r_SnowZoomRadius", "512.0f", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowStartAlpha( "r_SnowStartAlpha", "25", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowEndAlpha( "r_SnowEndAlpha", "255", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowColorRed( "r_SnowColorRed", "150", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowColorGreen( "r_SnowColorGreen", "175", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowColorBlue( "r_SnowColorBlue", "200", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowStartSize( "r_SnowStartSize", "1", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowEndSize( "r_SnowEndSize", "0", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowRayLength( "r_SnowRayLength", "8192.0f", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowRayRadius( "r_SnowRayRadius", "256", FCVAR_CHEAT, "Snow." ); +static ConVar r_SnowRayEnable( "r_SnowRayEnable", "1", FCVAR_CHEAT, "Snow." ); + +void DrawPrecipitation() +{ + for ( int i=0; i < g_Precipitations.Count(); i++ ) + { + g_Precipitations[i]->Render(); + } +} + + +//----------------------------------------------------------------------------- +// determines if a weather particle has hit something other than air +//----------------------------------------------------------------------------- +static bool IsInAir( const Vector& position ) +{ + int contents = enginetrace->GetPointContents( position ); + return (contents & CONTENTS_SOLID) == 0; +} + + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- + +ConVar CClient_Precipitation::s_raindensity( "r_raindensity","0.001", FCVAR_CHEAT); +ConVar CClient_Precipitation::s_rainwidth( "r_rainwidth", "0.5", FCVAR_CHEAT ); +ConVar CClient_Precipitation::s_rainlength( "r_rainlength", "0.1f", FCVAR_CHEAT ); +ConVar CClient_Precipitation::s_rainspeed( "r_rainspeed", "600.0f", FCVAR_CHEAT ); +ConVar r_rainalpha( "r_rainalpha", "0.4", FCVAR_CHEAT ); +ConVar r_rainalphapow( "r_rainalphapow", "0.8", FCVAR_CHEAT ); + + +Vector CClient_Precipitation::s_WindVector; // Stores the wind speed vector + + +void CClient_Precipitation::OnDataChanged( DataUpdateType_t updateType ) +{ + // Simulate every frame. + if ( updateType == DATA_UPDATE_CREATED ) + { + SetNextClientThink( CLIENT_THINK_ALWAYS ); + if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) + { + SnowFallManagerCreate( this ); + } + } + + m_flDensity = RemapVal( m_clrRender->a, 0, 255, 0, 0.001 ); + + BaseClass::OnDataChanged( updateType ); +} + + +void CClient_Precipitation::ClientThink() +{ + Simulate( gpGlobals->frametime ); +} + + +//----------------------------------------------------------------------------- +// +// Utility methods for the various simulation functions +// +//----------------------------------------------------------------------------- +inline bool CClient_Precipitation::SimulateRain( CPrecipitationParticle* pParticle, float dt ) +{ + if (GetRemainingLifetime( pParticle ) < 0.0f) + return false; + + Vector vOldPos = pParticle->m_Pos; + + // Update position + VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity, + pParticle->m_Pos ); + + // wind blows rain around + for ( int i = 0 ; i < 2 ; i++ ) + { + if ( pParticle->m_Velocity[i] < s_WindVector[i] ) + { + pParticle->m_Velocity[i] += ( 5 / pParticle->m_Mass ); + + // clamp + if ( pParticle->m_Velocity[i] > s_WindVector[i] ) + pParticle->m_Velocity[i] = s_WindVector[i]; + } + else if (pParticle->m_Velocity[i] > s_WindVector[i] ) + { + pParticle->m_Velocity[i] -= ( 5 / pParticle->m_Mass ); + + // clamp. + if ( pParticle->m_Velocity[i] < s_WindVector[i] ) + pParticle->m_Velocity[i] = s_WindVector[i]; + } + } + + // No longer in the air? punt. + if ( !IsInAir( pParticle->m_Pos ) ) + { + // Possibly make a splash if we hit a water surface and it's in front of the view. + if ( m_Splashes.Count() < 20 ) + { + if ( RandomInt( 0, 100 ) < r_RainSplashPercentage.GetInt() ) + { + trace_t trace; + UTIL_TraceLine(vOldPos, pParticle->m_Pos, MASK_WATER, NULL, COLLISION_GROUP_NONE, &trace); + if( trace.fraction < 1 ) + { + m_Splashes.AddToTail( trace.endpos ); + } + } + } + + // Tell the framework it's time to remove the particle from the list + return false; + } + + // We still want this particle + return true; +} + + +inline bool CClient_Precipitation::SimulateSnow( CPrecipitationParticle* pParticle, float dt ) +{ + if ( IsInAir( pParticle->m_Pos ) ) + { + // Update position + VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity, + pParticle->m_Pos ); + + // wind blows rain around + for ( int i = 0 ; i < 2 ; i++ ) + { + if ( pParticle->m_Velocity[i] < s_WindVector[i] ) + { + pParticle->m_Velocity[i] += ( 5.0f / pParticle->m_Mass ); + + // accelerating flakes get a trail + pParticle->m_Ramp = 0.5f; + + // clamp + if ( pParticle->m_Velocity[i] > s_WindVector[i] ) + pParticle->m_Velocity[i] = s_WindVector[i]; + } + else if (pParticle->m_Velocity[i] > s_WindVector[i] ) + { + pParticle->m_Velocity[i] -= ( 5.0f / pParticle->m_Mass ); + + // accelerating flakes get a trail + pParticle->m_Ramp = 0.5f; + + // clamp. + if ( pParticle->m_Velocity[i] < s_WindVector[i] ) + pParticle->m_Velocity[i] = s_WindVector[i]; + } + } + + return true; + } + + + // Kill the particle immediately! + return false; +} + + +void CClient_Precipitation::Simulate( float dt ) +{ + // NOTE: When client-side prechaching works, we need to remove this + Precache(); + + m_flHalfScreenWidth = (float)ScreenWidth() / 2; + + // Our sim methods needs dt and wind vector + if ( dt ) + { + ComputeWindVector( ); + } + + if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) + { + CreateAshParticle(); + return; + } + + // The snow fall manager handles the simulation. + if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) + return; + + // calculate the max amount of time it will take this flake to fall. + // This works if we assume the wind doesn't have a z component + if ( r_RainHack.GetInt() ) + m_Lifetime = (GetClientWorldEntity()->m_WorldMaxs[2] - GetClientWorldEntity()->m_WorldMins[2]) / m_Speed; + else + m_Lifetime = (WorldAlignMaxs()[2] - WorldAlignMins()[2]) / m_Speed; + + + if ( !r_RainSimulate.GetInt() ) + return; + + CFastTimer timer; + timer.Start(); + + // Emit new particles + EmitParticles( dt ); + + // Simulate all the particles. + int iNext; + if ( m_nPrecipType == PRECIPITATION_TYPE_RAIN ) + { + for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext ) + { + iNext = m_Particles.Next( i ); + if ( !SimulateRain( &m_Particles[i], dt ) ) + m_Particles.Remove( i ); + } + } + else if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW ) + { + for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext ) + { + iNext = m_Particles.Next( i ); + if ( !SimulateSnow( &m_Particles[i], dt ) ) + m_Particles.Remove( i ); + } + } + + if ( r_RainProfile.GetInt() ) + { + timer.End(); + engine->Con_NPrintf( 15, "Rain simulation: %du (%d tracers)", timer.GetDuration().GetMicroseconds(), m_Particles.Count() ); + } +} + + +//----------------------------------------------------------------------------- +// tracer rendering +//----------------------------------------------------------------------------- + +inline void CClient_Precipitation::RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb ) +{ + float scale; + Vector start, delta; + + if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) + return; + + if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) + return; + + + // make streaks 0.1 seconds long, but prevent from going past end + float lifetimeRemaining = GetRemainingLifetime( pParticle ); + if (lifetimeRemaining >= GetLength()) + scale = GetLength() * pParticle->m_Ramp; + else + scale = lifetimeRemaining * pParticle->m_Ramp; + + // NOTE: We need to do everything in screen space + Vector3DMultiplyPosition( CurrentWorldToViewMatrix(), pParticle->m_Pos, start ); + if ( start.z > -1 ) + return; + + Vector3DMultiply( CurrentWorldToViewMatrix(), pParticle->m_Velocity, delta ); + + // give a spiraling pattern to snow particles + if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW ) + { + Vector spiral, camSpiral; + float s, c; + + if ( pParticle->m_Mass > 1.0f ) + { + SinCos( gpGlobals->curtime * M_PI * (1+pParticle->m_Mass * 0.1f) + + pParticle->m_Mass * 5.0f, &s , &c ); + + // only spiral particles with a mass > 1, so some fall straight down + spiral[0] = 28 * c; + spiral[1] = 28 * s; + spiral[2] = 0.0f; + + Vector3DMultiply( CurrentWorldToViewMatrix(), spiral, camSpiral ); + + // X and Y are measured in world space; need to convert to camera space + VectorAdd( start, camSpiral, start ); + VectorAdd( delta, camSpiral, delta ); + } + + // shrink the trails on spiraling flakes. + pParticle->m_Ramp = 0.3f; + } + + delta[0] *= scale; + delta[1] *= scale; + delta[2] *= scale; + + // See c_tracer.* for this method + float flAlpha = r_rainalpha.GetFloat(); + float flWidth = GetWidth(); + + float flScreenSpaceWidth = flWidth * m_flHalfScreenWidth / -start.z; + if ( flScreenSpaceWidth < MIN_SCREENSPACE_RAIN_WIDTH ) + { + // Make the rain tracer at least the min size, but fade its alpha the smaller it gets. + flAlpha *= flScreenSpaceWidth / MIN_SCREENSPACE_RAIN_WIDTH; + flWidth = MIN_SCREENSPACE_RAIN_WIDTH * -start.z / m_flHalfScreenWidth; + } + flAlpha = pow( flAlpha, r_rainalphapow.GetFloat() ); + + float flColor[4] = { 1, 1, 1, flAlpha }; + Tracer_Draw( &mb, start, delta, flWidth, flColor, 1 ); +} + + +void CClient_Precipitation::CreateWaterSplashes() +{ + for ( int i=0; i < m_Splashes.Count(); i++ ) + { + Vector vSplash = m_Splashes[i]; + + if ( CurrentViewForward().Dot( vSplash - CurrentViewOrigin() ) > 1 ) + { + FX_WaterRipple( vSplash, g_flSplashScale, &g_vSplashColor, g_flSplashLifetime, g_flSplashAlpha ); + } + } + m_Splashes.Purge(); +} + + +void CClient_Precipitation::Render() +{ + if ( !r_DrawRain.GetInt() ) + return; + + // Don't render in monitors or in reflections or refractions. + if ( CurrentViewID() == VIEW_MONITOR ) + return; + + if ( view->GetDrawFlags() & (DF_RENDER_REFLECTION | DF_RENDER_REFRACTION) ) + return; + + if ( m_nPrecipType == PRECIPITATION_TYPE_ASH ) + return; + + if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL ) + return; + + // Create any queued up water splashes. + CreateWaterSplashes(); + + + CFastTimer timer; + timer.Start(); + + CMatRenderContextPtr pRenderContext( materials ); + + // We want to do our calculations in view space. + VMatrix tempView; + pRenderContext->GetMatrix( MATERIAL_VIEW, &tempView ); + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->LoadIdentity(); + + // Force the user clip planes to use the old view matrix + pRenderContext->EnableUserClipTransformOverride( true ); + pRenderContext->UserClipTransform( tempView ); + + // Draw all the rain tracers. + pRenderContext->Bind( m_MatHandle ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + if ( pMesh ) + { + CMeshBuilder mb; + mb.Begin( pMesh, MATERIAL_QUADS, m_Particles.Count() ); + + for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=m_Particles.Next( i ) ) + { + CPrecipitationParticle *p = &m_Particles[i]; + RenderParticle( p, mb ); + } + + mb.End( false, true ); + } + + pRenderContext->EnableUserClipTransformOverride( false ); + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->LoadMatrix( tempView ); + + if ( r_RainProfile.GetInt() ) + { + timer.End(); + engine->Con_NPrintf( 16, "Rain render : %du", timer.GetDuration().GetMicroseconds() ); + } +} + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- + +CClient_Precipitation::CClient_Precipitation() : m_Remainder(0.0f) +{ + m_nPrecipType = PRECIPITATION_TYPE_RAIN; + m_MatHandle = INVALID_MATERIAL_HANDLE; + m_flHalfScreenWidth = 1; + + g_Precipitations.AddToTail( this ); +} + +CClient_Precipitation::~CClient_Precipitation() +{ + g_Precipitations.FindAndRemove( this ); + SnowFallManagerDestroy(); +} + +//----------------------------------------------------------------------------- +// Precache data +//----------------------------------------------------------------------------- + +#define SNOW_SPEED 80.0f +#define RAIN_SPEED 425.0f + +#define RAIN_TRACER_WIDTH 0.35f +#define SNOW_TRACER_WIDTH 0.7f + +void CClient_Precipitation::Precache( ) +{ + if ( !m_MatHandle ) + { + // Compute precipitation emission speed + switch( m_nPrecipType ) + { + case PRECIPITATION_TYPE_SNOW: + m_Speed = SNOW_SPEED; + m_MatHandle = materials->FindMaterial( "particle/snow", TEXTURE_GROUP_CLIENT_EFFECTS ); + m_InitialRamp = 0.6f; + m_Width = SNOW_TRACER_WIDTH; + break; + + case PRECIPITATION_TYPE_RAIN: + Assert( m_nPrecipType == PRECIPITATION_TYPE_RAIN ); + m_Speed = RAIN_SPEED; + m_MatHandle = materials->FindMaterial( "particle/rain", TEXTURE_GROUP_CLIENT_EFFECTS ); + m_InitialRamp = 1.0f; + m_Color[3] = 1.0f; // make translucent + m_Width = RAIN_TRACER_WIDTH; + break; + default: + m_InitialRamp = 1.0f; + m_Color[3] = 1.0f; // make translucent + break; + } + + // Store off the color + m_Color[0] = 1.0f; + m_Color[1] = 1.0f; + m_Color[2] = 1.0f; + } +} + + +//----------------------------------------------------------------------------- +// Gets the tracer width and speed +//----------------------------------------------------------------------------- + +inline float CClient_Precipitation::GetWidth() const +{ +// return m_Width; + return s_rainwidth.GetFloat(); +} + +inline float CClient_Precipitation::GetLength() const +{ +// return m_Length; + return s_rainlength.GetFloat(); +} + +inline float CClient_Precipitation::GetSpeed() const +{ +// return m_Speed; + return s_rainspeed.GetFloat(); +} + + +//----------------------------------------------------------------------------- +// Gets the remaining lifetime of the particle +//----------------------------------------------------------------------------- + +inline float CClient_Precipitation::GetRemainingLifetime( CPrecipitationParticle* pParticle ) const +{ + float timeSinceSpawn = gpGlobals->curtime - pParticle->m_SpawnTime; + return m_Lifetime - timeSinceSpawn; +} + +//----------------------------------------------------------------------------- +// Creates a particle +//----------------------------------------------------------------------------- + +inline CPrecipitationParticle* CClient_Precipitation::CreateParticle() +{ + int i = m_Particles.AddToTail(); + CPrecipitationParticle* pParticle = &m_Particles[i]; + + pParticle->m_SpawnTime = gpGlobals->curtime; + pParticle->m_Ramp = m_InitialRamp; + + return pParticle; +} + + +//----------------------------------------------------------------------------- +// Compute the emission area +//----------------------------------------------------------------------------- + +bool CClient_Precipitation::ComputeEmissionArea( Vector& origin, Vector2D& size ) +{ + // FIXME: Compute the precipitation area based on computational power + float emissionSize = r_RainRadius.GetFloat(); // size of box to emit particles in + + Vector vMins = WorldAlignMins(); + Vector vMaxs = WorldAlignMaxs(); + if ( r_RainHack.GetInt() ) + { + vMins = GetClientWorldEntity()->m_WorldMins; + vMaxs = GetClientWorldEntity()->m_WorldMaxs; + } + + // calculate a volume around the player to snow in. Intersect this big magic + // box around the player with the volume of the current environmental ent. + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return false; + + // Determine how much time it'll take a falling particle to hit the player + float emissionHeight = MIN( vMaxs[2], pPlayer->GetAbsOrigin()[2] + 512 ); + float distToFall = emissionHeight - pPlayer->GetAbsOrigin()[2]; + float fallTime = distToFall / GetSpeed(); + + // Based on the windspeed, figure out the center point of the emission + Vector2D center; + center[0] = pPlayer->GetAbsOrigin()[0] - fallTime * s_WindVector[0]; + center[1] = pPlayer->GetAbsOrigin()[1] - fallTime * s_WindVector[1]; + + Vector2D lobound, hibound; + lobound[0] = center[0] - emissionSize * 0.5f; + lobound[1] = center[1] - emissionSize * 0.5f; + hibound[0] = lobound[0] + emissionSize; + hibound[1] = lobound[1] + emissionSize; + + // Cull non-intersecting. + if ( ( vMaxs[0] < lobound[0] ) || ( vMaxs[1] < lobound[1] ) || + ( vMins[0] > hibound[0] ) || ( vMins[1] > hibound[1] ) ) + return false; + + origin[0] = MAX( vMins[0], lobound[0] ); + origin[1] = MAX( vMins[1], lobound[1] ); + origin[2] = emissionHeight; + + hibound[0] = MIN( vMaxs[0], hibound[0] ); + hibound[1] = MIN( vMaxs[1], hibound[1] ); + + size[0] = hibound[0] - origin[0]; + size[1] = hibound[1] - origin[1]; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pDebugName - +// Output : AshDebrisEffect* +//----------------------------------------------------------------------------- +AshDebrisEffect* AshDebrisEffect::Create( const char *pDebugName ) +{ + return new AshDebrisEffect( pDebugName ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pParticle - +// timeDelta - +// Output : float +//----------------------------------------------------------------------------- +float AshDebrisEffect::UpdateAlpha( const SimpleParticle *pParticle ) +{ + return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) ); +} + +#define ASH_PARTICLE_NOISE 0x4 + +float AshDebrisEffect::UpdateRoll( SimpleParticle *pParticle, float timeDelta ) +{ + float flRoll = CSimpleEmitter::UpdateRoll(pParticle, timeDelta ); + + if ( pParticle->m_iFlags & ASH_PARTICLE_NOISE ) + { + Vector vTempEntVel = pParticle->m_vecVelocity; + float fastFreq = gpGlobals->curtime * 1.5; + + float s, c; + SinCos( fastFreq, &s, &c ); + + pParticle->m_Pos = ( pParticle->m_Pos + Vector( + vTempEntVel[0] * timeDelta * s, + vTempEntVel[1] * timeDelta * s, 0 ) ); + } + + return flRoll; +} + +void CClient_Precipitation::CreateAshParticle( void ) +{ + // Make sure the emitter is setup + if ( m_pAshEmitter == NULL ) + { + if ( ( m_pAshEmitter = AshDebrisEffect::Create( "ashtray" ) ) == NULL ) + return; + + m_tAshParticleTimer.Init( 192 ); + m_tAshParticleTraceTimer.Init( 15 ); + m_bActiveAshEmitter = false; + m_iAshCount = 0; + } + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + + if ( pPlayer == NULL ) + return; + + Vector vForward; + pPlayer->GetVectors( &vForward, NULL, NULL ); + vForward.z = 0.0f; + + float curTime = gpGlobals->frametime; + + Vector vPushOrigin; + + Vector absmins = WorldAlignMins(); + Vector absmaxs = WorldAlignMaxs(); + + //15 Traces a second. + while ( m_tAshParticleTraceTimer.NextEvent( curTime ) ) + { + trace_t tr; + + Vector vTraceStart = pPlayer->EyePosition(); + Vector vTraceEnd = pPlayer->EyePosition() + vForward * MAX_TRACE_LENGTH; + + UTIL_TraceLine( vTraceStart, vTraceEnd, MASK_SHOT_HULL & (~CONTENTS_GRATE), pPlayer, COLLISION_GROUP_NONE, &tr ); + + //debugoverlay->AddLineOverlay( vTraceStart, tr.endpos, 255, 0, 0, 0, 0.2 ); + + if ( tr.fraction != 1.0f ) + { + trace_t tr2; + + UTIL_TraceModel( vTraceStart, tr.endpos, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), this, COLLISION_GROUP_NONE, &tr2 ); + + if ( tr2.m_pEnt == this ) + { + m_bActiveAshEmitter = true; + + if ( tr2.startsolid == false ) + { + m_vAshSpawnOrigin = tr2.endpos + vForward * 256; + } + else + { + m_vAshSpawnOrigin = vTraceStart; + } + } + else + { + m_bActiveAshEmitter = false; + } + } + } + + if ( m_bActiveAshEmitter == false ) + return; + + Vector vecVelocity = pPlayer->GetAbsVelocity(); + + + float flVelocity = VectorNormalize( vecVelocity ); + Vector offset = m_vAshSpawnOrigin; + + m_pAshEmitter->SetSortOrigin( offset ); + + PMaterialHandle hMaterial[4]; + hMaterial[0] = ParticleMgr()->GetPMaterial( "effects/fleck_ash1" ); + hMaterial[1] = ParticleMgr()->GetPMaterial( "effects/fleck_ash2" ); + hMaterial[2] = ParticleMgr()->GetPMaterial( "effects/fleck_ash3" ); + hMaterial[3] = ParticleMgr()->GetPMaterial( "effects/ember_swirling001" ); + + SimpleParticle *pParticle; + + Vector vSpawnOrigin = vec3_origin; + + if ( flVelocity > 0 ) + { + vSpawnOrigin = ( vForward * 256 ) + ( vecVelocity * ( flVelocity * 2 ) ); + } + + // Add as many particles as we need + while ( m_tAshParticleTimer.NextEvent( curTime ) ) + { + int iRandomAltitude = RandomInt( 0, 128 ); + + offset = m_vAshSpawnOrigin + vSpawnOrigin + RandomVector( -256, 256 ); + offset.z = m_vAshSpawnOrigin.z + iRandomAltitude; + + if ( offset[0] > absmaxs[0] + || offset[1] > absmaxs[1] + || offset[2] > absmaxs[2] + || offset[0] < absmins[0] + || offset[1] < absmins[1] + || offset[2] < absmins[2] ) + continue; + + m_iAshCount++; + + bool bEmberTime = false; + + if ( m_iAshCount >= 250 ) + { + bEmberTime = true; + m_iAshCount = 0; + } + + int iRandom = random->RandomInt(0,2); + + if ( bEmberTime == true ) + { + offset = m_vAshSpawnOrigin + (vForward * 256) + RandomVector( -128, 128 ); + offset.z = pPlayer->EyePosition().z + RandomFloat( -16, 64 ); + + iRandom = 3; + } + + pParticle = (SimpleParticle *) m_pAshEmitter->AddParticle( sizeof(SimpleParticle), hMaterial[iRandom], offset ); + + if (pParticle == NULL) + continue; + + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = RemapVal( iRandomAltitude, 0, 128, 4, 8 ); + + if ( bEmberTime == true ) + { + Vector vGoal = pPlayer->EyePosition() + RandomVector( -64, 64 ); + Vector vDir = vGoal - offset; + VectorNormalize( vDir ); + + pParticle->m_vecVelocity = vDir * 75; + pParticle->m_flDieTime = 2.5f; + } + else + { + pParticle->m_vecVelocity = Vector( RandomFloat( -20.0f, 20.0f ), RandomFloat( -20.0f, 20.0f ), RandomFloat( -10, -15 ) ); + } + + float color = random->RandomInt( 125, 225 ); + pParticle->m_uchColor[0] = color; + pParticle->m_uchColor[1] = color; + pParticle->m_uchColor[2] = color; + + pParticle->m_uchStartSize = 1; + pParticle->m_uchEndSize = 1; + + pParticle->m_uchStartAlpha = 255; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f ); + + pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN; + + if ( random->RandomInt( 0, 10 ) <= 1 ) + { + pParticle->m_iFlags |= ASH_PARTICLE_NOISE; + } + } + } + +void CClient_Precipitation::CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity ) +{ + // Create the particle + CPrecipitationParticle* p = CreateParticle(); + if (!p) + return; + + VectorCopy( vVelocity, p->m_Velocity ); + p->m_Pos = vSpawnPosition; + + p->m_Velocity[ 0 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt()); + p->m_Velocity[ 1 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt()); + + p->m_Mass = random->RandomFloat( 0.5, 1.5 ); +} + +//----------------------------------------------------------------------------- +// emit the precipitation particles +//----------------------------------------------------------------------------- + +void CClient_Precipitation::EmitParticles( float fTimeDelta ) +{ + Vector2D size; + Vector vel, org; + + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( !pPlayer ) + return; + Vector vPlayerCenter = pPlayer->WorldSpaceCenter(); + + // Compute where to emit + if (!ComputeEmissionArea( org, size )) + return; + + // clamp this to prevent creating a bunch of rain or snow at one time. + if( fTimeDelta > 0.075f ) + fTimeDelta = 0.075f; + + // FIXME: Compute the precipitation density based on computational power + float density = m_flDensity; + + if (density > 0.01f) + density = 0.01f; + + // Compute number of particles to emit based on precip density and emission area and dt + float fParticles = size[0] * size[1] * density * fTimeDelta + m_Remainder; + int cParticles = (int)fParticles; + m_Remainder = fParticles - cParticles; + + // calculate the max amount of time it will take this flake to fall. + // This works if we assume the wind doesn't have a z component + VectorCopy( s_WindVector, vel ); + vel[2] -= GetSpeed(); + + // Emit all the particles + for ( int i = 0 ; i < cParticles ; i++ ) + { + Vector vParticlePos = org; + vParticlePos[ 0 ] += size[ 0 ] * random->RandomFloat(0, 1); + vParticlePos[ 1 ] += size[ 1 ] * random->RandomFloat(0, 1); + + // Figure out where the particle should lie in Z by tracing a line from the player's height up to the + // desired height and making sure it doesn't hit a wall. + Vector vPlayerHeight = vParticlePos; + vPlayerHeight.z = vPlayerCenter.z; + + trace_t trace; + UTIL_TraceLine( vPlayerHeight, vParticlePos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace ); + if ( trace.fraction < 1 ) + { + // If we hit a brush, then don't spawn the particle. + if ( trace.surface.flags & SURF_SKY ) + { + vParticlePos = trace.endpos; + } + else + { + continue; + } + } + + CreateRainOrSnowParticle( vParticlePos, vel ); + } +} + + +//----------------------------------------------------------------------------- +// Computes the wind vector +//----------------------------------------------------------------------------- + +void CClient_Precipitation::ComputeWindVector( ) +{ + // Compute the wind direction + QAngle windangle( 0, cl_winddir.GetFloat(), 0 ); // used to turn wind yaw direction into a vector + + // Randomize the wind angle and speed slightly to get us a little variation + windangle[1] = windangle[1] + random->RandomFloat( -10, 10 ); + float windspeed = cl_windspeed.GetFloat() * (1.0 + random->RandomFloat( -0.2, 0.2 )); + + AngleVectors( windangle, &s_WindVector ); + VectorScale( s_WindVector, windspeed, s_WindVector ); +} + + +CHandle g_pPrecipHackEnt; + +class CPrecipHack : public CAutoGameSystemPerFrame +{ +public: + CPrecipHack( char const *name ) : CAutoGameSystemPerFrame( name ) + { + m_bLevelInitted = false; + } + + virtual void LevelInitPostEntity() + { + if ( r_RainHack.GetInt() ) + { + CClient_Precipitation *pPrecipHackEnt = new CClient_Precipitation; + pPrecipHackEnt->InitializeAsClientEntity( NULL, RENDER_GROUP_TRANSLUCENT_ENTITY ); + g_pPrecipHackEnt = pPrecipHackEnt; + } + m_bLevelInitted = true; + } + + virtual void LevelShutdownPreEntity() + { + if ( r_RainHack.GetInt() && g_pPrecipHackEnt ) + { + g_pPrecipHackEnt->Release(); + } + m_bLevelInitted = false; + } + + virtual void Update( float frametime ) + { + // Handle changes to the cvar at runtime. + if ( m_bLevelInitted ) + { + if ( r_RainHack.GetInt() && !g_pPrecipHackEnt ) + LevelInitPostEntity(); + else if ( !r_RainHack.GetInt() && g_pPrecipHackEnt ) + LevelShutdownPreEntity(); + } + } + + bool m_bLevelInitted; +}; +CPrecipHack g_PrecipHack( "CPrecipHack" ); + +#else + +void DrawPrecipitation() +{ +} + +#endif // _XBOX + +//----------------------------------------------------------------------------- +// EnvWind - global wind info +//----------------------------------------------------------------------------- +class C_EnvWind : public C_BaseEntity +{ +public: + C_EnvWind(); + + DECLARE_CLIENTCLASS(); + DECLARE_CLASS( C_EnvWind, C_BaseEntity ); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual bool ShouldDraw( void ) { return false; } + + virtual void ClientThink( ); + +private: + C_EnvWind( const C_EnvWind & ); + + CEnvWindShared m_EnvWindShared; +}; + +// Receive datatables +BEGIN_RECV_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared) + RecvPropInt (RECVINFO(m_iMinWind)), + RecvPropInt (RECVINFO(m_iMaxWind)), + RecvPropInt (RECVINFO(m_iMinGust)), + RecvPropInt (RECVINFO(m_iMaxGust)), + RecvPropFloat (RECVINFO(m_flMinGustDelay)), + RecvPropFloat (RECVINFO(m_flMaxGustDelay)), + RecvPropInt (RECVINFO(m_iGustDirChange)), + RecvPropInt (RECVINFO(m_iWindSeed)), + RecvPropInt (RECVINFO(m_iInitialWindDir)), + RecvPropFloat (RECVINFO(m_flInitialWindSpeed)), + RecvPropFloat (RECVINFO(m_flStartTime)), + RecvPropFloat (RECVINFO(m_flGustDuration)), +// RecvPropInt (RECVINFO(m_iszGustSound)), +END_RECV_TABLE() + +IMPLEMENT_CLIENTCLASS_DT( C_EnvWind, DT_EnvWind, CEnvWind ) + RecvPropDataTable(RECVINFO_DT(m_EnvWindShared), 0, &REFERENCE_RECV_TABLE(DT_EnvWindShared)), +END_RECV_TABLE() + + +C_EnvWind::C_EnvWind() +{ +} + +//----------------------------------------------------------------------------- +// Post data update! +//----------------------------------------------------------------------------- +void C_EnvWind::OnDataChanged( DataUpdateType_t updateType ) +{ + // Whenever we get an update, reset the entire state. + // Note that the fields have already been stored by the datatables, + // but there's still work to be done in the init block + m_EnvWindShared.Init( entindex(), m_EnvWindShared.m_iWindSeed, + m_EnvWindShared.m_flStartTime, m_EnvWindShared.m_iInitialWindDir, + m_EnvWindShared.m_flInitialWindSpeed ); + + SetNextClientThink(0.0f); + + BaseClass::OnDataChanged( updateType ); +} + +void C_EnvWind::ClientThink( ) +{ + // Update the wind speed + float flNextThink = m_EnvWindShared.WindThink( gpGlobals->curtime ); + SetNextClientThink(flNextThink); +} + + + +//================================================== +// EmberParticle +//================================================== + +class CEmberEmitter : public CSimpleEmitter +{ +public: + CEmberEmitter( const char *pDebugName ); + static CSmartPtr Create( const char *pDebugName ); + virtual void UpdateVelocity( SimpleParticle *pParticle, float timeDelta ); + virtual Vector UpdateColor( const SimpleParticle *pParticle ); + +private: + CEmberEmitter( const CEmberEmitter & ); +}; + + +//----------------------------------------------------------------------------- +// Purpose: +// Input : fTimeDelta - +// Output : Vector +//----------------------------------------------------------------------------- +CEmberEmitter::CEmberEmitter( const char *pDebugName ) : CSimpleEmitter( pDebugName ) +{ +} + + +CSmartPtr CEmberEmitter::Create( const char *pDebugName ) +{ + return new CEmberEmitter( pDebugName ); +} + + +void CEmberEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) +{ + float speed = VectorNormalize( pParticle->m_vecVelocity ); + Vector offset; + + speed -= ( 1.0f * timeDelta ); + + offset.Random( -0.025f, 0.025f ); + offset[2] = 0.0f; + + pParticle->m_vecVelocity += offset; + VectorNormalize( pParticle->m_vecVelocity ); + + pParticle->m_vecVelocity *= speed; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pParticle - +// timeDelta - +//----------------------------------------------------------------------------- +Vector CEmberEmitter::UpdateColor( const SimpleParticle *pParticle ) +{ + Vector color; + float ramp = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime ); + + color[0] = ( (float) pParticle->m_uchColor[0] * ramp ) / 255.0f; + color[1] = ( (float) pParticle->m_uchColor[1] * ramp ) / 255.0f; + color[2] = ( (float) pParticle->m_uchColor[2] * ramp ) / 255.0f; + + return color; +} + +//================================================== +// C_Embers +//================================================== + +class C_Embers : public C_BaseEntity +{ +public: + DECLARE_CLIENTCLASS(); + DECLARE_CLASS( C_Embers, C_BaseEntity ); + + C_Embers(); + ~C_Embers(); + + void Start( void ); + + virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual bool ShouldDraw( void ); + virtual void AddEntity( void ); + + //Server-side + int m_nDensity; + int m_nLifetime; + int m_nSpeed; + bool m_bEmit; + +protected: + + void SpawnEmber( void ); + + PMaterialHandle m_hMaterial; + TimedEvent m_tParticleSpawn; + CSmartPtr m_pEmitter; + +}; + +//Receive datatable +IMPLEMENT_CLIENTCLASS_DT( C_Embers, DT_Embers, CEmbers ) + RecvPropInt( RECVINFO( m_nDensity ) ), + RecvPropInt( RECVINFO( m_nLifetime ) ), + RecvPropInt( RECVINFO( m_nSpeed ) ), + RecvPropInt( RECVINFO( m_bEmit ) ), +END_RECV_TABLE() + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bnewentity - +//----------------------------------------------------------------------------- +C_Embers::C_Embers() +{ + m_pEmitter = CEmberEmitter::Create( "C_Embers" ); +} + +C_Embers::~C_Embers() +{ +} + +void C_Embers::OnDataChanged( DataUpdateType_t updateType ) +{ + BaseClass::OnDataChanged( updateType ); + + if ( updateType == DATA_UPDATE_CREATED ) + { + m_pEmitter->SetSortOrigin( GetAbsOrigin() ); + + Start(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool C_Embers::ShouldDraw() +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_Embers::Start( void ) +{ + //Various setup info + m_tParticleSpawn.Init( m_nDensity ); + + m_hMaterial = m_pEmitter->GetPMaterial( "particle/fire" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_Embers::AddEntity( void ) +{ + if ( m_bEmit == false ) + return; + + float tempDelta = gpGlobals->frametime; + + while( m_tParticleSpawn.NextEvent( tempDelta ) ) + { + SpawnEmber(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void C_Embers::SpawnEmber( void ) +{ + Vector offset, mins, maxs; + + modelinfo->GetModelBounds( GetModel(), mins, maxs ); + + //Setup our spawn position + offset[0] = random->RandomFloat( mins[0], maxs[0] ); + offset[1] = random->RandomFloat( mins[1], maxs[1] ); + offset[2] = random->RandomFloat( mins[2], maxs[2] ); + + //Spawn the particle + SimpleParticle *sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof( SimpleParticle ), m_hMaterial, offset ); + + if (sParticle == NULL) + return; + + float cScale = random->RandomFloat( 0.75f, 1.0f ); + + //Set it up + sParticle->m_flLifetime = 0.0f; + sParticle->m_flDieTime = m_nLifetime; + + sParticle->m_uchColor[0] = m_clrRender->r * cScale; + sParticle->m_uchColor[1] = m_clrRender->g * cScale; + sParticle->m_uchColor[2] = m_clrRender->b * cScale; + sParticle->m_uchStartAlpha = 255; + sParticle->m_uchEndAlpha = 0; + sParticle->m_uchStartSize = 1; + sParticle->m_uchEndSize = 0; + sParticle->m_flRollDelta = 0; + sParticle->m_flRoll = 0; + + //Set the velocity + Vector velocity; + + AngleVectors( GetAbsAngles(), &velocity ); + + sParticle->m_vecVelocity = velocity * m_nSpeed; + + sParticle->m_vecVelocity[0] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) ); + sParticle->m_vecVelocity[1] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) ); + sParticle->m_vecVelocity[2] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) ); + + UpdateVisibility(); +} + +//----------------------------------------------------------------------------- +// Quadratic spline beam effect +//----------------------------------------------------------------------------- +#include "beamdraw.h" + +class C_QuadraticBeam : public C_BaseEntity +{ +public: + DECLARE_CLIENTCLASS(); + DECLARE_CLASS( C_QuadraticBeam, C_BaseEntity ); + + //virtual void OnDataChanged( DataUpdateType_t updateType ); + virtual bool ShouldDraw( void ) { return true; } + virtual int DrawModel( int ); + + virtual void GetRenderBounds( Vector& mins, Vector& maxs ) + { + ClearBounds( mins, maxs ); + AddPointToBounds( vec3_origin, mins, maxs ); + AddPointToBounds( m_targetPosition, mins, maxs ); + AddPointToBounds( m_controlPosition, mins, maxs ); + mins -= GetRenderOrigin(); + maxs -= GetRenderOrigin(); + } + +protected: + + Vector m_targetPosition; + Vector m_controlPosition; + float m_scrollRate; + float m_flWidth; +}; + +//Receive datatable +IMPLEMENT_CLIENTCLASS_DT( C_QuadraticBeam, DT_QuadraticBeam, CEnvQuadraticBeam ) + RecvPropVector( RECVINFO(m_targetPosition) ), + RecvPropVector( RECVINFO(m_controlPosition) ), + RecvPropFloat( RECVINFO(m_scrollRate) ), + RecvPropFloat( RECVINFO(m_flWidth) ), +END_RECV_TABLE() + +Vector Color32ToVector( const color32 &color ) +{ + return Vector( color.r * (1.0/255.0f), color.g * (1.0/255.0f), color.b * (1.0/255.0f) ); +} + +int C_QuadraticBeam::DrawModel( int ) +{ + Draw_SetSpriteTexture( GetModel(), 0, GetRenderMode() ); + Vector color = Color32ToVector( GetRenderColor() ); + DrawBeamQuadratic( GetRenderOrigin(), m_controlPosition, m_targetPosition, m_flWidth, color, gpGlobals->curtime*m_scrollRate ); + return 1; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class SnowFallEffect : public CSimpleEmitter +{ +public: + + SnowFallEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {} + static SnowFallEffect* Create( const char *pDebugName ) + { + return new SnowFallEffect( pDebugName ); + } + + void UpdateVelocity( SimpleParticle *pParticle, float timeDelta ) + { + float flSpeed = VectorNormalize( pParticle->m_vecVelocity ); + flSpeed -= timeDelta; + + pParticle->m_vecVelocity.x += RandomFloat( -0.025f, 0.025f ); + pParticle->m_vecVelocity.y += RandomFloat( -0.025f, 0.025f ); + VectorNormalize( pParticle->m_vecVelocity ); + + pParticle->m_vecVelocity *= flSpeed; + + Vector vecWindVelocity; + GetWindspeedAtTime( gpGlobals->curtime, vecWindVelocity ); + pParticle->m_vecVelocity += ( vecWindVelocity * r_SnowWindScale.GetFloat() ); + } + + void SimulateParticles( CParticleSimulateIterator *pIterator ) + { + float timeDelta = pIterator->GetTimeDelta(); + + SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst(); + while ( pParticle ) + { + //Update velocity + UpdateVelocity( pParticle, timeDelta ); + pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta; + + //Should this particle die? + pParticle->m_flLifetime += timeDelta; + UpdateRoll( pParticle, timeDelta ); + + if ( pParticle->m_flLifetime >= pParticle->m_flDieTime ) + { + pIterator->RemoveParticle( pParticle ); + } + else if ( !IsInAir( pParticle->m_Pos ) ) + { + pIterator->RemoveParticle( pParticle ); + } + + pParticle = (SimpleParticle*)pIterator->GetNext(); + } + } + + int GetParticleCount( void ) + { + return GetBinding().GetNumActiveParticles(); + } + + void SetBounds( const Vector &vecMin, const Vector &vecMax ) + { + GetBinding().SetBBox( vecMin, vecMax, true ); + } + + bool IsTransparent( void ) { return false; } + +private: + + SnowFallEffect( const SnowFallEffect & ); +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class CSnowFallManager : public C_BaseEntity +{ +public: + + CSnowFallManager(); + ~CSnowFallManager(); + + bool CreateEmitter( void ); + + void SpawnClientEntity( void ); + void ClientThink(); + + void AddSnowFallEntity( CClient_Precipitation *pSnowEntity ); + + // Snow Effect + enum + { + SNOWFALL_NONE = 0, + SNOWFALL_AROUND_PLAYER, + SNOWFALL_IN_ENTITY, + }; + + bool IsTransparent( void ) { return false; } + +private: + + bool CreateSnowFallEmitter( void ); + void CreateSnowFall( void ); + void CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ); + void CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale ); + void CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ); + void CreateSnowParticlesSphere( float flRadius ); + void CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward ); + void CreateSnowFallParticle( const Vector &vecParticleSpawn, int iBBox ); + + int StandingInSnowVolume( Vector &vecPoint ); + void FindSnowVolumes( Vector &vecCenter, float flRadius, Vector &vecEyePos, Vector &vecForward ); + + void UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax ); + +private: + + enum { MAX_SNOW_PARTICLES = 500 }; + enum { MAX_SNOW_LIST = 32 }; + + TimedEvent m_tSnowFallParticleTimer; + TimedEvent m_tSnowFallParticleTraceTimer; + + int m_iSnowFallArea; + CSmartPtr m_pSnowFallEmitter; + Vector m_vecSnowFallEmitOrigin; + float m_flSnowRadius; + + Vector m_vecMin; + Vector m_vecMax; + + int m_nActiveSnowCount; + int m_aActiveSnow[MAX_SNOW_LIST]; + + bool m_bRayParticles; + + struct SnowFall_t + { + PMaterialHandle m_hMaterial; + CClient_Precipitation *m_pEntity; + SnowFallEffect *m_pEffect; + Vector m_vecMin; + Vector m_vecMax; + }; + + CUtlVector m_aSnow; +}; + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSnowFallManager::CSnowFallManager( void ) +{ + m_iSnowFallArea = SNOWFALL_NONE; + m_pSnowFallEmitter = NULL; + m_vecSnowFallEmitOrigin.Init(); + m_flSnowRadius = 0.0f; + m_vecMin.Init( FLT_MAX, FLT_MAX, FLT_MAX ); + m_vecMax.Init( FLT_MIN, FLT_MIN, FLT_MIN ); + m_nActiveSnowCount = 0; + m_aSnow.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CSnowFallManager::~CSnowFallManager( void ) +{ + m_aSnow.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSnowFallManager::CreateEmitter( void ) +{ + return CreateSnowFallEmitter(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSnowFallManager::SpawnClientEntity( void ) +{ + m_tSnowFallParticleTimer.Init( 500 ); + m_tSnowFallParticleTraceTimer.Init( 6 ); + m_iSnowFallArea = SNOWFALL_NONE; + + // Have the Snow Fall Manager think for all the snow fall entities. + SetNextClientThink( CLIENT_THINK_ALWAYS ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CSnowFallManager::CreateSnowFallEmitter( void ) +{ + if ( ( m_pSnowFallEmitter = SnowFallEffect::Create( "snowfall" ) ) == NULL ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSnowFallManager::ClientThink( void ) +{ + if ( !r_SnowEnable.GetBool() ) + return; + + // Make sure we have a snow fall emitter. + if ( !m_pSnowFallEmitter ) + { + if ( !CreateSnowFallEmitter() ) + return; + } + + CreateSnowFall(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pSnowEntity - +//----------------------------------------------------------------------------- +void CSnowFallManager::AddSnowFallEntity( CClient_Precipitation *pSnowEntity ) +{ + if ( !pSnowEntity ) + return; + + int nSnowCount = m_aSnow.Count(); + int iSnow = 0; + for ( iSnow = 0; iSnow < nSnowCount; ++iSnow ) + { + if ( m_aSnow[iSnow].m_pEntity == pSnowEntity ) + break; + } + + if ( iSnow != nSnowCount ) + return; + + iSnow = m_aSnow.AddToTail(); + m_aSnow[iSnow].m_pEntity = pSnowEntity; + m_aSnow[iSnow].m_pEffect = SnowFallEffect::Create( "snowfall" ); + m_aSnow[iSnow].m_hMaterial = ParticleMgr()->GetPMaterial( "particle/snow" ); + + VectorCopy( pSnowEntity->WorldAlignMins(), m_aSnow[iSnow].m_vecMin ); + VectorCopy( pSnowEntity->WorldAlignMaxs(), m_aSnow[iSnow].m_vecMax ); + + UpdateBounds( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSnowFallManager::UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax ) +{ + int iAxis = 0; + for ( iAxis = 0; iAxis < 3; ++iAxis ) + { + if ( vecSnowMin[iAxis] < m_vecMin[iAxis] ) + { + m_vecMin[iAxis] = vecSnowMin[iAxis]; + } + + if ( vecSnowMax[iAxis] > m_vecMax[iAxis] ) + { + m_vecMax[iAxis] = vecSnowMax[iAxis]; + } + } + + Assert( m_pSnowFallEmitter ); + m_pSnowFallEmitter->SetBounds( m_vecMin, m_vecMax ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecPoint - +// Output : int +//----------------------------------------------------------------------------- +int CSnowFallManager::StandingInSnowVolume( Vector &vecPoint ) +{ + trace_t traceSnow; + + int nSnowCount = m_aSnow.Count(); + int iSnow = 0; + for ( iSnow = 0; iSnow < nSnowCount; ++iSnow ) + { + UTIL_TraceModel( vecPoint, vecPoint, vec3_origin, vec3_origin, static_cast( m_aSnow[iSnow].m_pEntity ), COLLISION_GROUP_NONE, &traceSnow ); + if ( traceSnow.startsolid ) + return iSnow; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecCenter - +// flRadius - +//----------------------------------------------------------------------------- +void CSnowFallManager::FindSnowVolumes( Vector &vecCenter, float flRadius, Vector &vecEyePos, Vector &vecForward ) +{ + // Reset. + m_nActiveSnowCount = 0; + m_bRayParticles = false; + + int nSnowCount = m_aSnow.Count(); + int iSnow = 0; + for ( iSnow = 0; iSnow < nSnowCount; ++iSnow ) + { + // Check to see if the volume is in the PVS. + bool bInPVS = g_pClientLeafSystem->IsRenderableInPVS( m_aSnow[iSnow].m_pEntity->GetClientRenderable() ); + if ( !bInPVS ) + continue; + + // Check to see if a snow volume is inside the given radius. + if ( IsBoxIntersectingSphere( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, vecCenter, flRadius ) ) + { + m_aActiveSnow[m_nActiveSnowCount] = iSnow; + ++m_nActiveSnowCount; + if ( m_nActiveSnowCount >= MAX_SNOW_LIST ) + { + DevWarning( 1, "Max Active Snow Volume Count!\n" ); + break; + } + } + // Check to see if a snow volume is outside of the sphere radius, but is along line-of-sight. + else + { + CBaseTrace trace; + Vector vecNewForward; + vecNewForward = vecForward * r_SnowRayLength.GetFloat(); + vecNewForward.z = 0.0f; + IntersectRayWithBox( vecEyePos, vecNewForward, m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, 0.325f, &trace ); + if ( trace.fraction < 1.0f ) + { + m_aActiveSnow[m_nActiveSnowCount] = iSnow; + ++m_nActiveSnowCount; + if ( m_nActiveSnowCount >= MAX_SNOW_LIST ) + { + DevWarning( 1, "Max Active Snow Volume Count!\n" ); + break; + } + + m_bRayParticles = true; + } + } + } + + // Debugging code! +#ifdef _DEBUG + if ( r_SnowDebugBox.GetFloat() != 0.0f ) + { + for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow ) + { + Vector vecCenter, vecMin, vecMax; + vecCenter = ( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax ) * 0.5; + vecMin = m_aSnow[iSnow].m_vecMin - vecCenter; + vecMax = m_aSnow[iSnow].m_vecMax - vecCenter; + debugoverlay->AddBoxOverlay( vecCenter, vecMin, vecMax, QAngle( 0, 0, 0 ), 200, 0, 0, 25, r_SnowDebugBox.GetFloat() ); + } + } +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CSnowFallManager::CreateSnowFall( void ) +{ +#if 1 + VPROF_BUDGET( "SnowFall", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); +#endif + + // Check to see if we have a local player before starting the snow around a local player. + C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); + if ( pPlayer == NULL ) + return; + + // Get the current frame time. + float flCurrentTime = gpGlobals->frametime; + + // Get the players data to determine where the snow emitter should reside. + VectorCopy( pPlayer->EyePosition(), m_vecSnowFallEmitOrigin ); + Vector vecForward; + pPlayer->GetVectors( &vecForward, NULL, NULL ); + vecForward.z = 0.0f; + Vector vecVelocity = pPlayer->GetAbsVelocity(); + float flSpeed = VectorNormalize( vecVelocity ); + m_vecSnowFallEmitOrigin += ( vecForward * ( 64.0f + ( flSpeed * 0.4f * r_SnowPosScale.GetFloat() ) ) ); + m_vecSnowFallEmitOrigin += ( vecVelocity * ( flSpeed * 1.25f * r_SnowSpeedScale.GetFloat() ) ); + + // Check to see if the player is zoomed. + bool bZoomed = ( pPlayer->GetFOV() != pPlayer->GetDefaultFOV() ); + float flZoomScale = 1.0f; + if ( bZoomed ) + { + flZoomScale = pPlayer->GetDefaultFOV() / pPlayer->GetFOV(); + flZoomScale *= 0.5f; + } + + // Time to test for a snow volume yet? (Only do this 6 times a second!) + if ( m_tSnowFallParticleTraceTimer.NextEvent( flCurrentTime ) ) + { + // Reset the active snow emitter. + m_iSnowFallArea = SNOWFALL_NONE; + + // Set the trace start and the emit origin. + Vector vecTraceStart; + VectorCopy( pPlayer->EyePosition(), vecTraceStart ); + + int iSnowVolume = StandingInSnowVolume( vecTraceStart ); + if ( iSnowVolume != -1 ) + { + m_flSnowRadius = r_SnowInsideRadius.GetFloat() + ( flSpeed * 0.5f ); + m_iSnowFallArea = SNOWFALL_AROUND_PLAYER; + } + else + { + m_flSnowRadius = r_SnowOutsideRadius.GetFloat(); + } + + float flRadius = m_flSnowRadius; + if ( bZoomed ) + { + if ( m_iSnowFallArea == SNOWFALL_AROUND_PLAYER ) + { + flRadius = r_SnowOutsideRadius.GetFloat() * flZoomScale; + } + else + { + flRadius *= flZoomScale; + } + } + + Vector vecEyePos = pPlayer->EyePosition(); + FindSnowVolumes( m_vecSnowFallEmitOrigin, flRadius, vecEyePos, vecForward ); + if ( m_nActiveSnowCount != 0 && m_iSnowFallArea != SNOWFALL_AROUND_PLAYER ) + { + // We found an active snow emitter. + m_iSnowFallArea = SNOWFALL_IN_ENTITY; + + } + } + + if ( m_iSnowFallArea == SNOWFALL_NONE ) + return; + + // Set the origin in the snow emitter. + m_pSnowFallEmitter->SetSortOrigin( m_vecSnowFallEmitOrigin ); + + // Create snow fall particles. + CreateSnowFallParticles( flCurrentTime, m_flSnowRadius, pPlayer->EyePosition(), vecForward, flZoomScale ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flCurrentTime - +// flRadius - +// &vecEyePos - +// &vecForward - +// flZoomScale - +//----------------------------------------------------------------------------- +void CSnowFallManager::CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ) + { + // Outside of a snow volume. + if ( m_iSnowFallArea == SNOWFALL_IN_ENTITY ) + { + CreateOutsideVolumeSnowParticles( flCurrentTime, flRadius, flZoomScale ); + } + // Inside of a snow volume. + else + { + CreateInsideVolumeSnowParticles( flCurrentTime, flRadius, vecEyePos, vecForward, flZoomScale ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flCurrentTime - +// flRadius - +// flZoomScale - +//----------------------------------------------------------------------------- +void CSnowFallManager::CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale ) +{ + Vector vecParticleSpawn; + + // Outside of a snow volume. + int iSnow = 0; + float flRadiusScaled = flRadius * flZoomScale; + float flRadius2 = flRadiusScaled * flRadiusScaled; + + // Add as many particles as we need + while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) ) + { + // Check for a max particle count. + if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() ) + continue; + + vecParticleSpawn.x = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ); + vecParticleSpawn.y = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ); + vecParticleSpawn.z = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ); + + float flDistance2 = ( m_vecSnowFallEmitOrigin - vecParticleSpawn ).LengthSqr(); + if ( flDistance2 < flRadius2 ) + { + CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] ); + } + + iSnow = ( iSnow + 1 ) % m_nActiveSnowCount; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flCurrentTime - +// flRadius - +// &vecEyePos - +// &vecForward - +// flZoomScale - +//----------------------------------------------------------------------------- +void CSnowFallManager::CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale ) +{ + Vector vecParticleSpawn; + + // Check/Setup for zoom. + bool bZoomed = ( flZoomScale > 1.0f ); + float flZoomRadius = 0.0f; + Vector vecZoomEmitOrigin; + if ( bZoomed ) + { + vecZoomEmitOrigin = m_vecSnowFallEmitOrigin + ( vecForward * ( r_SnowZoomOffset.GetFloat() * flZoomScale ) ); + flZoomRadius = flRadius * flZoomScale; + } + + int iIndex = 0; + + // Add as many particles as we need + while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) ) + { + // Check for a max particle count. + if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() ) + continue; + + // Create particle inside of sphere. + if ( iIndex > 0 ) + { + CreateSnowParticlesSphere( flZoomRadius ); + CreateSnowParticlesRay( flZoomRadius, vecEyePos, vecForward ); + } + else + { + CreateSnowParticlesSphere( flRadius ); + CreateSnowParticlesRay( flRadius, vecEyePos, vecForward ); + } + + // Increment if zoomed. + if ( bZoomed ) + { + iIndex = ( iIndex + 1 ) % 3; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flRadius - +//----------------------------------------------------------------------------- +void CSnowFallManager::CreateSnowParticlesSphere( float flRadius ) +{ + Vector vecParticleSpawn; + + vecParticleSpawn.x = m_vecSnowFallEmitOrigin.x + RandomFloat( -flRadius, flRadius ); + vecParticleSpawn.y = m_vecSnowFallEmitOrigin.y + RandomFloat( -flRadius, flRadius ); + vecParticleSpawn.z = m_vecSnowFallEmitOrigin.z + RandomFloat( -flRadius, flRadius ); + + int iSnow = 0; + for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow ) + { + if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) ) + continue; + if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) ) + continue; + if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) ) + continue; + + break; + } + + if ( iSnow == m_nActiveSnowCount ) + return; + + CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &vecEyePos - +// &vecForward - +//----------------------------------------------------------------------------- +void CSnowFallManager::CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward ) +{ + // Check to see if we should create particles along line-of-sight. + if ( !m_bRayParticles && r_SnowRayEnable.GetBool() ) + return; + + Vector vecParticleSpawn; + + // Create a particle down the player's view beyond the radius. + float flRayRadius = r_SnowRayRadius.GetFloat(); + + Vector vecNewForward; + vecNewForward = vecForward * RandomFloat( flRadius, r_SnowRayLength.GetFloat() ); + + vecParticleSpawn.x = vecEyePos.x + vecNewForward.x; + vecParticleSpawn.y = vecEyePos.y + vecNewForward.y; + vecParticleSpawn.z = vecEyePos.z + RandomFloat( 72, flRayRadius ); + vecParticleSpawn.x += RandomFloat( -flRayRadius, flRayRadius ); + vecParticleSpawn.y += RandomFloat( -flRayRadius, flRayRadius ); + + int iSnow = 0; + for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow ) + { + if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) ) + continue; + if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) ) + continue; + if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) ) + continue; + + break; + } + + if ( iSnow == m_nActiveSnowCount ) + return; + + CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] ); +} + +void CSnowFallManager::CreateSnowFallParticle( const Vector &vecParticleSpawn, int iSnow ) +{ + SimpleParticle *pParticle = ( SimpleParticle* )m_pSnowFallEmitter->AddParticle( sizeof( SimpleParticle ), m_aSnow[iSnow].m_hMaterial, vecParticleSpawn ); + if ( pParticle == NULL ) + return; + + pParticle->m_flLifetime = 0.0f; + pParticle->m_vecVelocity = Vector( RandomFloat( -5.0f, 5.0f ), RandomFloat( -5.0f, 5.0f ), ( RandomFloat( -25, -35 ) * r_SnowFallSpeed.GetFloat() ) ); + pParticle->m_flDieTime = fabs( ( vecParticleSpawn.z - m_aSnow[iSnow].m_vecMin.z ) / ( pParticle->m_vecVelocity.z - 0.1 ) ); + + // Probably want to put the color in the snow entity. +// pParticle->m_uchColor[0] = 150;//color; +// pParticle->m_uchColor[1] = 175;//color; +// pParticle->m_uchColor[2] = 200;//color; + pParticle->m_uchColor[0] = r_SnowColorRed.GetInt(); + pParticle->m_uchColor[1] = r_SnowColorGreen.GetInt(); + pParticle->m_uchColor[2] = r_SnowColorBlue.GetInt(); + + pParticle->m_uchStartSize = r_SnowStartSize.GetInt(); + pParticle->m_uchEndSize = r_SnowEndSize.GetInt(); + +// pParticle->m_uchStartAlpha = 255; + pParticle->m_uchStartAlpha = r_SnowStartAlpha.GetInt(); + pParticle->m_uchEndAlpha = r_SnowEndAlpha.GetInt(); + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f ); + + pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity ) +{ + if ( !s_pSnowFallMgr ) + { + s_pSnowFallMgr = new CSnowFallManager(); + s_pSnowFallMgr->CreateEmitter(); + s_pSnowFallMgr->InitializeAsClientEntity( NULL, RENDER_GROUP_OTHER ); + if ( !s_pSnowFallMgr ) + return false; + } + + s_pSnowFallMgr->AddSnowFallEntity( pSnowEntity ); + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void SnowFallManagerDestroy( void ) +{ + if ( s_pSnowFallMgr ) + { + delete s_pSnowFallMgr; + s_pSnowFallMgr = NULL; + } +} -- cgit v1.2.3