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.

339 lines
8.3 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "particle_prototype.h"
  9. #include "particle_util.h"
  10. #include "baseparticleentity.h"
  11. #include "engine/IEngineTrace.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. // ------------------------------------------------------------------------- //
  15. // Defines.
  16. // ------------------------------------------------------------------------- //
  17. #define MAX_FIRE_EMITTERS 128
  18. #define FIRE_PARTICLE_LIFETIME 2
  19. Vector g_FireSpreadDirection(0,0,1);
  20. class FireRamp
  21. {
  22. public:
  23. FireRamp(const Vector &s, const Vector &e)
  24. {
  25. m_Start=s;
  26. m_End=e;
  27. }
  28. Vector m_Start;
  29. Vector m_End;
  30. };
  31. FireRamp g_FireRamps[] =
  32. {
  33. FireRamp(Vector(1,0,0), Vector(1,1,0)),
  34. FireRamp(Vector(0.5,0.5,0), Vector(0,0,0))
  35. };
  36. #define NUM_FIRE_RAMPS (sizeof(g_FireRamps) / sizeof(g_FireRamps[0]))
  37. #define NUM_FIREGRID_OFFSETS 8
  38. Vector g_Offsets[NUM_FIREGRID_OFFSETS] =
  39. {
  40. Vector(-1,-1,-1),
  41. Vector( 1,-1,-1),
  42. Vector(-1, 1,-1),
  43. Vector( 1, 1,-1),
  44. Vector(-1,-1, 1),
  45. Vector( 1,-1, 1),
  46. Vector(-1, 1, 1),
  47. Vector( 1, 1, 1),
  48. };
  49. // If you follow g_Offset[index], you can follow g_Offsets[GetOppositeOffset(index)] to get back.
  50. inline int GetOppositeOffset(int offset)
  51. {
  52. return NUM_FIREGRID_OFFSETS - offset - 1;
  53. }
  54. // ------------------------------------------------------------------------- //
  55. // Classes.
  56. // ------------------------------------------------------------------------- //
  57. class C_ParticleFire : public C_BaseParticleEntity, public IPrototypeAppEffect
  58. {
  59. public:
  60. DECLARE_CLASS( C_ParticleFire, C_BaseParticleEntity );
  61. DECLARE_CLIENTCLASS();
  62. C_ParticleFire();
  63. ~C_ParticleFire();
  64. class FireEmitter
  65. {
  66. public:
  67. Vector m_Pos;
  68. TimedEvent m_SpawnEvent;
  69. float m_Lifetime; // How long it's been emitting.
  70. unsigned char m_DirectionsTested; // 1 bit for each of g_Offsets.
  71. };
  72. class FireParticle : public Particle
  73. {
  74. public:
  75. Vector m_StartPos; // The particle moves from m_StartPos to (m_StartPos+m_Direction) over its lifetime.
  76. Vector m_Direction;
  77. float m_Lifetime;
  78. float m_SpinAngle;
  79. unsigned char m_iRamp; // Which fire ramp are we using?
  80. };
  81. // C_BaseEntity.
  82. public:
  83. virtual void OnDataChanged( DataUpdateType_t updateType );
  84. // IPrototypeAppEffect.
  85. public:
  86. virtual void Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs);
  87. // IParticleEffect.
  88. public:
  89. virtual void Update(float fTimeDelta);
  90. virtual void RenderParticles( CParticleRenderIterator *pIterator );
  91. virtual void SimulateParticles( CParticleSimulateIterator *pIterator );
  92. public:
  93. CParticleMgr *m_pParticleMgr;
  94. PMaterialHandle m_MaterialHandle;
  95. // Controls where the initial fire goes.
  96. Vector m_vOrigin;
  97. Vector m_vDirection;
  98. TimedEvent m_EmitterSpawn;
  99. FireEmitter m_Emitters[MAX_FIRE_EMITTERS];
  100. int m_nEmitters;
  101. private:
  102. C_ParticleFire( const C_ParticleFire & );
  103. };
  104. // ------------------------------------------------------------------------- //
  105. // Tables.
  106. // ------------------------------------------------------------------------- //
  107. // Expose to the particle app.
  108. EXPOSE_PROTOTYPE_EFFECT(ParticleFire, C_ParticleFire);
  109. // Datatable..
  110. IMPLEMENT_CLIENTCLASS_DT_NOBASE(C_ParticleFire, DT_ParticleFire, CParticleFire)
  111. RecvPropVector(RECVINFO(m_vOrigin)),
  112. RecvPropVector(RECVINFO(m_vDirection)),
  113. END_RECV_TABLE()
  114. // ------------------------------------------------------------------------- //
  115. // C_FireSmoke implementation.
  116. // ------------------------------------------------------------------------- //
  117. C_ParticleFire::C_ParticleFire()
  118. {
  119. m_pParticleMgr = NULL;
  120. m_MaterialHandle = INVALID_MATERIAL_HANDLE;
  121. }
  122. C_ParticleFire::~C_ParticleFire()
  123. {
  124. if(m_pParticleMgr)
  125. m_pParticleMgr->RemoveEffect( &m_ParticleEffect );
  126. }
  127. void C_ParticleFire::OnDataChanged(DataUpdateType_t updateType)
  128. {
  129. C_BaseEntity::OnDataChanged(updateType);
  130. if(updateType == DATA_UPDATE_CREATED)
  131. {
  132. Start(ParticleMgr(), NULL);
  133. }
  134. }
  135. void C_ParticleFire::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs)
  136. {
  137. m_pParticleMgr = pParticleMgr;
  138. m_pParticleMgr->AddEffect( &m_ParticleEffect, this );
  139. m_MaterialHandle = m_ParticleEffect.FindOrAddMaterial("particle/particle_fire");
  140. // Start
  141. m_nEmitters = 0;
  142. m_EmitterSpawn.Init(15);
  143. }
  144. static float fireSpreadDist = 15;
  145. static float size = 20;
  146. void C_ParticleFire::Update(float fTimeDelta)
  147. {
  148. if(!m_pParticleMgr)
  149. {
  150. assert(false);
  151. return;
  152. }
  153. // Add new emitters.
  154. if(m_nEmitters < MAX_FIRE_EMITTERS)
  155. {
  156. float tempDelta = fTimeDelta;
  157. while(m_EmitterSpawn.NextEvent(tempDelta))
  158. {
  159. FireEmitter *pEmitter = NULL;
  160. if(m_nEmitters == 0)
  161. {
  162. // Make the first emitter.
  163. trace_t trace;
  164. UTIL_TraceLine(m_vOrigin, m_vOrigin+m_vDirection*1000, MASK_SOLID_BRUSHONLY, NULL, COLLISION_GROUP_NONE, &trace);
  165. if(trace.fraction < 1)
  166. {
  167. pEmitter = &m_Emitters[m_nEmitters];
  168. pEmitter->m_Pos = trace.endpos + trace.plane.normal * (size - 1);
  169. pEmitter->m_DirectionsTested = 0;
  170. }
  171. }
  172. else
  173. {
  174. static int nTries = 50;
  175. for(int iTry=0; iTry < nTries; iTry++)
  176. {
  177. FireEmitter *pSourceEmitter = &m_Emitters[ RandomInt( 0, m_nEmitters-1 )];
  178. int iOffset = RandomInt( 0, NUM_FIREGRID_OFFSETS - 1 );
  179. if(pSourceEmitter->m_DirectionsTested & (1 << iOffset))
  180. continue;
  181. // Test the corners of the new cube. If some points are solid and some are not, then
  182. // we can put fire here.
  183. Vector basePos = pSourceEmitter->m_Pos + g_Offsets[iOffset] * fireSpreadDist;
  184. int nSolidCorners = 0;
  185. for(int iCorner=0; iCorner < NUM_FIREGRID_OFFSETS; iCorner++)
  186. {
  187. Vector vCorner = basePos + g_Offsets[iCorner]*fireSpreadDist;
  188. if ( enginetrace->GetPointContents(vCorner) & CONTENTS_SOLID )
  189. ++nSolidCorners;
  190. }
  191. // Don't test this square again.
  192. pSourceEmitter->m_DirectionsTested |= 1 << iOffset;
  193. if(nSolidCorners != 0 && nSolidCorners != NUM_FIREGRID_OFFSETS)
  194. {
  195. pEmitter = &m_Emitters[m_nEmitters];
  196. pEmitter->m_Pos = basePos;
  197. pEmitter->m_DirectionsTested = 1 << GetOppositeOffset(iOffset);
  198. }
  199. }
  200. }
  201. if(pEmitter)
  202. {
  203. pEmitter->m_Lifetime = 0;
  204. pEmitter->m_SpawnEvent.Init(1);
  205. ++m_nEmitters;
  206. }
  207. }
  208. }
  209. // Spawn particles out of the emitters.
  210. for(int i=0; i < m_nEmitters; i++)
  211. {
  212. FireEmitter *pEmitter = &m_Emitters[i];
  213. float tempDelta = fTimeDelta;
  214. while(pEmitter->m_SpawnEvent.NextEvent(tempDelta))
  215. {
  216. FireParticle *pParticle = (FireParticle*)m_ParticleEffect.AddParticle(sizeof(FireParticle), m_MaterialHandle);
  217. if(pParticle)
  218. {
  219. static float particleSpeed = 15;
  220. pParticle->m_StartPos = pEmitter->m_Pos;
  221. pParticle->m_Direction = g_FireSpreadDirection * particleSpeed + RandomVector(0, particleSpeed*0.5);
  222. pParticle->m_iRamp = RandomInt( 0, NUM_FIRE_RAMPS - 1 );
  223. pParticle->m_Lifetime = 0;
  224. }
  225. }
  226. }
  227. }
  228. void C_ParticleFire::RenderParticles( CParticleRenderIterator *pIterator )
  229. {
  230. const FireParticle *pParticle = (const FireParticle*)pIterator->GetFirst();
  231. while ( pParticle )
  232. {
  233. float smooth01 = 1 - (cos(pParticle->m_Lifetime * 3.14159 / FIRE_PARTICLE_LIFETIME) * 0.5 + 0.5);
  234. float smooth00 = 1 - (cos(pParticle->m_Lifetime * 3.14159 * 2 / FIRE_PARTICLE_LIFETIME) * 0.5 + 0.5);
  235. FireRamp *pRamp = &g_FireRamps[pParticle->m_iRamp];
  236. Vector curColor = pRamp->m_Start + (pRamp->m_End - pRamp->m_Start) * smooth01;
  237. // Render.
  238. Vector tPos;
  239. TransformParticle(m_pParticleMgr->GetModelView(), pParticle->m_Pos, tPos);
  240. float sortKey = (int)tPos.z;
  241. RenderParticle_ColorSize(
  242. pIterator->GetParticleDraw(),
  243. tPos,
  244. curColor,
  245. smooth00,
  246. size);
  247. pParticle = (const FireParticle*)pIterator->GetNext( sortKey );
  248. }
  249. }
  250. void C_ParticleFire::SimulateParticles( CParticleSimulateIterator *pIterator )
  251. {
  252. FireParticle *pParticle = (FireParticle*)pIterator->GetFirst();
  253. while ( pParticle )
  254. {
  255. // Should this particle die?
  256. pParticle->m_Lifetime += pIterator->GetTimeDelta();
  257. if(pParticle->m_Lifetime > FIRE_PARTICLE_LIFETIME)
  258. {
  259. pIterator->RemoveParticle( pParticle );
  260. }
  261. else
  262. {
  263. float smooth01 = 1 - (cos(pParticle->m_Lifetime * 3.14159 / FIRE_PARTICLE_LIFETIME) * 0.5 + 0.5);
  264. pParticle->m_Pos = pParticle->m_StartPos + pParticle->m_Direction * smooth01;
  265. }
  266. pParticle = (FireParticle*)pIterator->GetNext();
  267. }
  268. }