123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- #include "pch.h"
- //////////////////////////////////////////////////////////////////////////////
- //
- // Art names
- //
- //////////////////////////////////////////////////////////////////////////////
- #define AWF_EFFECT_AFTERBURNER_SMOKE "f102bmp"
- #define AWF_EFFECT_FIRE "firebmp"
- #define AWF_EFFECT_SMOKE "f102bmp"
- #define AWF_EFFECT_DAMAGE "f102bmp"
- #define AWF_EFFECT_SPARK "f17bmp"
- //////////////////////////////////////////////////////////////////////////////
- //
- // Particle Behaviors
- //
- //////////////////////////////////////////////////////////////////////////////
- class SmokeBehavior : public ParticleBehavior
- {
- private:
- TRef<Image> m_pImageSmoke;
- public:
- //////////////////////////////////////////////////////////////////////
- /* void */ SmokeBehavior (Modeler* pModeler)
- {
- m_pImageSmoke = pModeler->LoadImage (AWF_EFFECT_SMOKE, true);
- }
- //////////////////////////////////////////////////////////////////////
- virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
- {
- // get the surface we will render from
- TRef<Surface> pSmokeSurface = m_pImageSmoke->GetSurface();
- // compute the remaining duration of the effect
- float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / pParticleData->m_fDuration;
- // the effect is grey, but is linearly fading to 0.0 in all components,
- // but green is scaled back a tad to reduce a greenish hue appearing
- Color color;
- float fColorComponent = 0.25f * fCountdownToFinish;
- color.SetRGBA (fColorComponent, fColorComponent * 0.8f, fColorComponent, fColorComponent);
- // the effect is growing linearly
- float fStartSize = 0.75f;
- float fFinalSize = 3.0f;
- float fDeltaSize = fFinalSize - fStartSize;
- float fSize = pParticleData->m_fSize * (fStartSize + (fDeltaSize * (1.0f - fCountdownToFinish)));
- // compute the new location of the effect
- Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
- // draw the decal
- pContext->DrawDecal (pSmokeSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), fSize, pParticleData->m_fAngle);
- }
- //////////////////////////////////////////////////////////////////////
- };
- //////////////////////////////////////////////////////////////////////////////
- class FireBehavior : public ParticleBehavior
- {
- private:
- TRef<Image> m_pImageFire;
- public:
- //////////////////////////////////////////////////////////////////////
- /* void */ FireBehavior (Modeler* pModeler)
- {
- m_pImageFire = pModeler->LoadImage (AWF_EFFECT_FIRE, true);
- }
- //////////////////////////////////////////////////////////////////////
- virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
- {
- // get the surface we will render from
- TRef<Surface> pFireSurface = m_pImageFire->GetSurface();
- // set parameters and compute the durations of the two phases of the effect
- float fRampUpFraction = 0.1f;
- float fRampUpDuration = pParticleData->m_fDuration * fRampUpFraction;
- float fRampDownDuration = pParticleData->m_fDuration - fRampUpDuration;
- // compute the remaining duration of the effect, and clamp it at 1.0 if
- // the effect is still ramping up
- float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / fRampDownDuration;
- if (fCountdownToFinish > 1.0f)
- fCountdownToFinish = 1.0f;
- // if the effect hasn't finished
- Color color;
- float fSize;
- // Several parts of this effect countdown from 0.5 to 0.0, rather than from
- // 1.0 to 0.0, so we precompute that value for efficiency
- float fScaledCountdownToFinish = 0.5f * fCountdownToFinish;
- // check to see which phase the effect is in
- if (fElapsedTime < fRampUpDuration)
- {
- // The effect is ramping up, so the puff is white, and it grows from half size
- // to full size during the ramp up period using an exponential growth
- color.SetRGBA (1.0f, fCountdownToFinish, fCountdownToFinish, fScaledCountdownToFinish * fCountdownToFinish);
- fSize = pParticleData->m_fSize * (0.5f + (0.5f * powf (fElapsedTime / fRampUpDuration, 0.666f)));
- }
- else
- {
- // the effect is ramping down, during which it changes from yellow to red, and
- // shrinks to half size using an exponential decay
- color.SetRGBA (1.0f, fCountdownToFinish, fCountdownToFinish * 0.25f, fScaledCountdownToFinish * fCountdownToFinish);
- fSize = pParticleData->m_fSize * (0.5f + (0.5f * powf (fCountdownToFinish, 0.666f)));
- }
- // compute the new location of the effect
- Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
- // draw the decal
- pContext->DrawDecal (pFireSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), fSize, pParticleData->m_fAngle);
- }
- //////////////////////////////////////////////////////////////////////
- };
- //////////////////////////////////////////////////////////////////////////////
- class DamageBehavior : public ParticleBehavior
- {
- private:
- TRef<Image> m_pImageDamage;
- public:
- //////////////////////////////////////////////////////////////////////
- /* void */ DamageBehavior (Modeler* pModeler)
- {
- m_pImageDamage = pModeler->LoadImage (AWF_EFFECT_DAMAGE, true);
- }
- //////////////////////////////////////////////////////////////////////
- virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
- {
- // we use smoke behavior instead of this
- }
- //////////////////////////////////////////////////////////////////////
- };
- //////////////////////////////////////////////////////////////////////////////
- class AfterburnerBehavior : public ParticleBehavior
- {
- private:
- TRef<Image> m_pImageSmoke;
- public:
- //////////////////////////////////////////////////////////////////////
- /* void */ AfterburnerBehavior (Modeler* pModeler)
- {
- m_pImageSmoke = pModeler->LoadImage (AWF_EFFECT_AFTERBURNER_SMOKE, true);
- }
- //////////////////////////////////////////////////////////////////////
- virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
- {
- // get the surface we will render from
- TRef<Surface> pSmokeSurface = m_pImageSmoke->GetSurface();
- // set parameters and compute the durations of the two phases of the effect
- float fRampUpFraction = 0.1f;
- float fRampUpDuration = pParticleData->m_fDuration * fRampUpFraction;
- float fRampDownDuration = pParticleData->m_fDuration - fRampUpDuration;
- // compute the remaining duration of the effect, and clamp it at 1.0 if
- // the effect is still ramping up
- float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / fRampDownDuration;
- if (fCountdownToFinish > 1.0f)
- fCountdownToFinish = 1.0f;
- // the effect starts yellow and turns grey, but is linearly fading to 0.0 in all components,
- Color color;
- float fColorComponent = 0.25f * fCountdownToFinish;
- if (fElapsedTime < fRampUpDuration)
- color.SetRGBA (fColorComponent * 4.0f, fColorComponent * 3.0f, fColorComponent, fColorComponent);
- else
- // green is scaled back a tad to reduce a greenish hue appearing
- color.SetRGBA (fColorComponent, fColorComponent * 0.8f, fColorComponent, fColorComponent);
- // the effect is growing linearly
- float fStartSize = 1.0f;
- float fFinalSize = 2.0f;
- float fDeltaSize = fFinalSize - fStartSize;
- float fSize = pParticleData->m_fSize * (fStartSize + (fDeltaSize * (1.0f - fCountdownToFinish)));
- // compute the new location of the effect
- Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
- // draw the decal
- pContext->DrawDecal (pSmokeSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), fSize, pParticleData->m_fAngle);
- }
- //////////////////////////////////////////////////////////////////////
- };
- //////////////////////////////////////////////////////////////////////////////
- class SparkBehavior : public ParticleBehavior
- {
- private:
- TRef<Image> m_pImageSpark;
- public:
- //////////////////////////////////////////////////////////////////////
- /* void */ SparkBehavior (Modeler* pModeler)
- {
- m_pImageSpark = pModeler->LoadImage (AWF_EFFECT_SPARK, true);
- }
- //////////////////////////////////////////////////////////////////////
- virtual void Render (Context* pContext, ParticleData* pParticleData, float fElapsedTime)
- {
- // get the surface we will render from
- TRef<Surface> pSparkSurface = m_pImageSpark->GetSurface();
- // draw multiple copies of the effect to produce a blur effect
- for (int i = 0; i < 1; i++)
- {
- // compute the remaining duration of the effect
- float fCountdownToFinish = (pParticleData->m_fDuration - fElapsedTime) / pParticleData->m_fDuration;
- // the effect is hot blue, fading out
- Color color;
- float fColorComponent = 0.5f * fCountdownToFinish * fCountdownToFinish;
- color.SetRGBA (fColorComponent * 1.5f, fColorComponent * 2.0f, fCountdownToFinish, fColorComponent);
- // compute the new location of the effect
- Vector vecEffectPosition = pParticleData->m_vecPosition + (pParticleData->m_vecVelocity * fElapsedTime) + (pParticleData->m_vecAcceleration * (fElapsedTime * fElapsedTime * 0.5f));
- // draw the decal
- pContext->DrawDecal (pSparkSurface, color, vecEffectPosition, Vector::GetZero(), Vector::GetZero(), pParticleData->m_fSize, pParticleData->m_fAngle + (fCountdownToFinish * pi));
- // scale the elapsed time to produce a blur effect
- fElapsedTime *= 0.97f;
- }
- }
- //////////////////////////////////////////////////////////////////////
- };
- //////////////////////////////////////////////////////////////////////////////
- //
- // ParticleData
- //
- //////////////////////////////////////////////////////////////////////////////
- /* void */ ParticleData::ParticleData (void)
- {
- m_pParticleBehavior = NULL;
- }
- //////////////////////////////////////////////////////////////////////////////
- void ParticleData::InitParticle (float fTime, ParticleBehavior* pParticleBehavior)
- {
- m_fStartTime = fTime;
- m_fAngle = random (0.0f, 2.0f * pi);
- m_pParticleBehavior = pParticleBehavior;
- }
- //////////////////////////////////////////////////////////////////////////////
- //
- // ParticleGeo
- //
- //////////////////////////////////////////////////////////////////////////////
- #define PARTICLE_BUFFER_SIZE 256
- //
- // the smoke quality number is used as a multiplier for how many particles
- // we will be generating in toto
- #define TOTAL_PARTICLE_BUFFER_SIZE PARTICLE_BUFFER_SIZE * 5
- class ParticleGeoImpl : public ParticleGeo
- {
- private:
- // storage of the particles in a rolling buffer (eliminates oldest first when overflow occurs)
- ParticleData m_dataParticle[TOTAL_PARTICLE_BUFFER_SIZE];
- int m_iIndexCurrentParticle;
- // behaviors
- SmokeBehavior m_smokeBehavior;
- FireBehavior m_fireBehavior;
- DamageBehavior m_damageBehavior;
- AfterburnerBehavior m_afterburnerBehavior;
- SparkBehavior m_sparkBehavior;
- // the current time
- float m_fTime;
- Number* GetTime() { return Number::Cast (GetChild (0)); }
- public:
- //////////////////////////////////////////////////////////////////////
- ParticleGeoImpl(Modeler* pModeler, Number* pTime) : ParticleGeo (pTime),
- m_iIndexCurrentParticle (0),
- m_smokeBehavior (pModeler),
- m_fireBehavior (pModeler),
- m_damageBehavior (pModeler),
- m_afterburnerBehavior (pModeler),
- m_sparkBehavior (pModeler),
- m_fTime (pTime->GetValue ())
- {
- }
- //////////////////////////////////////////////////////////////////////
- ParticleData* GetNextParticle (ParticleBehavior* pParticleBehavior)
- {
- // compute how much of the buffer we are going to use
- int iParticleBufferSize = PARTICLE_BUFFER_SIZE * ThingGeo::GetShowSmoke ();
- assert (iParticleBufferSize <= TOTAL_PARTICLE_BUFFER_SIZE);
-
- // increment to the next buffer entry
- m_iIndexCurrentParticle = (m_iIndexCurrentParticle + 1) % iParticleBufferSize;
- // initialize the new particle entry
- m_dataParticle[m_iIndexCurrentParticle].InitParticle (m_fTime, pParticleBehavior);
- // return a pointer to it
- return &m_dataParticle[m_iIndexCurrentParticle];
- }
- //////////////////////////////////////////////////////////////////////
- ParticleData* AddAfterburner (void)
- {
- return GetNextParticle (&m_afterburnerBehavior);
- }
- //////////////////////////////////////////////////////////////////////
- ParticleData* AddDamage (void)
- {
- return GetNextParticle (&m_damageBehavior);
- }
- //////////////////////////////////////////////////////////////////////
- ParticleData* AddSmoke (void)
- {
- return GetNextParticle (&m_smokeBehavior);
- }
- //////////////////////////////////////////////////////////////////////
- ParticleData* AddFire (void)
- {
- return GetNextParticle (&m_fireBehavior);
- }
- //////////////////////////////////////////////////////////////////////
- ParticleData* AddSpark (void)
- {
- return GetNextParticle (&m_sparkBehavior);
- }
- //////////////////////////////////////////////////////////////////////
- void Evaluate (void)
- {
- m_fTime = GetTime ()->GetValue ();
- }
- //////////////////////////////////////////////////////////////////////
- ZString GetFunctionName (void)
- {
- return "ParticleGeo";
- }
- //////////////////////////////////////////////////////////////////////
- void Render(Context* pContext)
- {
- // compute how much of the buffer we are going to use
- int iParticleBufferSize = PARTICLE_BUFFER_SIZE * ThingGeo::GetShowSmoke ();
- // loop over all of the particles
- for(int index = 0; index < iParticleBufferSize; ++index)
- {
- // some particles have shorter lives than others, so we check for an easy out
- if (m_dataParticle[index].m_pParticleBehavior)
- {
- // compute the elapsed time of this particle
- float fElapsedTime = m_fTime - m_dataParticle[index].m_fStartTime;
- // check the elapsed time against the duration of the particle
- if (fElapsedTime <= m_dataParticle[index].m_fDuration)
- {
- // the particle is still alive, so render it
- m_dataParticle[index].m_pParticleBehavior->Render (pContext, &m_dataParticle[index], fElapsedTime);
- }
- else
- {
- // the particle is expired, so clear the behavior pointer
- // so we don't do extra work next time around
- m_dataParticle[index].m_pParticleBehavior = NULL;
- }
- }
- }
- }
- //////////////////////////////////////////////////////////////////////
- };
- //////////////////////////////////////////////////////////////////////////////
- TRef<ParticleGeo> CreateParticleGeo (Modeler* pModeler, Number* pTime)
- {
- return new ParticleGeoImpl (pModeler, pTime);
- }
- //////////////////////////////////////////////////////////////////////////////
|