Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2670 lines
82 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "c_effects.h"
  10. #include "c_tracer.h"
  11. #include "view.h"
  12. #include "particles_simple.h"
  13. #include "env_wind_shared.h"
  14. #include "engine/IEngineTrace.h"
  15. #include "engine/ivmodelinfo.h"
  16. #include "precipitation_shared.h"
  17. #include "fx_water.h"
  18. #include "c_world.h"
  19. #include "iviewrender.h"
  20. #include "engine/ivdebugoverlay.h"
  21. #include "precache_register.h"
  22. #include "collisionutils.h"
  23. #include "tier0/vprof.h"
  24. #include "viewrender.h"
  25. #include "raytrace.h"
  26. #ifdef INFESTED_DLL
  27. #include "c_asw_player.h"
  28. #include "c_asw_marine.h"
  29. #include "asw_input.h"
  30. #endif
  31. #ifdef CSTRIKE15
  32. #include "c_cs_player.h"
  33. #endif
  34. // memdbgon must be the last include file in a .cpp file!!!
  35. #include "tier0/memdbgon.h"
  36. ConVar cl_winddir ( "cl_winddir", "0", FCVAR_CHEAT, "Weather effects wind direction angle" );
  37. ConVar cl_windspeed ( "cl_windspeed", "0", FCVAR_CHEAT, "Weather effects wind speed scalar" );
  38. Vector g_vSplashColor( 0.5, 0.5, 0.5 );
  39. float g_flSplashScale = 0.15;
  40. float g_flSplashLifetime = 0.5f;
  41. float g_flSplashAlpha = 0.3f;
  42. ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // N% chance of a rain particle making a splash.
  43. ConVar r_RainParticleDensity( "r_RainParticleDensity", "1", FCVAR_NONE, "Density of Particle Rain 0-1" );
  44. ConVar r_RainAllowInSplitScreen( "r_RainAllowInSplitScreen", "0", FCVAR_NONE, "Allows rain in splitscreen" );
  45. float GUST_INTERVAL_MIN = 1;
  46. float GUST_INTERVAL_MAX = 2;
  47. float GUST_LIFETIME_MIN = 1;
  48. float GUST_LIFETIME_MAX = 3;
  49. float MIN_SCREENSPACE_RAIN_WIDTH = 1;
  50. ConVar r_RainHack( "r_RainHack", "0", FCVAR_CHEAT );
  51. ConVar r_RainRadius( "r_RainRadius", "1500", FCVAR_CHEAT );
  52. ConVar r_RainSideVel( "r_RainSideVel", "130", FCVAR_CHEAT, "How much sideways velocity rain gets." );
  53. // Performance optimization by Certain Affinity
  54. // calling IsInAir() for 800 particles was taking 4 ms
  55. ConVar r_RainCheck( "r_RainCheck", "0", FCVAR_CHEAT, "Enable/disable IsInAir() check for rain drops?" );
  56. ConVar r_RainSimulate( "r_RainSimulate", "1", FCVAR_CHEAT, "Enable/disable rain simulation." );
  57. ConVar r_DrawRain( "r_DrawRain", "1", FCVAR_CHEAT, "Enable/disable rain rendering." );
  58. ConVar r_RainProfile( "r_RainProfile", "0", FCVAR_CHEAT, "Enable/disable rain profiling." );
  59. ConVar r_RainDebugDuration( "r_RainDebugDuration", "0", FCVAR_CHEAT, "Shows rain tracelines for this many seconds (0 disables)" );
  60. //Precahce the effects
  61. PRECACHE_REGISTER_BEGIN( GLOBAL, PrecachePrecipitation )
  62. #ifndef PORTAL2
  63. PRECACHE( MATERIAL, "particle/rain" )
  64. PRECACHE( MATERIAL, "particle/snow" )
  65. PRECACHE( PARTICLE_SYSTEM, "rain_storm" )
  66. PRECACHE( PARTICLE_SYSTEM, "rain_storm_screen" )
  67. PRECACHE( PARTICLE_SYSTEM, "rain_storm_outer" )
  68. PRECACHE( PARTICLE_SYSTEM, "rain" )
  69. PRECACHE( PARTICLE_SYSTEM, "rain_outer" )
  70. PRECACHE( PARTICLE_SYSTEM, "ash" )
  71. PRECACHE( PARTICLE_SYSTEM, "ash_outer" )
  72. #ifdef INFESTED_DLL
  73. PRECACHE( PARTICLE_SYSTEM, "asw_snow" )
  74. PRECACHE( PARTICLE_SYSTEM, "asw_snow_outer" )
  75. #else
  76. PRECACHE( PARTICLE_SYSTEM, "snow" )
  77. PRECACHE( PARTICLE_SYSTEM, "snow_outer" )
  78. #endif
  79. #endif // !PORTAL2
  80. PRECACHE_REGISTER_END()
  81. CUtlVector< RayTracingEnvironment* > g_RayTraceEnvironments;
  82. //-----------------------------------------------------------------------------
  83. // Precipitation blocker entity
  84. //-----------------------------------------------------------------------------
  85. // Just receive the normal data table stuff
  86. IMPLEMENT_CLIENTCLASS_DT(C_PrecipitationBlocker, DT_PrecipitationBlocker, CPrecipitationBlocker)
  87. END_RECV_TABLE()
  88. static CUtlVector< C_PrecipitationBlocker * > g_PrecipitationBlockers;
  89. C_PrecipitationBlocker::C_PrecipitationBlocker()
  90. {
  91. g_PrecipitationBlockers.AddToTail( this );
  92. }
  93. C_PrecipitationBlocker::~C_PrecipitationBlocker()
  94. {
  95. g_PrecipitationBlockers.FindAndRemove( this );
  96. }
  97. bool ParticleIsBlocked( const Vector &end, const Vector &start )
  98. {
  99. for ( int i=0; i<g_PrecipitationBlockers.Count(); ++i )
  100. {
  101. C_PrecipitationBlocker *blocker = g_PrecipitationBlockers[i];
  102. if ( blocker->CollisionProp()->IsPointInBounds( end ) )
  103. {
  104. return true;
  105. }
  106. }
  107. return false;
  108. }
  109. // Just receive the normal data table stuff
  110. IMPLEMENT_CLIENTCLASS_DT(CClient_Precipitation, DT_Precipitation, CPrecipitation)
  111. RecvPropInt( RECVINFO( m_nPrecipType ) ),
  112. #ifdef INFESTED_DLL
  113. RecvPropInt( RECVINFO( m_nSnowDustAmount ) ),
  114. #endif
  115. END_RECV_TABLE()
  116. static ConVar r_SnowEnable( "r_SnowEnable", "1", FCVAR_CHEAT, "Snow Enable" );
  117. static ConVar r_SnowParticles( "r_SnowParticles", "500", FCVAR_CHEAT, "Snow." );
  118. static ConVar r_SnowInsideRadius( "r_SnowInsideRadius", "256", FCVAR_CHEAT, "Snow." );
  119. static ConVar r_SnowOutsideRadius( "r_SnowOutsideRadius", "1024", FCVAR_CHEAT, "Snow." );
  120. static ConVar r_SnowSpeedScale( "r_SnowSpeedScale", "1", FCVAR_CHEAT, "Snow." );
  121. static ConVar r_SnowPosScale( "r_SnowPosScale", "1", FCVAR_CHEAT, "Snow." );
  122. static ConVar r_SnowFallSpeed( "r_SnowFallSpeed", "1.5", FCVAR_CHEAT, "Snow fall speed scale." );
  123. static ConVar r_SnowWindScale( "r_SnowWindScale", "0.0035", FCVAR_CHEAT, "Snow." );
  124. static ConVar r_SnowDebugBox( "r_SnowDebugBox", "0", FCVAR_CHEAT, "Snow Debug Boxes." );
  125. static ConVar r_SnowZoomOffset( "r_SnowZoomOffset", "384.0f", FCVAR_CHEAT, "Snow." );
  126. static ConVar r_SnowZoomRadius( "r_SnowZoomRadius", "512.0f", FCVAR_CHEAT, "Snow." );
  127. static ConVar r_SnowStartAlpha( "r_SnowStartAlpha", "25", FCVAR_CHEAT, "Snow." );
  128. static ConVar r_SnowEndAlpha( "r_SnowEndAlpha", "255", FCVAR_CHEAT, "Snow." );
  129. //static ConVar r_SnowColorRed( "r_SnowColorRed", "150", FCVAR_CHEAT, "Snow." );
  130. //static ConVar r_SnowColorGreen( "r_SnowColorGreen", "175", FCVAR_CHEAT, "Snow." );
  131. //static ConVar r_SnowColorBlue( "r_SnowColorBlue", "200", FCVAR_CHEAT, "Snow." );
  132. static ConVar r_SnowStartSize( "r_SnowStartSize", "1", FCVAR_CHEAT, "Snow." );
  133. static ConVar r_SnowEndSize( "r_SnowEndSize", "0", FCVAR_CHEAT, "Snow." );
  134. static ConVar r_SnowRayLength( "r_SnowRayLength", "8192.0f", FCVAR_CHEAT, "Snow." );
  135. static ConVar r_SnowRayRadius( "r_SnowRayRadius", "256", FCVAR_CHEAT, "Snow." );
  136. static ConVar r_SnowRayEnable( "r_SnowRayEnable", "1", FCVAR_CHEAT, "Snow." );
  137. void DrawPrecipitation()
  138. {
  139. for ( int i=0; i < g_Precipitations.Count(); i++ )
  140. {
  141. g_Precipitations[i]->Render();
  142. }
  143. }
  144. //-----------------------------------------------------------------------------
  145. // determines if a weather particle has hit something other than air
  146. //-----------------------------------------------------------------------------
  147. static bool IsInAir( const Vector& position )
  148. {
  149. int contents = enginetrace->GetPointContents( position );
  150. return (contents & CONTENTS_SOLID) == 0;
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Globals
  154. //-----------------------------------------------------------------------------
  155. ConVar CClient_Precipitation::s_raindensity( "r_raindensity","0.001", FCVAR_CHEAT);
  156. ConVar CClient_Precipitation::s_rainwidth( "r_rainwidth", "0.5", FCVAR_CHEAT );
  157. ConVar CClient_Precipitation::s_rainlength( "r_rainlength", "0.1f", FCVAR_CHEAT );
  158. ConVar CClient_Precipitation::s_rainspeed( "r_rainspeed", "600.0f", FCVAR_CHEAT );
  159. ConVar r_rainalpha( "r_rainalpha", "0.4", FCVAR_CHEAT );
  160. ConVar r_rainalphapow( "r_rainalphapow", "0.8", FCVAR_CHEAT );
  161. Vector CClient_Precipitation::s_WindVector; // Stores the wind speed vector
  162. void CClient_Precipitation::OnDataChanged( DataUpdateType_t updateType )
  163. {
  164. // Simulate every frame.
  165. if ( updateType == DATA_UPDATE_CREATED )
  166. {
  167. SetNextClientThink( CLIENT_THINK_ALWAYS );
  168. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  169. {
  170. SnowFallManagerCreate( this );
  171. }
  172. }
  173. m_flDensity = RemapVal( GetRenderAlpha(), 0, 100, 0, 1 );
  174. BaseClass::OnDataChanged( updateType );
  175. }
  176. void CClient_Precipitation::ClientThink()
  177. {
  178. Simulate( gpGlobals->frametime );
  179. }
  180. //-----------------------------------------------------------------------------
  181. //
  182. // Utility methods for the various simulation functions
  183. //
  184. //-----------------------------------------------------------------------------
  185. inline bool CClient_Precipitation::SimulateRain( CPrecipitationParticle* pParticle, float dt )
  186. {
  187. if (GetRemainingLifetime( pParticle ) < 0.0f)
  188. return false;
  189. Vector vOldPos = pParticle->m_Pos;
  190. // Update position
  191. VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity,
  192. pParticle->m_Pos );
  193. if (cl_windspeed.GetFloat() > 0) // determines if s_WindVector is zeroes
  194. {
  195. // wind blows rain around
  196. float drift = 5 / pParticle->m_Mass;
  197. for ( int i = 0 ; i < 2 ; i++ ) // X and Y components
  198. {
  199. float vel = pParticle->m_Velocity[i];
  200. float wind = s_WindVector[i];
  201. if ( vel < wind )
  202. {
  203. vel = MIN( vel+drift, wind);
  204. }
  205. else if ( vel > wind )
  206. {
  207. vel = MAX( vel-drift, wind);
  208. }
  209. pParticle->m_Velocity[i] = vel;
  210. }
  211. }
  212. // Left4Dead does not use rain splashes on water surfaces
  213. // This change could allow rain into some solids, but the code
  214. // already performed a ray-test to calculate the particle lifetime,
  215. // so it should still be blocked by normal bsp surfaces.
  216. if (r_RainCheck.GetInt() != 0)
  217. {
  218. // No longer in the air? punt.
  219. if ( !IsInAir( pParticle->m_Pos ) )
  220. {
  221. // Possibly make a splash if we hit a water surface and it's in front of the view.
  222. if ( m_Splashes.Count() < 20 )
  223. {
  224. if ( RandomInt( 0, 100 ) < r_RainSplashPercentage.GetInt() )
  225. {
  226. trace_t trace;
  227. UTIL_TraceLine(vOldPos, pParticle->m_Pos, MASK_WATER, NULL, COLLISION_GROUP_NONE, &trace);
  228. if( trace.fraction < 1 )
  229. {
  230. m_Splashes.AddToTail( trace.endpos );
  231. }
  232. }
  233. }
  234. // Tell the framework it's time to remove the particle from the list
  235. return false;
  236. }
  237. }
  238. // We still want this particle
  239. return true;
  240. }
  241. inline bool CClient_Precipitation::SimulateSnow( CPrecipitationParticle* pParticle, float dt )
  242. {
  243. if ( IsInAir( pParticle->m_Pos ) )
  244. {
  245. // Update position
  246. VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity,
  247. pParticle->m_Pos );
  248. // wind blows rain around
  249. for ( int i = 0 ; i < 2 ; i++ )
  250. {
  251. if ( pParticle->m_Velocity[i] < s_WindVector[i] )
  252. {
  253. pParticle->m_Velocity[i] += ( 5.0f / pParticle->m_Mass );
  254. // accelerating flakes get a trail
  255. pParticle->m_Ramp = 0.5f;
  256. // clamp
  257. if ( pParticle->m_Velocity[i] > s_WindVector[i] )
  258. pParticle->m_Velocity[i] = s_WindVector[i];
  259. }
  260. else if (pParticle->m_Velocity[i] > s_WindVector[i] )
  261. {
  262. pParticle->m_Velocity[i] -= ( 5.0f / pParticle->m_Mass );
  263. // accelerating flakes get a trail
  264. pParticle->m_Ramp = 0.5f;
  265. // clamp.
  266. if ( pParticle->m_Velocity[i] < s_WindVector[i] )
  267. pParticle->m_Velocity[i] = s_WindVector[i];
  268. }
  269. }
  270. return true;
  271. }
  272. // Kill the particle immediately!
  273. return false;
  274. }
  275. void CClient_Precipitation::Simulate( float dt )
  276. {
  277. if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAIN ||
  278. m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH ||
  279. m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM ||
  280. m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW )
  281. {
  282. CreateParticlePrecip();
  283. return;
  284. }
  285. // NOTE: When client-side prechaching works, we need to remove this
  286. Precache();
  287. m_flHalfScreenWidth = (float)ScreenWidth() / 2;
  288. // Our sim methods needs dt and wind vector
  289. if ( dt )
  290. {
  291. ComputeWindVector( );
  292. }
  293. if ( m_nPrecipType == PRECIPITATION_TYPE_ASH )
  294. {
  295. CreateAshParticle();
  296. return;
  297. }
  298. // The snow fall manager handles the simulation.
  299. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  300. return;
  301. // calculate the max amount of time it will take this flake to fall.
  302. // This works if we assume the wind doesn't have a z component
  303. if ( r_RainHack.GetInt() )
  304. m_Lifetime = (GetClientWorldEntity()->m_WorldMaxs[2] - GetClientWorldEntity()->m_WorldMins[2]) / m_Speed;
  305. else
  306. m_Lifetime = (WorldAlignMaxs()[2] - WorldAlignMins()[2]) / m_Speed;
  307. if ( !r_RainSimulate.GetInt() )
  308. return;
  309. CFastTimer timer;
  310. timer.Start();
  311. // Emit new particles
  312. EmitParticles( dt );
  313. // Simulate all the particles.
  314. int iNext;
  315. if ( m_nPrecipType == PRECIPITATION_TYPE_RAIN )
  316. {
  317. for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext )
  318. {
  319. iNext = m_Particles.Next( i );
  320. if ( !SimulateRain( &m_Particles[i], dt ) )
  321. m_Particles.Remove( i );
  322. }
  323. }
  324. else if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW )
  325. {
  326. for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext )
  327. {
  328. iNext = m_Particles.Next( i );
  329. if ( !SimulateSnow( &m_Particles[i], dt ) )
  330. m_Particles.Remove( i );
  331. }
  332. }
  333. if ( r_RainProfile.GetInt() )
  334. {
  335. timer.End();
  336. engine->Con_NPrintf( 15, "Rain simulation: %du (%d tracers)", timer.GetDuration().GetMicroseconds(), m_Particles.Count() );
  337. }
  338. }
  339. //-----------------------------------------------------------------------------
  340. // tracer rendering
  341. //-----------------------------------------------------------------------------
  342. inline void CClient_Precipitation::RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb )
  343. {
  344. float scale;
  345. Vector start, delta;
  346. if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAIN || m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH
  347. || m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM || m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW )
  348. return;
  349. if ( m_nPrecipType == PRECIPITATION_TYPE_ASH )
  350. return;
  351. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  352. return;
  353. // make streaks 0.1 seconds long, but prevent from going past end
  354. float lifetimeRemaining = GetRemainingLifetime( pParticle );
  355. if (lifetimeRemaining >= GetLength())
  356. scale = GetLength() * pParticle->m_Ramp;
  357. else
  358. scale = lifetimeRemaining * pParticle->m_Ramp;
  359. // NOTE: We need to do everything in screen space
  360. Vector3DMultiplyPosition( CurrentWorldToViewMatrix(), pParticle->m_Pos, start );
  361. if ( start.z > -1 )
  362. return;
  363. Vector3DMultiply( CurrentWorldToViewMatrix(), pParticle->m_Velocity, delta );
  364. // give a spiraling pattern to snow particles
  365. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW )
  366. {
  367. Vector spiral, camSpiral;
  368. float s, c;
  369. if ( pParticle->m_Mass > 1.0f )
  370. {
  371. SinCos( gpGlobals->curtime * M_PI * (1+pParticle->m_Mass * 0.1f) +
  372. pParticle->m_Mass * 5.0f, &s , &c );
  373. // only spiral particles with a mass > 1, so some fall straight down
  374. spiral[0] = 28 * c;
  375. spiral[1] = 28 * s;
  376. spiral[2] = 0.0f;
  377. Vector3DMultiply( CurrentWorldToViewMatrix(), spiral, camSpiral );
  378. // X and Y are measured in world space; need to convert to camera space
  379. VectorAdd( start, camSpiral, start );
  380. VectorAdd( delta, camSpiral, delta );
  381. }
  382. // shrink the trails on spiraling flakes.
  383. pParticle->m_Ramp = 0.3f;
  384. }
  385. delta[0] *= scale;
  386. delta[1] *= scale;
  387. delta[2] *= scale;
  388. // See c_tracer.* for this method
  389. float flAlpha = r_rainalpha.GetFloat();
  390. float flWidth = GetWidth();
  391. float flScreenSpaceWidth = flWidth * m_flHalfScreenWidth / -start.z;
  392. if ( flScreenSpaceWidth < MIN_SCREENSPACE_RAIN_WIDTH )
  393. {
  394. // Make the rain tracer at least the min size, but fade its alpha the smaller it gets.
  395. flAlpha *= flScreenSpaceWidth / MIN_SCREENSPACE_RAIN_WIDTH;
  396. flWidth = MIN_SCREENSPACE_RAIN_WIDTH * -start.z / m_flHalfScreenWidth;
  397. }
  398. flAlpha = pow( flAlpha, r_rainalphapow.GetFloat() );
  399. float flColor[4] = { 1, 1, 1, flAlpha };
  400. Tracer_Draw( &mb, start, delta, flWidth, flColor, 1 );
  401. }
  402. void CClient_Precipitation::CreateWaterSplashes()
  403. {
  404. for ( int i=0; i < m_Splashes.Count(); i++ )
  405. {
  406. Vector vSplash = m_Splashes[i];
  407. if ( CurrentViewForward().Dot( vSplash - CurrentViewOrigin() ) > 1 )
  408. {
  409. FX_WaterRipple( vSplash, g_flSplashScale, &g_vSplashColor, g_flSplashLifetime, g_flSplashAlpha );
  410. }
  411. }
  412. m_Splashes.Purge();
  413. }
  414. void CClient_Precipitation::Render()
  415. {
  416. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  417. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  418. if ( !r_DrawRain.GetInt() )
  419. return;
  420. if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAIN || m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH
  421. || m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM || m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW )
  422. return;
  423. // Don't render in monitors or in reflections or refractions.
  424. if ( CurrentViewID() == VIEW_MONITOR )
  425. return;
  426. if ( view->GetDrawFlags() & (DF_RENDER_REFLECTION | DF_RENDER_REFRACTION) )
  427. return;
  428. if ( m_nPrecipType == PRECIPITATION_TYPE_ASH )
  429. return;
  430. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  431. return;
  432. // Create any queued up water splashes.
  433. CreateWaterSplashes();
  434. CFastTimer timer;
  435. timer.Start();
  436. CMatRenderContextPtr pRenderContext( materials );
  437. // We want to do our calculations in view space.
  438. VMatrix tempView;
  439. pRenderContext->GetMatrix( MATERIAL_VIEW, &tempView );
  440. pRenderContext->MatrixMode( MATERIAL_VIEW );
  441. pRenderContext->LoadIdentity();
  442. // Force the user clip planes to use the old view matrix
  443. pRenderContext->EnableUserClipTransformOverride( true );
  444. pRenderContext->UserClipTransform( tempView );
  445. // Draw all the rain tracers.
  446. pRenderContext->Bind( m_MatHandle );
  447. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  448. if ( pMesh )
  449. {
  450. CMeshBuilder mb;
  451. mb.Begin( pMesh, MATERIAL_QUADS, m_Particles.Count() );
  452. for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=m_Particles.Next( i ) )
  453. {
  454. CPrecipitationParticle *p = &m_Particles[i];
  455. if ( p->m_nSplitScreenPlayerSlot != nSlot )
  456. continue;
  457. RenderParticle( p, mb );
  458. }
  459. mb.End( false, true );
  460. }
  461. pRenderContext->EnableUserClipTransformOverride( false );
  462. pRenderContext->MatrixMode( MATERIAL_VIEW );
  463. pRenderContext->LoadMatrix( tempView );
  464. if ( r_RainProfile.GetInt() )
  465. {
  466. timer.End();
  467. engine->Con_NPrintf( 16, "Rain render : %du", timer.GetDuration().GetMicroseconds() );
  468. }
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Constructor, destructor
  472. //-----------------------------------------------------------------------------
  473. CClient_Precipitation::CClient_Precipitation() : m_Remainder(0.0f)
  474. {
  475. m_nPrecipType = PRECIPITATION_TYPE_RAIN;
  476. m_MatHandle = INVALID_MATERIAL_HANDLE;
  477. m_flHalfScreenWidth = 1;
  478. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  479. {
  480. m_pParticlePrecipInnerNear[ i ] = NULL;
  481. m_pParticlePrecipInnerFar[ i ] = NULL;
  482. m_pParticlePrecipOuter[ i ] = NULL;
  483. m_bActiveParticlePrecipEmitter[ i ] = false;
  484. }
  485. g_Precipitations.AddToTail( this );
  486. }
  487. CClient_Precipitation::~CClient_Precipitation()
  488. {
  489. g_Precipitations.FindAndRemove( this );
  490. SnowFallManagerDestroy();
  491. }
  492. //-----------------------------------------------------------------------------
  493. // Precache data
  494. //-----------------------------------------------------------------------------
  495. #define SNOW_SPEED 80.0f
  496. #define RAIN_SPEED 425.0f
  497. #define RAIN_TRACER_WIDTH 0.35f
  498. #define SNOW_TRACER_WIDTH 0.7f
  499. void CClient_Precipitation::Precache( )
  500. {
  501. if ( !m_MatHandle )
  502. {
  503. // Compute precipitation emission speed
  504. switch( m_nPrecipType )
  505. {
  506. case PRECIPITATION_TYPE_SNOW:
  507. m_Speed = SNOW_SPEED;
  508. m_MatHandle = materials->FindMaterial( "particle/snow", TEXTURE_GROUP_CLIENT_EFFECTS );
  509. m_InitialRamp = 0.6f;
  510. m_Width = SNOW_TRACER_WIDTH;
  511. break;
  512. case PRECIPITATION_TYPE_RAIN:
  513. Assert( m_nPrecipType == PRECIPITATION_TYPE_RAIN );
  514. m_Speed = RAIN_SPEED;
  515. m_MatHandle = materials->FindMaterial( "particle/rain", TEXTURE_GROUP_CLIENT_EFFECTS );
  516. m_InitialRamp = 1.0f;
  517. m_Color[3] = 1.0f; // make translucent
  518. m_Width = RAIN_TRACER_WIDTH;
  519. break;
  520. default:
  521. m_InitialRamp = 1.0f;
  522. m_Color[3] = 1.0f; // make translucent
  523. break;
  524. }
  525. // Store off the color
  526. m_Color[0] = 1.0f;
  527. m_Color[1] = 1.0f;
  528. m_Color[2] = 1.0f;
  529. }
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Gets the tracer width and speed
  533. //-----------------------------------------------------------------------------
  534. inline float CClient_Precipitation::GetWidth() const
  535. {
  536. // return m_Width;
  537. return s_rainwidth.GetFloat();
  538. }
  539. inline float CClient_Precipitation::GetLength() const
  540. {
  541. // return m_Length;
  542. return s_rainlength.GetFloat();
  543. }
  544. inline float CClient_Precipitation::GetSpeed() const
  545. {
  546. // return m_Speed;
  547. return s_rainspeed.GetFloat();
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Gets the remaining lifetime of the particle
  551. //-----------------------------------------------------------------------------
  552. inline float CClient_Precipitation::GetRemainingLifetime( CPrecipitationParticle* pParticle ) const
  553. {
  554. float timeSinceSpawn = gpGlobals->curtime - pParticle->m_SpawnTime;
  555. return pParticle->m_flMaxLifetime - timeSinceSpawn; // TERROR: use per-particle lifetime not dependent on func_precipitation lower bound
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Creates a particle
  559. //-----------------------------------------------------------------------------
  560. inline CPrecipitationParticle* CClient_Precipitation::CreateParticle()
  561. {
  562. int i = m_Particles.AddToTail();
  563. CPrecipitationParticle* pParticle = &m_Particles[i];
  564. pParticle->m_SpawnTime = gpGlobals->curtime;
  565. pParticle->m_Ramp = m_InitialRamp;
  566. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  567. pParticle->m_nSplitScreenPlayerSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  568. return pParticle;
  569. }
  570. //-----------------------------------------------------------------------------
  571. // Compute the emission area
  572. //-----------------------------------------------------------------------------
  573. bool CClient_Precipitation::ComputeEmissionArea( Vector& origin, Vector2D& size, C_BaseCombatCharacter *pCharacter )
  574. {
  575. // calculate a volume around the player to snow in. Intersect this big magic
  576. // box around the player with the volume of the current environmental ent.
  577. if ( !pCharacter )
  578. return false;
  579. // FIXME: Compute the precipitation area based on computational power
  580. float emissionSize = r_RainRadius.GetFloat(); // size of box to emit particles in
  581. Vector vMins = WorldAlignMins();
  582. Vector vMaxs = WorldAlignMaxs();
  583. if ( r_RainHack.GetInt() )
  584. {
  585. vMins = GetClientWorldEntity()->m_WorldMins;
  586. vMaxs = GetClientWorldEntity()->m_WorldMaxs;
  587. }
  588. // Determine how much time it'll take a falling particle to hit the player
  589. float emissionHeight = MIN( vMaxs[2], pCharacter->GetAbsOrigin()[2] + 512 );
  590. float distToFall = emissionHeight - pCharacter->GetAbsOrigin()[2];
  591. float fallTime = distToFall / GetSpeed();
  592. // Based on the windspeed, figure out the center point of the emission
  593. Vector2D center;
  594. center[0] = pCharacter->GetAbsOrigin()[0] - fallTime * s_WindVector[0];
  595. center[1] = pCharacter->GetAbsOrigin()[1] - fallTime * s_WindVector[1];
  596. Vector2D lobound, hibound;
  597. lobound[0] = center[0] - emissionSize * 0.5f;
  598. lobound[1] = center[1] - emissionSize * 0.5f;
  599. hibound[0] = lobound[0] + emissionSize;
  600. hibound[1] = lobound[1] + emissionSize;
  601. // Cull non-intersecting.
  602. if ( ( vMaxs[0] < lobound[0] ) || ( vMaxs[1] < lobound[1] ) ||
  603. ( vMins[0] > hibound[0] ) || ( vMins[1] > hibound[1] ) )
  604. return false;
  605. origin[0] = MAX( vMins[0], lobound[0] );
  606. origin[1] = MAX( vMins[1], lobound[1] );
  607. origin[2] = emissionHeight;
  608. hibound[0] = MIN( vMaxs[0], hibound[0] );
  609. hibound[1] = MIN( vMaxs[1], hibound[1] );
  610. size[0] = hibound[0] - origin[0];
  611. size[1] = hibound[1] - origin[1];
  612. return true;
  613. }
  614. //-----------------------------------------------------------------------------
  615. // Purpose:
  616. // Input : *pDebugName -
  617. // Output : AshDebrisEffect*
  618. //-----------------------------------------------------------------------------
  619. AshDebrisEffect* AshDebrisEffect::Create( const char *pDebugName )
  620. {
  621. return new AshDebrisEffect( pDebugName );
  622. }
  623. //-----------------------------------------------------------------------------
  624. // Purpose:
  625. // Input : *pParticle -
  626. // timeDelta -
  627. // Output : float
  628. //-----------------------------------------------------------------------------
  629. float AshDebrisEffect::UpdateAlpha( const SimpleParticle *pParticle )
  630. {
  631. return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) );
  632. }
  633. #define ASH_PARTICLE_NOISE 0x4
  634. float AshDebrisEffect::UpdateRoll( SimpleParticle *pParticle, float timeDelta )
  635. {
  636. float flRoll = CSimpleEmitter::UpdateRoll(pParticle, timeDelta );
  637. if ( pParticle->m_iFlags & ASH_PARTICLE_NOISE )
  638. {
  639. Vector vTempEntVel = pParticle->m_vecVelocity;
  640. float fastFreq = gpGlobals->curtime * 1.5;
  641. float s, c;
  642. SinCos( fastFreq, &s, &c );
  643. pParticle->m_Pos = ( pParticle->m_Pos + Vector(
  644. vTempEntVel[0] * timeDelta * s,
  645. vTempEntVel[1] * timeDelta * s, 0 ) );
  646. }
  647. return flRoll;
  648. }
  649. void CClient_Precipitation::CreateAshParticle( void )
  650. {
  651. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  652. {
  653. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  654. // Make sure the emitter is setup
  655. if ( m_Ash[ hh ].m_pAshEmitter == NULL )
  656. {
  657. if ( ( m_Ash[ hh ].m_pAshEmitter = AshDebrisEffect::Create( "ashtray" ) ) == NULL )
  658. continue;
  659. m_Ash[ hh ].m_tAshParticleTimer.Init( 192 );
  660. m_Ash[ hh ].m_tAshParticleTraceTimer.Init( 15 );
  661. m_Ash[ hh ].m_bActiveAshEmitter = false;
  662. m_Ash[ hh ].m_iAshCount = 0;
  663. m_Ash[ hh ].m_pAshEmitter->SetShouldDrawForSplitScreenUser( hh );
  664. }
  665. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  666. if ( pPlayer == NULL )
  667. continue;
  668. Vector vForward;
  669. pPlayer->GetVectors( &vForward, NULL, NULL );
  670. vForward.z = 0.0f;
  671. float curTime = gpGlobals->frametime;
  672. Vector vPushOrigin;
  673. Vector absmins = WorldAlignMins();
  674. Vector absmaxs = WorldAlignMaxs();
  675. //15 Traces a second.
  676. while ( m_Ash[ hh ].m_tAshParticleTraceTimer.NextEvent( curTime ) )
  677. {
  678. trace_t tr;
  679. Vector vTraceStart = pPlayer->EyePosition();
  680. Vector vTraceEnd = pPlayer->EyePosition() + vForward * MAX_TRACE_LENGTH;
  681. UTIL_TraceLine( vTraceStart, vTraceEnd, MASK_SHOT_HULL & (~CONTENTS_GRATE), pPlayer, COLLISION_GROUP_NONE, &tr );
  682. //UTIL_TraceModel( vTraceStart, vTraceEnd, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), this, COLLISION_GROUP_NONE, &tr );
  683. //debugoverlay->AddLineOverlay( vTraceStart, tr.endpos, 255, 0, 0, 0, 0.2 );
  684. if ( tr.fraction != 1.0f )
  685. {
  686. trace_t tr2;
  687. UTIL_TraceModel( vTraceStart, tr.endpos, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), this, COLLISION_GROUP_NONE, &tr2 );
  688. if ( tr2.m_pEnt == this )
  689. {
  690. m_Ash[ hh ].m_bActiveAshEmitter = true;
  691. if ( tr2.startsolid == false )
  692. {
  693. m_Ash[ hh ].m_vAshSpawnOrigin = tr2.endpos + vForward * 256;
  694. }
  695. else
  696. {
  697. m_Ash[ hh ].m_vAshSpawnOrigin = vTraceStart;
  698. }
  699. }
  700. else
  701. {
  702. m_Ash[ hh ].m_bActiveAshEmitter = false;
  703. }
  704. }
  705. }
  706. if ( m_Ash[ hh ].m_bActiveAshEmitter == false )
  707. continue;
  708. Vector vecVelocity = pPlayer->GetAbsVelocity();
  709. float flVelocity = VectorNormalize( vecVelocity );
  710. Vector offset = m_Ash[ hh ].m_vAshSpawnOrigin;
  711. m_Ash[ hh ].m_pAshEmitter->SetSortOrigin( offset );
  712. PMaterialHandle hMaterial[4];
  713. hMaterial[0] = ParticleMgr()->GetPMaterial( "effects/fleck_ash1" );
  714. hMaterial[1] = ParticleMgr()->GetPMaterial( "effects/fleck_ash2" );
  715. hMaterial[2] = ParticleMgr()->GetPMaterial( "effects/fleck_ash3" );
  716. hMaterial[3] = ParticleMgr()->GetPMaterial( "effects/ember_swirling001" );
  717. SimpleParticle *pParticle;
  718. Vector vSpawnOrigin = vec3_origin;
  719. if ( flVelocity > 0 )
  720. {
  721. vSpawnOrigin = ( vForward * 256 ) + ( vecVelocity * ( flVelocity * 2 ) );
  722. }
  723. // Add as many particles as we need
  724. while ( m_Ash[ hh ].m_tAshParticleTimer.NextEvent( curTime ) )
  725. {
  726. int iRandomAltitude = RandomInt( 0, 128 );
  727. offset = m_Ash[ hh ].m_vAshSpawnOrigin + vSpawnOrigin + RandomVector( -256, 256 );
  728. offset.z = m_Ash[ hh ].m_vAshSpawnOrigin.z + iRandomAltitude;
  729. if ( offset[0] > absmaxs[0]
  730. || offset[1] > absmaxs[1]
  731. || offset[2] > absmaxs[2]
  732. || offset[0] < absmins[0]
  733. || offset[1] < absmins[1]
  734. || offset[2] < absmins[2] )
  735. continue;
  736. m_Ash[ hh ].m_iAshCount++;
  737. bool bEmberTime = false;
  738. if ( m_Ash[ hh ].m_iAshCount >= 250 )
  739. {
  740. bEmberTime = true;
  741. m_Ash[ hh ].m_iAshCount = 0;
  742. }
  743. int iRandom = random->RandomInt(0,2);
  744. if ( bEmberTime == true )
  745. {
  746. offset = m_Ash[ hh ].m_vAshSpawnOrigin + (vForward * 256) + RandomVector( -128, 128 );
  747. offset.z = pPlayer->EyePosition().z + RandomFloat( -16, 64 );
  748. iRandom = 3;
  749. }
  750. pParticle = (SimpleParticle *)m_Ash[ hh ].m_pAshEmitter->AddParticle( sizeof(SimpleParticle), hMaterial[iRandom], offset );
  751. if (pParticle == NULL)
  752. continue;
  753. pParticle->m_flLifetime = 0.0f;
  754. pParticle->m_flDieTime = RemapVal( iRandomAltitude, 0, 128, 4, 8 );
  755. if ( bEmberTime == true )
  756. {
  757. Vector vGoal = pPlayer->EyePosition() + RandomVector( -64, 64 );
  758. Vector vDir = vGoal - offset;
  759. VectorNormalize( vDir );
  760. pParticle->m_vecVelocity = vDir * 75;
  761. pParticle->m_flDieTime = 2.5f;
  762. }
  763. else
  764. {
  765. pParticle->m_vecVelocity = Vector( RandomFloat( -20.0f, 20.0f ), RandomFloat( -20.0f, 20.0f ), RandomFloat( -10, -15 ) );
  766. }
  767. float color = random->RandomInt( 125, 225 );
  768. pParticle->m_uchColor[0] = color;
  769. pParticle->m_uchColor[1] = color;
  770. pParticle->m_uchColor[2] = color;
  771. pParticle->m_uchStartSize = 1;
  772. pParticle->m_uchEndSize = 1.5;
  773. pParticle->m_uchStartAlpha = 255;
  774. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  775. pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f );
  776. pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN;
  777. if ( random->RandomInt( 0, 10 ) <= 1 )
  778. {
  779. pParticle->m_iFlags |= ASH_PARTICLE_NOISE;
  780. }
  781. }
  782. }
  783. }
  784. void CClient_Precipitation::CreateParticlePrecip( void )
  785. {
  786. if ( !r_RainAllowInSplitScreen.GetBool() && engine->IsSplitScreenActive() )
  787. {
  788. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  789. {
  790. if ( m_pParticlePrecipOuter[hh] != NULL )
  791. {
  792. DestroyInnerParticlePrecip( hh );
  793. DestroyOuterParticlePrecip( hh );
  794. }
  795. }
  796. return;
  797. }
  798. if ( !m_bParticlePrecipInitialized )
  799. {
  800. InitializeParticlePrecip();
  801. }
  802. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  803. {
  804. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  805. #ifdef CSTRIKE15
  806. C_CSPlayer *pPlayer = GetLocalOrInEyeCSPlayer();
  807. #else
  808. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  809. #endif
  810. if ( pPlayer == NULL )
  811. continue;
  812. // Make sure the emitter is setup
  813. if ( !m_bActiveParticlePrecipEmitter[hh] )
  814. {
  815. //Update 8 times per second.
  816. m_tParticlePrecipTraceTimer[hh].Init( 8 );
  817. DestroyInnerParticlePrecip( hh );
  818. DestroyOuterParticlePrecip( hh );
  819. m_bActiveParticlePrecipEmitter[hh] = true;
  820. }
  821. UpdateParticlePrecip( pPlayer, hh );
  822. }
  823. }
  824. void CClient_Precipitation::UpdateParticlePrecip( C_BasePlayer *pPlayer, int nSlot )
  825. {
  826. if ( !pPlayer )
  827. return;
  828. Vector vForward;
  829. Vector vRight;
  830. #ifdef INFESTED_DLL
  831. vForward[PITCH] = 0;
  832. vForward[YAW] = ASWInput()->ASW_GetCameraPitch();
  833. vForward[ROLL] = -ASWInput()->ASW_GetCameraYaw();
  834. vForward.NormalizeInPlace();
  835. #else
  836. pPlayer->GetVectors( &vForward, &vRight, NULL );
  837. vForward.z = 0.0f;
  838. vForward.NormalizeInPlace();
  839. Vector vForward45Right = vForward + vRight;
  840. Vector vForward45Left = vForward - vRight;
  841. vForward45Right.NormalizeInPlace();
  842. vForward45Left.NormalizeInPlace();
  843. fltx4 TMax = ReplicateX4( 320.0f );
  844. SubFloat( TMax, 3 ) = FLT_MAX;
  845. #endif
  846. float curTime = gpGlobals->frametime;
  847. while ( m_tParticlePrecipTraceTimer[nSlot].NextEvent( curTime ) )
  848. {
  849. #ifdef INFESTED_DLL
  850. Vector vPlayerPos = MainViewOrigin( nSlot );
  851. Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 4 );
  852. Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 4 ) + ( vForward * 32 );
  853. Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 4 ) + ( vForward * 100 );
  854. Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), (float)m_nSnowDustAmount/100.0f, 0 ) * m_flDensity;
  855. RayTracingEnvironment *RtEnv = g_RayTraceEnvironments.Element( 0 );
  856. bool bInside = !engine->CullBox( RtEnv->m_MinBound, RtEnv->m_MaxBound );
  857. bool bNearby = false;
  858. #else
  859. Vector vPlayerPos = pPlayer->EyePosition();
  860. Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 180 );
  861. Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 32 );
  862. Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 100 );
  863. Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity;
  864. // Get the rain volume Ray Tracing Environment. Currently hard coded to 0, should have this lookup
  865. RayTracingEnvironment *RtEnv = g_RayTraceEnvironments.Element( 0 );
  866. // Our 4 Rays are forward, off to the left and right, and directly up.
  867. // Use the first three to determine if there's generally visible rain where we're looking.
  868. // The forth, straight up, tells us if we're standing inside a rain volume
  869. // (based on the normal that we hit or if we miss entirely)
  870. FourRays frRays;
  871. FourVectors fvDirection;
  872. fvDirection = FourVectors( vForward, vForward45Left, vForward45Right, Vector( 0, 0, 1 ) );
  873. frRays.direction = fvDirection;
  874. frRays.origin.DuplicateVector( vPlayerPos );
  875. RayTracingResult Result;
  876. RtEnv->Trace4Rays( frRays, Four_Zeros, TMax, &Result );
  877. i32x4 in4HitIds = LoadAlignedIntSIMD( Result.HitIds );
  878. fltx4 fl4HitIds = SignedIntConvertToFltSIMD ( in4HitIds );
  879. fltx4 fl4Tolerance = ReplicateX4( 300.0f );
  880. // ignore upwards test for tolerance, as we may be below an area which is raining, but with it not visible in front of us
  881. //SubFloat( fl4Tolerance, 3 ) = 0.0f;
  882. bool bInside = ( Result.HitIds[3] != -1 && Result.surface_normal.Vec( 3 ).z < 0.0f );
  883. bool bNearby = ( IsAnyTrue( CmpGeSIMD ( fl4HitIds, Four_Zeros ) ) && IsAnyTrue( CmpGeSIMD( fl4Tolerance, Result.HitDistance ) ) );
  884. #endif
  885. if ( bInside || bNearby )
  886. {
  887. #ifdef INFESTED_DLL
  888. //debugoverlay->AddBoxOverlay(vPlayerPos, Vector( -12, -12, -12 ), Vector( 12, 12, 12 ), QAngle( 0, 0, 0 ), 255, 0, 0, 32, 0.2f );
  889. //debugoverlay->AddBoxOverlay(vOffsetPosNear, Vector( -10, -10, -10 ), Vector( 10, 10, 10 ), QAngle( 0, 0, 0 ), 0, 255, 0, 32, 0.2f );
  890. //debugoverlay->AddBoxOverlay(vOffsetPosFar, Vector( -5, -5, -5 ), Vector( 5, 5, 5 ), QAngle( 0, 0, 0 ), 0, 0, 255, 32, 0.2f );
  891. // Update if we've already got systems, otherwise, create them.
  892. if ( m_pParticlePrecipInnerNear[nSlot] != NULL && m_pParticlePrecipInnerFar[nSlot] != NULL && m_pParticlePrecipOuter[nSlot] != NULL )
  893. {
  894. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 1, vOffsetPos );
  895. m_pParticlePrecipInnerNear[nSlot]->SetControlPoint( 1, vOffsetPosNear );
  896. m_pParticlePrecipInnerFar[nSlot]->SetControlPoint( 1, vOffsetPosFar );
  897. m_pParticlePrecipInnerNear[nSlot]->SetControlPoint( 3, vDensity );
  898. m_pParticlePrecipInnerFar[nSlot]->SetControlPoint( 3, vDensity );
  899. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 3, vDensity );
  900. }
  901. else
  902. {
  903. DispatchInnerParticlePrecip( nSlot, pPlayer, vForward );
  904. }
  905. #else
  906. //We can see a rain volume, but it's farther than 180 units away, only use far effect.
  907. if ( !bInside && SubFloat( FindLowestSIMD3( Result.HitDistance ), 0 ) >= m_flParticleInnerDist )
  908. {
  909. // Kill the inner rain if it's previously been in use
  910. if ( m_pParticlePrecipInnerNear[nSlot] != NULL )
  911. {
  912. DestroyInnerParticlePrecip( nSlot );
  913. }
  914. // Update if we've already got systems, otherwise, create them.
  915. if ( m_pParticlePrecipOuter[nSlot] != NULL )
  916. {
  917. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 1, vOffsetPos );
  918. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 3, vDensity );
  919. }
  920. else
  921. {
  922. DispatchOuterParticlePrecip( nSlot, pPlayer, vForward );
  923. }
  924. }
  925. else //We're close enough to use the near effect.
  926. {
  927. // Update if we've already got systems, otherwise, create them.
  928. if ( m_pParticlePrecipInnerNear[nSlot] != NULL && m_pParticlePrecipInnerFar[nSlot] != NULL && m_pParticlePrecipOuter[nSlot] != NULL )
  929. {
  930. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 1, vOffsetPos );
  931. m_pParticlePrecipInnerNear[nSlot]->SetControlPoint( 1, vOffsetPosNear );
  932. m_pParticlePrecipInnerFar[nSlot]->SetControlPoint( 1, vOffsetPosFar );
  933. m_pParticlePrecipInnerNear[nSlot]->SetControlPoint( 3, vDensity );
  934. m_pParticlePrecipInnerFar[nSlot]->SetControlPoint( 3, vDensity );
  935. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 3, vDensity );
  936. }
  937. else
  938. {
  939. DispatchInnerParticlePrecip( nSlot, pPlayer, vForward );
  940. }
  941. }
  942. #endif
  943. }
  944. else // No rain in the area, kill any leftover systems.
  945. {
  946. DestroyInnerParticlePrecip( nSlot );
  947. DestroyOuterParticlePrecip( nSlot );
  948. }
  949. }
  950. }
  951. void CClient_Precipitation::InitializeParticlePrecip( void )
  952. {
  953. //Set up which type of precipitation particle we'll use
  954. if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLEASH )
  955. {
  956. m_pParticleInnerNearDef = "ash";
  957. m_pParticleInnerFarDef = "ash";
  958. m_pParticleOuterDef = "ash_outer";
  959. m_flParticleInnerDist = 280.0;
  960. }
  961. else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLESNOW )
  962. {
  963. #ifdef INFESTED_DLL
  964. m_pParticleInnerNearDef = "asw_snow";
  965. m_pParticleInnerFarDef = "asw_snow";
  966. m_pParticleOuterDef = "asw_snow_outer";
  967. m_flParticleInnerDist = 240.0;
  968. #else
  969. m_pParticleInnerNearDef = "snow";
  970. m_pParticleInnerFarDef = "snow";
  971. m_pParticleOuterDef = "snow_outer";
  972. m_flParticleInnerDist = 280.0;
  973. #endif
  974. }
  975. else if ( m_nPrecipType == PRECIPITATION_TYPE_PARTICLERAINSTORM )
  976. {
  977. m_pParticleInnerNearDef = "rain_storm";
  978. m_pParticleInnerFarDef = "rain_storm_screen";
  979. m_pParticleOuterDef = "rain_storm_outer";
  980. m_flParticleInnerDist = 0.0;
  981. }
  982. else //default to rain
  983. {
  984. m_pParticleInnerNearDef = "rain";
  985. m_pParticleInnerFarDef = "rain";
  986. m_pParticleOuterDef = "rain_outer";
  987. m_flParticleInnerDist = 180.0;
  988. }
  989. Assert( m_pParticleInnerFarDef != NULL );
  990. //We'll want to change this if/when we add more raytrace environments.
  991. g_RayTraceEnvironments.PurgeAndDeleteElements();
  992. // Sets up ray tracing environments for all func_precipitations and func_precipitation_blockers
  993. RayTracingEnvironment *rtEnvRainEmission = new RayTracingEnvironment();
  994. g_RayTraceEnvironments.AddToTail( rtEnvRainEmission );
  995. RayTracingEnvironment *rtEnvRainBlocker = new RayTracingEnvironment();
  996. g_RayTraceEnvironments.AddToTail( rtEnvRainBlocker );
  997. rtEnvRainEmission->Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram
  998. rtEnvRainBlocker->Flags |= RTE_FLAGS_DONT_STORE_TRIANGLE_COLORS; // save some ram
  999. int nTriCount = 1;
  1000. for ( int i=0; i<g_Precipitations.Count(); ++i )
  1001. {
  1002. CClient_Precipitation *volume = g_Precipitations[i];
  1003. vcollide_t *pCollide = modelinfo->GetVCollide( volume->GetModelIndex() );
  1004. if ( !pCollide || pCollide->solidCount <= 0 )
  1005. continue;
  1006. Vector *outVerts;
  1007. int vertCount = g_pPhysicsCollision->CreateDebugMesh( pCollide->solids[0], &outVerts );
  1008. if ( vertCount )
  1009. {
  1010. for ( int j = 0; j < vertCount; j += 3 )
  1011. {
  1012. rtEnvRainEmission->AddTriangle( nTriCount++, outVerts[j], outVerts[j + 1], outVerts[j + 2], Vector( 1, 1, 1 ) );
  1013. }
  1014. }
  1015. physcollision->DestroyDebugMesh( vertCount, outVerts );
  1016. }
  1017. rtEnvRainEmission->SetupAccelerationStructure();
  1018. nTriCount = 1;
  1019. for ( int i=0; i<g_PrecipitationBlockers.Count(); ++i )
  1020. {
  1021. C_PrecipitationBlocker *blocker = g_PrecipitationBlockers[i];
  1022. vcollide_t *pCollide = modelinfo->GetVCollide( blocker->GetModelIndex() );
  1023. if ( !pCollide || pCollide->solidCount <= 0 )
  1024. continue;
  1025. Vector *outVerts;
  1026. int vertCount = g_pPhysicsCollision->CreateDebugMesh( pCollide->solids[0], &outVerts );
  1027. if ( vertCount )
  1028. {
  1029. for ( int j = 0; j < vertCount; j += 3 )
  1030. {
  1031. rtEnvRainBlocker->AddTriangle( nTriCount++, outVerts[j], outVerts[j + 1], outVerts[j + 2], Vector( 1, 1, 1 ) );
  1032. }
  1033. }
  1034. physcollision->DestroyDebugMesh( vertCount, outVerts );
  1035. }
  1036. rtEnvRainBlocker->SetupAccelerationStructure();
  1037. m_bParticlePrecipInitialized = true;
  1038. }
  1039. void CClient_Precipitation::DestroyInnerParticlePrecip( int nSlot )
  1040. {
  1041. if ( m_pParticlePrecipInnerFar[nSlot] != NULL )
  1042. {
  1043. m_pParticlePrecipInnerFar[nSlot]->StopEmission();
  1044. m_pParticlePrecipInnerFar[nSlot] = NULL;
  1045. }
  1046. if ( m_pParticlePrecipInnerNear[nSlot] != NULL )
  1047. {
  1048. m_pParticlePrecipInnerNear[nSlot]->StopEmission();
  1049. m_pParticlePrecipInnerNear[nSlot] = NULL;
  1050. }
  1051. }
  1052. void CClient_Precipitation::DestroyOuterParticlePrecip( int nSlot )
  1053. {
  1054. if ( m_pParticlePrecipOuter[nSlot] != NULL )
  1055. {
  1056. m_pParticlePrecipOuter[nSlot]->StopEmission();
  1057. m_pParticlePrecipOuter[nSlot] = NULL;
  1058. }
  1059. }
  1060. void CClient_Precipitation::DispatchOuterParticlePrecip( int nSlot, C_BasePlayer *pPlayer, Vector vForward )
  1061. {
  1062. DestroyOuterParticlePrecip( nSlot );
  1063. #ifdef INFESTED_DLL
  1064. Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), (float)m_nSnowDustAmount/100.0f, 0 ) * m_flDensity;
  1065. Vector vPlayerPos = MainViewOrigin( nSlot );
  1066. #else
  1067. Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity;
  1068. Vector vPlayerPos = pPlayer->EyePosition();
  1069. #endif
  1070. m_pParticlePrecipOuter[nSlot] = ParticleProp()->Create( m_pParticleOuterDef, PATTACH_ABSORIGIN_FOLLOW );
  1071. m_pParticlePrecipOuter[nSlot]->SetControlPointEntity( 2, pPlayer );
  1072. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 1, vPlayerPos + Vector (0, 0, 180 ) );
  1073. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 3, vDensity );
  1074. m_pParticlePrecipOuter[nSlot]->SetDrawOnlyForSplitScreenUser( nSlot );
  1075. }
  1076. void CClient_Precipitation::DispatchInnerParticlePrecip( int nSlot, C_BasePlayer *pPlayer, Vector vForward )
  1077. {
  1078. DestroyInnerParticlePrecip( nSlot );
  1079. DestroyOuterParticlePrecip( nSlot );
  1080. #ifdef INFESTED_DLL
  1081. Vector vPlayerPos = MainViewOrigin( nSlot );
  1082. Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 64 );
  1083. Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 64 ) + ( vForward * 32 );
  1084. Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 64 ) + ( vForward * m_flParticleInnerDist );
  1085. Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), (float)m_nSnowDustAmount/100.0f, 0 ) * m_flDensity;
  1086. #else
  1087. Vector vPlayerPos = pPlayer->EyePosition();
  1088. Vector vOffsetPos = vPlayerPos + Vector ( 0, 0, 180 );
  1089. Vector vOffsetPosNear = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * 32 );
  1090. Vector vOffsetPosFar = vPlayerPos + Vector ( 0, 0, 180 ) + ( vForward * m_flParticleInnerDist ); // 100.0
  1091. Vector vDensity = Vector( r_RainParticleDensity.GetFloat(), 0, 0 ) * m_flDensity;
  1092. #endif
  1093. m_pParticlePrecipOuter[nSlot] = ParticleProp()->Create( m_pParticleOuterDef, PATTACH_ABSORIGIN_FOLLOW );
  1094. m_pParticlePrecipInnerNear[nSlot] = ParticleProp()->Create( m_pParticleInnerNearDef, PATTACH_ABSORIGIN_FOLLOW );
  1095. m_pParticlePrecipInnerFar[nSlot] = ParticleProp()->Create( m_pParticleInnerFarDef, PATTACH_ABSORIGIN_FOLLOW );
  1096. m_pParticlePrecipOuter[nSlot]->SetControlPointEntity( 2, pPlayer );
  1097. m_pParticlePrecipInnerNear[nSlot]->SetControlPointEntity( 2, pPlayer );
  1098. m_pParticlePrecipInnerFar[nSlot]->SetControlPointEntity( 2, pPlayer );
  1099. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 1, vOffsetPos );
  1100. m_pParticlePrecipInnerNear[nSlot]->SetControlPoint( 1, vOffsetPosNear );
  1101. m_pParticlePrecipInnerFar[nSlot]->SetControlPoint( 1, vOffsetPosFar );
  1102. m_pParticlePrecipInnerNear[nSlot]->SetControlPoint( 3, vDensity );
  1103. m_pParticlePrecipInnerFar[nSlot]->SetControlPoint( 3, vDensity );
  1104. m_pParticlePrecipOuter[nSlot]->SetControlPoint( 3, vDensity );
  1105. m_pParticlePrecipOuter[nSlot]->SetDrawOnlyForSplitScreenUser( nSlot );
  1106. m_pParticlePrecipInnerNear[nSlot]->SetDrawOnlyForSplitScreenUser( nSlot );
  1107. m_pParticlePrecipInnerFar[nSlot]->SetDrawOnlyForSplitScreenUser( nSlot );
  1108. }
  1109. // TERROR: adding end pos for lifetime calcs
  1110. void CClient_Precipitation::CreateRainOrSnowParticle( const Vector &vSpawnPosition, const Vector &vEndPosition, const Vector &vVelocity )
  1111. {
  1112. // Create the particle
  1113. CPrecipitationParticle* p = CreateParticle();
  1114. if (!p)
  1115. return;
  1116. VectorCopy( vVelocity, p->m_Velocity );
  1117. p->m_Pos = vSpawnPosition;
  1118. /* TERROR: moving random velocity out so it can be included in endpos calcs
  1119. p->m_Velocity[ 0 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt());
  1120. p->m_Velocity[ 1 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt());
  1121. */
  1122. p->m_Mass = random->RandomFloat( 0.5, 1.5 );
  1123. p->m_flMaxLifetime = fabs((vSpawnPosition.z - vEndPosition.z) / vVelocity.z);
  1124. }
  1125. //-----------------------------------------------------------------------------
  1126. // emit the precipitation particles
  1127. //-----------------------------------------------------------------------------
  1128. void CClient_Precipitation::EmitParticles( float fTimeDelta )
  1129. {
  1130. Vector2D size;
  1131. Vector vel, org;
  1132. FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh )
  1133. {
  1134. ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh );
  1135. #ifdef CSTRIKE15
  1136. C_CSPlayer *pPlayer = GetLocalOrInEyeCSPlayer();
  1137. #else
  1138. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1139. #endif
  1140. if ( !pPlayer )
  1141. continue;
  1142. Vector vPlayerCenter = pPlayer->WorldSpaceCenter();
  1143. // Compute where to emit
  1144. if (!ComputeEmissionArea( org, size, pPlayer ))
  1145. continue;
  1146. // clamp this to prevent creating a bunch of rain or snow at one time.
  1147. if( fTimeDelta > 0.075f )
  1148. fTimeDelta = 0.075f;
  1149. // FIXME: Compute the precipitation density based on computational power
  1150. float density = m_flDensity * 0.001;
  1151. if (density > 0.01f)
  1152. density = 0.01f;
  1153. // Compute number of particles to emit based on precip density and emission area and dt
  1154. float fParticles = size[0] * size[1] * density * fTimeDelta + m_Remainder;
  1155. int cParticles = (int)fParticles;
  1156. m_Remainder = fParticles - cParticles;
  1157. // calculate the max amount of time it will take this flake to fall.
  1158. // This works if we assume the wind doesn't have a z component
  1159. VectorCopy( s_WindVector, vel );
  1160. vel[2] -= GetSpeed();
  1161. // Emit all the particles
  1162. for ( int i = 0 ; i < cParticles ; i++ )
  1163. {
  1164. // TERROR: moving random velocity out so it can be included in endpos calcs
  1165. Vector vParticleVel = vel;
  1166. vParticleVel[ 0 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt());
  1167. vParticleVel[ 1 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt());
  1168. Vector vParticlePos = org;
  1169. vParticlePos[ 0 ] += size[ 0 ] * random->RandomFloat(0, 1);
  1170. vParticlePos[ 1 ] += size[ 1 ] * random->RandomFloat(0, 1);
  1171. // Figure out where the particle should lie in Z by tracing a line from the player's height up to the
  1172. // desired height and making sure it doesn't hit a wall.
  1173. Vector vPlayerHeight = vParticlePos;
  1174. vPlayerHeight.z = vPlayerCenter.z;
  1175. if ( ParticleIsBlocked( vPlayerHeight, vParticlePos ) )
  1176. {
  1177. if ( r_RainDebugDuration.GetBool() )
  1178. {
  1179. debugoverlay->AddLineOverlay( vPlayerHeight, vParticlePos, 255, 0, 0, false, r_RainDebugDuration.GetFloat() );
  1180. }
  1181. continue;
  1182. }
  1183. Vector vUnitParticleVel = vParticleVel;
  1184. float fallHeight = vParticlePos.z - vPlayerHeight.z;
  1185. vUnitParticleVel /= fallHeight;
  1186. vPlayerHeight.x += vUnitParticleVel.x * fallHeight;
  1187. vPlayerHeight.y += vUnitParticleVel.y * fallHeight;
  1188. trace_t trace;
  1189. UTIL_TraceLine( vPlayerHeight, vParticlePos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  1190. if ( trace.fraction < 1 )
  1191. {
  1192. // If we hit a brush, then don't spawn the particle.
  1193. if ( trace.surface.flags & SURF_SKY )
  1194. {
  1195. vParticlePos = trace.endpos;
  1196. if ( r_RainDebugDuration.GetBool() )
  1197. {
  1198. debugoverlay->AddLineOverlay( vPlayerHeight, trace.endpos, 0, 0, 255, false, r_RainDebugDuration.GetFloat() );
  1199. }
  1200. }
  1201. else
  1202. {
  1203. if ( r_RainDebugDuration.GetBool() )
  1204. {
  1205. debugoverlay->AddLineOverlay( vPlayerHeight, trace.endpos, 255, 0, 0, false, r_RainDebugDuration.GetFloat() );
  1206. }
  1207. continue;
  1208. }
  1209. }
  1210. // TERROR: Find an endpos
  1211. Vector vParticleEndPos( vPlayerHeight );
  1212. //vParticleEndPos.z -= 256.0f;
  1213. //UTIL_TraceLine( vPlayerHeight, vParticleEndPos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  1214. //vParticleEndPos = trace.endpos;
  1215. if ( r_RainDebugDuration.GetBool() )
  1216. {
  1217. debugoverlay->AddLineOverlay( vParticlePos, vParticleEndPos, 0, 255, 0, true, r_RainDebugDuration.GetFloat() );
  1218. }
  1219. CreateRainOrSnowParticle( vParticlePos, vParticleEndPos, vParticleVel );
  1220. }
  1221. }
  1222. }
  1223. //-----------------------------------------------------------------------------
  1224. // Computes the wind vector
  1225. //-----------------------------------------------------------------------------
  1226. void CClient_Precipitation::ComputeWindVector( )
  1227. {
  1228. // Compute the wind direction
  1229. QAngle windangle( 0, cl_winddir.GetFloat(), 0 ); // used to turn wind yaw direction into a vector
  1230. // Randomize the wind angle and speed slightly to get us a little variation
  1231. windangle[1] = windangle[1] + random->RandomFloat( -10, 10 );
  1232. float windspeed = cl_windspeed.GetFloat() * (1.0 + random->RandomFloat( -0.2, 0.2 ));
  1233. AngleVectors( windangle, &s_WindVector );
  1234. VectorScale( s_WindVector, windspeed, s_WindVector );
  1235. }
  1236. CHandle<CClient_Precipitation> g_pPrecipHackEnt;
  1237. class CPrecipHack : public CAutoGameSystemPerFrame
  1238. {
  1239. public:
  1240. explicit CPrecipHack( char const *name ) : CAutoGameSystemPerFrame( name )
  1241. {
  1242. m_bLevelInitted = false;
  1243. }
  1244. virtual void LevelInitPostEntity()
  1245. {
  1246. if ( r_RainHack.GetInt() )
  1247. {
  1248. CClient_Precipitation *pPrecipHackEnt = new CClient_Precipitation;
  1249. pPrecipHackEnt->InitializeAsClientEntity( NULL, false );
  1250. g_pPrecipHackEnt = pPrecipHackEnt;
  1251. }
  1252. m_bLevelInitted = true;
  1253. }
  1254. virtual void LevelShutdownPreEntity()
  1255. {
  1256. if ( r_RainHack.GetInt() && g_pPrecipHackEnt )
  1257. {
  1258. UTIL_Remove( g_pPrecipHackEnt );
  1259. }
  1260. m_bLevelInitted = false;
  1261. }
  1262. virtual void Update( float frametime )
  1263. {
  1264. // Handle changes to the cvar at runtime.
  1265. if ( m_bLevelInitted )
  1266. {
  1267. if ( r_RainHack.GetInt() && !g_pPrecipHackEnt )
  1268. LevelInitPostEntity();
  1269. else if ( !r_RainHack.GetInt() && g_pPrecipHackEnt )
  1270. LevelShutdownPreEntity();
  1271. }
  1272. }
  1273. bool m_bLevelInitted;
  1274. };
  1275. CPrecipHack g_PrecipHack( "CPrecipHack" );
  1276. //-----------------------------------------------------------------------------
  1277. // EnvWind - global wind info
  1278. //-----------------------------------------------------------------------------
  1279. class C_EnvWind : public C_BaseEntity
  1280. {
  1281. public:
  1282. C_EnvWind();
  1283. DECLARE_CLIENTCLASS();
  1284. DECLARE_CLASS( C_EnvWind, C_BaseEntity );
  1285. virtual void OnDataChanged( DataUpdateType_t updateType );
  1286. virtual bool ShouldDraw( void ) { return false; }
  1287. virtual void ClientThink( );
  1288. private:
  1289. C_EnvWind( const C_EnvWind & );
  1290. CEnvWindShared m_EnvWindShared;
  1291. };
  1292. // Receive datatables
  1293. BEGIN_RECV_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared)
  1294. RecvPropInt (RECVINFO(m_iMinWind)),
  1295. RecvPropInt (RECVINFO(m_iMaxWind)),
  1296. RecvPropInt (RECVINFO(m_iMinGust)),
  1297. RecvPropInt (RECVINFO(m_iMaxGust)),
  1298. RecvPropFloat (RECVINFO(m_flMinGustDelay)),
  1299. RecvPropFloat (RECVINFO(m_flMaxGustDelay)),
  1300. RecvPropInt (RECVINFO(m_iGustDirChange)),
  1301. RecvPropInt (RECVINFO(m_iWindSeed)),
  1302. RecvPropInt (RECVINFO(m_iInitialWindDir)),
  1303. RecvPropFloat (RECVINFO(m_flInitialWindSpeed)),
  1304. RecvPropFloat (RECVINFO(m_flStartTime)),
  1305. RecvPropFloat (RECVINFO(m_flGustDuration)),
  1306. // RecvPropInt (RECVINFO(m_iszGustSound)),
  1307. END_RECV_TABLE()
  1308. IMPLEMENT_CLIENTCLASS_DT( C_EnvWind, DT_EnvWind, CEnvWind )
  1309. RecvPropDataTable(RECVINFO_DT(m_EnvWindShared), 0, &REFERENCE_RECV_TABLE(DT_EnvWindShared)),
  1310. END_RECV_TABLE()
  1311. C_EnvWind::C_EnvWind()
  1312. {
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Post data update!
  1316. //-----------------------------------------------------------------------------
  1317. void C_EnvWind::OnDataChanged( DataUpdateType_t updateType )
  1318. {
  1319. // Whenever we get an update, reset the entire state.
  1320. // Note that the fields have already been stored by the datatables,
  1321. // but there's still work to be done in the init block
  1322. m_EnvWindShared.Init( entindex(), m_EnvWindShared.m_iWindSeed,
  1323. m_EnvWindShared.m_flStartTime, m_EnvWindShared.m_iInitialWindDir,
  1324. m_EnvWindShared.m_flInitialWindSpeed );
  1325. SetNextClientThink(0.0f);
  1326. BaseClass::OnDataChanged( updateType );
  1327. }
  1328. void C_EnvWind::ClientThink( )
  1329. {
  1330. // Update the wind speed
  1331. float flNextThink = m_EnvWindShared.WindThink( gpGlobals->curtime );
  1332. SetNextClientThink(flNextThink);
  1333. }
  1334. //==================================================
  1335. // EmberParticle
  1336. //==================================================
  1337. class CEmberEmitter : public CSimpleEmitter
  1338. {
  1339. public:
  1340. explicit CEmberEmitter( const char *pDebugName );
  1341. static CSmartPtr<CEmberEmitter> Create( const char *pDebugName );
  1342. virtual void UpdateVelocity( SimpleParticle *pParticle, float timeDelta );
  1343. virtual Vector UpdateColor( const SimpleParticle *pParticle );
  1344. private:
  1345. CEmberEmitter( const CEmberEmitter & );
  1346. };
  1347. //-----------------------------------------------------------------------------
  1348. // Purpose:
  1349. // Input : fTimeDelta -
  1350. // Output : Vector
  1351. //-----------------------------------------------------------------------------
  1352. CEmberEmitter::CEmberEmitter( const char *pDebugName ) : CSimpleEmitter( pDebugName )
  1353. {
  1354. }
  1355. CSmartPtr<CEmberEmitter> CEmberEmitter::Create( const char *pDebugName )
  1356. {
  1357. return new CEmberEmitter( pDebugName );
  1358. }
  1359. void CEmberEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  1360. {
  1361. float speed = VectorNormalize( pParticle->m_vecVelocity );
  1362. Vector offset;
  1363. speed -= ( 1.0f * timeDelta );
  1364. offset.Random( -0.025f, 0.025f );
  1365. offset[2] = 0.0f;
  1366. pParticle->m_vecVelocity += offset;
  1367. VectorNormalize( pParticle->m_vecVelocity );
  1368. pParticle->m_vecVelocity *= speed;
  1369. }
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose:
  1372. // Input : *pParticle -
  1373. // timeDelta -
  1374. //-----------------------------------------------------------------------------
  1375. Vector CEmberEmitter::UpdateColor( const SimpleParticle *pParticle )
  1376. {
  1377. Vector color;
  1378. float ramp = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime );
  1379. color[0] = ( (float) pParticle->m_uchColor[0] * ramp ) / 255.0f;
  1380. color[1] = ( (float) pParticle->m_uchColor[1] * ramp ) / 255.0f;
  1381. color[2] = ( (float) pParticle->m_uchColor[2] * ramp ) / 255.0f;
  1382. return color;
  1383. }
  1384. //==================================================
  1385. // C_Embers
  1386. //==================================================
  1387. class C_Embers : public C_BaseEntity
  1388. {
  1389. public:
  1390. DECLARE_CLIENTCLASS();
  1391. DECLARE_CLASS( C_Embers, C_BaseEntity );
  1392. C_Embers();
  1393. ~C_Embers();
  1394. void Start( void );
  1395. virtual void OnDataChanged( DataUpdateType_t updateType );
  1396. virtual bool ShouldDraw( void );
  1397. virtual bool Simulate( void );
  1398. //Server-side
  1399. int m_nDensity;
  1400. int m_nLifetime;
  1401. int m_nSpeed;
  1402. bool m_bEmit;
  1403. protected:
  1404. void SpawnEmber( void );
  1405. PMaterialHandle m_hMaterial;
  1406. TimedEvent m_tParticleSpawn;
  1407. CSmartPtr<CEmberEmitter> m_pEmitter;
  1408. };
  1409. //Receive datatable
  1410. IMPLEMENT_CLIENTCLASS_DT( C_Embers, DT_Embers, CEmbers )
  1411. RecvPropInt( RECVINFO( m_nDensity ) ),
  1412. RecvPropInt( RECVINFO( m_nLifetime ) ),
  1413. RecvPropInt( RECVINFO( m_nSpeed ) ),
  1414. RecvPropInt( RECVINFO( m_bEmit ) ),
  1415. END_RECV_TABLE()
  1416. //-----------------------------------------------------------------------------
  1417. // Purpose:
  1418. // Input : bnewentity -
  1419. //-----------------------------------------------------------------------------
  1420. C_Embers::C_Embers()
  1421. {
  1422. m_pEmitter = CEmberEmitter::Create( "C_Embers" );
  1423. AddToEntityList(ENTITY_LIST_SIMULATE);
  1424. }
  1425. C_Embers::~C_Embers()
  1426. {
  1427. }
  1428. void C_Embers::OnDataChanged( DataUpdateType_t updateType )
  1429. {
  1430. BaseClass::OnDataChanged( updateType );
  1431. if ( updateType == DATA_UPDATE_CREATED )
  1432. {
  1433. m_pEmitter->SetSortOrigin( GetAbsOrigin() );
  1434. Start();
  1435. }
  1436. if ( m_bEmit )
  1437. {
  1438. AddToEntityList(ENTITY_LIST_SIMULATE);
  1439. }
  1440. }
  1441. //-----------------------------------------------------------------------------
  1442. // Purpose:
  1443. // Output : Returns true on success, false on failure.
  1444. //-----------------------------------------------------------------------------
  1445. bool C_Embers::ShouldDraw()
  1446. {
  1447. return true;
  1448. }
  1449. //-----------------------------------------------------------------------------
  1450. // Purpose:
  1451. //-----------------------------------------------------------------------------
  1452. void C_Embers::Start( void )
  1453. {
  1454. //Various setup info
  1455. m_tParticleSpawn.Init( m_nDensity );
  1456. m_hMaterial = m_pEmitter->GetPMaterial( "particle/fire" );
  1457. }
  1458. //-----------------------------------------------------------------------------
  1459. // Purpose:
  1460. //-----------------------------------------------------------------------------
  1461. bool C_Embers::Simulate( void )
  1462. {
  1463. if ( m_bEmit == false )
  1464. return false;
  1465. float tempDelta = gpGlobals->frametime;
  1466. while( m_tParticleSpawn.NextEvent( tempDelta ) )
  1467. {
  1468. SpawnEmber();
  1469. }
  1470. return true;
  1471. }
  1472. //-----------------------------------------------------------------------------
  1473. // Purpose:
  1474. //-----------------------------------------------------------------------------
  1475. void C_Embers::SpawnEmber( void )
  1476. {
  1477. Vector offset, mins, maxs;
  1478. modelinfo->GetModelBounds( GetModel(), mins, maxs );
  1479. //Setup our spawn position
  1480. offset[0] = random->RandomFloat( mins[0], maxs[0] );
  1481. offset[1] = random->RandomFloat( mins[1], maxs[1] );
  1482. offset[2] = random->RandomFloat( mins[2], maxs[2] );
  1483. //Spawn the particle
  1484. SimpleParticle *sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof( SimpleParticle ), m_hMaterial, offset );
  1485. if (sParticle == NULL)
  1486. return;
  1487. float cScale = random->RandomFloat( 0.75f, 1.0f );
  1488. //Set it up
  1489. sParticle->m_flLifetime = 0.0f;
  1490. sParticle->m_flDieTime = m_nLifetime;
  1491. sParticle->m_uchColor[0] = GetRenderColorR() * cScale;
  1492. sParticle->m_uchColor[1] = GetRenderColorG() * cScale;
  1493. sParticle->m_uchColor[2] = GetRenderColorB() * cScale;
  1494. sParticle->m_uchStartAlpha = 255;
  1495. sParticle->m_uchEndAlpha = 0;
  1496. sParticle->m_uchStartSize = 1;
  1497. sParticle->m_uchEndSize = 0;
  1498. sParticle->m_flRollDelta = 0;
  1499. sParticle->m_flRoll = 0;
  1500. //Set the velocity
  1501. Vector velocity;
  1502. AngleVectors( GetAbsAngles(), &velocity );
  1503. sParticle->m_vecVelocity = velocity * m_nSpeed;
  1504. sParticle->m_vecVelocity[0] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) );
  1505. sParticle->m_vecVelocity[1] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) );
  1506. sParticle->m_vecVelocity[2] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) );
  1507. UpdateVisibility();
  1508. }
  1509. //-----------------------------------------------------------------------------
  1510. // Quadratic spline beam effect
  1511. //-----------------------------------------------------------------------------
  1512. #include "beamdraw.h"
  1513. class C_QuadraticBeam : public C_BaseEntity
  1514. {
  1515. public:
  1516. DECLARE_CLIENTCLASS();
  1517. DECLARE_CLASS( C_QuadraticBeam, C_BaseEntity );
  1518. //virtual void OnDataChanged( DataUpdateType_t updateType );
  1519. virtual bool ShouldDraw( void ) { return true; }
  1520. virtual int DrawModel( int, const RenderableInstance_t &instance );
  1521. virtual void GetRenderBounds( Vector& mins, Vector& maxs )
  1522. {
  1523. ClearBounds( mins, maxs );
  1524. AddPointToBounds( vec3_origin, mins, maxs );
  1525. AddPointToBounds( m_targetPosition, mins, maxs );
  1526. AddPointToBounds( m_controlPosition, mins, maxs );
  1527. mins -= GetRenderOrigin();
  1528. maxs -= GetRenderOrigin();
  1529. }
  1530. protected:
  1531. Vector m_targetPosition;
  1532. Vector m_controlPosition;
  1533. float m_scrollRate;
  1534. float m_flWidth;
  1535. };
  1536. //Receive datatable
  1537. IMPLEMENT_CLIENTCLASS_DT( C_QuadraticBeam, DT_QuadraticBeam, CEnvQuadraticBeam )
  1538. RecvPropVector( RECVINFO(m_targetPosition) ),
  1539. RecvPropVector( RECVINFO(m_controlPosition) ),
  1540. RecvPropFloat( RECVINFO(m_scrollRate) ),
  1541. RecvPropFloat( RECVINFO(m_flWidth) ),
  1542. END_RECV_TABLE()
  1543. Vector Color24ToVector( const color24 &color )
  1544. {
  1545. return Vector( color.r * (1.0/255.0f), color.g * (1.0/255.0f), color.b * (1.0/255.0f) );
  1546. }
  1547. int C_QuadraticBeam::DrawModel( int, const RenderableInstance_t &instance )
  1548. {
  1549. Draw_SetSpriteTexture( GetModel(), 0, GetRenderMode() );
  1550. Vector color = Color24ToVector( GetRenderColor() );
  1551. DrawBeamQuadratic( GetRenderOrigin(), m_controlPosition, m_targetPosition, m_flWidth, color, gpGlobals->curtime*m_scrollRate );
  1552. return 1;
  1553. }
  1554. //-----------------------------------------------------------------------------
  1555. //-----------------------------------------------------------------------------
  1556. class SnowFallEffect : public CSimpleEmitter
  1557. {
  1558. public:
  1559. explicit SnowFallEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {}
  1560. static SnowFallEffect* Create( const char *pDebugName )
  1561. {
  1562. return new SnowFallEffect( pDebugName );
  1563. }
  1564. void UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  1565. {
  1566. float flSpeed = VectorNormalize( pParticle->m_vecVelocity );
  1567. flSpeed -= timeDelta;
  1568. int randomVelocityValue = RandomInt( 0, 65536 );
  1569. float velX = ((128 - (randomVelocityValue / 256)) / 128.0f) * 0.025f;
  1570. float velY = ((128 - (randomVelocityValue % 256)) / 128.0f) * 0.025f;
  1571. VectorNormalize( pParticle->m_vecVelocity );
  1572. pParticle->m_vecVelocity.x += velX;
  1573. pParticle->m_vecVelocity.y += velY;
  1574. VectorNormalize( pParticle->m_vecVelocity );
  1575. pParticle->m_vecVelocity *= flSpeed;
  1576. Vector vecWindVelocity;
  1577. GetWindspeedAtTime( gpGlobals->curtime, vecWindVelocity );
  1578. pParticle->m_vecVelocity += ( vecWindVelocity * r_SnowWindScale.GetFloat() );
  1579. }
  1580. void SimulateParticles( CParticleSimulateIterator *pIterator )
  1581. {
  1582. float timeDelta = pIterator->GetTimeDelta();
  1583. SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst();
  1584. while ( pParticle )
  1585. {
  1586. //Update velocity
  1587. UpdateVelocity( pParticle, timeDelta );
  1588. pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta;
  1589. //Should this particle die?
  1590. pParticle->m_flLifetime += timeDelta;
  1591. UpdateRoll( pParticle, timeDelta );
  1592. if ( pParticle->m_flLifetime >= pParticle->m_flDieTime )
  1593. {
  1594. pIterator->RemoveParticle( pParticle );
  1595. }
  1596. else if ( !IsInAir( pParticle->m_Pos ) )
  1597. {
  1598. pIterator->RemoveParticle( pParticle );
  1599. }
  1600. pParticle = (SimpleParticle*)pIterator->GetNext();
  1601. }
  1602. }
  1603. int GetParticleCount( void )
  1604. {
  1605. return GetBinding().GetNumActiveParticles();
  1606. }
  1607. void SetBounds( const Vector &vecMin, const Vector &vecMax )
  1608. {
  1609. GetBinding().SetBBox( vecMin, vecMax, true );
  1610. }
  1611. RenderableTranslucencyType_t ComputeTranslucencyType( void ) { return RENDERABLE_IS_OPAQUE; }
  1612. private:
  1613. SnowFallEffect( const SnowFallEffect & );
  1614. };
  1615. //-----------------------------------------------------------------------------
  1616. //-----------------------------------------------------------------------------
  1617. class CSnowFallManager : public C_BaseEntity
  1618. {
  1619. public:
  1620. CSnowFallManager();
  1621. ~CSnowFallManager();
  1622. bool CreateEmitter( void );
  1623. void SpawnClientEntity( void );
  1624. void ClientThink();
  1625. void AddSnowFallEntity( CClient_Precipitation *pSnowEntity );
  1626. // Snow Effect
  1627. enum
  1628. {
  1629. SNOWFALL_NONE = 0,
  1630. SNOWFALL_AROUND_PLAYER,
  1631. SNOWFALL_IN_ENTITY,
  1632. };
  1633. RenderableTranslucencyType_t ComputeTranslucencyType( void ) { return RENDERABLE_IS_OPAQUE; }
  1634. void SetSplitScreenPlayerSlot( int nSlot );
  1635. private:
  1636. bool CreateSnowFallEmitter( void );
  1637. void CreateSnowFall( void );
  1638. void CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale, C_BasePlayer *pLocalPlayer );
  1639. void CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale, C_BasePlayer *pLocalPlayer );
  1640. void CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale, C_BasePlayer *pLocalPlayer );
  1641. void CreateSnowParticlesSphere( float flRadius, C_BasePlayer *pLocalPlayer );
  1642. void CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward, C_BasePlayer *pLocalPlayer );
  1643. void CreateSnowFallParticle( const Vector &vecParticleSpawn, int iBBox, C_BasePlayer *pLocalPlayer );
  1644. int StandingInSnowVolume( Vector &vecPoint );
  1645. void FindSnowVolumes( Vector const &vecCenter, float flRadius, Vector const &vecEyePos, Vector const &vecForward );
  1646. void UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax );
  1647. private:
  1648. enum { MAX_SNOW_PARTICLES = 500 };
  1649. enum { MAX_SNOW_LIST = 32 };
  1650. TimedEvent m_tSnowFallParticleTimer;
  1651. TimedEvent m_tSnowFallParticleTraceTimer;
  1652. int m_iSnowFallArea;
  1653. CSmartPtr<SnowFallEffect> m_pSnowFallEmitter;
  1654. Vector m_vecSnowFallEmitOrigin;
  1655. float m_flSnowRadius;
  1656. Vector m_vecMin;
  1657. Vector m_vecMax;
  1658. int m_nActiveSnowCount;
  1659. int m_aActiveSnow[MAX_SNOW_LIST];
  1660. bool m_bRayParticles;
  1661. struct SnowFall_t
  1662. {
  1663. PMaterialHandle m_hMaterial;
  1664. CClient_Precipitation *m_pEntity;
  1665. CSmartPtr<SnowFallEffect> m_pEffect;
  1666. Vector m_vecMin;
  1667. Vector m_vecMax;
  1668. };
  1669. CUtlVector<SnowFall_t> m_aSnow;
  1670. int m_nSplitScreenPlayerSlot;
  1671. };
  1672. //-----------------------------------------------------------------------------
  1673. // Purpose:
  1674. //-----------------------------------------------------------------------------
  1675. CSnowFallManager::CSnowFallManager( void )
  1676. {
  1677. m_iSnowFallArea = SNOWFALL_NONE;
  1678. m_pSnowFallEmitter = NULL;
  1679. m_vecSnowFallEmitOrigin.Init();
  1680. m_flSnowRadius = 0.0f;
  1681. m_vecMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  1682. m_vecMax.Init( FLT_MIN, FLT_MIN, FLT_MIN );
  1683. m_nActiveSnowCount = 0;
  1684. m_aSnow.Purge();
  1685. m_nSplitScreenPlayerSlot = -1;
  1686. }
  1687. //-----------------------------------------------------------------------------
  1688. // Purpose:
  1689. //-----------------------------------------------------------------------------
  1690. CSnowFallManager::~CSnowFallManager( void )
  1691. {
  1692. m_aSnow.Purge();
  1693. }
  1694. //-----------------------------------------------------------------------------
  1695. // Purpose:
  1696. // Output : Returns true on success, false on failure.
  1697. //-----------------------------------------------------------------------------
  1698. bool CSnowFallManager::CreateEmitter( void )
  1699. {
  1700. return CreateSnowFallEmitter();
  1701. }
  1702. //-----------------------------------------------------------------------------
  1703. // Purpose:
  1704. //-----------------------------------------------------------------------------
  1705. void CSnowFallManager::SpawnClientEntity( void )
  1706. {
  1707. m_tSnowFallParticleTimer.Init( 500 );
  1708. m_tSnowFallParticleTraceTimer.Init( 6 );
  1709. m_iSnowFallArea = SNOWFALL_NONE;
  1710. // Have the Snow Fall Manager think for all the snow fall entities.
  1711. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1712. }
  1713. //-----------------------------------------------------------------------------
  1714. // Purpose:
  1715. // Output : Returns true on success, false on failure.
  1716. //-----------------------------------------------------------------------------
  1717. bool CSnowFallManager::CreateSnowFallEmitter( void )
  1718. {
  1719. if ( ( m_pSnowFallEmitter = SnowFallEffect::Create( "snowfall" ) ) == NULL )
  1720. return false;
  1721. m_pSnowFallEmitter->SetShouldDrawForSplitScreenUser( m_nSplitScreenPlayerSlot );
  1722. return true;
  1723. }
  1724. void CSnowFallManager::SetSplitScreenPlayerSlot( int nSlot )
  1725. {
  1726. m_nSplitScreenPlayerSlot = nSlot;
  1727. }
  1728. //-----------------------------------------------------------------------------
  1729. // Purpose:
  1730. //-----------------------------------------------------------------------------
  1731. void CSnowFallManager::ClientThink( void )
  1732. {
  1733. if ( !IsValidSplitScreenSlot( m_nSplitScreenPlayerSlot ) )
  1734. return;
  1735. ACTIVE_SPLITSCREEN_PLAYER_GUARD( m_nSplitScreenPlayerSlot );
  1736. if ( !r_SnowEnable.GetBool() )
  1737. return;
  1738. // Make sure we have a snow fall emitter.
  1739. if ( !m_pSnowFallEmitter )
  1740. {
  1741. if ( !CreateSnowFallEmitter() )
  1742. return;
  1743. }
  1744. CreateSnowFall();
  1745. }
  1746. //-----------------------------------------------------------------------------
  1747. // Purpose:
  1748. // Input : *pSnowEntity -
  1749. //-----------------------------------------------------------------------------
  1750. void CSnowFallManager::AddSnowFallEntity( CClient_Precipitation *pSnowEntity )
  1751. {
  1752. if ( !pSnowEntity )
  1753. return;
  1754. int nSnowCount = m_aSnow.Count();
  1755. int iSnow = 0;
  1756. for ( iSnow = 0; iSnow < nSnowCount; ++iSnow )
  1757. {
  1758. if ( m_aSnow[iSnow].m_pEntity == pSnowEntity )
  1759. break;
  1760. }
  1761. if ( iSnow != nSnowCount )
  1762. return;
  1763. iSnow = m_aSnow.AddToTail();
  1764. m_aSnow[iSnow].m_pEntity = pSnowEntity;
  1765. m_aSnow[iSnow].m_pEffect = SnowFallEffect::Create( "snowfall" );
  1766. m_aSnow[iSnow].m_hMaterial = ParticleMgr()->GetPMaterial( "particle/snow" );
  1767. VectorCopy( pSnowEntity->WorldAlignMins(), m_aSnow[iSnow].m_vecMin );
  1768. VectorCopy( pSnowEntity->WorldAlignMaxs(), m_aSnow[iSnow].m_vecMax );
  1769. UpdateBounds( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax );
  1770. }
  1771. //-----------------------------------------------------------------------------
  1772. // Purpose:
  1773. //-----------------------------------------------------------------------------
  1774. void CSnowFallManager::UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax )
  1775. {
  1776. int iAxis = 0;
  1777. for ( iAxis = 0; iAxis < 3; ++iAxis )
  1778. {
  1779. if ( vecSnowMin[iAxis] < m_vecMin[iAxis] )
  1780. {
  1781. m_vecMin[iAxis] = vecSnowMin[iAxis];
  1782. }
  1783. if ( vecSnowMax[iAxis] > m_vecMax[iAxis] )
  1784. {
  1785. m_vecMax[iAxis] = vecSnowMax[iAxis];
  1786. }
  1787. }
  1788. Assert( m_pSnowFallEmitter );
  1789. m_pSnowFallEmitter->SetBounds( m_vecMin, m_vecMax );
  1790. }
  1791. //-----------------------------------------------------------------------------
  1792. // Purpose:
  1793. // Input : &vecPoint -
  1794. // Output : int
  1795. //-----------------------------------------------------------------------------
  1796. int CSnowFallManager::StandingInSnowVolume( Vector &vecPoint )
  1797. {
  1798. trace_t traceSnow;
  1799. int nSnowCount = m_aSnow.Count();
  1800. int iSnow = 0;
  1801. for ( iSnow = 0; iSnow < nSnowCount; ++iSnow )
  1802. {
  1803. UTIL_TraceModel( vecPoint, vecPoint, vec3_origin, vec3_origin, static_cast<C_BaseEntity*>( m_aSnow[iSnow].m_pEntity ), COLLISION_GROUP_NONE, &traceSnow );
  1804. if ( traceSnow.startsolid )
  1805. return iSnow;
  1806. }
  1807. return -1;
  1808. }
  1809. //-----------------------------------------------------------------------------
  1810. // Purpose:
  1811. // Input : &vecCenter -
  1812. // flRadius -
  1813. //-----------------------------------------------------------------------------
  1814. void CSnowFallManager::FindSnowVolumes( Vector const &vecCenter, float flRadius, Vector const &vecEyePos, Vector const &vecForward )
  1815. {
  1816. // Reset.
  1817. m_nActiveSnowCount = 0;
  1818. m_bRayParticles = false;
  1819. int nSnowCount = m_aSnow.Count();
  1820. int iSnow = 0;
  1821. for ( iSnow = 0; iSnow < nSnowCount; ++iSnow )
  1822. {
  1823. // Check to see if the volume is in the PVS.
  1824. bool bInPVS = g_pClientLeafSystem->IsRenderableInPVS( m_aSnow[iSnow].m_pEntity->GetClientRenderable() );
  1825. if ( !bInPVS )
  1826. continue;
  1827. // Check to see if a snow volume is inside the given radius.
  1828. if ( IsBoxIntersectingSphere( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, vecCenter, flRadius ) )
  1829. {
  1830. m_aActiveSnow[m_nActiveSnowCount] = iSnow;
  1831. ++m_nActiveSnowCount;
  1832. if ( m_nActiveSnowCount >= MAX_SNOW_LIST )
  1833. {
  1834. DevWarning( 1, "Max Active Snow Volume Count!\n" );
  1835. break;
  1836. }
  1837. }
  1838. // Check to see if a snow volume is outside of the sphere radius, but is along line-of-sight.
  1839. else
  1840. {
  1841. CBaseTrace trace;
  1842. Vector vecNewForward;
  1843. vecNewForward = vecForward * r_SnowRayLength.GetFloat();
  1844. vecNewForward.z = 0.0f;
  1845. IntersectRayWithBox( vecEyePos, vecNewForward, m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, 0.325f, &trace );
  1846. if ( trace.fraction < 1.0f )
  1847. {
  1848. m_aActiveSnow[m_nActiveSnowCount] = iSnow;
  1849. ++m_nActiveSnowCount;
  1850. if ( m_nActiveSnowCount >= MAX_SNOW_LIST )
  1851. {
  1852. DevWarning( 1, "Max Active Snow Volume Count!\n" );
  1853. break;
  1854. }
  1855. m_bRayParticles = true;
  1856. }
  1857. }
  1858. }
  1859. // Debugging code!
  1860. #ifdef _DEBUG
  1861. if ( r_SnowDebugBox.GetFloat() != 0.0f )
  1862. {
  1863. for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow )
  1864. {
  1865. Vector vecCenter, vecMin, vecMax;
  1866. vecCenter = ( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax ) * 0.5;
  1867. vecMin = m_aSnow[iSnow].m_vecMin - vecCenter;
  1868. vecMax = m_aSnow[iSnow].m_vecMax - vecCenter;
  1869. debugoverlay->AddBoxOverlay( vecCenter, vecMin, vecMax, QAngle( 0, 0, 0 ), 200, 0, 0, 25, r_SnowDebugBox.GetFloat() );
  1870. }
  1871. }
  1872. #endif
  1873. }
  1874. //-----------------------------------------------------------------------------
  1875. // Purpose:
  1876. //-----------------------------------------------------------------------------
  1877. void CSnowFallManager::CreateSnowFall( void )
  1878. {
  1879. #if 1
  1880. VPROF_BUDGET( "SnowFall", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  1881. #endif
  1882. ASSERT_LOCAL_PLAYER_RESOLVABLE();
  1883. // Check to see if we have a local player before starting the snow around a local player.
  1884. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1885. if ( pPlayer == NULL )
  1886. return;
  1887. // Get the current frame time.
  1888. float flCurrentTime = gpGlobals->frametime;
  1889. // Get the players data to determine where the snow emitter should reside.
  1890. VectorCopy( pPlayer->EyePosition(), m_vecSnowFallEmitOrigin );
  1891. Vector vecForward;
  1892. pPlayer->GetVectors( &vecForward, NULL, NULL );
  1893. vecForward.z = 0.0f;
  1894. Vector vecVelocity = pPlayer->GetAbsVelocity();
  1895. float flSpeed = VectorNormalize( vecVelocity );
  1896. m_vecSnowFallEmitOrigin += ( vecForward * ( 64.0f + ( flSpeed * 0.4f * r_SnowPosScale.GetFloat() ) ) );
  1897. m_vecSnowFallEmitOrigin += ( vecVelocity * ( flSpeed * 1.25f * r_SnowSpeedScale.GetFloat() ) );
  1898. // Check to see if the player is zoomed.
  1899. bool bZoomed = ( pPlayer->GetFOV() != pPlayer->GetDefaultFOV() );
  1900. float flZoomScale = 1.0f;
  1901. if ( bZoomed )
  1902. {
  1903. flZoomScale = pPlayer->GetDefaultFOV() / pPlayer->GetFOV();
  1904. flZoomScale *= 0.5f;
  1905. }
  1906. // Time to test for a snow volume yet? (Only do this 6 times a second!)
  1907. if ( m_tSnowFallParticleTraceTimer.NextEvent( flCurrentTime ) )
  1908. {
  1909. // Reset the active snow emitter.
  1910. m_iSnowFallArea = SNOWFALL_NONE;
  1911. // Set the trace start and the emit origin.
  1912. Vector vecTraceStart;
  1913. VectorCopy( pPlayer->EyePosition(), vecTraceStart );
  1914. int iSnowVolume = StandingInSnowVolume( vecTraceStart );
  1915. if ( iSnowVolume != -1 )
  1916. {
  1917. m_flSnowRadius = r_SnowInsideRadius.GetFloat() + ( flSpeed * 0.5f );
  1918. m_iSnowFallArea = SNOWFALL_AROUND_PLAYER;
  1919. }
  1920. else
  1921. {
  1922. m_flSnowRadius = r_SnowOutsideRadius.GetFloat();
  1923. }
  1924. float flRadius = m_flSnowRadius;
  1925. if ( bZoomed )
  1926. {
  1927. if ( m_iSnowFallArea == SNOWFALL_AROUND_PLAYER )
  1928. {
  1929. flRadius = r_SnowOutsideRadius.GetFloat() * flZoomScale;
  1930. }
  1931. else
  1932. {
  1933. flRadius *= flZoomScale;
  1934. }
  1935. }
  1936. FindSnowVolumes( m_vecSnowFallEmitOrigin, flRadius, pPlayer->EyePosition(), vecForward );
  1937. if ( m_nActiveSnowCount != 0 && m_iSnowFallArea != SNOWFALL_AROUND_PLAYER )
  1938. {
  1939. // We found an active snow emitter.
  1940. m_iSnowFallArea = SNOWFALL_IN_ENTITY;
  1941. }
  1942. }
  1943. if ( m_iSnowFallArea == SNOWFALL_NONE )
  1944. return;
  1945. // Set the origin in the snow emitter.
  1946. m_pSnowFallEmitter->SetSortOrigin( m_vecSnowFallEmitOrigin );
  1947. // Create snow fall particles.
  1948. CreateSnowFallParticles( flCurrentTime, m_flSnowRadius, pPlayer->EyePosition(), vecForward, flZoomScale, pPlayer );
  1949. }
  1950. //-----------------------------------------------------------------------------
  1951. // Purpose:
  1952. // Input : flCurrentTime -
  1953. // flRadius -
  1954. // &vecEyePos -
  1955. // &vecForward -
  1956. // flZoomScale -
  1957. //-----------------------------------------------------------------------------
  1958. void CSnowFallManager::CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale, C_BasePlayer *pLocalPlayer )
  1959. {
  1960. const float SnowfallRate = 500.0f;
  1961. if ( m_nActiveSnowCount > 0 )
  1962. {
  1963. C_BaseEntity *pEntity = m_aSnow[ m_aActiveSnow[0] ].m_pEntity;
  1964. int density = pEntity->GetRenderAlpha();
  1965. density = clamp( density, 0, 100 );
  1966. if ( pEntity && density > 0 )
  1967. {
  1968. m_tSnowFallParticleTimer.ResetRate( SnowfallRate * density * 0.01f );
  1969. }
  1970. else
  1971. {
  1972. m_tSnowFallParticleTimer.ResetRate( SnowfallRate );
  1973. }
  1974. }
  1975. else
  1976. {
  1977. m_tSnowFallParticleTimer.ResetRate( SnowfallRate );
  1978. }
  1979. // Outside of a snow volume.
  1980. if ( m_iSnowFallArea == SNOWFALL_IN_ENTITY )
  1981. {
  1982. CreateOutsideVolumeSnowParticles( flCurrentTime, flRadius, flZoomScale, pLocalPlayer );
  1983. }
  1984. // Inside of a snow volume.
  1985. else
  1986. {
  1987. CreateInsideVolumeSnowParticles( flCurrentTime, flRadius, vecEyePos, vecForward, flZoomScale, pLocalPlayer );
  1988. }
  1989. }
  1990. //-----------------------------------------------------------------------------
  1991. // Purpose:
  1992. // Input : flCurrentTime -
  1993. // flRadius -
  1994. // flZoomScale -
  1995. //-----------------------------------------------------------------------------
  1996. void CSnowFallManager::CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale, C_BasePlayer *pLocalPlayer )
  1997. {
  1998. Vector vecParticleSpawn;
  1999. // Outside of a snow volume.
  2000. int iSnow = 0;
  2001. float flRadiusScaled = flRadius * flZoomScale;
  2002. float flRadius2 = flRadiusScaled * flRadiusScaled;
  2003. // Add as many particles as we need
  2004. while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) )
  2005. {
  2006. // Check for a max particle count.
  2007. if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() )
  2008. continue;
  2009. vecParticleSpawn.x = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x );
  2010. vecParticleSpawn.y = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y );
  2011. vecParticleSpawn.z = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z );
  2012. float flDistance2 = ( m_vecSnowFallEmitOrigin - vecParticleSpawn ).LengthSqr();
  2013. if ( flDistance2 < flRadius2 )
  2014. {
  2015. CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow], pLocalPlayer );
  2016. }
  2017. iSnow = ( iSnow + 1 ) % m_nActiveSnowCount;
  2018. }
  2019. }
  2020. //-----------------------------------------------------------------------------
  2021. // Purpose:
  2022. // Input : flCurrentTime -
  2023. // flRadius -
  2024. // &vecEyePos -
  2025. // &vecForward -
  2026. // flZoomScale -
  2027. //-----------------------------------------------------------------------------
  2028. void CSnowFallManager::CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale, C_BasePlayer *pLocalPlayer )
  2029. {
  2030. Vector vecParticleSpawn;
  2031. // Check/Setup for zoom.
  2032. bool bZoomed = ( flZoomScale > 1.0f );
  2033. float flZoomRadius = 0.0f;
  2034. Vector vecZoomEmitOrigin;
  2035. if ( bZoomed )
  2036. {
  2037. vecZoomEmitOrigin = m_vecSnowFallEmitOrigin + ( vecForward * ( r_SnowZoomOffset.GetFloat() * flZoomScale ) );
  2038. flZoomRadius = flRadius * flZoomScale;
  2039. }
  2040. int iIndex = 0;
  2041. // Add as many particles as we need
  2042. while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) )
  2043. {
  2044. // Check for a max particle count.
  2045. if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() )
  2046. continue;
  2047. // Create particle inside of sphere.
  2048. if ( iIndex > 0 )
  2049. {
  2050. CreateSnowParticlesSphere( flZoomRadius, pLocalPlayer );
  2051. CreateSnowParticlesRay( flZoomRadius, vecEyePos, vecForward, pLocalPlayer );
  2052. }
  2053. else
  2054. {
  2055. CreateSnowParticlesSphere( flRadius, pLocalPlayer );
  2056. CreateSnowParticlesRay( flRadius, vecEyePos, vecForward, pLocalPlayer );
  2057. }
  2058. // Increment if zoomed.
  2059. if ( bZoomed )
  2060. {
  2061. iIndex = ( iIndex + 1 ) % 3;
  2062. }
  2063. }
  2064. }
  2065. //-----------------------------------------------------------------------------
  2066. // Purpose:
  2067. // Input : flRadius -
  2068. //-----------------------------------------------------------------------------
  2069. void CSnowFallManager::CreateSnowParticlesSphere( float flRadius, C_BasePlayer *pLocalPlayer )
  2070. {
  2071. Vector vecParticleSpawn;
  2072. vecParticleSpawn.x = m_vecSnowFallEmitOrigin.x + RandomFloat( -flRadius, flRadius );
  2073. vecParticleSpawn.y = m_vecSnowFallEmitOrigin.y + RandomFloat( -flRadius, flRadius );
  2074. vecParticleSpawn.z = m_vecSnowFallEmitOrigin.z + RandomFloat( -flRadius, flRadius );
  2075. int iSnow = 0;
  2076. for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow )
  2077. {
  2078. if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) )
  2079. continue;
  2080. if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) )
  2081. continue;
  2082. if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) )
  2083. continue;
  2084. break;
  2085. }
  2086. if ( iSnow == m_nActiveSnowCount )
  2087. return;
  2088. CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow], pLocalPlayer );
  2089. }
  2090. //-----------------------------------------------------------------------------
  2091. // Purpose:
  2092. // Input : &vecEyePos -
  2093. // &vecForward -
  2094. //-----------------------------------------------------------------------------
  2095. void CSnowFallManager::CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward, C_BasePlayer *pLocalPlayer )
  2096. {
  2097. // Check to see if we should create particles along line-of-sight.
  2098. if ( !m_bRayParticles && r_SnowRayEnable.GetBool() )
  2099. return;
  2100. Vector vecParticleSpawn;
  2101. // Create a particle down the player's view beyond the radius.
  2102. float flRayRadius = r_SnowRayRadius.GetFloat();
  2103. Vector vecNewForward;
  2104. vecNewForward = vecForward * RandomFloat( flRadius, r_SnowRayLength.GetFloat() );
  2105. vecParticleSpawn.x = vecEyePos.x + vecNewForward.x;
  2106. vecParticleSpawn.y = vecEyePos.y + vecNewForward.y;
  2107. vecParticleSpawn.z = vecEyePos.z + RandomFloat( 72, flRayRadius );
  2108. vecParticleSpawn.x += RandomFloat( -flRayRadius, flRayRadius );
  2109. vecParticleSpawn.y += RandomFloat( -flRayRadius, flRayRadius );
  2110. int iSnow = 0;
  2111. for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow )
  2112. {
  2113. if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) )
  2114. continue;
  2115. if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) )
  2116. continue;
  2117. if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) )
  2118. continue;
  2119. break;
  2120. }
  2121. if ( iSnow == m_nActiveSnowCount )
  2122. return;
  2123. CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow], pLocalPlayer );
  2124. }
  2125. void CSnowFallManager::CreateSnowFallParticle( const Vector &vecParticleSpawn, int iSnow, C_BasePlayer *pLocalPlayer )
  2126. {
  2127. SimpleParticle *pParticle = ( SimpleParticle* )m_pSnowFallEmitter->AddParticle( sizeof( SimpleParticle ), m_aSnow[iSnow].m_hMaterial, vecParticleSpawn );
  2128. if ( pParticle == NULL )
  2129. return;
  2130. pParticle->m_flLifetime = 0.0f;
  2131. pParticle->m_vecVelocity = Vector( RandomFloat( -5.0f, 5.0f ), RandomFloat( -5.0f, 5.0f ), ( RandomFloat( -25, -35 ) * r_SnowFallSpeed.GetFloat() ) );
  2132. pParticle->m_flDieTime = fabs( ( vecParticleSpawn.z - m_aSnow[iSnow].m_vecMin.z ) / ( pParticle->m_vecVelocity.z - 0.1 ) );
  2133. // We finally now use the color of the func_precipitation entity for snow color.
  2134. pParticle->m_uchColor[0] = m_aSnow[iSnow].m_pEntity->GetRenderColorR();
  2135. pParticle->m_uchColor[1] = m_aSnow[iSnow].m_pEntity->GetRenderColorG();
  2136. pParticle->m_uchColor[2] = m_aSnow[iSnow].m_pEntity->GetRenderColorB();
  2137. //pParticle->m_uchColor[0] = r_SnowColorRed.GetInt();
  2138. //pParticle->m_uchColor[1] = r_SnowColorGreen.GetInt();
  2139. //pParticle->m_uchColor[2] = r_SnowColorBlue.GetInt();
  2140. pParticle->m_uchStartSize = r_SnowStartSize.GetInt();
  2141. pParticle->m_uchEndSize = r_SnowEndSize.GetInt();
  2142. // pParticle->m_uchStartAlpha = 255;
  2143. pParticle->m_uchStartAlpha = r_SnowStartAlpha.GetInt();
  2144. pParticle->m_uchEndAlpha = r_SnowEndAlpha.GetInt();
  2145. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  2146. pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f );
  2147. pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN;
  2148. }
  2149. //-----------------------------------------------------------------------------
  2150. // Purpose:
  2151. //-----------------------------------------------------------------------------
  2152. bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity )
  2153. {
  2154. bool bret = true;
  2155. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  2156. {
  2157. if ( !s_pSnowFallMgr[ i ] )
  2158. {
  2159. ACTIVE_SPLITSCREEN_PLAYER_GUARD( i );
  2160. s_pSnowFallMgr[ i ] = new CSnowFallManager();
  2161. if ( !s_pSnowFallMgr[ i ] )
  2162. {
  2163. bret = false;
  2164. break;
  2165. }
  2166. s_pSnowFallMgr[ i ]->SetSplitScreenPlayerSlot( i );
  2167. s_pSnowFallMgr[ i ]->CreateEmitter();
  2168. s_pSnowFallMgr[ i ]->InitializeAsClientEntity( NULL, false );
  2169. g_pClientLeafSystem->EnableRendering( s_pSnowFallMgr[ i ]->RenderHandle(), false );
  2170. }
  2171. s_pSnowFallMgr[ i ]->AddSnowFallEntity( pSnowEntity );
  2172. }
  2173. return bret;
  2174. }
  2175. //-----------------------------------------------------------------------------
  2176. // Purpose:
  2177. //-----------------------------------------------------------------------------
  2178. void SnowFallManagerDestroy( void )
  2179. {
  2180. for ( int i = 0; i < MAX_SPLITSCREEN_PLAYERS; ++i )
  2181. {
  2182. if ( s_pSnowFallMgr[ i ] )
  2183. {
  2184. delete s_pSnowFallMgr[ i ];
  2185. s_pSnowFallMgr[ i ] = NULL;
  2186. }
  2187. }
  2188. }