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.

365 lines
9.6 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "fx.h"
  9. #include "c_func_dust.h"
  10. #include "func_dust_shared.h"
  11. #include "c_te_particlesystem.h"
  12. #include "env_wind_shared.h"
  13. #include "engine/IEngineTrace.h"
  14. #include "tier0/vprof.h"
  15. #include "clienteffectprecachesystem.h"
  16. #include "particles_ez.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_Func_Dust, DT_Func_Dust, CFunc_Dust )
  20. RecvPropInt( RECVINFO(m_Color) ),
  21. RecvPropInt( RECVINFO(m_SpawnRate) ),
  22. RecvPropFloat( RECVINFO(m_flSizeMin) ),
  23. RecvPropFloat( RECVINFO(m_flSizeMax) ),
  24. RecvPropInt( RECVINFO(m_LifetimeMin) ),
  25. RecvPropInt( RECVINFO(m_LifetimeMax) ),
  26. RecvPropInt( RECVINFO(m_DustFlags) ),
  27. RecvPropInt( RECVINFO(m_SpeedMax) ),
  28. RecvPropInt( RECVINFO(m_DistMax) ),
  29. RecvPropInt( RECVINFO( m_nModelIndex ) ),
  30. RecvPropFloat( RECVINFO( m_FallSpeed ) ),
  31. RecvPropDataTable( RECVINFO_DT( m_Collision ), 0, &REFERENCE_RECV_TABLE(DT_CollisionProperty) ),
  32. END_RECV_TABLE()
  33. // ------------------------------------------------------------------------------------ //
  34. // CDustEffect implementation.
  35. // ------------------------------------------------------------------------------------ //
  36. #define DUST_ACCEL 50
  37. void CDustEffect::RenderParticles( CParticleRenderIterator *pIterator )
  38. {
  39. const CFuncDustParticle *pParticle = (const CFuncDustParticle*)pIterator->GetFirst();
  40. while ( pParticle )
  41. {
  42. // Velocity.
  43. float flAlpha;
  44. if( m_pDust->m_DustFlags & DUSTFLAGS_FROZEN )
  45. {
  46. flAlpha = 1;
  47. }
  48. else
  49. {
  50. // Alpha.
  51. float flAngle = (pParticle->m_flLifetime / pParticle->m_flDieTime) * M_PI * 2;
  52. flAlpha = sin( flAngle - (M_PI * 0.5f) ) * 0.5f + 0.5f;
  53. }
  54. Vector tPos;
  55. TransformParticle( ParticleMgr()->GetModelView(), pParticle->m_Pos, tPos );
  56. float sortKey = (int) tPos.z;
  57. if( -tPos.z <= m_pDust->m_DistMax )
  58. {
  59. flAlpha *= 1 + (tPos.z / m_pDust->m_DistMax);
  60. // Draw it.
  61. float flSize = pParticle->m_flSize;
  62. if( m_pDust->m_DustFlags & DUSTFLAGS_SCALEMOTES )
  63. flSize *= -tPos.z;
  64. RenderParticle_Color255Size(
  65. pIterator->GetParticleDraw(),
  66. tPos,
  67. Vector( m_pDust->m_Color.r, m_pDust->m_Color.g, m_pDust->m_Color.b ),
  68. flAlpha * m_pDust->m_Color.a,
  69. flSize
  70. );
  71. }
  72. pParticle = (const CFuncDustParticle*)pIterator->GetNext( sortKey );
  73. }
  74. }
  75. void CDustEffect::SimulateParticles( CParticleSimulateIterator *pIterator )
  76. {
  77. Vector vecWind;
  78. GetWindspeedAtTime( gpGlobals->curtime, vecWind );
  79. CFuncDustParticle *pParticle = (CFuncDustParticle*)pIterator->GetFirst();
  80. while ( pParticle )
  81. {
  82. // Velocity.
  83. if( !(m_pDust->m_DustFlags & DUSTFLAGS_FROZEN) )
  84. {
  85. // Kill the particle?
  86. pParticle->m_flLifetime += pIterator->GetTimeDelta();
  87. if( pParticle->m_flLifetime >= pParticle->m_flDieTime )
  88. {
  89. pIterator->RemoveParticle( pParticle );
  90. }
  91. else
  92. {
  93. for ( int i = 0 ; i < 2 ; i++ )
  94. {
  95. if ( pParticle->m_vVelocity[i] < vecWind[i] )
  96. {
  97. pParticle->m_vVelocity[i] += ( gpGlobals->frametime * DUST_ACCEL );
  98. // clamp
  99. if ( pParticle->m_vVelocity[i] > vecWind[i] )
  100. pParticle->m_vVelocity[i] = vecWind[i];
  101. }
  102. else if (pParticle->m_vVelocity[i] > vecWind[i] )
  103. {
  104. pParticle->m_vVelocity[i] -= ( gpGlobals->frametime * DUST_ACCEL );
  105. // clamp.
  106. if ( pParticle->m_vVelocity[i] < vecWind[i] )
  107. pParticle->m_vVelocity[i] = vecWind[i];
  108. }
  109. }
  110. // Apply velocity.
  111. pParticle->m_Pos.MulAdd( pParticle->m_Pos, pParticle->m_vVelocity, pIterator->GetTimeDelta() );
  112. }
  113. }
  114. pParticle = (CFuncDustParticle*)pIterator->GetNext();
  115. }
  116. }
  117. // ------------------------------------------------------------------------------------ //
  118. // C_Func_Dust implementation.
  119. // ------------------------------------------------------------------------------------ //
  120. C_Func_Dust::C_Func_Dust() : m_Effect( "C_Func_Dust" )
  121. {
  122. m_Effect.m_pDust = this;
  123. m_Effect.SetDynamicallyAllocated( false ); // So it doesn't try to delete itself.
  124. }
  125. C_Func_Dust::~C_Func_Dust()
  126. {
  127. }
  128. void C_Func_Dust::OnDataChanged( DataUpdateType_t updateType )
  129. {
  130. BaseClass::OnDataChanged( updateType );
  131. if( updateType == DATA_UPDATE_CREATED )
  132. {
  133. m_hMaterial = m_Effect.GetPMaterial( "particle/sparkles" );
  134. m_Effect.SetSortOrigin( WorldSpaceCenter( ) );
  135. // Let us think each frame.
  136. SetNextClientThink( CLIENT_THINK_ALWAYS );
  137. // If we're setup to be frozen, just make a bunch of particles initially.
  138. if( m_DustFlags & DUSTFLAGS_FROZEN )
  139. {
  140. for( int i=0; i < m_SpawnRate; i++ )
  141. {
  142. AttemptSpawnNewParticle();
  143. }
  144. }
  145. }
  146. m_Spawner.Init( m_SpawnRate ); // N particles per second
  147. }
  148. void C_Func_Dust::ClientThink()
  149. {
  150. // If frozen, don't make new particles.
  151. if( m_DustFlags & DUSTFLAGS_FROZEN )
  152. return;
  153. // Spawn particles?
  154. if( m_DustFlags & DUSTFLAGS_ON )
  155. {
  156. float flDelta = MIN( gpGlobals->frametime, 0.1f );
  157. while( m_Spawner.NextEvent( flDelta ) )
  158. {
  159. AttemptSpawnNewParticle();
  160. }
  161. }
  162. // Tell the particle manager our bbox.
  163. Vector vWorldMins, vWorldMaxs;
  164. CollisionProp()->WorldSpaceAABB( &vWorldMins, &vWorldMaxs );
  165. vWorldMins -= Vector( m_flSizeMax, m_flSizeMax, m_flSizeMax );
  166. vWorldMaxs += Vector( m_flSizeMax, m_flSizeMax, m_flSizeMax );
  167. m_Effect.GetBinding().SetBBox( vWorldMins, vWorldMaxs );
  168. }
  169. bool C_Func_Dust::ShouldDraw()
  170. {
  171. return false;
  172. }
  173. void C_Func_Dust::AttemptSpawnNewParticle()
  174. {
  175. // Find a random spot inside our bmodel.
  176. static int nTests=10;
  177. for( int iTest=0; iTest < nTests; iTest++ )
  178. {
  179. Vector vPercent = RandomVector( 0, 1 );
  180. Vector vTest = WorldAlignMins() + (WorldAlignMaxs() - WorldAlignMins()) * vPercent;
  181. int contents = enginetrace->GetPointContents_Collideable( GetCollideable(), vTest );
  182. if( contents & CONTENTS_SOLID )
  183. {
  184. CFuncDustParticle *pParticle = (CFuncDustParticle*)m_Effect.AddParticle( 10, m_hMaterial, vTest );
  185. if( pParticle )
  186. {
  187. pParticle->m_vVelocity = RandomVector( -m_SpeedMax, m_SpeedMax );
  188. pParticle->m_vVelocity.z -= m_FallSpeed;
  189. pParticle->m_flLifetime = 0;
  190. pParticle->m_flDieTime = RemapVal( rand(), 0, VALVE_RAND_MAX, m_LifetimeMin, m_LifetimeMax );
  191. if( m_DustFlags & DUSTFLAGS_SCALEMOTES )
  192. pParticle->m_flSize = RemapVal( rand(), 0, VALVE_RAND_MAX, m_flSizeMin/10000.0f, m_flSizeMax/10000.0f );
  193. else
  194. pParticle->m_flSize = RemapVal( rand(), 0, VALVE_RAND_MAX, m_flSizeMin, m_flSizeMax );
  195. pParticle->m_Color = m_Color;
  196. }
  197. break;
  198. }
  199. }
  200. }
  201. //
  202. // Dust
  203. //
  204. //-----------------------------------------------------------------------------
  205. // Spew out dust!
  206. //-----------------------------------------------------------------------------
  207. void FX_Dust( const Vector &vecOrigin, const Vector &vecDirection, float flSize, float flSpeed )
  208. {
  209. VPROF_BUDGET( "FX_Dust", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  210. int numPuffs = (flSize*0.5f);
  211. if ( numPuffs < 1 )
  212. numPuffs = 1;
  213. if ( numPuffs > 32 )
  214. numPuffs = 32;
  215. float speed = flSpeed * 0.1f;
  216. if ( speed < 0 )
  217. speed = 1.0f;
  218. if (speed > 48.0f )
  219. speed = 48.0f;
  220. //FIXME: Better sampling area
  221. Vector offset = vecOrigin + ( vecDirection * flSize );
  222. //Find area ambient light color and use it to tint smoke
  223. Vector worldLight = WorldGetLightForPoint( offset, true );
  224. // Throw puffs
  225. SimpleParticle particle;
  226. for ( int i = 0; i < numPuffs; i++ )
  227. {
  228. offset.Random( -(flSize*0.25f), flSize*0.25f );
  229. offset += vecOrigin + ( vecDirection * flSize );
  230. particle.m_Pos = offset;
  231. particle.m_flLifetime = 0.0f;
  232. particle.m_flDieTime = random->RandomFloat( 0.4f, 1.0f );
  233. particle.m_vecVelocity = vecDirection * random->RandomFloat( speed*0.5f, speed ) * i;
  234. particle.m_vecVelocity[2] = 0.0f;
  235. int color = random->RandomInt( 48, 64 );
  236. particle.m_uchColor[0] = (color+16) + ( worldLight[0] * (float) color );
  237. particle.m_uchColor[1] = (color+8) + ( worldLight[1] * (float) color );
  238. particle.m_uchColor[2] = color + ( worldLight[2] * (float) color );
  239. particle.m_uchStartAlpha= random->RandomInt( 64, 128 );
  240. particle.m_uchEndAlpha = 0;
  241. particle.m_uchStartSize = random->RandomInt( 2, 8 );
  242. particle.m_uchEndSize = random->RandomInt( 24, 48 );
  243. particle.m_flRoll = random->RandomInt( 0, 360 );
  244. particle.m_flRollDelta = random->RandomFloat( -0.5f, 0.5f );
  245. AddSimpleParticle( &particle, g_Mat_DustPuff[random->RandomInt(0,1)] );
  246. }
  247. }
  248. class C_TEDust: public C_TEParticleSystem
  249. {
  250. public:
  251. DECLARE_CLASS( C_TEDust, C_TEParticleSystem );
  252. DECLARE_CLIENTCLASS();
  253. C_TEDust();
  254. virtual ~C_TEDust();
  255. public:
  256. virtual void PostDataUpdate( DataUpdateType_t updateType );
  257. virtual bool ShouldDraw() { return true; }
  258. public:
  259. float m_flSize;
  260. float m_flSpeed;
  261. Vector m_vecDirection;
  262. protected:
  263. void GetDustColor( Vector &color );
  264. };
  265. IMPLEMENT_CLIENTCLASS_EVENT_DT( C_TEDust, DT_TEDust, CTEDust )
  266. RecvPropFloat(RECVINFO(m_flSize)),
  267. RecvPropFloat(RECVINFO(m_flSpeed)),
  268. RecvPropVector(RECVINFO(m_vecDirection)),
  269. END_RECV_TABLE()
  270. //==================================================
  271. // C_TEDust
  272. //==================================================
  273. C_TEDust::C_TEDust()
  274. {
  275. }
  276. C_TEDust::~C_TEDust()
  277. {
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose:
  281. // Input : bNewEntity - whether or not to start a new entity
  282. //-----------------------------------------------------------------------------
  283. void C_TEDust::PostDataUpdate( DataUpdateType_t updateType )
  284. {
  285. FX_Dust( m_vecOrigin, m_vecDirection, m_flSize, m_flSpeed );
  286. }
  287. void TE_Dust( IRecipientFilter& filter, float delay,
  288. const Vector &pos, const Vector &dir, float size, float speed )
  289. {
  290. FX_Dust( pos, dir, size, speed );
  291. }