particlegeo.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. #include "pch.h"
  2. //////////////////////////////////////////////////////////////////////////////
  3. //
  4. // Art names
  5. //
  6. //////////////////////////////////////////////////////////////////////////////
  7. #define AWF_EFFECT_AFTERBURNER_SMOKE "f102bmp"
  8. #define AWF_EFFECT_FIRE "firebmp"
  9. #define AWF_EFFECT_SMOKE "f102bmp"
  10. #define AWF_EFFECT_DAMAGE "f102bmp"
  11. #define AWF_EFFECT_SPARK "f17bmp"
  12. //////////////////////////////////////////////////////////////////////////////
  13. //
  14. // Particle Behaviors
  15. //
  16. //////////////////////////////////////////////////////////////////////////////
  17. class SmokeBehavior : public ParticleBehavior
  18. {
  19. private:
  20. TRef<Image> m_pImageSmoke;
  21. public:
  22. //////////////////////////////////////////////////////////////////////
  23. /* void */ SmokeBehavior (Modeler* pModeler)
  24. {
  25. m_pImageSmoke = pModeler->LoadImage (AWF_EFFECT_SMOKE, true);
  26. }
  27. //////////////////////////////////////////////////////////////////////
  28. virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
  29. {
  30. // get the surface we will render from
  31. TRef<Surface> pSmokeSurface = m_pImageSmoke->GetSurface();
  32. // compute the remaining duration of the effect
  33. float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / pParticleData->m_fDuration;
  34. // the effect is grey, but is linearly fading to 0.0 in all components,
  35. // but green is scaled back a tad to reduce a greenish hue appearing
  36. Color color;
  37. float fColorComponent = 0.25f * fCountdownToFinish;
  38. color.SetRGBA (fColorComponent, fColorComponent * 0.8f, fColorComponent, fColorComponent);
  39. // the effect is growing linearly
  40. float fStartSize = 0.75f;
  41. float fFinalSize = 3.0f;
  42. float fDeltaSize = fFinalSize - fStartSize;
  43. float fSize = pParticleData->m_fSize * (fStartSize + (fDeltaSize * (1.0f - fCountdownToFinish)));
  44. // compute the new location of the effect
  45. Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
  46. // draw the decal
  47. pContext->DrawDecal (pSmokeSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), fSize, pParticleData->m_fAngle);
  48. }
  49. //////////////////////////////////////////////////////////////////////
  50. };
  51. //////////////////////////////////////////////////////////////////////////////
  52. class FireBehavior : public ParticleBehavior
  53. {
  54. private:
  55. TRef<Image> m_pImageFire;
  56. public:
  57. //////////////////////////////////////////////////////////////////////
  58. /* void */ FireBehavior (Modeler* pModeler)
  59. {
  60. m_pImageFire = pModeler->LoadImage (AWF_EFFECT_FIRE, true);
  61. }
  62. //////////////////////////////////////////////////////////////////////
  63. virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
  64. {
  65. // get the surface we will render from
  66. TRef<Surface> pFireSurface = m_pImageFire->GetSurface();
  67. // set parameters and compute the durations of the two phases of the effect
  68. float fRampUpFraction = 0.1f;
  69. float fRampUpDuration = pParticleData->m_fDuration * fRampUpFraction;
  70. float fRampDownDuration = pParticleData->m_fDuration - fRampUpDuration;
  71. // compute the remaining duration of the effect, and clamp it at 1.0 if
  72. // the effect is still ramping up
  73. float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / fRampDownDuration;
  74. if (fCountdownToFinish > 1.0f)
  75. fCountdownToFinish = 1.0f;
  76. // if the effect hasn't finished
  77. Color color;
  78. float fSize;
  79. // Several parts of this effect countdown from 0.5 to 0.0, rather than from
  80. // 1.0 to 0.0, so we precompute that value for efficiency
  81. float fScaledCountdownToFinish = 0.5f * fCountdownToFinish;
  82. // check to see which phase the effect is in
  83. if (fElapsedTime < fRampUpDuration)
  84. {
  85. // The effect is ramping up, so the puff is white, and it grows from half size
  86. // to full size during the ramp up period using an exponential growth
  87. color.SetRGBA (1.0f, fCountdownToFinish, fCountdownToFinish, fScaledCountdownToFinish * fCountdownToFinish);
  88. fSize = pParticleData->m_fSize * (0.5f + (0.5f * powf (fElapsedTime / fRampUpDuration, 0.666f)));
  89. }
  90. else
  91. {
  92. // the effect is ramping down, during which it changes from yellow to red, and
  93. // shrinks to half size using an exponential decay
  94. color.SetRGBA (1.0f, fCountdownToFinish, fCountdownToFinish * 0.25f, fScaledCountdownToFinish * fCountdownToFinish);
  95. fSize = pParticleData->m_fSize * (0.5f + (0.5f * powf (fCountdownToFinish, 0.666f)));
  96. }
  97. // compute the new location of the effect
  98. Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
  99. // draw the decal
  100. pContext->DrawDecal (pFireSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), fSize, pParticleData->m_fAngle);
  101. }
  102. //////////////////////////////////////////////////////////////////////
  103. };
  104. //////////////////////////////////////////////////////////////////////////////
  105. class DamageBehavior : public ParticleBehavior
  106. {
  107. private:
  108. TRef<Image> m_pImageDamage;
  109. public:
  110. //////////////////////////////////////////////////////////////////////
  111. /* void */ DamageBehavior (Modeler* pModeler)
  112. {
  113. m_pImageDamage = pModeler->LoadImage (AWF_EFFECT_DAMAGE, true);
  114. }
  115. //////////////////////////////////////////////////////////////////////
  116. virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
  117. {
  118. // we use smoke behavior instead of this
  119. }
  120. //////////////////////////////////////////////////////////////////////
  121. };
  122. //////////////////////////////////////////////////////////////////////////////
  123. class AfterburnerBehavior : public ParticleBehavior
  124. {
  125. private:
  126. TRef<Image> m_pImageSmoke;
  127. public:
  128. //////////////////////////////////////////////////////////////////////
  129. /* void */ AfterburnerBehavior (Modeler* pModeler)
  130. {
  131. m_pImageSmoke = pModeler->LoadImage (AWF_EFFECT_AFTERBURNER_SMOKE, true);
  132. }
  133. //////////////////////////////////////////////////////////////////////
  134. virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
  135. {
  136. // get the surface we will render from
  137. TRef<Surface> pSmokeSurface = m_pImageSmoke->GetSurface();
  138. // set parameters and compute the durations of the two phases of the effect
  139. float fRampUpFraction = 0.1f;
  140. float fRampUpDuration = pParticleData->m_fDuration * fRampUpFraction;
  141. float fRampDownDuration = pParticleData->m_fDuration - fRampUpDuration;
  142. // compute the remaining duration of the effect, and clamp it at 1.0 if
  143. // the effect is still ramping up
  144. float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / fRampDownDuration;
  145. if (fCountdownToFinish > 1.0f)
  146. fCountdownToFinish = 1.0f;
  147. // the effect starts yellow and turns grey, but is linearly fading to 0.0 in all components,
  148. Color color;
  149. float fColorComponent = 0.25f * fCountdownToFinish;
  150. if (fElapsedTime < fRampUpDuration)
  151. color.SetRGBA (fColorComponent * 4.0f, fColorComponent * 3.0f, fColorComponent, fColorComponent);
  152. else
  153. // green is scaled back a tad to reduce a greenish hue appearing
  154. color.SetRGBA (fColorComponent, fColorComponent * 0.8f, fColorComponent, fColorComponent);
  155. // the effect is growing linearly
  156. float fStartSize = 1.0f;
  157. float fFinalSize = 2.0f;
  158. float fDeltaSize = fFinalSize - fStartSize;
  159. float fSize = pParticleData->m_fSize * (fStartSize + (fDeltaSize * (1.0f - fCountdownToFinish)));
  160. // compute the new location of the effect
  161. Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
  162. // draw the decal
  163. pContext->DrawDecal (pSmokeSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), fSize, pParticleData->m_fAngle);
  164. }
  165. //////////////////////////////////////////////////////////////////////
  166. };
  167. //////////////////////////////////////////////////////////////////////////////
  168. class SparkBehavior : public ParticleBehavior
  169. {
  170. private:
  171. TRef<Image> m_pImageSpark;
  172. public:
  173. //////////////////////////////////////////////////////////////////////
  174. /* void */ SparkBehavior (Modeler* pModeler)
  175. {
  176. m_pImageSpark = pModeler->LoadImage (AWF_EFFECT_SPARK, true);
  177. }
  178. //////////////////////////////////////////////////////////////////////
  179. virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
  180. {
  181. // get the surface we will render from
  182. TRef<Surface> pSparkSurface = m_pImageSpark->GetSurface();
  183. // draw multiple copies of the effect to produce a blur effect
  184. for (int i = 0; i < 1; i++)
  185. {
  186. // compute the remaining duration of the effect
  187. float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / pParticleData->m_fDuration;
  188. // the effect is hot blue, fading out
  189. Color color;
  190. float fColorComponent = 0.5f * fCountdownToFinish * fCountdownToFinish;
  191. color.SetRGBA (fColorComponent * 1.5f, fColorComponent * 2.0f, fCountdownToFinish, fColorComponent);
  192. // compute the new location of the effect
  193. Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
  194. // draw the decal
  195. pContext->DrawDecal (pSparkSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), pParticleData->m_fSize, pParticleData->m_fAngle + (fCountdownToFinish * pi));
  196. // scale the elapsed time to produce a blur effect
  197. fElapsedTime *= 0.97f;
  198. }
  199. }
  200. //////////////////////////////////////////////////////////////////////
  201. };
  202. //////////////////////////////////////////////////////////////////////////////
  203. //
  204. // ParticleData
  205. //
  206. //////////////////////////////////////////////////////////////////////////////
  207. /* void */ ParticleData::ParticleData (void)
  208. {
  209. m_pParticleBehavior = NULL;
  210. }
  211. //////////////////////////////////////////////////////////////////////////////
  212. void ParticleData::InitParticle (float fTime, ParticleBehavior* pParticleBehavior)
  213. {
  214. m_fStartTime = fTime;
  215. m_fAngle = random (0.0f, 2.0f * pi);
  216. m_pParticleBehavior = pParticleBehavior;
  217. }
  218. //////////////////////////////////////////////////////////////////////////////
  219. //
  220. // ParticleGeo
  221. //
  222. //////////////////////////////////////////////////////////////////////////////
  223. #define PARTICLE_BUFFER_SIZE 256
  224. //
  225. // the smoke quality number is used as a multiplier for how many particles
  226. // we will be generating in toto
  227. #define TOTAL_PARTICLE_BUFFER_SIZE PARTICLE_BUFFER_SIZE * 5
  228. class ParticleGeoImpl : public ParticleGeo
  229. {
  230. private:
  231. // storage of the particles in a rolling buffer (eliminates oldest first when overflow occurs)
  232. ParticleData m_dataParticle[TOTAL_PARTICLE_BUFFER_SIZE];
  233. int m_iIndexCurrentParticle;
  234. // behaviors
  235. SmokeBehavior m_smokeBehavior;
  236. FireBehavior m_fireBehavior;
  237. DamageBehavior m_damageBehavior;
  238. AfterburnerBehavior m_afterburnerBehavior;
  239. SparkBehavior m_sparkBehavior;
  240. // the current time
  241. float m_fTime;
  242. Number* GetTime() { return Number::Cast (GetChild (0)); }
  243. public:
  244. //////////////////////////////////////////////////////////////////////
  245. ParticleGeoImpl(Modeler* pModeler, Number* pTime) : ParticleGeo (pTime),
  246. m_iIndexCurrentParticle (0),
  247. m_smokeBehavior (pModeler),
  248. m_fireBehavior (pModeler),
  249. m_damageBehavior (pModeler),
  250. m_afterburnerBehavior (pModeler),
  251. m_sparkBehavior (pModeler),
  252. m_fTime (pTime->GetValue ())
  253. {
  254. }
  255. //////////////////////////////////////////////////////////////////////
  256. ParticleData* GetNextParticle (ParticleBehavior* pParticleBehavior)
  257. {
  258. // compute how much of the buffer we are going to use
  259. int iParticleBufferSize = PARTICLE_BUFFER_SIZE * ThingGeo::GetShowSmoke ();
  260. assert (iParticleBufferSize <= TOTAL_PARTICLE_BUFFER_SIZE);
  261. // increment to the next buffer entry
  262. m_iIndexCurrentParticle = (m_iIndexCurrentParticle + 1) % iParticleBufferSize;
  263. // initialize the new particle entry
  264. m_dataParticle[m_iIndexCurrentParticle].InitParticle (m_fTime, pParticleBehavior);
  265. // return a pointer to it
  266. return &m_dataParticle[m_iIndexCurrentParticle];
  267. }
  268. //////////////////////////////////////////////////////////////////////
  269. ParticleData* AddAfterburner (void)
  270. {
  271. return GetNextParticle (&m_afterburnerBehavior);
  272. }
  273. //////////////////////////////////////////////////////////////////////
  274. ParticleData* AddDamage (void)
  275. {
  276. return GetNextParticle (&m_damageBehavior);
  277. }
  278. //////////////////////////////////////////////////////////////////////
  279. ParticleData* AddSmoke (void)
  280. {
  281. return GetNextParticle (&m_smokeBehavior);
  282. }
  283. //////////////////////////////////////////////////////////////////////
  284. ParticleData* AddFire (void)
  285. {
  286. return GetNextParticle (&m_fireBehavior);
  287. }
  288. //////////////////////////////////////////////////////////////////////
  289. ParticleData* AddSpark (void)
  290. {
  291. return GetNextParticle (&m_sparkBehavior);
  292. }
  293. //////////////////////////////////////////////////////////////////////
  294. void Evaluate (void)
  295. {
  296. m_fTime = GetTime ()->GetValue ();
  297. }
  298. //////////////////////////////////////////////////////////////////////
  299. ZString GetFunctionName (void)
  300. {
  301. return "ParticleGeo";
  302. }
  303. //////////////////////////////////////////////////////////////////////
  304. void Render(Context* pContext)
  305. {
  306. // compute how much of the buffer we are going to use
  307. int iParticleBufferSize = PARTICLE_BUFFER_SIZE * ThingGeo::GetShowSmoke ();
  308. // loop over all of the particles
  309. for(int index = 0; index < iParticleBufferSize; ++index)
  310. {
  311. // some particles have shorter lives than others, so we check for an easy out
  312. if (m_dataParticle[index].m_pParticleBehavior)
  313. {
  314. // compute the elapsed time of this particle
  315. float fElapsedTime = m_fTime - m_dataParticle[index].m_fStartTime;
  316. // check the elapsed time against the duration of the particle
  317. if (fElapsedTime <= m_dataParticle[index].m_fDuration)
  318. {
  319. // the particle is still alive, so render it
  320. m_dataParticle[index].m_pParticleBehavior->Render (pContext, &m_dataParticle[index], fElapsedTime);
  321. }
  322. else
  323. {
  324. // the particle is expired, so clear the behavior pointer
  325. // so we don't do extra work next time around
  326. m_dataParticle[index].m_pParticleBehavior = NULL;
  327. }
  328. }
  329. }
  330. }
  331. //////////////////////////////////////////////////////////////////////
  332. };
  333. //////////////////////////////////////////////////////////////////////////////
  334. TRef<ParticleGeo> CreateParticleGeo (Modeler* pModeler, Number* pTime)
  335. {
  336. return new ParticleGeoImpl (pModeler, pTime);
  337. }
  338. //////////////////////////////////////////////////////////////////////////////