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.

524 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements a particle system steam jet.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "particle_prototype.h"
  9. #include "particle_util.h"
  10. #include "baseparticleentity.h"
  11. #include "fx.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //NOTENOTE: Mirrored in dlls\steamjet.h
  15. #define STEAM_NORMAL 0
  16. #define STEAM_HEATWAVE 1
  17. #define STEAMJET_NUMRAMPS 5
  18. #define SF_EMISSIVE 0x00000001
  19. //==================================================
  20. // C_SteamJet
  21. //==================================================
  22. class C_SteamJet : public C_BaseParticleEntity, public IPrototypeAppEffect
  23. {
  24. public:
  25. DECLARE_CLIENTCLASS();
  26. DECLARE_CLASS( C_SteamJet, C_BaseParticleEntity );
  27. C_SteamJet();
  28. ~C_SteamJet();
  29. class SteamJetParticle : public Particle
  30. {
  31. public:
  32. Vector m_Velocity;
  33. float m_flRoll;
  34. float m_flRollDelta;
  35. float m_Lifetime;
  36. float m_DieTime;
  37. unsigned char m_uchStartSize;
  38. unsigned char m_uchEndSize;
  39. };
  40. int IsEmissive( void ) { return ( m_spawnflags & SF_EMISSIVE ); }
  41. //C_BaseEntity
  42. public:
  43. virtual void OnDataChanged( DataUpdateType_t updateType );
  44. //IPrototypeAppEffect
  45. public:
  46. virtual void Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs);
  47. virtual bool GetPropEditInfo(RecvTable **ppTable, void **ppObj);
  48. //IParticleEffect
  49. public:
  50. virtual void Update(float fTimeDelta);
  51. virtual void RenderParticles( CParticleRenderIterator *pIterator );
  52. virtual void SimulateParticles( CParticleSimulateIterator *pIterator );
  53. //Stuff from the datatable
  54. public:
  55. float m_SpreadSpeed;
  56. float m_Speed;
  57. float m_StartSize;
  58. float m_EndSize;
  59. float m_Rate;
  60. float m_JetLength; // Length of the jet. Lifetime is derived from this.
  61. int m_bEmit; // Emit particles?
  62. int m_nType; // Type of particles to emit
  63. bool m_bFaceLeft; // For support of legacy env_steamjet entity, which faced left instead of forward.
  64. int m_spawnflags;
  65. float m_flRollSpeed;
  66. private:
  67. void UpdateLightingRamp();
  68. private:
  69. // Stored the last time it updates the lighting ramp, so it can cache the values.
  70. Vector m_vLastRampUpdatePos;
  71. QAngle m_vLastRampUpdateAngles;
  72. float m_Lifetime; // Calculated from m_JetLength / m_Speed;
  73. // We sample the world to get these colors and ramp the particles.
  74. Vector m_Ramps[STEAMJET_NUMRAMPS];
  75. CParticleMgr *m_pParticleMgr;
  76. PMaterialHandle m_MaterialHandle;
  77. TimedEvent m_ParticleSpawn;
  78. private:
  79. C_SteamJet( const C_SteamJet & );
  80. };
  81. // ------------------------------------------------------------------------- //
  82. // Tables.
  83. // ------------------------------------------------------------------------- //
  84. // Expose to the particle app.
  85. EXPOSE_PROTOTYPE_EFFECT(SteamJet, C_SteamJet);
  86. // Datatable..
  87. IMPLEMENT_CLIENTCLASS_DT(C_SteamJet, DT_SteamJet, CSteamJet)
  88. RecvPropFloat(RECVINFO(m_SpreadSpeed), 0),
  89. RecvPropFloat(RECVINFO(m_Speed), 0),
  90. RecvPropFloat(RECVINFO(m_StartSize), 0),
  91. RecvPropFloat(RECVINFO(m_EndSize), 0),
  92. RecvPropFloat(RECVINFO(m_Rate), 0),
  93. RecvPropFloat(RECVINFO(m_JetLength), 0),
  94. RecvPropInt(RECVINFO(m_bEmit), 0),
  95. RecvPropInt(RECVINFO(m_bFaceLeft), 0),
  96. RecvPropInt(RECVINFO(m_nType), 0),
  97. RecvPropInt( RECVINFO( m_spawnflags ) ),
  98. RecvPropFloat(RECVINFO(m_flRollSpeed), 0 ),
  99. END_RECV_TABLE()
  100. // ------------------------------------------------------------------------- //
  101. // C_SteamJet implementation.
  102. // ------------------------------------------------------------------------- //
  103. C_SteamJet::C_SteamJet()
  104. {
  105. m_pParticleMgr = NULL;
  106. m_MaterialHandle = INVALID_MATERIAL_HANDLE;
  107. m_SpreadSpeed = 15;
  108. m_Speed = 120;
  109. m_StartSize = 10;
  110. m_EndSize = 25;
  111. m_Rate = 26;
  112. m_JetLength = 80;
  113. m_bEmit = true;
  114. m_bFaceLeft = false;
  115. m_ParticleEffect.SetAlwaysSimulate( false ); // Don't simulate outside the PVS or frustum.
  116. m_vLastRampUpdatePos.Init( 1e24, 1e24, 1e24 );
  117. m_vLastRampUpdateAngles.Init( 1e24, 1e24, 1e24 );
  118. }
  119. C_SteamJet::~C_SteamJet()
  120. {
  121. if(m_pParticleMgr)
  122. m_pParticleMgr->RemoveEffect( &m_ParticleEffect );
  123. }
  124. //-----------------------------------------------------------------------------
  125. // Purpose: Called after a data update has occured
  126. // Input : bnewentity -
  127. //-----------------------------------------------------------------------------
  128. void C_SteamJet::OnDataChanged(DataUpdateType_t updateType)
  129. {
  130. C_BaseEntity::OnDataChanged(updateType);
  131. if(updateType == DATA_UPDATE_CREATED)
  132. {
  133. Start(ParticleMgr(), NULL);
  134. }
  135. // Recalulate lifetime in case length or speed changed.
  136. m_Lifetime = m_JetLength / m_Speed;
  137. m_ParticleEffect.SetParticleCullRadius( MAX(m_StartSize, m_EndSize) );
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose: Starts the effect
  141. // Input : *pParticleMgr -
  142. // *pArgs -
  143. //-----------------------------------------------------------------------------
  144. void C_SteamJet::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs)
  145. {
  146. pParticleMgr->AddEffect( &m_ParticleEffect, this );
  147. switch(m_nType)
  148. {
  149. case STEAM_NORMAL:
  150. default:
  151. m_MaterialHandle = g_Mat_DustPuff[0];
  152. break;
  153. case STEAM_HEATWAVE:
  154. m_MaterialHandle = m_ParticleEffect.FindOrAddMaterial("sprites/heatwave");
  155. break;
  156. }
  157. m_ParticleSpawn.Init(m_Rate);
  158. m_Lifetime = m_JetLength / m_Speed;
  159. m_pParticleMgr = pParticleMgr;
  160. UpdateLightingRamp();
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Purpose:
  164. // Input : **ppTable -
  165. // **ppObj -
  166. // Output : Returns true on success, false on failure.
  167. //-----------------------------------------------------------------------------
  168. bool C_SteamJet::GetPropEditInfo( RecvTable **ppTable, void **ppObj )
  169. {
  170. *ppTable = &REFERENCE_RECV_TABLE(DT_SteamJet);
  171. *ppObj = this;
  172. return true;
  173. }
  174. // This might be useful someday.
  175. /*
  176. void CalcFastApproximateRenderBoundsAABB( C_BaseEntity *pEnt, float flBloatSize, Vector *pMin, Vector *pMax )
  177. {
  178. C_BaseEntity *pParent = pEnt->GetMoveParent();
  179. if ( pParent )
  180. {
  181. // Get the parent's abs space world bounds.
  182. CalcFastApproximateRenderBoundsAABB( pParent, 0, pMin, pMax );
  183. // Add the maximum of our local render bounds. This is making the assumption that we can be at any
  184. // point and at any angle within the parent's world space bounds.
  185. Vector vAddMins, vAddMaxs;
  186. pEnt->GetRenderBounds( vAddMins, vAddMaxs );
  187. flBloatSize += MAX( vAddMins.Length(), vAddMaxs.Length() );
  188. }
  189. else
  190. {
  191. // Start out with our own render bounds. Since we don't have a parent, this won't incur any nasty
  192. pEnt->GetRenderBoundsWorldspace( *pMin, *pMax );
  193. }
  194. // Bloat the box.
  195. if ( flBloatSize )
  196. {
  197. *pMin -= Vector( flBloatSize, flBloatSize, flBloatSize );
  198. *pMax += Vector( flBloatSize, flBloatSize, flBloatSize );
  199. }
  200. }
  201. */
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. // Input : fTimeDelta -
  205. //-----------------------------------------------------------------------------
  206. void C_SteamJet::Update(float fTimeDelta)
  207. {
  208. if(!m_pParticleMgr)
  209. {
  210. assert(false);
  211. return;
  212. }
  213. if( m_bEmit )
  214. {
  215. // Add new particles.
  216. int nToEmit = 0;
  217. float tempDelta = fTimeDelta;
  218. while( m_ParticleSpawn.NextEvent(tempDelta) )
  219. ++nToEmit;
  220. if ( nToEmit > 0 )
  221. {
  222. Vector forward, right, up;
  223. AngleVectors(GetAbsAngles(), &forward, &right, &up);
  224. // Legacy env_steamjet entities faced left instead of forward.
  225. if (m_bFaceLeft)
  226. {
  227. Vector temp = forward;
  228. forward = -right;
  229. right = temp;
  230. }
  231. // EVIL: Ideally, we could tell the renderer our OBB, and let it build a big box that encloses
  232. // the entity with its parent so it doesn't have to setup its parent's bones here.
  233. Vector vEndPoint = GetAbsOrigin() + forward * m_Speed;
  234. Vector vMin, vMax;
  235. VectorMin( GetAbsOrigin(), vEndPoint, vMin );
  236. VectorMax( GetAbsOrigin(), vEndPoint, vMax );
  237. m_ParticleEffect.SetBBox( vMin, vMax );
  238. if ( m_ParticleEffect.WasDrawnPrevFrame() )
  239. {
  240. while ( nToEmit-- )
  241. {
  242. // Make a new particle.
  243. if( SteamJetParticle *pParticle = (SteamJetParticle*) m_ParticleEffect.AddParticle( sizeof(SteamJetParticle), m_MaterialHandle ) )
  244. {
  245. pParticle->m_Pos = GetAbsOrigin();
  246. pParticle->m_Velocity =
  247. FRand(-m_SpreadSpeed,m_SpreadSpeed) * right +
  248. FRand(-m_SpreadSpeed,m_SpreadSpeed) * up +
  249. m_Speed * forward;
  250. pParticle->m_Lifetime = 0;
  251. pParticle->m_DieTime = m_Lifetime;
  252. pParticle->m_uchStartSize = m_StartSize;
  253. pParticle->m_uchEndSize = m_EndSize;
  254. pParticle->m_flRoll = random->RandomFloat( 0, 360 );
  255. pParticle->m_flRollDelta = random->RandomFloat( -m_flRollSpeed, m_flRollSpeed );
  256. }
  257. }
  258. }
  259. UpdateLightingRamp();
  260. }
  261. }
  262. }
  263. // Render a quad on the screen where you pass in color and size.
  264. // Normal is random and "flutters"
  265. inline void RenderParticle_ColorSizePerturbNormal(
  266. ParticleDraw* pDraw,
  267. const Vector &pos,
  268. const Vector &color,
  269. const float alpha,
  270. const float size
  271. )
  272. {
  273. // Don't render totally transparent particles.
  274. if( alpha < 0.001f )
  275. return;
  276. CMeshBuilder *pBuilder = pDraw->GetMeshBuilder();
  277. if( !pBuilder )
  278. return;
  279. unsigned char ubColor[4];
  280. ubColor[0] = (unsigned char)RoundFloatToInt( color.x * 254.9f );
  281. ubColor[1] = (unsigned char)RoundFloatToInt( color.y * 254.9f );
  282. ubColor[2] = (unsigned char)RoundFloatToInt( color.z * 254.9f );
  283. ubColor[3] = (unsigned char)RoundFloatToInt( alpha * 254.9f );
  284. Vector vNorm;
  285. vNorm.Random( -1.0f, 1.0f );
  286. // Add the 4 corner vertices.
  287. pBuilder->Position3f( pos.x-size, pos.y-size, pos.z );
  288. pBuilder->Color4ubv( ubColor );
  289. pBuilder->Normal3fv( vNorm.Base() );
  290. pBuilder->TexCoord2f( 0, 0, 1.0f );
  291. pBuilder->AdvanceVertex();
  292. pBuilder->Position3f( pos.x-size, pos.y+size, pos.z );
  293. pBuilder->Color4ubv( ubColor );
  294. pBuilder->Normal3fv( vNorm.Base() );
  295. pBuilder->TexCoord2f( 0, 0, 0 );
  296. pBuilder->AdvanceVertex();
  297. pBuilder->Position3f( pos.x+size, pos.y+size, pos.z );
  298. pBuilder->Color4ubv( ubColor );
  299. pBuilder->Normal3fv( vNorm.Base() );
  300. pBuilder->TexCoord2f( 0, 1.0f, 0 );
  301. pBuilder->AdvanceVertex();
  302. pBuilder->Position3f( pos.x+size, pos.y-size, pos.z );
  303. pBuilder->Color4ubv( ubColor );
  304. pBuilder->Normal3fv( vNorm.Base() );
  305. pBuilder->TexCoord2f( 0, 1.0f, 1.0f );
  306. pBuilder->AdvanceVertex();
  307. }
  308. void C_SteamJet::RenderParticles( CParticleRenderIterator *pIterator )
  309. {
  310. const SteamJetParticle *pParticle = (const SteamJetParticle*)pIterator->GetFirst();
  311. while ( pParticle )
  312. {
  313. // Render.
  314. Vector tPos;
  315. TransformParticle(m_pParticleMgr->GetModelView(), pParticle->m_Pos, tPos);
  316. float sortKey = tPos.z;
  317. float lifetimeT = pParticle->m_Lifetime / (pParticle->m_DieTime + 0.001);
  318. float fRamp = lifetimeT * (STEAMJET_NUMRAMPS-1);
  319. int iRamp = (int)fRamp;
  320. float fraction = fRamp - iRamp;
  321. Vector vRampColor = m_Ramps[iRamp] + (m_Ramps[iRamp+1] - m_Ramps[iRamp]) * fraction;
  322. vRampColor[0] = MIN( 1.0f, vRampColor[0] );
  323. vRampColor[1] = MIN( 1.0f, vRampColor[1] );
  324. vRampColor[2] = MIN( 1.0f, vRampColor[2] );
  325. float sinLifetime = sin(pParticle->m_Lifetime * 3.14159f / pParticle->m_DieTime);
  326. if ( m_nType == STEAM_HEATWAVE )
  327. {
  328. RenderParticle_ColorSizePerturbNormal(
  329. pIterator->GetParticleDraw(),
  330. tPos,
  331. vRampColor,
  332. sinLifetime * (GetRenderAlpha()/255.0f),
  333. FLerp(m_StartSize, m_EndSize, pParticle->m_Lifetime));
  334. }
  335. else
  336. {
  337. RenderParticle_ColorSizeAngle(
  338. pIterator->GetParticleDraw(),
  339. tPos,
  340. vRampColor,
  341. sinLifetime * (GetRenderAlpha()/255.0f),
  342. FLerp(pParticle->m_uchStartSize, pParticle->m_uchEndSize, pParticle->m_Lifetime),
  343. pParticle->m_flRoll );
  344. }
  345. pParticle = (const SteamJetParticle*)pIterator->GetNext( sortKey );
  346. }
  347. }
  348. void C_SteamJet::SimulateParticles( CParticleSimulateIterator *pIterator )
  349. {
  350. //Don't simulate if we're emiting particles...
  351. //This fixes the cases where looking away from a steam jet and then looking back would cause a break on the stream.
  352. if ( m_ParticleEffect.WasDrawnPrevFrame() == false && m_bEmit )
  353. return;
  354. SteamJetParticle *pParticle = (SteamJetParticle*)pIterator->GetFirst();
  355. while ( pParticle )
  356. {
  357. // Should this particle die?
  358. pParticle->m_Lifetime += pIterator->GetTimeDelta();
  359. if( pParticle->m_Lifetime > pParticle->m_DieTime )
  360. {
  361. pIterator->RemoveParticle( pParticle );
  362. }
  363. else
  364. {
  365. pParticle->m_flRoll += pParticle->m_flRollDelta * pIterator->GetTimeDelta();
  366. pParticle->m_Pos = pParticle->m_Pos + pParticle->m_Velocity * pIterator->GetTimeDelta();
  367. }
  368. pParticle = (SteamJetParticle*)pIterator->GetNext();
  369. }
  370. }
  371. void C_SteamJet::UpdateLightingRamp()
  372. {
  373. if( VectorsAreEqual( m_vLastRampUpdatePos, GetAbsOrigin(), 0.1 ) &&
  374. QAnglesAreEqual( m_vLastRampUpdateAngles, GetAbsAngles(), 0.1 ) )
  375. {
  376. return;
  377. }
  378. m_vLastRampUpdatePos = GetAbsOrigin();
  379. m_vLastRampUpdateAngles = GetAbsAngles();
  380. // Sample the world lighting where we think the particles will be.
  381. Vector forward, right, up;
  382. AngleVectors(GetAbsAngles(), &forward, &right, &up);
  383. // Legacy env_steamjet entities faced left instead of forward.
  384. if (m_bFaceLeft)
  385. {
  386. Vector temp = forward;
  387. forward = -right;
  388. right = temp;
  389. }
  390. Vector startPos = GetAbsOrigin();
  391. Vector endPos = GetAbsOrigin() + forward * (m_Speed * m_Lifetime);
  392. for(int iRamp=0; iRamp < STEAMJET_NUMRAMPS; iRamp++)
  393. {
  394. float t = (float)iRamp / (STEAMJET_NUMRAMPS-1);
  395. Vector vTestPos = startPos + (endPos - startPos) * t;
  396. Vector *pRamp = &m_Ramps[iRamp];
  397. *pRamp = WorldGetLightForPoint(vTestPos, false);
  398. if ( IsEmissive() )
  399. {
  400. pRamp->x += (GetRenderColorR()/255.0f);
  401. pRamp->y += (GetRenderColorG()/255.0f);
  402. pRamp->z += (GetRenderColorB()/255.0f);
  403. pRamp->x = clamp( pRamp->x, 0.0f, 1.0f );
  404. pRamp->y = clamp( pRamp->y, 0.0f, 1.0f );
  405. pRamp->z = clamp( pRamp->z, 0.0f, 1.0f );
  406. }
  407. else
  408. {
  409. pRamp->x *= (GetRenderColorR()/255.0f);
  410. pRamp->y *= (GetRenderColorG()/255.0f);
  411. pRamp->z *= (GetRenderColorB()/255.0f);
  412. }
  413. // Renormalize?
  414. float maxVal = MAX(pRamp->x, MAX(pRamp->y, pRamp->z));
  415. if(maxVal > 1)
  416. {
  417. *pRamp = *pRamp / maxVal;
  418. }
  419. }
  420. }