Team Fortress 2 Source Code as on 22/4/2020
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.

2224 lines
64 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "c_tracer.h"
  10. #include "view.h"
  11. #include "initializer.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 "clienteffectprecachesystem.h"
  22. #include "collisionutils.h"
  23. #include "tier0/vprof.h"
  24. #include "viewrender.h"
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. ConVar cl_winddir ( "cl_winddir", "0", FCVAR_CHEAT, "Weather effects wind direction angle" );
  28. ConVar cl_windspeed ( "cl_windspeed", "0", FCVAR_CHEAT, "Weather effects wind speed scalar" );
  29. Vector g_vSplashColor( 0.5, 0.5, 0.5 );
  30. float g_flSplashScale = 0.15;
  31. float g_flSplashLifetime = 0.5f;
  32. float g_flSplashAlpha = 0.3f;
  33. ConVar r_RainSplashPercentage( "r_RainSplashPercentage", "20", FCVAR_CHEAT ); // N% chance of a rain particle making a splash.
  34. float GUST_INTERVAL_MIN = 1;
  35. float GUST_INTERVAL_MAX = 2;
  36. float GUST_LIFETIME_MIN = 1;
  37. float GUST_LIFETIME_MAX = 3;
  38. float MIN_SCREENSPACE_RAIN_WIDTH = 1;
  39. #ifndef _XBOX
  40. ConVar r_RainHack( "r_RainHack", "0", FCVAR_CHEAT );
  41. ConVar r_RainRadius( "r_RainRadius", "1500", FCVAR_CHEAT );
  42. ConVar r_RainSideVel( "r_RainSideVel", "130", FCVAR_CHEAT, "How much sideways velocity rain gets." );
  43. ConVar r_RainSimulate( "r_RainSimulate", "1", FCVAR_CHEAT, "Enable/disable rain simulation." );
  44. ConVar r_DrawRain( "r_DrawRain", "1", FCVAR_CHEAT, "Enable/disable rain rendering." );
  45. ConVar r_RainProfile( "r_RainProfile", "0", FCVAR_CHEAT, "Enable/disable rain profiling." );
  46. //Precahce the effects
  47. CLIENTEFFECT_REGISTER_BEGIN( PrecachePrecipitation )
  48. CLIENTEFFECT_MATERIAL( "particle/rain" )
  49. CLIENTEFFECT_MATERIAL( "particle/snow" )
  50. CLIENTEFFECT_REGISTER_END()
  51. //-----------------------------------------------------------------------------
  52. // Precipitation particle type
  53. //-----------------------------------------------------------------------------
  54. class CPrecipitationParticle
  55. {
  56. public:
  57. Vector m_Pos;
  58. Vector m_Velocity;
  59. float m_SpawnTime; // Note: Tweak with this to change lifetime
  60. float m_Mass;
  61. float m_Ramp;
  62. float m_flCurLifetime;
  63. float m_flMaxLifetime;
  64. };
  65. class CClient_Precipitation;
  66. static CUtlVector<CClient_Precipitation*> g_Precipitations;
  67. //===========
  68. // Snow fall
  69. //===========
  70. class CSnowFallManager;
  71. static CSnowFallManager *s_pSnowFallMgr = NULL;
  72. bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity );
  73. void SnowFallManagerDestroy( void );
  74. class AshDebrisEffect : public CSimpleEmitter
  75. {
  76. public:
  77. AshDebrisEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {}
  78. static AshDebrisEffect* Create( const char *pDebugName );
  79. virtual float UpdateAlpha( const SimpleParticle *pParticle );
  80. virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta );
  81. private:
  82. AshDebrisEffect( const AshDebrisEffect & );
  83. };
  84. //-----------------------------------------------------------------------------
  85. // Precipitation base entity
  86. //-----------------------------------------------------------------------------
  87. class CClient_Precipitation : public C_BaseEntity
  88. {
  89. class CPrecipitationEffect;
  90. friend class CClient_Precipitation::CPrecipitationEffect;
  91. public:
  92. DECLARE_CLASS( CClient_Precipitation, C_BaseEntity );
  93. DECLARE_CLIENTCLASS();
  94. CClient_Precipitation();
  95. virtual ~CClient_Precipitation();
  96. // Inherited from C_BaseEntity
  97. virtual void Precache( );
  98. void Render();
  99. private:
  100. // Creates a single particle
  101. CPrecipitationParticle* CreateParticle();
  102. virtual void OnDataChanged( DataUpdateType_t updateType );
  103. virtual void ClientThink();
  104. void Simulate( float dt );
  105. // Renders the particle
  106. void RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb );
  107. void CreateWaterSplashes();
  108. // Emits the actual particles
  109. void EmitParticles( float fTimeDelta );
  110. // Computes where we're gonna emit
  111. bool ComputeEmissionArea( Vector& origin, Vector2D& size );
  112. // Gets the tracer width and speed
  113. float GetWidth() const;
  114. float GetLength() const;
  115. float GetSpeed() const;
  116. // Gets the remaining lifetime of the particle
  117. float GetRemainingLifetime( CPrecipitationParticle* pParticle ) const;
  118. // Computes the wind vector
  119. static void ComputeWindVector( );
  120. // simulation methods
  121. bool SimulateRain( CPrecipitationParticle* pParticle, float dt );
  122. bool SimulateSnow( CPrecipitationParticle* pParticle, float dt );
  123. void CreateAshParticle( void );
  124. void CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity );
  125. // Information helpful in creating and rendering particles
  126. IMaterial *m_MatHandle; // material used
  127. float m_Color[4]; // precip color
  128. float m_Lifetime; // Precip lifetime
  129. float m_InitialRamp; // Initial ramp value
  130. float m_Speed; // Precip speed
  131. float m_Width; // Tracer width
  132. float m_Remainder; // particles we should render next time
  133. PrecipitationType_t m_nPrecipType; // Precip type
  134. float m_flHalfScreenWidth; // Precalculated each frame.
  135. float m_flDensity;
  136. // Some state used in rendering and simulation
  137. // Used to modify the rain density and wind from the console
  138. static ConVar s_raindensity;
  139. static ConVar s_rainwidth;
  140. static ConVar s_rainlength;
  141. static ConVar s_rainspeed;
  142. static Vector s_WindVector; // Stores the wind speed vector
  143. CUtlLinkedList<CPrecipitationParticle> m_Particles;
  144. CUtlVector<Vector> m_Splashes;
  145. CSmartPtr<AshDebrisEffect> m_pAshEmitter;
  146. TimedEvent m_tAshParticleTimer;
  147. TimedEvent m_tAshParticleTraceTimer;
  148. bool m_bActiveAshEmitter;
  149. Vector m_vAshSpawnOrigin;
  150. int m_iAshCount;
  151. private:
  152. CClient_Precipitation( const CClient_Precipitation & ); // not defined, not accessible
  153. };
  154. // Just receive the normal data table stuff
  155. IMPLEMENT_CLIENTCLASS_DT(CClient_Precipitation, DT_Precipitation, CPrecipitation)
  156. RecvPropInt( RECVINFO( m_nPrecipType ) )
  157. END_RECV_TABLE()
  158. static ConVar r_SnowEnable( "r_SnowEnable", "1", FCVAR_CHEAT, "Snow Enable" );
  159. static ConVar r_SnowParticles( "r_SnowParticles", "500", FCVAR_CHEAT, "Snow." );
  160. static ConVar r_SnowInsideRadius( "r_SnowInsideRadius", "256", FCVAR_CHEAT, "Snow." );
  161. static ConVar r_SnowOutsideRadius( "r_SnowOutsideRadius", "1024", FCVAR_CHEAT, "Snow." );
  162. static ConVar r_SnowSpeedScale( "r_SnowSpeedScale", "1", FCVAR_CHEAT, "Snow." );
  163. static ConVar r_SnowPosScale( "r_SnowPosScale", "1", FCVAR_CHEAT, "Snow." );
  164. static ConVar r_SnowFallSpeed( "r_SnowFallSpeed", "1.5", FCVAR_CHEAT, "Snow fall speed scale." );
  165. static ConVar r_SnowWindScale( "r_SnowWindScale", "0.0035", FCVAR_CHEAT, "Snow." );
  166. static ConVar r_SnowDebugBox( "r_SnowDebugBox", "0", FCVAR_CHEAT, "Snow Debug Boxes." );
  167. static ConVar r_SnowZoomOffset( "r_SnowZoomOffset", "384.0f", FCVAR_CHEAT, "Snow." );
  168. static ConVar r_SnowZoomRadius( "r_SnowZoomRadius", "512.0f", FCVAR_CHEAT, "Snow." );
  169. static ConVar r_SnowStartAlpha( "r_SnowStartAlpha", "25", FCVAR_CHEAT, "Snow." );
  170. static ConVar r_SnowEndAlpha( "r_SnowEndAlpha", "255", FCVAR_CHEAT, "Snow." );
  171. static ConVar r_SnowColorRed( "r_SnowColorRed", "150", FCVAR_CHEAT, "Snow." );
  172. static ConVar r_SnowColorGreen( "r_SnowColorGreen", "175", FCVAR_CHEAT, "Snow." );
  173. static ConVar r_SnowColorBlue( "r_SnowColorBlue", "200", FCVAR_CHEAT, "Snow." );
  174. static ConVar r_SnowStartSize( "r_SnowStartSize", "1", FCVAR_CHEAT, "Snow." );
  175. static ConVar r_SnowEndSize( "r_SnowEndSize", "0", FCVAR_CHEAT, "Snow." );
  176. static ConVar r_SnowRayLength( "r_SnowRayLength", "8192.0f", FCVAR_CHEAT, "Snow." );
  177. static ConVar r_SnowRayRadius( "r_SnowRayRadius", "256", FCVAR_CHEAT, "Snow." );
  178. static ConVar r_SnowRayEnable( "r_SnowRayEnable", "1", FCVAR_CHEAT, "Snow." );
  179. void DrawPrecipitation()
  180. {
  181. for ( int i=0; i < g_Precipitations.Count(); i++ )
  182. {
  183. g_Precipitations[i]->Render();
  184. }
  185. }
  186. //-----------------------------------------------------------------------------
  187. // determines if a weather particle has hit something other than air
  188. //-----------------------------------------------------------------------------
  189. static bool IsInAir( const Vector& position )
  190. {
  191. int contents = enginetrace->GetPointContents( position );
  192. return (contents & CONTENTS_SOLID) == 0;
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Globals
  196. //-----------------------------------------------------------------------------
  197. ConVar CClient_Precipitation::s_raindensity( "r_raindensity","0.001", FCVAR_CHEAT);
  198. ConVar CClient_Precipitation::s_rainwidth( "r_rainwidth", "0.5", FCVAR_CHEAT );
  199. ConVar CClient_Precipitation::s_rainlength( "r_rainlength", "0.1f", FCVAR_CHEAT );
  200. ConVar CClient_Precipitation::s_rainspeed( "r_rainspeed", "600.0f", FCVAR_CHEAT );
  201. ConVar r_rainalpha( "r_rainalpha", "0.4", FCVAR_CHEAT );
  202. ConVar r_rainalphapow( "r_rainalphapow", "0.8", FCVAR_CHEAT );
  203. Vector CClient_Precipitation::s_WindVector; // Stores the wind speed vector
  204. void CClient_Precipitation::OnDataChanged( DataUpdateType_t updateType )
  205. {
  206. // Simulate every frame.
  207. if ( updateType == DATA_UPDATE_CREATED )
  208. {
  209. SetNextClientThink( CLIENT_THINK_ALWAYS );
  210. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  211. {
  212. SnowFallManagerCreate( this );
  213. }
  214. }
  215. m_flDensity = RemapVal( m_clrRender->a, 0, 255, 0, 0.001 );
  216. BaseClass::OnDataChanged( updateType );
  217. }
  218. void CClient_Precipitation::ClientThink()
  219. {
  220. Simulate( gpGlobals->frametime );
  221. }
  222. //-----------------------------------------------------------------------------
  223. //
  224. // Utility methods for the various simulation functions
  225. //
  226. //-----------------------------------------------------------------------------
  227. inline bool CClient_Precipitation::SimulateRain( CPrecipitationParticle* pParticle, float dt )
  228. {
  229. if (GetRemainingLifetime( pParticle ) < 0.0f)
  230. return false;
  231. Vector vOldPos = pParticle->m_Pos;
  232. // Update position
  233. VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity,
  234. pParticle->m_Pos );
  235. // wind blows rain around
  236. for ( int i = 0 ; i < 2 ; i++ )
  237. {
  238. if ( pParticle->m_Velocity[i] < s_WindVector[i] )
  239. {
  240. pParticle->m_Velocity[i] += ( 5 / pParticle->m_Mass );
  241. // clamp
  242. if ( pParticle->m_Velocity[i] > s_WindVector[i] )
  243. pParticle->m_Velocity[i] = s_WindVector[i];
  244. }
  245. else if (pParticle->m_Velocity[i] > s_WindVector[i] )
  246. {
  247. pParticle->m_Velocity[i] -= ( 5 / pParticle->m_Mass );
  248. // clamp.
  249. if ( pParticle->m_Velocity[i] < s_WindVector[i] )
  250. pParticle->m_Velocity[i] = s_WindVector[i];
  251. }
  252. }
  253. // No longer in the air? punt.
  254. if ( !IsInAir( pParticle->m_Pos ) )
  255. {
  256. // Possibly make a splash if we hit a water surface and it's in front of the view.
  257. if ( m_Splashes.Count() < 20 )
  258. {
  259. if ( RandomInt( 0, 100 ) < r_RainSplashPercentage.GetInt() )
  260. {
  261. trace_t trace;
  262. UTIL_TraceLine(vOldPos, pParticle->m_Pos, MASK_WATER, NULL, COLLISION_GROUP_NONE, &trace);
  263. if( trace.fraction < 1 )
  264. {
  265. m_Splashes.AddToTail( trace.endpos );
  266. }
  267. }
  268. }
  269. // Tell the framework it's time to remove the particle from the list
  270. return false;
  271. }
  272. // We still want this particle
  273. return true;
  274. }
  275. inline bool CClient_Precipitation::SimulateSnow( CPrecipitationParticle* pParticle, float dt )
  276. {
  277. if ( IsInAir( pParticle->m_Pos ) )
  278. {
  279. // Update position
  280. VectorMA( pParticle->m_Pos, dt, pParticle->m_Velocity,
  281. pParticle->m_Pos );
  282. // wind blows rain around
  283. for ( int i = 0 ; i < 2 ; i++ )
  284. {
  285. if ( pParticle->m_Velocity[i] < s_WindVector[i] )
  286. {
  287. pParticle->m_Velocity[i] += ( 5.0f / pParticle->m_Mass );
  288. // accelerating flakes get a trail
  289. pParticle->m_Ramp = 0.5f;
  290. // clamp
  291. if ( pParticle->m_Velocity[i] > s_WindVector[i] )
  292. pParticle->m_Velocity[i] = s_WindVector[i];
  293. }
  294. else if (pParticle->m_Velocity[i] > s_WindVector[i] )
  295. {
  296. pParticle->m_Velocity[i] -= ( 5.0f / pParticle->m_Mass );
  297. // accelerating flakes get a trail
  298. pParticle->m_Ramp = 0.5f;
  299. // clamp.
  300. if ( pParticle->m_Velocity[i] < s_WindVector[i] )
  301. pParticle->m_Velocity[i] = s_WindVector[i];
  302. }
  303. }
  304. return true;
  305. }
  306. // Kill the particle immediately!
  307. return false;
  308. }
  309. void CClient_Precipitation::Simulate( float dt )
  310. {
  311. // NOTE: When client-side prechaching works, we need to remove this
  312. Precache();
  313. m_flHalfScreenWidth = (float)ScreenWidth() / 2;
  314. // Our sim methods needs dt and wind vector
  315. if ( dt )
  316. {
  317. ComputeWindVector( );
  318. }
  319. if ( m_nPrecipType == PRECIPITATION_TYPE_ASH )
  320. {
  321. CreateAshParticle();
  322. return;
  323. }
  324. // The snow fall manager handles the simulation.
  325. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  326. return;
  327. // calculate the max amount of time it will take this flake to fall.
  328. // This works if we assume the wind doesn't have a z component
  329. if ( r_RainHack.GetInt() )
  330. m_Lifetime = (GetClientWorldEntity()->m_WorldMaxs[2] - GetClientWorldEntity()->m_WorldMins[2]) / m_Speed;
  331. else
  332. m_Lifetime = (WorldAlignMaxs()[2] - WorldAlignMins()[2]) / m_Speed;
  333. if ( !r_RainSimulate.GetInt() )
  334. return;
  335. CFastTimer timer;
  336. timer.Start();
  337. // Emit new particles
  338. EmitParticles( dt );
  339. // Simulate all the particles.
  340. int iNext;
  341. if ( m_nPrecipType == PRECIPITATION_TYPE_RAIN )
  342. {
  343. for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext )
  344. {
  345. iNext = m_Particles.Next( i );
  346. if ( !SimulateRain( &m_Particles[i], dt ) )
  347. m_Particles.Remove( i );
  348. }
  349. }
  350. else if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW )
  351. {
  352. for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=iNext )
  353. {
  354. iNext = m_Particles.Next( i );
  355. if ( !SimulateSnow( &m_Particles[i], dt ) )
  356. m_Particles.Remove( i );
  357. }
  358. }
  359. if ( r_RainProfile.GetInt() )
  360. {
  361. timer.End();
  362. engine->Con_NPrintf( 15, "Rain simulation: %du (%d tracers)", timer.GetDuration().GetMicroseconds(), m_Particles.Count() );
  363. }
  364. }
  365. //-----------------------------------------------------------------------------
  366. // tracer rendering
  367. //-----------------------------------------------------------------------------
  368. inline void CClient_Precipitation::RenderParticle( CPrecipitationParticle* pParticle, CMeshBuilder &mb )
  369. {
  370. float scale;
  371. Vector start, delta;
  372. if ( m_nPrecipType == PRECIPITATION_TYPE_ASH )
  373. return;
  374. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  375. return;
  376. // make streaks 0.1 seconds long, but prevent from going past end
  377. float lifetimeRemaining = GetRemainingLifetime( pParticle );
  378. if (lifetimeRemaining >= GetLength())
  379. scale = GetLength() * pParticle->m_Ramp;
  380. else
  381. scale = lifetimeRemaining * pParticle->m_Ramp;
  382. // NOTE: We need to do everything in screen space
  383. Vector3DMultiplyPosition( CurrentWorldToViewMatrix(), pParticle->m_Pos, start );
  384. if ( start.z > -1 )
  385. return;
  386. Vector3DMultiply( CurrentWorldToViewMatrix(), pParticle->m_Velocity, delta );
  387. // give a spiraling pattern to snow particles
  388. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOW )
  389. {
  390. Vector spiral, camSpiral;
  391. float s, c;
  392. if ( pParticle->m_Mass > 1.0f )
  393. {
  394. SinCos( gpGlobals->curtime * M_PI * (1+pParticle->m_Mass * 0.1f) +
  395. pParticle->m_Mass * 5.0f, &s , &c );
  396. // only spiral particles with a mass > 1, so some fall straight down
  397. spiral[0] = 28 * c;
  398. spiral[1] = 28 * s;
  399. spiral[2] = 0.0f;
  400. Vector3DMultiply( CurrentWorldToViewMatrix(), spiral, camSpiral );
  401. // X and Y are measured in world space; need to convert to camera space
  402. VectorAdd( start, camSpiral, start );
  403. VectorAdd( delta, camSpiral, delta );
  404. }
  405. // shrink the trails on spiraling flakes.
  406. pParticle->m_Ramp = 0.3f;
  407. }
  408. delta[0] *= scale;
  409. delta[1] *= scale;
  410. delta[2] *= scale;
  411. // See c_tracer.* for this method
  412. float flAlpha = r_rainalpha.GetFloat();
  413. float flWidth = GetWidth();
  414. float flScreenSpaceWidth = flWidth * m_flHalfScreenWidth / -start.z;
  415. if ( flScreenSpaceWidth < MIN_SCREENSPACE_RAIN_WIDTH )
  416. {
  417. // Make the rain tracer at least the min size, but fade its alpha the smaller it gets.
  418. flAlpha *= flScreenSpaceWidth / MIN_SCREENSPACE_RAIN_WIDTH;
  419. flWidth = MIN_SCREENSPACE_RAIN_WIDTH * -start.z / m_flHalfScreenWidth;
  420. }
  421. flAlpha = pow( flAlpha, r_rainalphapow.GetFloat() );
  422. float flColor[4] = { 1, 1, 1, flAlpha };
  423. Tracer_Draw( &mb, start, delta, flWidth, flColor, 1 );
  424. }
  425. void CClient_Precipitation::CreateWaterSplashes()
  426. {
  427. for ( int i=0; i < m_Splashes.Count(); i++ )
  428. {
  429. Vector vSplash = m_Splashes[i];
  430. if ( CurrentViewForward().Dot( vSplash - CurrentViewOrigin() ) > 1 )
  431. {
  432. FX_WaterRipple( vSplash, g_flSplashScale, &g_vSplashColor, g_flSplashLifetime, g_flSplashAlpha );
  433. }
  434. }
  435. m_Splashes.Purge();
  436. }
  437. void CClient_Precipitation::Render()
  438. {
  439. if ( !r_DrawRain.GetInt() )
  440. return;
  441. // Don't render in monitors or in reflections or refractions.
  442. if ( CurrentViewID() == VIEW_MONITOR )
  443. return;
  444. if ( view->GetDrawFlags() & (DF_RENDER_REFLECTION | DF_RENDER_REFRACTION) )
  445. return;
  446. if ( m_nPrecipType == PRECIPITATION_TYPE_ASH )
  447. return;
  448. if ( m_nPrecipType == PRECIPITATION_TYPE_SNOWFALL )
  449. return;
  450. // Create any queued up water splashes.
  451. CreateWaterSplashes();
  452. CFastTimer timer;
  453. timer.Start();
  454. CMatRenderContextPtr pRenderContext( materials );
  455. // We want to do our calculations in view space.
  456. VMatrix tempView;
  457. pRenderContext->GetMatrix( MATERIAL_VIEW, &tempView );
  458. pRenderContext->MatrixMode( MATERIAL_VIEW );
  459. pRenderContext->LoadIdentity();
  460. // Force the user clip planes to use the old view matrix
  461. pRenderContext->EnableUserClipTransformOverride( true );
  462. pRenderContext->UserClipTransform( tempView );
  463. // Draw all the rain tracers.
  464. pRenderContext->Bind( m_MatHandle );
  465. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  466. if ( pMesh )
  467. {
  468. CMeshBuilder mb;
  469. mb.Begin( pMesh, MATERIAL_QUADS, m_Particles.Count() );
  470. for ( int i=m_Particles.Head(); i != m_Particles.InvalidIndex(); i=m_Particles.Next( i ) )
  471. {
  472. CPrecipitationParticle *p = &m_Particles[i];
  473. RenderParticle( p, mb );
  474. }
  475. mb.End( false, true );
  476. }
  477. pRenderContext->EnableUserClipTransformOverride( false );
  478. pRenderContext->MatrixMode( MATERIAL_VIEW );
  479. pRenderContext->LoadMatrix( tempView );
  480. if ( r_RainProfile.GetInt() )
  481. {
  482. timer.End();
  483. engine->Con_NPrintf( 16, "Rain render : %du", timer.GetDuration().GetMicroseconds() );
  484. }
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Constructor, destructor
  488. //-----------------------------------------------------------------------------
  489. CClient_Precipitation::CClient_Precipitation() : m_Remainder(0.0f)
  490. {
  491. m_nPrecipType = PRECIPITATION_TYPE_RAIN;
  492. m_MatHandle = INVALID_MATERIAL_HANDLE;
  493. m_flHalfScreenWidth = 1;
  494. g_Precipitations.AddToTail( this );
  495. }
  496. CClient_Precipitation::~CClient_Precipitation()
  497. {
  498. g_Precipitations.FindAndRemove( this );
  499. SnowFallManagerDestroy();
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Precache data
  503. //-----------------------------------------------------------------------------
  504. #define SNOW_SPEED 80.0f
  505. #define RAIN_SPEED 425.0f
  506. #define RAIN_TRACER_WIDTH 0.35f
  507. #define SNOW_TRACER_WIDTH 0.7f
  508. void CClient_Precipitation::Precache( )
  509. {
  510. if ( !m_MatHandle )
  511. {
  512. // Compute precipitation emission speed
  513. switch( m_nPrecipType )
  514. {
  515. case PRECIPITATION_TYPE_SNOW:
  516. m_Speed = SNOW_SPEED;
  517. m_MatHandle = materials->FindMaterial( "particle/snow", TEXTURE_GROUP_CLIENT_EFFECTS );
  518. m_InitialRamp = 0.6f;
  519. m_Width = SNOW_TRACER_WIDTH;
  520. break;
  521. case PRECIPITATION_TYPE_RAIN:
  522. Assert( m_nPrecipType == PRECIPITATION_TYPE_RAIN );
  523. m_Speed = RAIN_SPEED;
  524. m_MatHandle = materials->FindMaterial( "particle/rain", TEXTURE_GROUP_CLIENT_EFFECTS );
  525. m_InitialRamp = 1.0f;
  526. m_Color[3] = 1.0f; // make translucent
  527. m_Width = RAIN_TRACER_WIDTH;
  528. break;
  529. default:
  530. m_InitialRamp = 1.0f;
  531. m_Color[3] = 1.0f; // make translucent
  532. break;
  533. }
  534. // Store off the color
  535. m_Color[0] = 1.0f;
  536. m_Color[1] = 1.0f;
  537. m_Color[2] = 1.0f;
  538. }
  539. }
  540. //-----------------------------------------------------------------------------
  541. // Gets the tracer width and speed
  542. //-----------------------------------------------------------------------------
  543. inline float CClient_Precipitation::GetWidth() const
  544. {
  545. // return m_Width;
  546. return s_rainwidth.GetFloat();
  547. }
  548. inline float CClient_Precipitation::GetLength() const
  549. {
  550. // return m_Length;
  551. return s_rainlength.GetFloat();
  552. }
  553. inline float CClient_Precipitation::GetSpeed() const
  554. {
  555. // return m_Speed;
  556. return s_rainspeed.GetFloat();
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Gets the remaining lifetime of the particle
  560. //-----------------------------------------------------------------------------
  561. inline float CClient_Precipitation::GetRemainingLifetime( CPrecipitationParticle* pParticle ) const
  562. {
  563. float timeSinceSpawn = gpGlobals->curtime - pParticle->m_SpawnTime;
  564. return m_Lifetime - timeSinceSpawn;
  565. }
  566. //-----------------------------------------------------------------------------
  567. // Creates a particle
  568. //-----------------------------------------------------------------------------
  569. inline CPrecipitationParticle* CClient_Precipitation::CreateParticle()
  570. {
  571. int i = m_Particles.AddToTail();
  572. CPrecipitationParticle* pParticle = &m_Particles[i];
  573. pParticle->m_SpawnTime = gpGlobals->curtime;
  574. pParticle->m_Ramp = m_InitialRamp;
  575. return pParticle;
  576. }
  577. //-----------------------------------------------------------------------------
  578. // Compute the emission area
  579. //-----------------------------------------------------------------------------
  580. bool CClient_Precipitation::ComputeEmissionArea( Vector& origin, Vector2D& size )
  581. {
  582. // FIXME: Compute the precipitation area based on computational power
  583. float emissionSize = r_RainRadius.GetFloat(); // size of box to emit particles in
  584. Vector vMins = WorldAlignMins();
  585. Vector vMaxs = WorldAlignMaxs();
  586. if ( r_RainHack.GetInt() )
  587. {
  588. vMins = GetClientWorldEntity()->m_WorldMins;
  589. vMaxs = GetClientWorldEntity()->m_WorldMaxs;
  590. }
  591. // calculate a volume around the player to snow in. Intersect this big magic
  592. // box around the player with the volume of the current environmental ent.
  593. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  594. if ( !pPlayer )
  595. return false;
  596. // Determine how much time it'll take a falling particle to hit the player
  597. float emissionHeight = MIN( vMaxs[2], pPlayer->GetAbsOrigin()[2] + 512 );
  598. float distToFall = emissionHeight - pPlayer->GetAbsOrigin()[2];
  599. float fallTime = distToFall / GetSpeed();
  600. // Based on the windspeed, figure out the center point of the emission
  601. Vector2D center;
  602. center[0] = pPlayer->GetAbsOrigin()[0] - fallTime * s_WindVector[0];
  603. center[1] = pPlayer->GetAbsOrigin()[1] - fallTime * s_WindVector[1];
  604. Vector2D lobound, hibound;
  605. lobound[0] = center[0] - emissionSize * 0.5f;
  606. lobound[1] = center[1] - emissionSize * 0.5f;
  607. hibound[0] = lobound[0] + emissionSize;
  608. hibound[1] = lobound[1] + emissionSize;
  609. // Cull non-intersecting.
  610. if ( ( vMaxs[0] < lobound[0] ) || ( vMaxs[1] < lobound[1] ) ||
  611. ( vMins[0] > hibound[0] ) || ( vMins[1] > hibound[1] ) )
  612. return false;
  613. origin[0] = MAX( vMins[0], lobound[0] );
  614. origin[1] = MAX( vMins[1], lobound[1] );
  615. origin[2] = emissionHeight;
  616. hibound[0] = MIN( vMaxs[0], hibound[0] );
  617. hibound[1] = MIN( vMaxs[1], hibound[1] );
  618. size[0] = hibound[0] - origin[0];
  619. size[1] = hibound[1] - origin[1];
  620. return true;
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Purpose:
  624. // Input : *pDebugName -
  625. // Output : AshDebrisEffect*
  626. //-----------------------------------------------------------------------------
  627. AshDebrisEffect* AshDebrisEffect::Create( const char *pDebugName )
  628. {
  629. return new AshDebrisEffect( pDebugName );
  630. }
  631. //-----------------------------------------------------------------------------
  632. // Purpose:
  633. // Input : *pParticle -
  634. // timeDelta -
  635. // Output : float
  636. //-----------------------------------------------------------------------------
  637. float AshDebrisEffect::UpdateAlpha( const SimpleParticle *pParticle )
  638. {
  639. return ( ((float)pParticle->m_uchStartAlpha/255.0f) * sin( M_PI * (pParticle->m_flLifetime / pParticle->m_flDieTime) ) );
  640. }
  641. #define ASH_PARTICLE_NOISE 0x4
  642. float AshDebrisEffect::UpdateRoll( SimpleParticle *pParticle, float timeDelta )
  643. {
  644. float flRoll = CSimpleEmitter::UpdateRoll(pParticle, timeDelta );
  645. if ( pParticle->m_iFlags & ASH_PARTICLE_NOISE )
  646. {
  647. Vector vTempEntVel = pParticle->m_vecVelocity;
  648. float fastFreq = gpGlobals->curtime * 1.5;
  649. float s, c;
  650. SinCos( fastFreq, &s, &c );
  651. pParticle->m_Pos = ( pParticle->m_Pos + Vector(
  652. vTempEntVel[0] * timeDelta * s,
  653. vTempEntVel[1] * timeDelta * s, 0 ) );
  654. }
  655. return flRoll;
  656. }
  657. void CClient_Precipitation::CreateAshParticle( void )
  658. {
  659. // Make sure the emitter is setup
  660. if ( m_pAshEmitter == NULL )
  661. {
  662. if ( ( m_pAshEmitter = AshDebrisEffect::Create( "ashtray" ) ) == NULL )
  663. return;
  664. m_tAshParticleTimer.Init( 192 );
  665. m_tAshParticleTraceTimer.Init( 15 );
  666. m_bActiveAshEmitter = false;
  667. m_iAshCount = 0;
  668. }
  669. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  670. if ( pPlayer == NULL )
  671. return;
  672. Vector vForward;
  673. pPlayer->GetVectors( &vForward, NULL, NULL );
  674. vForward.z = 0.0f;
  675. float curTime = gpGlobals->frametime;
  676. Vector vPushOrigin;
  677. Vector absmins = WorldAlignMins();
  678. Vector absmaxs = WorldAlignMaxs();
  679. //15 Traces a second.
  680. while ( m_tAshParticleTraceTimer.NextEvent( curTime ) )
  681. {
  682. trace_t tr;
  683. Vector vTraceStart = pPlayer->EyePosition();
  684. Vector vTraceEnd = pPlayer->EyePosition() + vForward * MAX_TRACE_LENGTH;
  685. UTIL_TraceLine( vTraceStart, vTraceEnd, MASK_SHOT_HULL & (~CONTENTS_GRATE), pPlayer, COLLISION_GROUP_NONE, &tr );
  686. //debugoverlay->AddLineOverlay( vTraceStart, tr.endpos, 255, 0, 0, 0, 0.2 );
  687. if ( tr.fraction != 1.0f )
  688. {
  689. trace_t tr2;
  690. UTIL_TraceModel( vTraceStart, tr.endpos, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), this, COLLISION_GROUP_NONE, &tr2 );
  691. if ( tr2.m_pEnt == this )
  692. {
  693. m_bActiveAshEmitter = true;
  694. if ( tr2.startsolid == false )
  695. {
  696. m_vAshSpawnOrigin = tr2.endpos + vForward * 256;
  697. }
  698. else
  699. {
  700. m_vAshSpawnOrigin = vTraceStart;
  701. }
  702. }
  703. else
  704. {
  705. m_bActiveAshEmitter = false;
  706. }
  707. }
  708. }
  709. if ( m_bActiveAshEmitter == false )
  710. return;
  711. Vector vecVelocity = pPlayer->GetAbsVelocity();
  712. float flVelocity = VectorNormalize( vecVelocity );
  713. Vector offset = m_vAshSpawnOrigin;
  714. m_pAshEmitter->SetSortOrigin( offset );
  715. PMaterialHandle hMaterial[4];
  716. hMaterial[0] = ParticleMgr()->GetPMaterial( "effects/fleck_ash1" );
  717. hMaterial[1] = ParticleMgr()->GetPMaterial( "effects/fleck_ash2" );
  718. hMaterial[2] = ParticleMgr()->GetPMaterial( "effects/fleck_ash3" );
  719. hMaterial[3] = ParticleMgr()->GetPMaterial( "effects/ember_swirling001" );
  720. SimpleParticle *pParticle;
  721. Vector vSpawnOrigin = vec3_origin;
  722. if ( flVelocity > 0 )
  723. {
  724. vSpawnOrigin = ( vForward * 256 ) + ( vecVelocity * ( flVelocity * 2 ) );
  725. }
  726. // Add as many particles as we need
  727. while ( m_tAshParticleTimer.NextEvent( curTime ) )
  728. {
  729. int iRandomAltitude = RandomInt( 0, 128 );
  730. offset = m_vAshSpawnOrigin + vSpawnOrigin + RandomVector( -256, 256 );
  731. offset.z = m_vAshSpawnOrigin.z + iRandomAltitude;
  732. if ( offset[0] > absmaxs[0]
  733. || offset[1] > absmaxs[1]
  734. || offset[2] > absmaxs[2]
  735. || offset[0] < absmins[0]
  736. || offset[1] < absmins[1]
  737. || offset[2] < absmins[2] )
  738. continue;
  739. m_iAshCount++;
  740. bool bEmberTime = false;
  741. if ( m_iAshCount >= 250 )
  742. {
  743. bEmberTime = true;
  744. m_iAshCount = 0;
  745. }
  746. int iRandom = random->RandomInt(0,2);
  747. if ( bEmberTime == true )
  748. {
  749. offset = m_vAshSpawnOrigin + (vForward * 256) + RandomVector( -128, 128 );
  750. offset.z = pPlayer->EyePosition().z + RandomFloat( -16, 64 );
  751. iRandom = 3;
  752. }
  753. pParticle = (SimpleParticle *) m_pAshEmitter->AddParticle( sizeof(SimpleParticle), hMaterial[iRandom], offset );
  754. if (pParticle == NULL)
  755. continue;
  756. pParticle->m_flLifetime = 0.0f;
  757. pParticle->m_flDieTime = RemapVal( iRandomAltitude, 0, 128, 4, 8 );
  758. if ( bEmberTime == true )
  759. {
  760. Vector vGoal = pPlayer->EyePosition() + RandomVector( -64, 64 );
  761. Vector vDir = vGoal - offset;
  762. VectorNormalize( vDir );
  763. pParticle->m_vecVelocity = vDir * 75;
  764. pParticle->m_flDieTime = 2.5f;
  765. }
  766. else
  767. {
  768. pParticle->m_vecVelocity = Vector( RandomFloat( -20.0f, 20.0f ), RandomFloat( -20.0f, 20.0f ), RandomFloat( -10, -15 ) );
  769. }
  770. float color = random->RandomInt( 125, 225 );
  771. pParticle->m_uchColor[0] = color;
  772. pParticle->m_uchColor[1] = color;
  773. pParticle->m_uchColor[2] = color;
  774. pParticle->m_uchStartSize = 1;
  775. pParticle->m_uchEndSize = 1;
  776. pParticle->m_uchStartAlpha = 255;
  777. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  778. pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f );
  779. pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN;
  780. if ( random->RandomInt( 0, 10 ) <= 1 )
  781. {
  782. pParticle->m_iFlags |= ASH_PARTICLE_NOISE;
  783. }
  784. }
  785. }
  786. void CClient_Precipitation::CreateRainOrSnowParticle( Vector vSpawnPosition, Vector vVelocity )
  787. {
  788. // Create the particle
  789. CPrecipitationParticle* p = CreateParticle();
  790. if (!p)
  791. return;
  792. VectorCopy( vVelocity, p->m_Velocity );
  793. p->m_Pos = vSpawnPosition;
  794. p->m_Velocity[ 0 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt());
  795. p->m_Velocity[ 1 ] += random->RandomFloat(-r_RainSideVel.GetInt(), r_RainSideVel.GetInt());
  796. p->m_Mass = random->RandomFloat( 0.5, 1.5 );
  797. }
  798. //-----------------------------------------------------------------------------
  799. // emit the precipitation particles
  800. //-----------------------------------------------------------------------------
  801. void CClient_Precipitation::EmitParticles( float fTimeDelta )
  802. {
  803. Vector2D size;
  804. Vector vel, org;
  805. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  806. if ( !pPlayer )
  807. return;
  808. Vector vPlayerCenter = pPlayer->WorldSpaceCenter();
  809. // Compute where to emit
  810. if (!ComputeEmissionArea( org, size ))
  811. return;
  812. // clamp this to prevent creating a bunch of rain or snow at one time.
  813. if( fTimeDelta > 0.075f )
  814. fTimeDelta = 0.075f;
  815. // FIXME: Compute the precipitation density based on computational power
  816. float density = m_flDensity;
  817. if (density > 0.01f)
  818. density = 0.01f;
  819. // Compute number of particles to emit based on precip density and emission area and dt
  820. float fParticles = size[0] * size[1] * density * fTimeDelta + m_Remainder;
  821. int cParticles = (int)fParticles;
  822. m_Remainder = fParticles - cParticles;
  823. // calculate the max amount of time it will take this flake to fall.
  824. // This works if we assume the wind doesn't have a z component
  825. VectorCopy( s_WindVector, vel );
  826. vel[2] -= GetSpeed();
  827. // Emit all the particles
  828. for ( int i = 0 ; i < cParticles ; i++ )
  829. {
  830. Vector vParticlePos = org;
  831. vParticlePos[ 0 ] += size[ 0 ] * random->RandomFloat(0, 1);
  832. vParticlePos[ 1 ] += size[ 1 ] * random->RandomFloat(0, 1);
  833. // Figure out where the particle should lie in Z by tracing a line from the player's height up to the
  834. // desired height and making sure it doesn't hit a wall.
  835. Vector vPlayerHeight = vParticlePos;
  836. vPlayerHeight.z = vPlayerCenter.z;
  837. trace_t trace;
  838. UTIL_TraceLine( vPlayerHeight, vParticlePos, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace );
  839. if ( trace.fraction < 1 )
  840. {
  841. // If we hit a brush, then don't spawn the particle.
  842. if ( trace.surface.flags & SURF_SKY )
  843. {
  844. vParticlePos = trace.endpos;
  845. }
  846. else
  847. {
  848. continue;
  849. }
  850. }
  851. CreateRainOrSnowParticle( vParticlePos, vel );
  852. }
  853. }
  854. //-----------------------------------------------------------------------------
  855. // Computes the wind vector
  856. //-----------------------------------------------------------------------------
  857. void CClient_Precipitation::ComputeWindVector( )
  858. {
  859. // Compute the wind direction
  860. QAngle windangle( 0, cl_winddir.GetFloat(), 0 ); // used to turn wind yaw direction into a vector
  861. // Randomize the wind angle and speed slightly to get us a little variation
  862. windangle[1] = windangle[1] + random->RandomFloat( -10, 10 );
  863. float windspeed = cl_windspeed.GetFloat() * (1.0 + random->RandomFloat( -0.2, 0.2 ));
  864. AngleVectors( windangle, &s_WindVector );
  865. VectorScale( s_WindVector, windspeed, s_WindVector );
  866. }
  867. CHandle<CClient_Precipitation> g_pPrecipHackEnt;
  868. class CPrecipHack : public CAutoGameSystemPerFrame
  869. {
  870. public:
  871. CPrecipHack( char const *name ) : CAutoGameSystemPerFrame( name )
  872. {
  873. m_bLevelInitted = false;
  874. }
  875. virtual void LevelInitPostEntity()
  876. {
  877. if ( r_RainHack.GetInt() )
  878. {
  879. CClient_Precipitation *pPrecipHackEnt = new CClient_Precipitation;
  880. pPrecipHackEnt->InitializeAsClientEntity( NULL, RENDER_GROUP_TRANSLUCENT_ENTITY );
  881. g_pPrecipHackEnt = pPrecipHackEnt;
  882. }
  883. m_bLevelInitted = true;
  884. }
  885. virtual void LevelShutdownPreEntity()
  886. {
  887. if ( r_RainHack.GetInt() && g_pPrecipHackEnt )
  888. {
  889. g_pPrecipHackEnt->Release();
  890. }
  891. m_bLevelInitted = false;
  892. }
  893. virtual void Update( float frametime )
  894. {
  895. // Handle changes to the cvar at runtime.
  896. if ( m_bLevelInitted )
  897. {
  898. if ( r_RainHack.GetInt() && !g_pPrecipHackEnt )
  899. LevelInitPostEntity();
  900. else if ( !r_RainHack.GetInt() && g_pPrecipHackEnt )
  901. LevelShutdownPreEntity();
  902. }
  903. }
  904. bool m_bLevelInitted;
  905. };
  906. CPrecipHack g_PrecipHack( "CPrecipHack" );
  907. #else
  908. void DrawPrecipitation()
  909. {
  910. }
  911. #endif // _XBOX
  912. //-----------------------------------------------------------------------------
  913. // EnvWind - global wind info
  914. //-----------------------------------------------------------------------------
  915. class C_EnvWind : public C_BaseEntity
  916. {
  917. public:
  918. C_EnvWind();
  919. DECLARE_CLIENTCLASS();
  920. DECLARE_CLASS( C_EnvWind, C_BaseEntity );
  921. virtual void OnDataChanged( DataUpdateType_t updateType );
  922. virtual bool ShouldDraw( void ) { return false; }
  923. virtual void ClientThink( );
  924. private:
  925. C_EnvWind( const C_EnvWind & );
  926. CEnvWindShared m_EnvWindShared;
  927. };
  928. // Receive datatables
  929. BEGIN_RECV_TABLE_NOBASE(CEnvWindShared, DT_EnvWindShared)
  930. RecvPropInt (RECVINFO(m_iMinWind)),
  931. RecvPropInt (RECVINFO(m_iMaxWind)),
  932. RecvPropInt (RECVINFO(m_iMinGust)),
  933. RecvPropInt (RECVINFO(m_iMaxGust)),
  934. RecvPropFloat (RECVINFO(m_flMinGustDelay)),
  935. RecvPropFloat (RECVINFO(m_flMaxGustDelay)),
  936. RecvPropInt (RECVINFO(m_iGustDirChange)),
  937. RecvPropInt (RECVINFO(m_iWindSeed)),
  938. RecvPropInt (RECVINFO(m_iInitialWindDir)),
  939. RecvPropFloat (RECVINFO(m_flInitialWindSpeed)),
  940. RecvPropFloat (RECVINFO(m_flStartTime)),
  941. RecvPropFloat (RECVINFO(m_flGustDuration)),
  942. // RecvPropInt (RECVINFO(m_iszGustSound)),
  943. END_RECV_TABLE()
  944. IMPLEMENT_CLIENTCLASS_DT( C_EnvWind, DT_EnvWind, CEnvWind )
  945. RecvPropDataTable(RECVINFO_DT(m_EnvWindShared), 0, &REFERENCE_RECV_TABLE(DT_EnvWindShared)),
  946. END_RECV_TABLE()
  947. C_EnvWind::C_EnvWind()
  948. {
  949. }
  950. //-----------------------------------------------------------------------------
  951. // Post data update!
  952. //-----------------------------------------------------------------------------
  953. void C_EnvWind::OnDataChanged( DataUpdateType_t updateType )
  954. {
  955. // Whenever we get an update, reset the entire state.
  956. // Note that the fields have already been stored by the datatables,
  957. // but there's still work to be done in the init block
  958. m_EnvWindShared.Init( entindex(), m_EnvWindShared.m_iWindSeed,
  959. m_EnvWindShared.m_flStartTime, m_EnvWindShared.m_iInitialWindDir,
  960. m_EnvWindShared.m_flInitialWindSpeed );
  961. SetNextClientThink(0.0f);
  962. BaseClass::OnDataChanged( updateType );
  963. }
  964. void C_EnvWind::ClientThink( )
  965. {
  966. // Update the wind speed
  967. float flNextThink = m_EnvWindShared.WindThink( gpGlobals->curtime );
  968. SetNextClientThink(flNextThink);
  969. }
  970. //==================================================
  971. // EmberParticle
  972. //==================================================
  973. class CEmberEmitter : public CSimpleEmitter
  974. {
  975. public:
  976. CEmberEmitter( const char *pDebugName );
  977. static CSmartPtr<CEmberEmitter> Create( const char *pDebugName );
  978. virtual void UpdateVelocity( SimpleParticle *pParticle, float timeDelta );
  979. virtual Vector UpdateColor( const SimpleParticle *pParticle );
  980. private:
  981. CEmberEmitter( const CEmberEmitter & );
  982. };
  983. //-----------------------------------------------------------------------------
  984. // Purpose:
  985. // Input : fTimeDelta -
  986. // Output : Vector
  987. //-----------------------------------------------------------------------------
  988. CEmberEmitter::CEmberEmitter( const char *pDebugName ) : CSimpleEmitter( pDebugName )
  989. {
  990. }
  991. CSmartPtr<CEmberEmitter> CEmberEmitter::Create( const char *pDebugName )
  992. {
  993. return new CEmberEmitter( pDebugName );
  994. }
  995. void CEmberEmitter::UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  996. {
  997. float speed = VectorNormalize( pParticle->m_vecVelocity );
  998. Vector offset;
  999. speed -= ( 1.0f * timeDelta );
  1000. offset.Random( -0.025f, 0.025f );
  1001. offset[2] = 0.0f;
  1002. pParticle->m_vecVelocity += offset;
  1003. VectorNormalize( pParticle->m_vecVelocity );
  1004. pParticle->m_vecVelocity *= speed;
  1005. }
  1006. //-----------------------------------------------------------------------------
  1007. // Purpose:
  1008. // Input : *pParticle -
  1009. // timeDelta -
  1010. //-----------------------------------------------------------------------------
  1011. Vector CEmberEmitter::UpdateColor( const SimpleParticle *pParticle )
  1012. {
  1013. Vector color;
  1014. float ramp = 1.0f - ( pParticle->m_flLifetime / pParticle->m_flDieTime );
  1015. color[0] = ( (float) pParticle->m_uchColor[0] * ramp ) / 255.0f;
  1016. color[1] = ( (float) pParticle->m_uchColor[1] * ramp ) / 255.0f;
  1017. color[2] = ( (float) pParticle->m_uchColor[2] * ramp ) / 255.0f;
  1018. return color;
  1019. }
  1020. //==================================================
  1021. // C_Embers
  1022. //==================================================
  1023. class C_Embers : public C_BaseEntity
  1024. {
  1025. public:
  1026. DECLARE_CLIENTCLASS();
  1027. DECLARE_CLASS( C_Embers, C_BaseEntity );
  1028. C_Embers();
  1029. ~C_Embers();
  1030. void Start( void );
  1031. virtual void OnDataChanged( DataUpdateType_t updateType );
  1032. virtual bool ShouldDraw( void );
  1033. virtual void AddEntity( void );
  1034. //Server-side
  1035. int m_nDensity;
  1036. int m_nLifetime;
  1037. int m_nSpeed;
  1038. bool m_bEmit;
  1039. protected:
  1040. void SpawnEmber( void );
  1041. PMaterialHandle m_hMaterial;
  1042. TimedEvent m_tParticleSpawn;
  1043. CSmartPtr<CEmberEmitter> m_pEmitter;
  1044. };
  1045. //Receive datatable
  1046. IMPLEMENT_CLIENTCLASS_DT( C_Embers, DT_Embers, CEmbers )
  1047. RecvPropInt( RECVINFO( m_nDensity ) ),
  1048. RecvPropInt( RECVINFO( m_nLifetime ) ),
  1049. RecvPropInt( RECVINFO( m_nSpeed ) ),
  1050. RecvPropInt( RECVINFO( m_bEmit ) ),
  1051. END_RECV_TABLE()
  1052. //-----------------------------------------------------------------------------
  1053. // Purpose:
  1054. // Input : bnewentity -
  1055. //-----------------------------------------------------------------------------
  1056. C_Embers::C_Embers()
  1057. {
  1058. m_pEmitter = CEmberEmitter::Create( "C_Embers" );
  1059. }
  1060. C_Embers::~C_Embers()
  1061. {
  1062. }
  1063. void C_Embers::OnDataChanged( DataUpdateType_t updateType )
  1064. {
  1065. BaseClass::OnDataChanged( updateType );
  1066. if ( updateType == DATA_UPDATE_CREATED )
  1067. {
  1068. m_pEmitter->SetSortOrigin( GetAbsOrigin() );
  1069. Start();
  1070. }
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Purpose:
  1074. // Output : Returns true on success, false on failure.
  1075. //-----------------------------------------------------------------------------
  1076. bool C_Embers::ShouldDraw()
  1077. {
  1078. return true;
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. // Purpose:
  1082. //-----------------------------------------------------------------------------
  1083. void C_Embers::Start( void )
  1084. {
  1085. //Various setup info
  1086. m_tParticleSpawn.Init( m_nDensity );
  1087. m_hMaterial = m_pEmitter->GetPMaterial( "particle/fire" );
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose:
  1091. //-----------------------------------------------------------------------------
  1092. void C_Embers::AddEntity( void )
  1093. {
  1094. if ( m_bEmit == false )
  1095. return;
  1096. float tempDelta = gpGlobals->frametime;
  1097. while( m_tParticleSpawn.NextEvent( tempDelta ) )
  1098. {
  1099. SpawnEmber();
  1100. }
  1101. }
  1102. //-----------------------------------------------------------------------------
  1103. // Purpose:
  1104. //-----------------------------------------------------------------------------
  1105. void C_Embers::SpawnEmber( void )
  1106. {
  1107. Vector offset, mins, maxs;
  1108. modelinfo->GetModelBounds( GetModel(), mins, maxs );
  1109. //Setup our spawn position
  1110. offset[0] = random->RandomFloat( mins[0], maxs[0] );
  1111. offset[1] = random->RandomFloat( mins[1], maxs[1] );
  1112. offset[2] = random->RandomFloat( mins[2], maxs[2] );
  1113. //Spawn the particle
  1114. SimpleParticle *sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof( SimpleParticle ), m_hMaterial, offset );
  1115. if (sParticle == NULL)
  1116. return;
  1117. float cScale = random->RandomFloat( 0.75f, 1.0f );
  1118. //Set it up
  1119. sParticle->m_flLifetime = 0.0f;
  1120. sParticle->m_flDieTime = m_nLifetime;
  1121. sParticle->m_uchColor[0] = m_clrRender->r * cScale;
  1122. sParticle->m_uchColor[1] = m_clrRender->g * cScale;
  1123. sParticle->m_uchColor[2] = m_clrRender->b * cScale;
  1124. sParticle->m_uchStartAlpha = 255;
  1125. sParticle->m_uchEndAlpha = 0;
  1126. sParticle->m_uchStartSize = 1;
  1127. sParticle->m_uchEndSize = 0;
  1128. sParticle->m_flRollDelta = 0;
  1129. sParticle->m_flRoll = 0;
  1130. //Set the velocity
  1131. Vector velocity;
  1132. AngleVectors( GetAbsAngles(), &velocity );
  1133. sParticle->m_vecVelocity = velocity * m_nSpeed;
  1134. sParticle->m_vecVelocity[0] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) );
  1135. sParticle->m_vecVelocity[1] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) );
  1136. sParticle->m_vecVelocity[2] += random->RandomFloat( -(m_nSpeed/8), (m_nSpeed/8) );
  1137. UpdateVisibility();
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. // Quadratic spline beam effect
  1141. //-----------------------------------------------------------------------------
  1142. #include "beamdraw.h"
  1143. class C_QuadraticBeam : public C_BaseEntity
  1144. {
  1145. public:
  1146. DECLARE_CLIENTCLASS();
  1147. DECLARE_CLASS( C_QuadraticBeam, C_BaseEntity );
  1148. //virtual void OnDataChanged( DataUpdateType_t updateType );
  1149. virtual bool ShouldDraw( void ) { return true; }
  1150. virtual int DrawModel( int );
  1151. virtual void GetRenderBounds( Vector& mins, Vector& maxs )
  1152. {
  1153. ClearBounds( mins, maxs );
  1154. AddPointToBounds( vec3_origin, mins, maxs );
  1155. AddPointToBounds( m_targetPosition, mins, maxs );
  1156. AddPointToBounds( m_controlPosition, mins, maxs );
  1157. mins -= GetRenderOrigin();
  1158. maxs -= GetRenderOrigin();
  1159. }
  1160. protected:
  1161. Vector m_targetPosition;
  1162. Vector m_controlPosition;
  1163. float m_scrollRate;
  1164. float m_flWidth;
  1165. };
  1166. //Receive datatable
  1167. IMPLEMENT_CLIENTCLASS_DT( C_QuadraticBeam, DT_QuadraticBeam, CEnvQuadraticBeam )
  1168. RecvPropVector( RECVINFO(m_targetPosition) ),
  1169. RecvPropVector( RECVINFO(m_controlPosition) ),
  1170. RecvPropFloat( RECVINFO(m_scrollRate) ),
  1171. RecvPropFloat( RECVINFO(m_flWidth) ),
  1172. END_RECV_TABLE()
  1173. Vector Color32ToVector( const color32 &color )
  1174. {
  1175. return Vector( color.r * (1.0/255.0f), color.g * (1.0/255.0f), color.b * (1.0/255.0f) );
  1176. }
  1177. int C_QuadraticBeam::DrawModel( int )
  1178. {
  1179. Draw_SetSpriteTexture( GetModel(), 0, GetRenderMode() );
  1180. Vector color = Color32ToVector( GetRenderColor() );
  1181. DrawBeamQuadratic( GetRenderOrigin(), m_controlPosition, m_targetPosition, m_flWidth, color, gpGlobals->curtime*m_scrollRate );
  1182. return 1;
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. //-----------------------------------------------------------------------------
  1186. class SnowFallEffect : public CSimpleEmitter
  1187. {
  1188. public:
  1189. SnowFallEffect( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {}
  1190. static SnowFallEffect* Create( const char *pDebugName )
  1191. {
  1192. return new SnowFallEffect( pDebugName );
  1193. }
  1194. void UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
  1195. {
  1196. float flSpeed = VectorNormalize( pParticle->m_vecVelocity );
  1197. flSpeed -= timeDelta;
  1198. pParticle->m_vecVelocity.x += RandomFloat( -0.025f, 0.025f );
  1199. pParticle->m_vecVelocity.y += RandomFloat( -0.025f, 0.025f );
  1200. VectorNormalize( pParticle->m_vecVelocity );
  1201. pParticle->m_vecVelocity *= flSpeed;
  1202. Vector vecWindVelocity;
  1203. GetWindspeedAtTime( gpGlobals->curtime, vecWindVelocity );
  1204. pParticle->m_vecVelocity += ( vecWindVelocity * r_SnowWindScale.GetFloat() );
  1205. }
  1206. void SimulateParticles( CParticleSimulateIterator *pIterator )
  1207. {
  1208. float timeDelta = pIterator->GetTimeDelta();
  1209. SimpleParticle *pParticle = (SimpleParticle*)pIterator->GetFirst();
  1210. while ( pParticle )
  1211. {
  1212. //Update velocity
  1213. UpdateVelocity( pParticle, timeDelta );
  1214. pParticle->m_Pos += pParticle->m_vecVelocity * timeDelta;
  1215. //Should this particle die?
  1216. pParticle->m_flLifetime += timeDelta;
  1217. UpdateRoll( pParticle, timeDelta );
  1218. if ( pParticle->m_flLifetime >= pParticle->m_flDieTime )
  1219. {
  1220. pIterator->RemoveParticle( pParticle );
  1221. }
  1222. else if ( !IsInAir( pParticle->m_Pos ) )
  1223. {
  1224. pIterator->RemoveParticle( pParticle );
  1225. }
  1226. pParticle = (SimpleParticle*)pIterator->GetNext();
  1227. }
  1228. }
  1229. int GetParticleCount( void )
  1230. {
  1231. return GetBinding().GetNumActiveParticles();
  1232. }
  1233. void SetBounds( const Vector &vecMin, const Vector &vecMax )
  1234. {
  1235. GetBinding().SetBBox( vecMin, vecMax, true );
  1236. }
  1237. bool IsTransparent( void ) { return false; }
  1238. private:
  1239. SnowFallEffect( const SnowFallEffect & );
  1240. };
  1241. //-----------------------------------------------------------------------------
  1242. //-----------------------------------------------------------------------------
  1243. class CSnowFallManager : public C_BaseEntity
  1244. {
  1245. public:
  1246. CSnowFallManager();
  1247. ~CSnowFallManager();
  1248. bool CreateEmitter( void );
  1249. void SpawnClientEntity( void );
  1250. void ClientThink();
  1251. void AddSnowFallEntity( CClient_Precipitation *pSnowEntity );
  1252. // Snow Effect
  1253. enum
  1254. {
  1255. SNOWFALL_NONE = 0,
  1256. SNOWFALL_AROUND_PLAYER,
  1257. SNOWFALL_IN_ENTITY,
  1258. };
  1259. bool IsTransparent( void ) { return false; }
  1260. private:
  1261. bool CreateSnowFallEmitter( void );
  1262. void CreateSnowFall( void );
  1263. void CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale );
  1264. void CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale );
  1265. void CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale );
  1266. void CreateSnowParticlesSphere( float flRadius );
  1267. void CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward );
  1268. void CreateSnowFallParticle( const Vector &vecParticleSpawn, int iBBox );
  1269. int StandingInSnowVolume( Vector &vecPoint );
  1270. void FindSnowVolumes( Vector &vecCenter, float flRadius, Vector &vecEyePos, Vector &vecForward );
  1271. void UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax );
  1272. private:
  1273. enum { MAX_SNOW_PARTICLES = 500 };
  1274. enum { MAX_SNOW_LIST = 32 };
  1275. TimedEvent m_tSnowFallParticleTimer;
  1276. TimedEvent m_tSnowFallParticleTraceTimer;
  1277. int m_iSnowFallArea;
  1278. CSmartPtr<SnowFallEffect> m_pSnowFallEmitter;
  1279. Vector m_vecSnowFallEmitOrigin;
  1280. float m_flSnowRadius;
  1281. Vector m_vecMin;
  1282. Vector m_vecMax;
  1283. int m_nActiveSnowCount;
  1284. int m_aActiveSnow[MAX_SNOW_LIST];
  1285. bool m_bRayParticles;
  1286. struct SnowFall_t
  1287. {
  1288. PMaterialHandle m_hMaterial;
  1289. CClient_Precipitation *m_pEntity;
  1290. SnowFallEffect *m_pEffect;
  1291. Vector m_vecMin;
  1292. Vector m_vecMax;
  1293. };
  1294. CUtlVector<SnowFall_t> m_aSnow;
  1295. };
  1296. //-----------------------------------------------------------------------------
  1297. // Purpose:
  1298. //-----------------------------------------------------------------------------
  1299. CSnowFallManager::CSnowFallManager( void )
  1300. {
  1301. m_iSnowFallArea = SNOWFALL_NONE;
  1302. m_pSnowFallEmitter = NULL;
  1303. m_vecSnowFallEmitOrigin.Init();
  1304. m_flSnowRadius = 0.0f;
  1305. m_vecMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  1306. m_vecMax.Init( FLT_MIN, FLT_MIN, FLT_MIN );
  1307. m_nActiveSnowCount = 0;
  1308. m_aSnow.Purge();
  1309. }
  1310. //-----------------------------------------------------------------------------
  1311. // Purpose:
  1312. //-----------------------------------------------------------------------------
  1313. CSnowFallManager::~CSnowFallManager( void )
  1314. {
  1315. m_aSnow.Purge();
  1316. }
  1317. //-----------------------------------------------------------------------------
  1318. // Purpose:
  1319. // Output : Returns true on success, false on failure.
  1320. //-----------------------------------------------------------------------------
  1321. bool CSnowFallManager::CreateEmitter( void )
  1322. {
  1323. return CreateSnowFallEmitter();
  1324. }
  1325. //-----------------------------------------------------------------------------
  1326. // Purpose:
  1327. //-----------------------------------------------------------------------------
  1328. void CSnowFallManager::SpawnClientEntity( void )
  1329. {
  1330. m_tSnowFallParticleTimer.Init( 500 );
  1331. m_tSnowFallParticleTraceTimer.Init( 6 );
  1332. m_iSnowFallArea = SNOWFALL_NONE;
  1333. // Have the Snow Fall Manager think for all the snow fall entities.
  1334. SetNextClientThink( CLIENT_THINK_ALWAYS );
  1335. }
  1336. //-----------------------------------------------------------------------------
  1337. // Purpose:
  1338. // Output : Returns true on success, false on failure.
  1339. //-----------------------------------------------------------------------------
  1340. bool CSnowFallManager::CreateSnowFallEmitter( void )
  1341. {
  1342. if ( ( m_pSnowFallEmitter = SnowFallEffect::Create( "snowfall" ) ) == NULL )
  1343. return false;
  1344. return true;
  1345. }
  1346. //-----------------------------------------------------------------------------
  1347. // Purpose:
  1348. //-----------------------------------------------------------------------------
  1349. void CSnowFallManager::ClientThink( void )
  1350. {
  1351. if ( !r_SnowEnable.GetBool() )
  1352. return;
  1353. // Make sure we have a snow fall emitter.
  1354. if ( !m_pSnowFallEmitter )
  1355. {
  1356. if ( !CreateSnowFallEmitter() )
  1357. return;
  1358. }
  1359. CreateSnowFall();
  1360. }
  1361. //-----------------------------------------------------------------------------
  1362. // Purpose:
  1363. // Input : *pSnowEntity -
  1364. //-----------------------------------------------------------------------------
  1365. void CSnowFallManager::AddSnowFallEntity( CClient_Precipitation *pSnowEntity )
  1366. {
  1367. if ( !pSnowEntity )
  1368. return;
  1369. int nSnowCount = m_aSnow.Count();
  1370. int iSnow = 0;
  1371. for ( iSnow = 0; iSnow < nSnowCount; ++iSnow )
  1372. {
  1373. if ( m_aSnow[iSnow].m_pEntity == pSnowEntity )
  1374. break;
  1375. }
  1376. if ( iSnow != nSnowCount )
  1377. return;
  1378. iSnow = m_aSnow.AddToTail();
  1379. m_aSnow[iSnow].m_pEntity = pSnowEntity;
  1380. m_aSnow[iSnow].m_pEffect = SnowFallEffect::Create( "snowfall" );
  1381. m_aSnow[iSnow].m_hMaterial = ParticleMgr()->GetPMaterial( "particle/snow" );
  1382. VectorCopy( pSnowEntity->WorldAlignMins(), m_aSnow[iSnow].m_vecMin );
  1383. VectorCopy( pSnowEntity->WorldAlignMaxs(), m_aSnow[iSnow].m_vecMax );
  1384. UpdateBounds( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax );
  1385. }
  1386. //-----------------------------------------------------------------------------
  1387. // Purpose:
  1388. //-----------------------------------------------------------------------------
  1389. void CSnowFallManager::UpdateBounds( const Vector &vecSnowMin, const Vector &vecSnowMax )
  1390. {
  1391. int iAxis = 0;
  1392. for ( iAxis = 0; iAxis < 3; ++iAxis )
  1393. {
  1394. if ( vecSnowMin[iAxis] < m_vecMin[iAxis] )
  1395. {
  1396. m_vecMin[iAxis] = vecSnowMin[iAxis];
  1397. }
  1398. if ( vecSnowMax[iAxis] > m_vecMax[iAxis] )
  1399. {
  1400. m_vecMax[iAxis] = vecSnowMax[iAxis];
  1401. }
  1402. }
  1403. Assert( m_pSnowFallEmitter );
  1404. m_pSnowFallEmitter->SetBounds( m_vecMin, m_vecMax );
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. // Purpose:
  1408. // Input : &vecPoint -
  1409. // Output : int
  1410. //-----------------------------------------------------------------------------
  1411. int CSnowFallManager::StandingInSnowVolume( Vector &vecPoint )
  1412. {
  1413. trace_t traceSnow;
  1414. int nSnowCount = m_aSnow.Count();
  1415. int iSnow = 0;
  1416. for ( iSnow = 0; iSnow < nSnowCount; ++iSnow )
  1417. {
  1418. UTIL_TraceModel( vecPoint, vecPoint, vec3_origin, vec3_origin, static_cast<C_BaseEntity*>( m_aSnow[iSnow].m_pEntity ), COLLISION_GROUP_NONE, &traceSnow );
  1419. if ( traceSnow.startsolid )
  1420. return iSnow;
  1421. }
  1422. return -1;
  1423. }
  1424. //-----------------------------------------------------------------------------
  1425. // Purpose:
  1426. // Input : &vecCenter -
  1427. // flRadius -
  1428. //-----------------------------------------------------------------------------
  1429. void CSnowFallManager::FindSnowVolumes( Vector &vecCenter, float flRadius, Vector &vecEyePos, Vector &vecForward )
  1430. {
  1431. // Reset.
  1432. m_nActiveSnowCount = 0;
  1433. m_bRayParticles = false;
  1434. int nSnowCount = m_aSnow.Count();
  1435. int iSnow = 0;
  1436. for ( iSnow = 0; iSnow < nSnowCount; ++iSnow )
  1437. {
  1438. // Check to see if the volume is in the PVS.
  1439. bool bInPVS = g_pClientLeafSystem->IsRenderableInPVS( m_aSnow[iSnow].m_pEntity->GetClientRenderable() );
  1440. if ( !bInPVS )
  1441. continue;
  1442. // Check to see if a snow volume is inside the given radius.
  1443. if ( IsBoxIntersectingSphere( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, vecCenter, flRadius ) )
  1444. {
  1445. m_aActiveSnow[m_nActiveSnowCount] = iSnow;
  1446. ++m_nActiveSnowCount;
  1447. if ( m_nActiveSnowCount >= MAX_SNOW_LIST )
  1448. {
  1449. DevWarning( 1, "Max Active Snow Volume Count!\n" );
  1450. break;
  1451. }
  1452. }
  1453. // Check to see if a snow volume is outside of the sphere radius, but is along line-of-sight.
  1454. else
  1455. {
  1456. CBaseTrace trace;
  1457. Vector vecNewForward;
  1458. vecNewForward = vecForward * r_SnowRayLength.GetFloat();
  1459. vecNewForward.z = 0.0f;
  1460. IntersectRayWithBox( vecEyePos, vecNewForward, m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax, 0.325f, &trace );
  1461. if ( trace.fraction < 1.0f )
  1462. {
  1463. m_aActiveSnow[m_nActiveSnowCount] = iSnow;
  1464. ++m_nActiveSnowCount;
  1465. if ( m_nActiveSnowCount >= MAX_SNOW_LIST )
  1466. {
  1467. DevWarning( 1, "Max Active Snow Volume Count!\n" );
  1468. break;
  1469. }
  1470. m_bRayParticles = true;
  1471. }
  1472. }
  1473. }
  1474. // Debugging code!
  1475. #ifdef _DEBUG
  1476. if ( r_SnowDebugBox.GetFloat() != 0.0f )
  1477. {
  1478. for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow )
  1479. {
  1480. Vector vecMin, vecMax;
  1481. vecCenter = ( m_aSnow[iSnow].m_vecMin, m_aSnow[iSnow].m_vecMax ) * 0.5;
  1482. vecMin = m_aSnow[iSnow].m_vecMin - vecCenter;
  1483. vecMax = m_aSnow[iSnow].m_vecMax - vecCenter;
  1484. if ( debugoverlay )
  1485. {
  1486. debugoverlay->AddBoxOverlay( vecCenter, vecMin, vecMax, QAngle( 0, 0, 0 ), 200, 0, 0, 25, r_SnowDebugBox.GetFloat() );
  1487. }
  1488. }
  1489. }
  1490. #endif
  1491. }
  1492. //-----------------------------------------------------------------------------
  1493. // Purpose:
  1494. //-----------------------------------------------------------------------------
  1495. void CSnowFallManager::CreateSnowFall( void )
  1496. {
  1497. #if 1
  1498. VPROF_BUDGET( "SnowFall", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  1499. #endif
  1500. // Check to see if we have a local player before starting the snow around a local player.
  1501. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  1502. if ( pPlayer == NULL )
  1503. return;
  1504. // Get the current frame time.
  1505. float flCurrentTime = gpGlobals->frametime;
  1506. // Get the players data to determine where the snow emitter should reside.
  1507. VectorCopy( pPlayer->EyePosition(), m_vecSnowFallEmitOrigin );
  1508. Vector vecForward;
  1509. pPlayer->GetVectors( &vecForward, NULL, NULL );
  1510. vecForward.z = 0.0f;
  1511. Vector vecVelocity = pPlayer->GetAbsVelocity();
  1512. float flSpeed = VectorNormalize( vecVelocity );
  1513. m_vecSnowFallEmitOrigin += ( vecForward * ( 64.0f + ( flSpeed * 0.4f * r_SnowPosScale.GetFloat() ) ) );
  1514. m_vecSnowFallEmitOrigin += ( vecVelocity * ( flSpeed * 1.25f * r_SnowSpeedScale.GetFloat() ) );
  1515. // Check to see if the player is zoomed.
  1516. bool bZoomed = ( pPlayer->GetFOV() != pPlayer->GetDefaultFOV() );
  1517. float flZoomScale = 1.0f;
  1518. if ( bZoomed )
  1519. {
  1520. flZoomScale = pPlayer->GetDefaultFOV() / pPlayer->GetFOV();
  1521. flZoomScale *= 0.5f;
  1522. }
  1523. // Time to test for a snow volume yet? (Only do this 6 times a second!)
  1524. if ( m_tSnowFallParticleTraceTimer.NextEvent( flCurrentTime ) )
  1525. {
  1526. // Reset the active snow emitter.
  1527. m_iSnowFallArea = SNOWFALL_NONE;
  1528. // Set the trace start and the emit origin.
  1529. Vector vecTraceStart;
  1530. VectorCopy( pPlayer->EyePosition(), vecTraceStart );
  1531. int iSnowVolume = StandingInSnowVolume( vecTraceStart );
  1532. if ( iSnowVolume != -1 )
  1533. {
  1534. m_flSnowRadius = r_SnowInsideRadius.GetFloat() + ( flSpeed * 0.5f );
  1535. m_iSnowFallArea = SNOWFALL_AROUND_PLAYER;
  1536. }
  1537. else
  1538. {
  1539. m_flSnowRadius = r_SnowOutsideRadius.GetFloat();
  1540. }
  1541. float flRadius = m_flSnowRadius;
  1542. if ( bZoomed )
  1543. {
  1544. if ( m_iSnowFallArea == SNOWFALL_AROUND_PLAYER )
  1545. {
  1546. flRadius = r_SnowOutsideRadius.GetFloat() * flZoomScale;
  1547. }
  1548. else
  1549. {
  1550. flRadius *= flZoomScale;
  1551. }
  1552. }
  1553. Vector vecEyePos = pPlayer->EyePosition();
  1554. FindSnowVolumes( m_vecSnowFallEmitOrigin, flRadius, vecEyePos, vecForward );
  1555. if ( m_nActiveSnowCount != 0 && m_iSnowFallArea != SNOWFALL_AROUND_PLAYER )
  1556. {
  1557. // We found an active snow emitter.
  1558. m_iSnowFallArea = SNOWFALL_IN_ENTITY;
  1559. }
  1560. }
  1561. if ( m_iSnowFallArea == SNOWFALL_NONE )
  1562. return;
  1563. // Set the origin in the snow emitter.
  1564. m_pSnowFallEmitter->SetSortOrigin( m_vecSnowFallEmitOrigin );
  1565. // Create snow fall particles.
  1566. CreateSnowFallParticles( flCurrentTime, m_flSnowRadius, pPlayer->EyePosition(), vecForward, flZoomScale );
  1567. }
  1568. //-----------------------------------------------------------------------------
  1569. // Purpose:
  1570. // Input : flCurrentTime -
  1571. // flRadius -
  1572. // &vecEyePos -
  1573. // &vecForward -
  1574. // flZoomScale -
  1575. //-----------------------------------------------------------------------------
  1576. void CSnowFallManager::CreateSnowFallParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale )
  1577. {
  1578. // Outside of a snow volume.
  1579. if ( m_iSnowFallArea == SNOWFALL_IN_ENTITY )
  1580. {
  1581. CreateOutsideVolumeSnowParticles( flCurrentTime, flRadius, flZoomScale );
  1582. }
  1583. // Inside of a snow volume.
  1584. else
  1585. {
  1586. CreateInsideVolumeSnowParticles( flCurrentTime, flRadius, vecEyePos, vecForward, flZoomScale );
  1587. }
  1588. }
  1589. //-----------------------------------------------------------------------------
  1590. // Purpose:
  1591. // Input : flCurrentTime -
  1592. // flRadius -
  1593. // flZoomScale -
  1594. //-----------------------------------------------------------------------------
  1595. void CSnowFallManager::CreateOutsideVolumeSnowParticles( float flCurrentTime, float flRadius, float flZoomScale )
  1596. {
  1597. Vector vecParticleSpawn;
  1598. // Outside of a snow volume.
  1599. int iSnow = 0;
  1600. float flRadiusScaled = flRadius * flZoomScale;
  1601. float flRadius2 = flRadiusScaled * flRadiusScaled;
  1602. // Add as many particles as we need
  1603. while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) )
  1604. {
  1605. // Check for a max particle count.
  1606. if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() )
  1607. continue;
  1608. vecParticleSpawn.x = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x );
  1609. vecParticleSpawn.y = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y );
  1610. vecParticleSpawn.z = RandomFloat( m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z, m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z );
  1611. float flDistance2 = ( m_vecSnowFallEmitOrigin - vecParticleSpawn ).LengthSqr();
  1612. if ( flDistance2 < flRadius2 )
  1613. {
  1614. CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] );
  1615. }
  1616. iSnow = ( iSnow + 1 ) % m_nActiveSnowCount;
  1617. }
  1618. }
  1619. //-----------------------------------------------------------------------------
  1620. // Purpose:
  1621. // Input : flCurrentTime -
  1622. // flRadius -
  1623. // &vecEyePos -
  1624. // &vecForward -
  1625. // flZoomScale -
  1626. //-----------------------------------------------------------------------------
  1627. void CSnowFallManager::CreateInsideVolumeSnowParticles( float flCurrentTime, float flRadius, const Vector &vecEyePos, const Vector &vecForward, float flZoomScale )
  1628. {
  1629. Vector vecParticleSpawn;
  1630. // Check/Setup for zoom.
  1631. bool bZoomed = ( flZoomScale > 1.0f );
  1632. float flZoomRadius = 0.0f;
  1633. Vector vecZoomEmitOrigin;
  1634. if ( bZoomed )
  1635. {
  1636. vecZoomEmitOrigin = m_vecSnowFallEmitOrigin + ( vecForward * ( r_SnowZoomOffset.GetFloat() * flZoomScale ) );
  1637. flZoomRadius = flRadius * flZoomScale;
  1638. }
  1639. int iIndex = 0;
  1640. // Add as many particles as we need
  1641. while ( m_tSnowFallParticleTimer.NextEvent( flCurrentTime ) )
  1642. {
  1643. // Check for a max particle count.
  1644. if ( m_pSnowFallEmitter->GetParticleCount() >= r_SnowParticles.GetInt() )
  1645. continue;
  1646. // Create particle inside of sphere.
  1647. if ( iIndex > 0 )
  1648. {
  1649. CreateSnowParticlesSphere( flZoomRadius );
  1650. CreateSnowParticlesRay( flZoomRadius, vecEyePos, vecForward );
  1651. }
  1652. else
  1653. {
  1654. CreateSnowParticlesSphere( flRadius );
  1655. CreateSnowParticlesRay( flRadius, vecEyePos, vecForward );
  1656. }
  1657. // Increment if zoomed.
  1658. if ( bZoomed )
  1659. {
  1660. iIndex = ( iIndex + 1 ) % 3;
  1661. }
  1662. }
  1663. }
  1664. //-----------------------------------------------------------------------------
  1665. // Purpose:
  1666. // Input : flRadius -
  1667. //-----------------------------------------------------------------------------
  1668. void CSnowFallManager::CreateSnowParticlesSphere( float flRadius )
  1669. {
  1670. Vector vecParticleSpawn;
  1671. vecParticleSpawn.x = m_vecSnowFallEmitOrigin.x + RandomFloat( -flRadius, flRadius );
  1672. vecParticleSpawn.y = m_vecSnowFallEmitOrigin.y + RandomFloat( -flRadius, flRadius );
  1673. vecParticleSpawn.z = m_vecSnowFallEmitOrigin.z + RandomFloat( -flRadius, flRadius );
  1674. int iSnow = 0;
  1675. for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow )
  1676. {
  1677. if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) )
  1678. continue;
  1679. if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) )
  1680. continue;
  1681. if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) )
  1682. continue;
  1683. break;
  1684. }
  1685. if ( iSnow == m_nActiveSnowCount )
  1686. return;
  1687. CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] );
  1688. }
  1689. //-----------------------------------------------------------------------------
  1690. // Purpose:
  1691. // Input : &vecEyePos -
  1692. // &vecForward -
  1693. //-----------------------------------------------------------------------------
  1694. void CSnowFallManager::CreateSnowParticlesRay( float flRadius, const Vector &vecEyePos, const Vector &vecForward )
  1695. {
  1696. // Check to see if we should create particles along line-of-sight.
  1697. if ( !m_bRayParticles && r_SnowRayEnable.GetBool() )
  1698. return;
  1699. Vector vecParticleSpawn;
  1700. // Create a particle down the player's view beyond the radius.
  1701. float flRayRadius = r_SnowRayRadius.GetFloat();
  1702. Vector vecNewForward;
  1703. vecNewForward = vecForward * RandomFloat( flRadius, r_SnowRayLength.GetFloat() );
  1704. vecParticleSpawn.x = vecEyePos.x + vecNewForward.x;
  1705. vecParticleSpawn.y = vecEyePos.y + vecNewForward.y;
  1706. vecParticleSpawn.z = vecEyePos.z + RandomFloat( 72, flRayRadius );
  1707. vecParticleSpawn.x += RandomFloat( -flRayRadius, flRayRadius );
  1708. vecParticleSpawn.y += RandomFloat( -flRayRadius, flRayRadius );
  1709. int iSnow = 0;
  1710. for ( iSnow = 0; iSnow < m_nActiveSnowCount; ++iSnow )
  1711. {
  1712. if ( ( vecParticleSpawn.x < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.x ) || ( vecParticleSpawn.x > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.x ) )
  1713. continue;
  1714. if ( ( vecParticleSpawn.y < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.y ) || ( vecParticleSpawn.y > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.y ) )
  1715. continue;
  1716. if ( ( vecParticleSpawn.z < m_aSnow[m_aActiveSnow[iSnow]].m_vecMin.z ) || ( vecParticleSpawn.z > m_aSnow[m_aActiveSnow[iSnow]].m_vecMax.z ) )
  1717. continue;
  1718. break;
  1719. }
  1720. if ( iSnow == m_nActiveSnowCount )
  1721. return;
  1722. CreateSnowFallParticle( vecParticleSpawn, m_aActiveSnow[iSnow] );
  1723. }
  1724. void CSnowFallManager::CreateSnowFallParticle( const Vector &vecParticleSpawn, int iSnow )
  1725. {
  1726. SimpleParticle *pParticle = ( SimpleParticle* )m_pSnowFallEmitter->AddParticle( sizeof( SimpleParticle ), m_aSnow[iSnow].m_hMaterial, vecParticleSpawn );
  1727. if ( pParticle == NULL )
  1728. return;
  1729. pParticle->m_flLifetime = 0.0f;
  1730. pParticle->m_vecVelocity = Vector( RandomFloat( -5.0f, 5.0f ), RandomFloat( -5.0f, 5.0f ), ( RandomFloat( -25, -35 ) * r_SnowFallSpeed.GetFloat() ) );
  1731. pParticle->m_flDieTime = fabs( ( vecParticleSpawn.z - m_aSnow[iSnow].m_vecMin.z ) / ( pParticle->m_vecVelocity.z - 0.1 ) );
  1732. // Probably want to put the color in the snow entity.
  1733. // pParticle->m_uchColor[0] = 150;//color;
  1734. // pParticle->m_uchColor[1] = 175;//color;
  1735. // pParticle->m_uchColor[2] = 200;//color;
  1736. pParticle->m_uchColor[0] = r_SnowColorRed.GetInt();
  1737. pParticle->m_uchColor[1] = r_SnowColorGreen.GetInt();
  1738. pParticle->m_uchColor[2] = r_SnowColorBlue.GetInt();
  1739. pParticle->m_uchStartSize = r_SnowStartSize.GetInt();
  1740. pParticle->m_uchEndSize = r_SnowEndSize.GetInt();
  1741. // pParticle->m_uchStartAlpha = 255;
  1742. pParticle->m_uchStartAlpha = r_SnowStartAlpha.GetInt();
  1743. pParticle->m_uchEndAlpha = r_SnowEndAlpha.GetInt();
  1744. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  1745. pParticle->m_flRollDelta = random->RandomFloat( -0.15f, 0.15f );
  1746. pParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_WINDBLOWN;
  1747. }
  1748. //-----------------------------------------------------------------------------
  1749. // Purpose:
  1750. //-----------------------------------------------------------------------------
  1751. bool SnowFallManagerCreate( CClient_Precipitation *pSnowEntity )
  1752. {
  1753. if ( !s_pSnowFallMgr )
  1754. {
  1755. s_pSnowFallMgr = new CSnowFallManager();
  1756. s_pSnowFallMgr->CreateEmitter();
  1757. s_pSnowFallMgr->InitializeAsClientEntity( NULL, RENDER_GROUP_OTHER );
  1758. if ( !s_pSnowFallMgr )
  1759. return false;
  1760. }
  1761. s_pSnowFallMgr->AddSnowFallEntity( pSnowEntity );
  1762. return true;
  1763. }
  1764. //-----------------------------------------------------------------------------
  1765. // Purpose:
  1766. //-----------------------------------------------------------------------------
  1767. void SnowFallManagerDestroy( void )
  1768. {
  1769. if ( s_pSnowFallMgr )
  1770. {
  1771. delete s_pSnowFallMgr;
  1772. s_pSnowFallMgr = NULL;
  1773. }
  1774. }