2013-12-03 07:31:46 +04:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
# include "cbase.h"
2020-11-26 05:26:55 +03:00
# include "c_effects.h"
2013-12-03 07:31:46 +04:00
# 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"
2020-11-26 05:26:55 +03:00
# include "raytrace.h"
2013-12-03 07:31:46 +04:00
// 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.
2020-11-26 05:26:55 +03:00
ConVar r_RainParticleDensity ( " r_RainParticleDensity " , " 1 " , FCVAR_NONE , " Density of Particle Rain 0-1 " ) ;
# ifdef MAPBASE
2021-11-06 08:02:05 +03:00
ConVar r_RainParticleClampOffset_Rain ( " r_RainParticleClampOffset_Rain " , " 120 " , FCVAR_NONE , " How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain' type. " ) ;
2021-10-09 23:05:28 +03:00
ConVar r_RainParticleClampOffset_Ash ( " r_RainParticleClampOffset_Ash " , " 300 " , FCVAR_NONE , " How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Ash' type. " ) ;
ConVar r_RainParticleClampOffset_RainStorm ( " r_RainParticleClampOffset_RainStorm " , " 112 " , FCVAR_NONE , " How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Rain Storm' type. " ) ;
ConVar r_RainParticleClampOffset_Snow ( " r_RainParticleClampOffset_Snow " , " 300 " , FCVAR_NONE , " How far inward or outward to extrude clamped precipitation particle systems using the 'Particle Snow' type. " ) ;
2020-11-26 05:26:55 +03:00
ConVar r_RainParticleClampDebug ( " r_RainParticleClampDebug " , " 0 " , FCVAR_NONE , " Enables debug code for precipitation particle system clamping " ) ;
# endif
2013-12-03 07:31:46 +04:00
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 ( )
2020-11-26 05:26:55 +03:00
CUtlVector < RayTracingEnvironment * > g_RayTraceEnvironments ;
2013-12-03 07:31:46 +04:00
// Just receive the normal data table stuff
IMPLEMENT_CLIENTCLASS_DT ( CClient_Precipitation , DT_Precipitation , CPrecipitation )
2020-11-26 05:26:55 +03:00
RecvPropInt ( RECVINFO ( m_nPrecipType ) ) ,
# ifdef MAPBASE
RecvPropInt ( RECVINFO ( m_spawnflags ) ) ,
# endif
2013-12-03 07:31:46 +04:00
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 )
{
2020-11-26 05:26:55 +03:00
if ( IsParticleRainType ( m_nPrecipType ) )
{
CreateParticlePrecip ( ) ;
return ;
}
2013-12-03 07:31:46 +04:00
// 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 ;
2020-11-26 05:26:55 +03:00
if ( IsParticleRainType ( m_nPrecipType ) )
return ;
2013-12-03 07:31:46 +04:00
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 ;
2020-11-26 05:26:55 +03:00
if ( IsParticleRainType ( m_nPrecipType ) )
return ;
2013-12-03 07:31:46 +04:00
// 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 ;
2020-11-26 05:26:55 +03:00
m_pParticlePrecipInnerNear = NULL ;
m_pParticlePrecipInnerFar = NULL ;
m_pParticlePrecipOuter = NULL ;
m_bActiveParticlePrecipEmitter = false ;
2013-12-03 07:31:46 +04:00
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 ;
}
}
}
2020-11-26 05:26:55 +03:00
void CClient_Precipitation : : PrecacheParticlePrecip ( void )
{
if ( m_nPrecipType = = PRECIPITATION_TYPE_PARTICLEASH )
{
PrecacheParticleSystem ( " ash " ) ;
PrecacheParticleSystem ( " ash_outer " ) ;
}
else if ( m_nPrecipType = = PRECIPITATION_TYPE_PARTICLESNOW )
{
PrecacheParticleSystem ( " snow " ) ;
PrecacheParticleSystem ( " snow_outer " ) ;
}
else if ( m_nPrecipType = = PRECIPITATION_TYPE_PARTICLERAINSTORM )
{
PrecacheParticleSystem ( " rain_storm " ) ;
PrecacheParticleSystem ( " rain_storm_screen " ) ;
PrecacheParticleSystem ( " rain_storm_outer " ) ;
}
else //default to rain
{
PrecacheParticleSystem ( " rain " ) ;
PrecacheParticleSystem ( " rain_outer " ) ;
}
}
void CClient_Precipitation : : CreateParticlePrecip ( void )
{
if ( ! m_bParticlePrecipInitialized )
{
PrecacheParticlePrecip ( ) ;
InitializeParticlePrecip ( ) ;
}
C_BasePlayer * pPlayer = C_BasePlayer : : GetLocalPlayer ( ) ;
if ( pPlayer = = NULL )
return ;
// Make sure the emitter is setup
if ( ! m_bActiveParticlePrecipEmitter )
{
//Update 8 times per second.
m_tParticlePrecipTraceTimer . Init ( 8 ) ;
DestroyInnerParticlePrecip ( ) ;
DestroyOuterParticlePrecip ( ) ;
m_bActiveParticlePrecipEmitter = true ;
}
UpdateParticlePrecip ( pPlayer ) ;
}
2021-10-09 23:05:28 +03:00
# ifdef MAPBASE
void CClient_Precipitation : : ClampParticlePosition ( Vector & vPlayerPos , Vector & vOffsetPos , Vector & vOffsetPosNear , Vector & vOffsetPosFar )
{
Vector mins , maxs ;
modelinfo - > GetModelBounds ( GetModel ( ) , mins , maxs ) ;
// Account for precipitation height
maxs . z + = 180 ;
Vector vecOrigin ; //= WorldSpaceCenter();
VectorLerp ( mins , maxs , 0.5f , vecOrigin ) ;
maxs - = vecOrigin ;
mins - = vecOrigin ;
//float flMax = r_RainParticleClampOffset.GetFloat();
float flMax = 0 ;
switch ( m_nPrecipType )
{
case PRECIPITATION_TYPE_PARTICLERAIN :
flMax = r_RainParticleClampOffset_Rain . GetFloat ( ) ;
break ;
case PRECIPITATION_TYPE_PARTICLEASH :
flMax = r_RainParticleClampOffset_Ash . GetFloat ( ) ;
break ;
case PRECIPITATION_TYPE_PARTICLERAINSTORM :
flMax = r_RainParticleClampOffset_RainStorm . GetFloat ( ) ;
break ;
case PRECIPITATION_TYPE_PARTICLESNOW :
flMax = r_RainParticleClampOffset_Snow . GetFloat ( ) ;
break ;
}
Vector addend ( flMax , flMax , 0 ) ;
mins + = addend ;
maxs - = addend ;
if ( flMax > 0 )
{
// Unless this is extruding outwards, make sure the offset isn't inverting the bounds.
// This means precipitation triggers with bounds less than offset*2 will turn into a thin line
// and the involved precipitation will pretty much be spatial at all times, which is okay.
mins . x = clamp ( mins . x , - FLT_MAX , - 1 ) ;
mins . y = clamp ( mins . y , - FLT_MAX , - 1 ) ;
maxs . x = clamp ( maxs . x , 1 , FLT_MAX ) ;
maxs . y = clamp ( maxs . y , 1 , FLT_MAX ) ;
}
if ( r_RainParticleClampDebug . GetBool ( ) )
debugoverlay - > AddBoxOverlay ( vecOrigin , mins , maxs , vec3_angle , 255 , 0 , 0 , 128 , 0.15f ) ;
maxs + = vecOrigin ;
mins + = vecOrigin ;
CalcClosestPointOnAABB ( mins , maxs , vPlayerPos , vPlayerPos ) ;
CalcClosestPointOnAABB ( mins , maxs , vOffsetPos , vOffsetPos ) ;
CalcClosestPointOnAABB ( mins , maxs , vOffsetPosNear , vOffsetPosNear ) ;
CalcClosestPointOnAABB ( mins , maxs , vOffsetPosFar , vOffsetPosFar ) ;
}
# endif
2020-11-26 05:26:55 +03:00
void CClient_Precipitation : : UpdateParticlePrecip ( C_BasePlayer * pPlayer )
{
if ( ! pPlayer )
return ;
Vector vForward ;
Vector vRight ;
pPlayer - > GetVectors ( & vForward , & vRight , NULL ) ;
vForward . z = 0.0f ;
vForward . NormalizeInPlace ( ) ;
Vector vForward45Right = vForward + vRight ;
Vector vForward45Left = vForward - vRight ;
vForward45Right . NormalizeInPlace ( ) ;
vForward45Left . NormalizeInPlace ( ) ;
fltx4 TMax = ReplicateX4 ( 320.0f ) ;
SubFloat ( TMax , 3 ) = FLT_MAX ;
float curTime = gpGlobals - > frametime ;
while ( m_tParticlePrecipTraceTimer . NextEvent ( curTime ) )
{
Vector vPlayerPos = pPlayer - > EyePosition ( ) ;
Vector vOffsetPos = vPlayerPos + Vector ( 0 , 0 , 180 ) ;
Vector vOffsetPosNear = vPlayerPos + Vector ( 0 , 0 , 180 ) + ( vForward * 32 ) ;
Vector vOffsetPosFar = vPlayerPos + Vector ( 0 , 0 , 180 ) + ( vForward * 100 ) ;
# ifdef MAPBASE
if ( m_spawnflags & SF_PRECIP_PARTICLE_CLAMP )
{
2021-10-09 23:05:28 +03:00
ClampParticlePosition ( vPlayerPos , vOffsetPos , vOffsetPosNear , vOffsetPosFar ) ;
2020-11-26 05:26:55 +03:00
}
# endif
Vector vDensity = Vector ( r_RainParticleDensity . GetFloat ( ) , 0 , 0 ) * m_flDensity ;
// Get the rain volume Ray Tracing Environment. Currently hard coded to 0, should have this lookup
RayTracingEnvironment * RtEnv = g_RayTraceEnvironments . Element ( 0 ) ;
// Our 4 Rays are forward, off to the left and right, and directly up.
// Use the first three to determine if there's generally visible rain where we're looking.
// The forth, straight up, tells us if we're standing inside a rain volume
// (based on the normal that we hit or if we miss entirely)
FourRays frRays ;
FourVectors fvDirection ;
fvDirection = FourVectors ( vForward , vForward45Left , vForward45Right , Vector ( 0 , 0 , 1 ) ) ;
frRays . direction = fvDirection ;
frRays . origin . DuplicateVector ( vPlayerPos ) ;
RayTracingResult Result ;
RtEnv - > Trace4Rays ( frRays , Four_Zeros , TMax , & Result ) ;
i32x4 in4HitIds = LoadAlignedIntSIMD ( Result . HitIds ) ;
fltx4 fl4HitIds = SignedIntConvertToFltSIMD ( in4HitIds ) ;
fltx4 fl4Tolerance = ReplicateX4 ( 300.0f ) ;
// ignore upwards test for tolerance, as we may be below an area which is raining, but with it not visible in front of us
//SubFloat( fl4Tolerance, 3 ) = 0.0f;
bool bInside = ( Result . HitIds [ 3 ] ! = - 1 & & Result . surface_normal . Vec ( 3 ) . z < 0.0f ) ;
bool bNearby = ( IsAnyNegative ( CmpGeSIMD ( fl4HitIds , Four_Zeros ) ) & & IsAnyNegative ( CmpGeSIMD ( fl4Tolerance , Result . HitDistance ) ) ) ;
if ( bInside | | bNearby )
{
//We can see a rain volume, but it's farther than 180 units away, only use far effect.
if ( ! bInside & & SubFloat ( FindLowestSIMD3 ( Result . HitDistance ) , 0 ) > = m_flParticleInnerDist )
{
// Kill the inner rain if it's previously been in use
if ( m_pParticlePrecipInnerNear ! = NULL )
{
DestroyInnerParticlePrecip ( ) ;
}
// Update if we've already got systems, otherwise, create them.
if ( m_pParticlePrecipOuter ! = NULL )
{
m_pParticlePrecipOuter - > SetControlPoint ( 1 , vOffsetPos ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 3 , vDensity ) ;
}
else
{
DispatchOuterParticlePrecip ( pPlayer , vForward ) ;
}
}
else //We're close enough to use the near effect.
{
// Update if we've already got systems, otherwise, create them.
# ifdef MAPBASE
// The outer can now be suppressed without interfering with other functionality
if ( m_pParticlePrecipOuter ! = NULL )
{
m_pParticlePrecipOuter - > SetControlPoint ( 1 , vOffsetPos ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 3 , vDensity ) ;
}
if ( m_pParticlePrecipInnerNear ! = NULL & & m_pParticlePrecipInnerFar ! = NULL )
{
m_pParticlePrecipInnerNear - > SetControlPoint ( 1 , vOffsetPosNear ) ;
m_pParticlePrecipInnerFar - > SetControlPoint ( 1 , vOffsetPosFar ) ;
m_pParticlePrecipInnerNear - > SetControlPoint ( 3 , vDensity ) ;
m_pParticlePrecipInnerFar - > SetControlPoint ( 3 , vDensity ) ;
}
# else
if ( m_pParticlePrecipInnerNear ! = NULL & & m_pParticlePrecipInnerFar ! = NULL & & m_pParticlePrecipOuter ! = NULL )
{
m_pParticlePrecipOuter - > SetControlPoint ( 1 , vOffsetPos ) ;
m_pParticlePrecipInnerNear - > SetControlPoint ( 1 , vOffsetPosNear ) ;
m_pParticlePrecipInnerFar - > SetControlPoint ( 1 , vOffsetPosFar ) ;
m_pParticlePrecipInnerNear - > SetControlPoint ( 3 , vDensity ) ;
m_pParticlePrecipInnerFar - > SetControlPoint ( 3 , vDensity ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 3 , vDensity ) ;
}
# endif
else
{
DispatchInnerParticlePrecip ( pPlayer , vForward ) ;
}
}
}
else // No rain in the area, kill any leftover systems.
{
DestroyInnerParticlePrecip ( ) ;
DestroyOuterParticlePrecip ( ) ;
}
}
}
void CClient_Precipitation : : InitializeParticlePrecip ( void )
{
//Set up which type of precipitation particle we'll use
if ( m_nPrecipType = = PRECIPITATION_TYPE_PARTICLEASH )
{
m_pParticleInnerNearDef = " ash " ;
m_pParticleInnerFarDef = " ash " ;
m_pParticleOuterDef = " ash_outer " ;
m_flParticleInnerDist = 280.0 ;
}
else if ( m_nPrecipType = = PRECIPITATION_TYPE_PARTICLESNOW )
{
m_pParticleInnerNearDef = " snow " ;
m_pParticleInnerFarDef = " snow " ;
m_pParticleOuterDef = " snow_outer " ;
m_flParticleInnerDist = 280.0 ;
}
else if ( m_nPrecipType = = PRECIPITATION_TYPE_PARTICLERAINSTORM )
{
m_pParticleInnerNearDef = " rain_storm " ;
m_pParticleInnerFarDef = " rain_storm_screen " ;
m_pParticleOuterDef = " rain_storm_outer " ;
m_flParticleInnerDist = 0.0 ;
}
else //default to rain
{
m_pParticleInnerNearDef = " rain " ;
m_pParticleInnerFarDef = " rain " ;
m_pParticleOuterDef = " rain_outer " ;
m_flParticleInnerDist = 180.0 ;
}
Assert ( m_pParticleInnerFarDef ! = NULL ) ;
//We'll want to change this if/when we add more raytrace environments.
g_RayTraceEnvironments . PurgeAndDeleteElements ( ) ;
// Sets up ray tracing environments for all func_precipitations and func_precipitation_blockers
RayTracingEnvironment * rtEnvRainEmission = new RayTracingEnvironment ( ) ;
g_RayTraceEnvironments . AddToTail ( rtEnvRainEmission ) ;
RayTracingEnvironment * rtEnvRainBlocker = new RayTracingEnvironment ( ) ;
g_RayTraceEnvironments . AddToTail ( rtEnvRainBlocker ) ;
rtEnvRainEmission - > Flags | = RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS ; // save some ram
rtEnvRainBlocker - > Flags | = RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS ; // save some ram
int nTriCount = 1 ;
for ( int i = 0 ; i < g_Precipitations . Count ( ) ; + + i )
{
CClient_Precipitation * volume = g_Precipitations [ i ] ;
vcollide_t * pCollide = modelinfo - > GetVCollide ( volume - > GetModelIndex ( ) ) ;
if ( ! pCollide | | pCollide - > solidCount < = 0 )
continue ;
Vector * outVerts ;
int vertCount = physcollision - > CreateDebugMesh ( pCollide - > solids [ 0 ] , & outVerts ) ;
if ( vertCount )
{
for ( int j = 0 ; j < vertCount ; j + = 3 )
{
rtEnvRainEmission - > AddTriangle ( nTriCount + + , outVerts [ j ] , outVerts [ j + 1 ] , outVerts [ j + 2 ] , Vector ( 1 , 1 , 1 ) ) ;
}
}
physcollision - > DestroyDebugMesh ( vertCount , outVerts ) ;
}
rtEnvRainEmission - > SetupAccelerationStructure ( ) ;
m_bParticlePrecipInitialized = true ;
}
void CClient_Precipitation : : DestroyInnerParticlePrecip ( void )
{
if ( m_pParticlePrecipInnerFar ! = NULL )
{
m_pParticlePrecipInnerFar - > StopEmission ( ) ;
m_pParticlePrecipInnerFar = NULL ;
}
if ( m_pParticlePrecipInnerNear ! = NULL )
{
m_pParticlePrecipInnerNear - > StopEmission ( ) ;
m_pParticlePrecipInnerNear = NULL ;
}
}
void CClient_Precipitation : : DestroyOuterParticlePrecip ( void )
{
if ( m_pParticlePrecipOuter ! = NULL )
{
m_pParticlePrecipOuter - > StopEmission ( ) ;
m_pParticlePrecipOuter = NULL ;
}
}
void CClient_Precipitation : : DispatchOuterParticlePrecip ( C_BasePlayer * pPlayer , Vector vForward )
{
DestroyOuterParticlePrecip ( ) ;
# ifdef MAPBASE
if ( m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER )
return ;
# endif
Vector vDensity = Vector ( r_RainParticleDensity . GetFloat ( ) , 0 , 0 ) * m_flDensity ;
Vector vPlayerPos = pPlayer - > EyePosition ( ) ;
m_pParticlePrecipOuter = ParticleProp ( ) - > Create ( m_pParticleOuterDef , PATTACH_ABSORIGIN_FOLLOW ) ;
m_pParticlePrecipOuter - > SetControlPointEntity ( 2 , pPlayer ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 1 , vPlayerPos + Vector ( 0 , 0 , 180 ) ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 3 , vDensity ) ;
}
void CClient_Precipitation : : DispatchInnerParticlePrecip ( C_BasePlayer * pPlayer , Vector vForward )
{
DestroyInnerParticlePrecip ( ) ;
DestroyOuterParticlePrecip ( ) ;
Vector vPlayerPos = pPlayer - > EyePosition ( ) ;
Vector vOffsetPos = vPlayerPos + Vector ( 0 , 0 , 180 ) ;
Vector vOffsetPosNear = vPlayerPos + Vector ( 0 , 0 , 180 ) + ( vForward * 32 ) ;
Vector vOffsetPosFar = vPlayerPos + Vector ( 0 , 0 , 180 ) + ( vForward * m_flParticleInnerDist ) ; // 100.0
Vector vDensity = Vector ( r_RainParticleDensity . GetFloat ( ) , 0 , 0 ) * m_flDensity ;
2021-10-09 23:05:28 +03:00
# ifdef MAPBASE
if ( m_spawnflags & SF_PRECIP_PARTICLE_CLAMP )
{
ClampParticlePosition ( vPlayerPos , vOffsetPos , vOffsetPosNear , vOffsetPosFar ) ;
}
# endif
2020-11-26 05:26:55 +03:00
# ifdef MAPBASE
if ( ! ( m_spawnflags & SF_PRECIP_PARTICLE_NO_OUTER ) )
# endif
{
m_pParticlePrecipOuter = ParticleProp ( ) - > Create ( m_pParticleOuterDef , PATTACH_ABSORIGIN_FOLLOW ) ;
m_pParticlePrecipOuter - > SetControlPointEntity ( 2 , pPlayer ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 1 , vOffsetPos ) ;
m_pParticlePrecipOuter - > SetControlPoint ( 3 , vDensity ) ;
}
m_pParticlePrecipInnerNear = ParticleProp ( ) - > Create ( m_pParticleInnerNearDef , PATTACH_ABSORIGIN_FOLLOW ) ;
m_pParticlePrecipInnerFar = ParticleProp ( ) - > Create ( m_pParticleInnerFarDef , PATTACH_ABSORIGIN_FOLLOW ) ;
m_pParticlePrecipInnerNear - > SetControlPointEntity ( 2 , pPlayer ) ;
m_pParticlePrecipInnerFar - > SetControlPointEntity ( 2 , pPlayer ) ;
m_pParticlePrecipInnerNear - > SetControlPoint ( 1 , vOffsetPosNear ) ;
m_pParticlePrecipInnerFar - > SetControlPoint ( 1 , vOffsetPosFar ) ;
m_pParticlePrecipInnerNear - > SetControlPoint ( 3 , vDensity ) ;
m_pParticlePrecipInnerFar - > SetControlPoint ( 3 , vDensity ) ;
}
2013-12-03 07:31:46 +04:00
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 < CClient_Precipitation > 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)),
2019-08-31 22:28:20 +03:00
# ifdef MAPBASE
RecvPropFloat ( RECVINFO ( m_windRadius ) ) ,
RecvPropFloat ( RECVINFO ( m_windRadiusInner ) ) ,
RecvPropVector ( RECVINFO ( m_location ) ) ,
2020-06-21 18:10:56 +03:00
RecvPropFloat ( RECVINFO ( m_flTreeSwayScale ) ) ,
2019-08-31 22:28:20 +03:00
# endif
2013-12-03 07:31:46 +04:00
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 < CEmberEmitter > 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 > 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 < CEmberEmitter > 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 ;
2019-08-31 22:28:20 +03:00
# ifdef MAPBASE
Vector vecWindVelocity = GetWindspeedAtLocation ( pParticle - > m_Pos ) ;
# else
2013-12-03 07:31:46 +04:00
Vector vecWindVelocity ;
GetWindspeedAtTime ( gpGlobals - > curtime , vecWindVelocity ) ;
2019-08-31 22:28:20 +03:00
# endif
2013-12-03 07:31:46 +04:00
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 < SnowFallEffect > 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 < SnowFall_t > 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 < C_BaseEntity * > ( 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 ;
}
}