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.

501 lines
13 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 "baseparticleentity.h"
  10. #include "particles_simple.h"
  11. #include "filesystem.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. #ifdef HL2_EPISODIC
  15. #define SMOKESTACK_MAX_MATERIALS 8
  16. #else
  17. #define SMOKESTACK_MAX_MATERIALS 1
  18. #endif
  19. //==================================================
  20. // C_SmokeStack
  21. //==================================================
  22. class C_SmokeStack : public C_BaseParticleEntity, public IPrototypeAppEffect
  23. {
  24. public:
  25. DECLARE_CLIENTCLASS();
  26. DECLARE_CLASS( C_SmokeStack, C_BaseParticleEntity );
  27. C_SmokeStack();
  28. ~C_SmokeStack();
  29. class SmokeStackParticle : public Particle
  30. {
  31. public:
  32. Vector m_Velocity;
  33. Vector m_vAccel;
  34. float m_Lifetime;
  35. float m_flAngle;
  36. float m_flRollDelta;
  37. float m_flSortPos;
  38. };
  39. //C_BaseEntity
  40. public:
  41. virtual void OnDataChanged( DataUpdateType_t updateType );
  42. virtual void ClientThink();
  43. //IPrototypeAppEffect
  44. public:
  45. virtual void Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs);
  46. virtual bool GetPropEditInfo(RecvTable **ppTable, void **ppObj);
  47. //IParticleEffect
  48. public:
  49. virtual void Update(float fTimeDelta);
  50. virtual void RenderParticles( CParticleRenderIterator *pIterator );
  51. virtual void SimulateParticles( CParticleSimulateIterator *pIterator );
  52. virtual void StartRender( VMatrix &effectMatrix );
  53. private:
  54. void QueueLightParametersInRenderer();
  55. //Stuff from the datatable
  56. public:
  57. CParticleSphereRenderer m_Renderer;
  58. float m_SpreadSpeed;
  59. float m_Speed;
  60. float m_StartSize;
  61. float m_EndSize;
  62. float m_Rate;
  63. float m_JetLength; // Length of the jet. Lifetime is derived from this.
  64. int m_bEmit; // Emit particles?
  65. float m_flBaseSpread;
  66. class CLightInfo
  67. {
  68. public:
  69. Vector m_vPos;
  70. Vector m_vColor;
  71. float m_flIntensity;
  72. };
  73. // Note: there are two ways the directional light can be specified. The default is to use
  74. // DirLightColor and a default dirlight source (from above or below).
  75. // In this case, m_DirLight.m_vPos and m_DirLight.m_flIntensity are ignored.
  76. //
  77. // The other is to attach a directional env_particlelight to us.
  78. // In this case, m_DirLightSource is ignored and all the m_DirLight parameters are used.
  79. CParticleLightInfo m_AmbientLight;
  80. CParticleLightInfo m_DirLight;
  81. Vector m_vBaseColor;
  82. Vector m_vWind;
  83. float m_flTwist;
  84. int m_iMaterialModel;
  85. private:
  86. C_SmokeStack( const C_SmokeStack & );
  87. float m_TwistMat[2][2];
  88. int m_bTwist;
  89. float m_flAlphaScale;
  90. float m_InvLifetime; // Calculated from m_JetLength / m_Speed;
  91. CParticleMgr *m_pParticleMgr;
  92. PMaterialHandle m_MaterialHandle[SMOKESTACK_MAX_MATERIALS];
  93. TimedEvent m_ParticleSpawn;
  94. int m_iMaxFrames;
  95. bool m_bInView;
  96. float m_flRollSpeed;
  97. };
  98. // ------------------------------------------------------------------------- //
  99. // Tables.
  100. // ------------------------------------------------------------------------- //
  101. // Expose to the particle app.
  102. EXPOSE_PROTOTYPE_EFFECT(SmokeStack, C_SmokeStack);
  103. IMPLEMENT_CLIENTCLASS_DT(C_SmokeStack, DT_SmokeStack, CSmokeStack)
  104. RecvPropFloat(RECVINFO(m_SpreadSpeed), 0),
  105. RecvPropFloat(RECVINFO(m_Speed), 0),
  106. RecvPropFloat(RECVINFO(m_StartSize), 0),
  107. RecvPropFloat(RECVINFO(m_EndSize), 0),
  108. RecvPropFloat(RECVINFO(m_Rate), 0),
  109. RecvPropFloat(RECVINFO(m_JetLength), 0),
  110. RecvPropInt(RECVINFO(m_bEmit), 0),
  111. RecvPropFloat(RECVINFO(m_flBaseSpread)),
  112. RecvPropFloat(RECVINFO(m_flTwist)),
  113. RecvPropFloat(RECVINFO(m_flRollSpeed )),
  114. RecvPropIntWithMinusOneFlag( RECVINFO( m_iMaterialModel ) ),
  115. RecvPropVector( RECVINFO(m_AmbientLight.m_vPos) ),
  116. RecvPropVector( RECVINFO(m_AmbientLight.m_vColor) ),
  117. RecvPropFloat( RECVINFO(m_AmbientLight.m_flIntensity) ),
  118. RecvPropVector( RECVINFO(m_DirLight.m_vPos) ),
  119. RecvPropVector( RECVINFO(m_DirLight.m_vColor) ),
  120. RecvPropFloat( RECVINFO(m_DirLight.m_flIntensity) ),
  121. RecvPropVector(RECVINFO(m_vWind))
  122. END_RECV_TABLE()
  123. // ------------------------------------------------------------------------- //
  124. // C_SmokeStack implementation.
  125. // ------------------------------------------------------------------------- //
  126. C_SmokeStack::C_SmokeStack()
  127. {
  128. m_pParticleMgr = NULL;
  129. m_MaterialHandle[0] = INVALID_MATERIAL_HANDLE;
  130. m_iMaterialModel = -1;
  131. m_SpreadSpeed = 15;
  132. m_Speed = 30;
  133. m_StartSize = 10;
  134. m_EndSize = 15;
  135. m_Rate = 80;
  136. m_JetLength = 180;
  137. m_bEmit = true;
  138. m_flBaseSpread = 20;
  139. m_bInView = false;
  140. // Lighting is (base color) + (ambient / dist^2) + bump(directional / dist^2)
  141. // By default, we use bottom-up lighting for the directional.
  142. SetRenderColor( 0, 0, 0 );
  143. SetRenderAlpha( 255 );
  144. m_AmbientLight.m_vPos.Init(0,0,-100);
  145. m_AmbientLight.m_vColor.Init( 40, 40, 40 );
  146. m_AmbientLight.m_flIntensity = 8000;
  147. m_DirLight.m_vColor.Init( 255, 128, 0 );
  148. m_vWind.Init();
  149. m_flTwist = 0;
  150. }
  151. C_SmokeStack::~C_SmokeStack()
  152. {
  153. if(m_pParticleMgr)
  154. m_pParticleMgr->RemoveEffect( &m_ParticleEffect );
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Called after a data update has occured
  158. // Input : bnewentity -
  159. //-----------------------------------------------------------------------------
  160. void C_SmokeStack::OnDataChanged(DataUpdateType_t updateType)
  161. {
  162. C_BaseEntity::OnDataChanged(updateType);
  163. if(updateType == DATA_UPDATE_CREATED)
  164. {
  165. Start(ParticleMgr(), NULL);
  166. }
  167. // Recalulate lifetime in case length or speed changed.
  168. m_InvLifetime = m_Speed / m_JetLength;
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose: Starts the effect
  172. // Input : *pParticleMgr -
  173. // *pArgs -
  174. //-----------------------------------------------------------------------------
  175. void C_SmokeStack::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs)
  176. {
  177. pParticleMgr->AddEffect( &m_ParticleEffect, this );
  178. // Figure out the material name.
  179. char str[512] = "unset_material";
  180. const model_t *pModel = modelinfo->GetModel( m_iMaterialModel );
  181. if ( pModel )
  182. {
  183. Q_strncpy( str, modelinfo->GetModelName( pModel ), sizeof( str ) );
  184. // Get rid of the extension because the material system doesn't want it.
  185. char *pExt = Q_stristr( str, ".vmt" );
  186. if ( pExt )
  187. pExt[0] = 0;
  188. }
  189. m_MaterialHandle[0] = m_ParticleEffect.FindOrAddMaterial( str );
  190. #ifdef HL2_EPISODIC
  191. int iCount = 1;
  192. char szNames[512];
  193. int iLength = Q_strlen( str );
  194. str[iLength-1] = '\0';
  195. Q_snprintf( szNames, sizeof( szNames ), "%s%d.vmt", str, iCount );
  196. while ( filesystem->FileExists( VarArgs( "materials/%s", szNames ) ) && iCount < SMOKESTACK_MAX_MATERIALS )
  197. {
  198. char *pExt = Q_stristr( szNames, ".vmt" );
  199. if ( pExt )
  200. pExt[0] = 0;
  201. m_MaterialHandle[iCount] = m_ParticleEffect.FindOrAddMaterial( szNames );
  202. iCount++;
  203. }
  204. m_iMaxFrames = iCount-1;
  205. #endif
  206. m_ParticleSpawn.Init(m_Rate);
  207. m_InvLifetime = m_Speed / m_JetLength;
  208. m_pParticleMgr = pParticleMgr;
  209. // Figure out how we need to draw.
  210. IMaterial *pMaterial = pParticleMgr->PMaterialToIMaterial( m_MaterialHandle[0] );
  211. if( pMaterial )
  212. {
  213. m_Renderer.Init( pParticleMgr, pMaterial );
  214. }
  215. QueueLightParametersInRenderer();
  216. // For the first N seconds, always simulate so it can build up the smokestack.
  217. // Afterwards, we set it to freeze when it's not being rendered.
  218. m_ParticleEffect.SetAlwaysSimulate( true );
  219. SetNextClientThink( gpGlobals->curtime + 5 );
  220. }
  221. void C_SmokeStack::ClientThink()
  222. {
  223. m_ParticleEffect.SetAlwaysSimulate( false );
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. // Input : **ppTable -
  228. // **ppObj -
  229. // Output : Returns true on success, false on failure.
  230. //-----------------------------------------------------------------------------
  231. bool C_SmokeStack::GetPropEditInfo( RecvTable **ppTable, void **ppObj )
  232. {
  233. *ppTable = &REFERENCE_RECV_TABLE(DT_SmokeStack);
  234. *ppObj = this;
  235. return true;
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. // Input : fTimeDelta -
  240. //-----------------------------------------------------------------------------
  241. void C_SmokeStack::Update(float fTimeDelta)
  242. {
  243. if( !m_pParticleMgr )
  244. {
  245. assert(false);
  246. return;
  247. }
  248. // Don't spawn particles unless we're visible.
  249. if( m_bEmit && (m_ParticleEffect.WasDrawnPrevFrame() || m_ParticleEffect.GetAlwaysSimulate()) )
  250. {
  251. // Add new particles.
  252. Vector forward, right, up;
  253. AngleVectors(GetAbsAngles(), &forward, &right, &up);
  254. float tempDelta = fTimeDelta;
  255. while(m_ParticleSpawn.NextEvent(tempDelta))
  256. {
  257. int iRandomFrame = random->RandomInt( 0, m_iMaxFrames );
  258. #ifndef HL2_EPISODIC
  259. iRandomFrame = 0;
  260. #endif
  261. // Make a new particle.
  262. if(SmokeStackParticle *pParticle = (SmokeStackParticle*)m_ParticleEffect.AddParticle(sizeof(SmokeStackParticle), m_MaterialHandle[iRandomFrame]))
  263. {
  264. float angle = FRand( 0, 2.0f*M_PI_F );
  265. pParticle->m_Pos = GetAbsOrigin() +
  266. right * (cos( angle ) * m_flBaseSpread) +
  267. forward * (sin( angle ) * m_flBaseSpread);
  268. pParticle->m_Velocity =
  269. FRand(-m_SpreadSpeed,m_SpreadSpeed) * right +
  270. FRand(-m_SpreadSpeed,m_SpreadSpeed) * forward +
  271. m_Speed * up;
  272. pParticle->m_vAccel = m_vWind;
  273. pParticle->m_Lifetime = 0;
  274. pParticle->m_flAngle = 0.0f;
  275. #ifdef HL2_EPISODIC
  276. pParticle->m_flAngle = RandomFloat( 0, 360 );
  277. #endif
  278. pParticle->m_flRollDelta = random->RandomFloat( -m_flRollSpeed, m_flRollSpeed );
  279. pParticle->m_flSortPos = pParticle->m_Pos.z;
  280. }
  281. }
  282. }
  283. // Setup the twist matrix.
  284. float flTwist = (m_flTwist * (M_PI_F * 2.f) / 360.0f) * Helper_GetFrameTime();
  285. if( m_bTwist = !!flTwist )
  286. {
  287. m_TwistMat[0][0] = cos(flTwist);
  288. m_TwistMat[0][1] = sin(flTwist);
  289. m_TwistMat[1][0] = -sin(flTwist);
  290. m_TwistMat[1][1] = cos(flTwist);
  291. }
  292. QueueLightParametersInRenderer();
  293. }
  294. void C_SmokeStack::StartRender( VMatrix &effectMatrix )
  295. {
  296. m_Renderer.StartRender( effectMatrix );
  297. }
  298. void C_SmokeStack::QueueLightParametersInRenderer()
  299. {
  300. m_Renderer.SetBaseColor( Vector( GetRenderColorR() / 255.0f, GetRenderColorG() / 255.0f, GetRenderColorB() / 255.0f ) );
  301. m_Renderer.SetAmbientLight( m_AmbientLight );
  302. m_Renderer.SetDirectionalLight( m_DirLight );
  303. m_flAlphaScale = (float)GetRenderAlpha();
  304. }
  305. void C_SmokeStack::RenderParticles( CParticleRenderIterator *pIterator )
  306. {
  307. const SmokeStackParticle *pParticle = (const SmokeStackParticle*)pIterator->GetFirst();
  308. while ( pParticle )
  309. {
  310. // Transform.
  311. Vector tPos;
  312. TransformParticle( m_pParticleMgr->GetModelView(), pParticle->m_Pos, tPos );
  313. // Figure out its alpha. Squaring it after it gets halfway through its lifetime
  314. // makes it get translucent and fade out for a longer time.
  315. //float alpha = cosf( -M_PI_F + tLifetime * M_PI_F * 2.f ) * 0.5f + 0.5f;
  316. float tLifetime = pParticle->m_Lifetime * m_InvLifetime;
  317. float alpha = TableCos( -M_PI_F + tLifetime * M_PI_F * 2.f ) * 0.5f + 0.5f;
  318. if( tLifetime > 0.5f )
  319. alpha *= alpha;
  320. m_Renderer.RenderParticle(
  321. pIterator->GetParticleDraw(),
  322. pParticle->m_Pos,
  323. tPos,
  324. alpha * m_flAlphaScale,
  325. FLerp(m_StartSize, m_EndSize, tLifetime),
  326. DEG2RAD( pParticle->m_flAngle )
  327. );
  328. pParticle = (const SmokeStackParticle*)pIterator->GetNext( pParticle->m_flSortPos );
  329. }
  330. }
  331. void C_SmokeStack::SimulateParticles( CParticleSimulateIterator *pIterator )
  332. {
  333. bool bSortNow = true; // Change this to false if we see sorting issues.
  334. bool bQuickTest = false;
  335. bool bDrawn = m_ParticleEffect.WasDrawnPrevFrame() ? true : false;
  336. if ( bDrawn == true && m_bInView == false )
  337. {
  338. bSortNow = true;
  339. }
  340. if ( bDrawn == false && m_bInView == true )
  341. {
  342. bQuickTest = true;
  343. }
  344. #ifndef HL2_EPISODIC
  345. bQuickTest = false;
  346. bSortNow = true;
  347. #endif
  348. if( bQuickTest == false && m_bEmit && (!m_ParticleEffect.WasDrawnPrevFrame() && !m_ParticleEffect.GetAlwaysSimulate()) )
  349. return;
  350. SmokeStackParticle *pParticle = (SmokeStackParticle*)pIterator->GetFirst();
  351. while ( pParticle )
  352. {
  353. // Should this particle die?
  354. pParticle->m_Lifetime += pIterator->GetTimeDelta();
  355. float tLifetime = pParticle->m_Lifetime * m_InvLifetime;
  356. if( tLifetime >= 1 )
  357. {
  358. pIterator->RemoveParticle( pParticle );
  359. }
  360. else
  361. {
  362. // Transform.
  363. Vector tPos;
  364. if( m_bTwist )
  365. {
  366. Vector vTwist(
  367. pParticle->m_Pos.x - GetAbsOrigin().x,
  368. pParticle->m_Pos.y - GetAbsOrigin().y,
  369. 0);
  370. pParticle->m_Pos.x = vTwist.x * m_TwistMat[0][0] + vTwist.y * m_TwistMat[0][1] + GetAbsOrigin().x;
  371. pParticle->m_Pos.y = vTwist.x * m_TwistMat[1][0] + vTwist.y * m_TwistMat[1][1] + GetAbsOrigin().y;
  372. }
  373. #ifndef HL2_EPISODIC
  374. pParticle->m_Pos = pParticle->m_Pos +
  375. pParticle->m_Velocity * pIterator->GetTimeDelta() +
  376. pParticle->m_vAccel * (0.5f * pIterator->GetTimeDelta() * pIterator->GetTimeDelta());
  377. pParticle->m_Velocity += pParticle->m_vAccel * pIterator->GetTimeDelta();
  378. #else
  379. pParticle->m_Pos = pParticle->m_Pos + pParticle->m_Velocity * pIterator->GetTimeDelta() + pParticle->m_vAccel * pIterator->GetTimeDelta();
  380. #endif
  381. pParticle->m_flAngle += pParticle->m_flRollDelta * pIterator->GetTimeDelta();
  382. if ( bSortNow == true )
  383. {
  384. Vector tPos;
  385. TransformParticle( m_pParticleMgr->GetModelView(), pParticle->m_Pos, tPos );
  386. pParticle->m_flSortPos = tPos.z;
  387. }
  388. }
  389. pParticle = (SmokeStackParticle*)pIterator->GetNext();
  390. }
  391. m_bInView = bDrawn;
  392. }