From aef0a42780733d90f0ff8ff257d60d34181a9ed4 Mon Sep 17 00:00:00 2001 From: livingaftermidnight Date: Thu, 19 Jan 2006 11:19:55 +0000 Subject: [PATCH] This is the new particle engine. It's in and it compiles, but it's not implemented fully yet. Will get to that soon. This was SVN commit r3369. --- source/graphics/DefaultEmitter.cpp | 157 +++++++++++ source/graphics/DefaultEmitter.h | 38 +++ source/graphics/ParticleEmitter.cpp | 419 +++++++++++----------------- source/graphics/ParticleEmitter.h | 369 ++++++++++++++++-------- source/graphics/ParticleEngine.cpp | 253 +++++++++++++++++ source/graphics/ParticleEngine.h | 142 ++++++++++ 6 files changed, 1013 insertions(+), 365 deletions(-) create mode 100644 source/graphics/DefaultEmitter.cpp create mode 100644 source/graphics/DefaultEmitter.h create mode 100644 source/graphics/ParticleEngine.cpp create mode 100644 source/graphics/ParticleEngine.h diff --git a/source/graphics/DefaultEmitter.cpp b/source/graphics/DefaultEmitter.cpp new file mode 100644 index 0000000000..e2b93d14a0 --- /dev/null +++ b/source/graphics/DefaultEmitter.cpp @@ -0,0 +1,157 @@ +///////////////////////////////////////////////////// +// File Name: DefaultEmitter.cpp +// Date: 7/20/05 +// Author: Will Dull +// Purpose: Implementation of the default +// emitter class. +///////////////////////////////////////////////////// + +#include "precompiled.h" +#include "DefaultEmitter.h" + +CDefaultEmitter::CDefaultEmitter(const int MAX_PARTICLES, const int lifetime) : CEmitter(MAX_PARTICLES, lifetime) +{ + setupEmitter(); +} + +CDefaultEmitter::~CDefaultEmitter(void) +{ +} + +bool CDefaultEmitter::setupEmitter() +{ + pos.x = 0.0f; // XYZ Position + pos.y = 0.0f; // XYZ Position + pos.z = 0.0f; // XYZ Position + + yaw = DEGTORAD(0.0f); + yawVar = DEGTORAD(360.0f); + pitch = DEGTORAD(90.0f); + pitchVar = DEGTORAD(45.0f); + speed = 0.05f; + speedVar = 0.001f; + + blend_mode = 1; + particleCount = 0; + emitsPerFrame = 100; + emitVar = 15; + life = 90; + lifeVar = 65; + startColor.r = 240; + startColor.g = 240; + startColor.b = 15; + startColorVar.r = 15; + startColorVar.g = 15; + startColorVar.b = 15; + endColor.r = 240; + endColor.g = 15; + endColor.b = 15; + endColorVar.r = 15; + endColorVar.g = 15; + endColorVar.b = 15; + + force.x = 0.000f; + force.y = -0.001f; + force.z = 0.0f; + return true; +} + +bool CDefaultEmitter::updateEmitter() +{ + int emits; + // walk through the used list, and update each of the particles + tParticle *tempParticle = usedList; // start at the beginning of the used list + tParticle *prev = usedList; + while(tempParticle) // loop on a valid particle + { + // don't update if the particle is supposed to be dead + if(tempParticle->life > 0) + { + // update the particle + // Calculate the new pos + tempParticle->pos.x += tempParticle->dir.x; + tempParticle->pos.y += tempParticle->dir.y; + tempParticle->pos.z += tempParticle->dir.z; + + // Add global force to direction + tempParticle->dir.x += force.x; + tempParticle->dir.y += force.y; + tempParticle->dir.z += force.z; + + // Get the new color + tempParticle->color.r += tempParticle->deltaColor.r; + tempParticle->color.g += tempParticle->deltaColor.g; + tempParticle->color.b += tempParticle->deltaColor.b; + + // fade it out + if(decrementAlpha) + tempParticle->alpha -= tempParticle->alphaDelta; + + // gets a little older + if(decrementLife) + tempParticle->life--; + + // move to the next particle in the list + prev = tempParticle; + tempParticle = tempParticle->next; + } + else // this means the particle lifetime is over + { + // if this is the first particle in usedList + // then set the pointers to the next in the usedList + // and open up the tempParticle + if(tempParticle == usedList) + { + usedList = tempParticle->next; + tempParticle->next = openList; + // set the open list head to the particle + openList = tempParticle; + prev = usedList; + tempParticle = usedList; + } + else + { + + //// We need to pull the particle out of the + //// used list and insert it into the open list + + // fix the previous node in the list to skip over the one we are pulling out + prev->next = tempParticle->next; + // set the particle to point to the head of the open list + tempParticle->next = openList; + // set the open list head to the particle + openList = tempParticle; + // move on to the next iteration + tempParticle = prev->next; + } + // and there is one less + particleCount--; + } + } // end of while + if(emitterLife > 0 || emitterLife == -1) + { + // Emit particles for this frame + emits = emitsPerFrame + (int)((float)emitVar * RandomNum()); + + // if the particle life is -1 that means it's infinite + if(emitterLife != -1) + emitterLife--; + + for(int i = 0; i < emits; i++) + addParticle(); + return true; + } + else + { + if(particleCount > 0) + { + return true; + } + else + { + isFinished = true; + return false; // this will be checked for and then it will be deleted + } + } + return false; +} \ No newline at end of file diff --git a/source/graphics/DefaultEmitter.h b/source/graphics/DefaultEmitter.h new file mode 100644 index 0000000000..1b3b7ae8e4 --- /dev/null +++ b/source/graphics/DefaultEmitter.h @@ -0,0 +1,38 @@ +///////////////////////////////////////////////////// +// File Name: DefaultEmitter.h +// Date: 7/20/05 +// Author: Will Dull +// Purpose: Default emitter +///////////////////////////////////////////////////// + +#ifndef _DEFAULTEMITTER_H_ +#define _DEFAULTEMITTER_H_ + +#include "ParticleEmitter.h" + +class CDefaultEmitter : public CEmitter +{ +public: + CDefaultEmitter(const int MAX_PARTICLES = 4000, const int lifetime = -1); + + ////////////////////////////////////////////////////////////// + //Func Name: setupEmitter + //Date: 7/19/05 + //Author: Will Dull + //Notes: Sets up emitter to the default particle + // effect. + ////////////////////////////////////////////////////////////// + virtual bool setupEmitter(); + + ////////////////////////////////////////////////////////////// + //Func Name: updateEmitter + //Date: 7/19/05 + //Author: Will Dull + //Notes: Updates emitter. + ////////////////////////////////////////////////////////////// + virtual bool updateEmitter(); + + virtual ~CDefaultEmitter(void); +}; + +#endif \ No newline at end of file diff --git a/source/graphics/ParticleEmitter.cpp b/source/graphics/ParticleEmitter.cpp index 72eb68b197..5a4d39978c 100755 --- a/source/graphics/ParticleEmitter.cpp +++ b/source/graphics/ParticleEmitter.cpp @@ -1,280 +1,189 @@ -/*================================================================== -| -| Name: ParticleEmitter.cpp -| -|=================================================================== -| -| Author: Ben Vinegar -| Contact: benvinegar () hotmail ! com -| -| -| Last Modified: 03/08/04 -| -| Overview: Particle emitter class that emits particles from -| an origin (or area) with a variety of set colours, -| durations, forces and a single common sprite. -| -| -| Usage: Instantiate one emitter per desired effect. Set the -| various fields (preferably all, the defaults are rather -| boring) and then call Frame() - you guessed it - every -| frame. -| -| To do: TBA -| -| More Information: TBA -| -==================================================================*/ +///////////////////////////////////////////////////// +// File Name: ParticleEmitter.cpp +// Date: 6/29/05 +// Author: Will Dull +// Purpose: The base particle and emitter +// classes implementations. +///////////////////////////////////////////////////// #include "precompiled.h" - #include "ParticleEmitter.h" -#include "timer.h" -#include "ogl.h" -#include +#include "ParticleEngine.h" -using namespace std; +#define PAR_LOG(a,b,c) LOG_SYS::GetInstance()->WriteC(a, LOG_SYS::OBJ, b, c) -CParticleEmitter::CParticleEmitter() : - m_origin(0.0f, 0.0f, 0.0f), - m_originSpread(0.0f, 0.0f, 0.0f), - m_velocity(0.0f, 0.0f, 0.0f), - m_velocitySpread(0.0f, 0.0f, 0.0f), - m_gravity(0.0f, 0.0f, 0.0f), - m_maxParticles(0), - m_minParticles(0), - m_numParticles(0), - m_maxLifetime(0), - m_minLifetime(0), - m_timeOfLastFrame(0.0f), - m_timeSinceLastEmit(0.0f) -{ - m_particles.clear(); -} - -CParticleEmitter::~CParticleEmitter() -{ -} - -void CParticleEmitter::Frame() -{ - Update(); - Render(); -} - -void CParticleEmitter::Render() +CEmitter::CEmitter(const int MAX_PARTICLES, const int lifetime, int textureID) { + particleCount = 0; + // declare the pool of nodes + max_particles = MAX_PARTICLES; + heap = new tParticle[max_particles]; + emitterLife = lifetime; + decrementLife = true; + decrementAlpha = true; + renderParticles = true; + isFinished = false; + updateSpeed = 0.02f; + blend_mode = 1; + size = 0.15f; + texture = textureID; - glEnable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); + // init the used/open list + usedList = NULL; + openList = NULL; - glAlphaFunc(GL_GREATER, 0.0f); - glBlendFunc(GL_SRC_ALPHA,GL_ONE); - - vector::iterator itor = m_particles.begin(); - while (itor != m_particles.end()) + // link all the particles in the heap + // into one large open list + for(int i = 0; i < max_particles - 1; i++) { - CParticle * curParticle = (*itor); - - curParticle->Frame(); - ++itor; + heap[i].next = &(heap[i + 1]); } - glDisable(GL_ALPHA_TEST); - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); + openList = heap; } -void CParticleEmitter::Update() +CEmitter::~CEmitter(void) { - double timeElapsed = get_time() - m_timeOfLastFrame; - - // update existing particles - vector::iterator itor = m_particles.begin(); - while (itor != m_particles.end()) + /*int open = 0; + int used = 0; + int final = 0; + tParticle *iter = openList; + while(iter) { - CParticle * curParticle = (*itor); + open++; + iter = iter->next; + } - curParticle->Update(); + iter = usedList; + while(iter) + { + used++; + iter = iter->next; + } + final = open + used;*/ - // destroy particle if it has lived beyond its duration - if (curParticle->m_timeElapsedTotal >= curParticle->m_duration) + delete [] heap; +} + +bool CEmitter::addParticle() +{ + tColor start, end; + float fYaw, fPitch, fSpeed; + + if(!openList) + return false; + + if(particleCount < max_particles) + { + // get a particle from the open list + tParticle *particle = openList; + + // set it's initial position to the emitter's position + particle->pos.x = pos.x; + particle->pos.y = pos.y; + particle->pos.z = pos.z; + + // Calculate the starting direction vector + fYaw = yaw + (yawVar * RandomNum()); + fPitch = pitch + (pitchVar * RandomNum()); + + // Convert the rotations to a vector + RotationToDirection(fPitch,fYaw,&particle->dir); + + // Multiply in the speed factor + fSpeed = speed + (speedVar * RandomNum()); + particle->dir.x *= fSpeed; + particle->dir.y *= fSpeed; + particle->dir.z *= fSpeed; + + // Calculate the life span + particle->life = life + (int)((float)lifeVar * RandomNum()); + + // Calculate the colors + start.r = startColor.r + (startColorVar.r * RandomChar()); + start.g = startColor.g + (startColorVar.g * RandomChar()); + start.b = startColor.b + (startColorVar.b * RandomChar()); + end.r = endColor.r + (endColorVar.r * RandomChar()); + end.g = endColor.g + (endColorVar.g * RandomChar()); + end.b = endColor.b + (endColorVar.b * RandomChar()); + + // set the initial color of the particle + particle->color.r = start.r; + particle->color.g = start.g; + particle->color.b = start.b; + + // Create the color delta + particle->deltaColor.r = (end.r - start.r) / particle->life; + particle->deltaColor.g = (end.g - start.g) / particle->life; + particle->deltaColor.b = (end.b - start.b) / particle->life; + + particle->alpha = 255.0f; + particle->alphaDelta = particle->alpha / particle->life; + + particle->inPos = false; + + // Now, we pop a node from the open list and put it into the used list + //tParticleNode *tempNode = openList; // get the top of the list that we have been filling in + //openList = tempNode->next; + //tempNode->next = usedList; // have it link to the top of the used list + //usedList = tempNode; // set the new linked node as the start of the list + openList = particle->next; + particle->next = usedList; + usedList = particle; + + // update the length of the used list (particle Count) + particleCount++; + return true; + } + + return false; +} + +bool CEmitter::renderEmitter() +{ + if(renderParticles) + { + switch(blend_mode) { - m_particles.erase(itor); - delete curParticle; - --m_numParticles; + case 1: + glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Fire + break; + case 2: + glBlendFunc(GL_SRC_COLOR, GL_ONE); // Crappy Fire + break; + case 3: + glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR); // Plain Particles + break; + case 4: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR); // Nice fade out effect + break; } - ++itor; - } + // Bind the texture. Use the texture assigned to this emitter. + glBindTexture(GL_TEXTURE_2D, texture); - double secondsPerEmit = 1 / (m_minParticles / m_minLifetime); - - if (m_timeSinceLastEmit > secondsPerEmit) - { + glBegin(GL_QUADS); + { + tParticle *tempParticle = usedList; - float duration; - CVector3D position, velocity; - float colour[4]; - - bool moreParticlesToEmit = true; - while (moreParticlesToEmit) { - CParticle * newParticle = new CParticle(); - - // calculate particle duration - duration = (float)m_minLifetime; - duration += (rand() % (int)((m_maxLifetime - m_minLifetime) * 1000.0f + 1)) / 1000.0f; - newParticle->m_duration = duration; - - // calculate particle start position from spread - position = m_origin; - position.X += (rand() % (int)(m_originSpread.X * 2000.0f + 1)) / 1000.0f - m_originSpread.X; - position.Y += (rand() % (int)(m_originSpread.Y * 2000.0f + 1)) / 1000.0f - m_originSpread.Y; - position.Z += (rand() % (int)(m_originSpread.Z * 2000.0f + 1)) / 1000.0f - m_originSpread.Z; - newParticle->m_position = position; - - // calculate particle velocity from spread - velocity = m_velocity; - velocity.X += (rand() % (int)(m_velocitySpread.X * 2000.0f + 1)) / 1000.0f - m_velocitySpread.X; - velocity.Y += (rand() % (int)(m_velocitySpread.Y * 2000.0f + 1)) / 1000.0f - m_velocitySpread.Y; - velocity.Z += (rand() % (int)(m_velocitySpread.Z * 2000.0f + 1)) / 1000.0f - m_velocitySpread.Z; - newParticle->m_velocity = velocity; - - newParticle->m_gravity = m_gravity; - - // calculate and assign colour - memcpy2(colour, m_startColour, sizeof(float) * 4); - colour[0] += (rand() % (int)((m_endColour[0] - m_startColour[0]) * 1000.0f + 1)) / 1000.0f; - colour[1] += (rand() % (int)((m_endColour[1] - m_startColour[1]) * 1000.0f + 1)) / 1000.0f; - colour[2] += (rand() % (int)((m_endColour[2] - m_startColour[2]) * 1000.0f + 1)) / 1000.0f; - colour[3] += (rand() % (int)((m_endColour[3] - m_startColour[3]) * 1000.0f + 1)) / 1000.0f; - memcpy2(newParticle->m_colour, colour, sizeof(float) * 4); - - // assign sprite - newParticle->m_sprite = m_sprite; - - // final pre-processing init call - newParticle->Init(); - - // add to vector of particles - m_particles.push_back(newParticle); - - timeElapsed -= secondsPerEmit; - if (timeElapsed < secondsPerEmit) + while(tempParticle) { - moreParticlesToEmit = false; + tColor *pColor = &(tempParticle->color); + glColor4ub(pColor->r,pColor->g, pColor->b, (GLubyte)tempParticle->alpha); + glTexCoord2d(0.0, 0.0); + tVector *pPos = &(tempParticle->pos); + glVertex3f(pPos->x - size, pPos->y + size, pPos->z); + glTexCoord2d(0.0, 1.0); + glVertex3f(pPos->x - size, pPos->y - size, pPos->z); + glTexCoord2d(1.0, 1.0); + glVertex3f(pPos->x + size, pPos->y - size, pPos->z); + glTexCoord2d(1.0, 0.0); + glVertex3f(pPos->x + size, pPos->y + size, pPos->z); + tempParticle = tempParticle->next; } - - - ++m_numParticles; } - m_timeSinceLastEmit = 0.0f; + glEnd(); + + return true; } - else - m_timeSinceLastEmit += (float)timeElapsed; - - m_timeOfLastFrame = get_time(); -} - -void CParticleEmitter::SetSprite(CSprite * sprite) -{ - m_sprite = sprite; -} - -void CParticleEmitter::SetOrigin(CVector3D origin) -{ - m_origin = origin; -} - -void CParticleEmitter::SetOrigin(float x, float y, float z) -{ - m_origin.X = x; - m_origin.Y = y; - m_origin.Z = z; -} - -void CParticleEmitter::SetOriginSpread(CVector3D spread) -{ - m_originSpread = spread; -} - -void CParticleEmitter::SetOriginSpread(float x, float y, float z) -{ - m_originSpread.X = x; - m_originSpread.Y = y; - m_originSpread.Z = z; -} - -void CParticleEmitter::SetGravity(CVector3D gravity) -{ - m_gravity = gravity; -} - -void CParticleEmitter::SetGravity(float x, float y, float z) -{ - m_gravity.X = x; - m_gravity.Y = y; - m_gravity.Z = z; - -} - -void CParticleEmitter::SetVelocity(CVector3D velocity) -{ - m_velocity = velocity; -} - -void CParticleEmitter::SetVelocity(float x, float y, float z) -{ - m_velocity.X = x; - m_velocity.Y = y; - m_velocity.Z = z; -} - - -void CParticleEmitter::SetVelocitySpread(CVector3D spread) -{ - m_velocitySpread = spread; -} - -void CParticleEmitter::SetVelocitySpread(float x, float y, float z) -{ - m_velocitySpread.X = x; - m_velocitySpread.Y = y; - m_velocitySpread.Z = z; -} - -void CParticleEmitter::SetStartColour(float r, float g, float b, float a) -{ - m_startColour[0] = r; - m_startColour[1] = g; - m_startColour[2] = b; - m_startColour[3] = a; -} - -void CParticleEmitter::SetEndColour(float r, float g, float b, float a) -{ - m_endColour[0] = r; - m_endColour[1] = g; - m_endColour[2] = b; - m_endColour[3] = a; -} - -void CParticleEmitter::SetMaxLifetime(double maxLife) -{ - m_maxLifetime = maxLife; -} - -void CParticleEmitter::SetMinLifetime(double minLife) -{ - m_minLifetime = minLife; -} - -void CParticleEmitter::SetMaxParticles(int maxParticles) -{ - m_maxParticles = maxParticles; -} - -void CParticleEmitter::SetMinParticles(int minParticles) -{ - m_minParticles = minParticles; -} + return false; +} \ No newline at end of file diff --git a/source/graphics/ParticleEmitter.h b/source/graphics/ParticleEmitter.h index a529d53e48..e0f394a7a9 100755 --- a/source/graphics/ParticleEmitter.h +++ b/source/graphics/ParticleEmitter.h @@ -1,119 +1,268 @@ -/*================================================================== -| -| Name: ParticleEmitter.h -| -|=================================================================== -| -| Author: Ben Vinegar -| Contact: benvinegar () hotmail ! com -| -| -| Last Modified: 03/08/04 -| -| Overview: Particle emitter class that emits particles from -| an origin (or area) with a variety of set colours, -| durations, forces and a single common sprite. -| -| -| Usage: Instantiate one emitter per desired effect. Set the -| various fields (preferably all, the defaults are rather -| boring) and then call Frame() - you guessed it - every -| frame. -| -| To do: TBA -| -| More Information: TBA -| -==================================================================*/ +///////////////////////////////////////////////////// +// File Name: ParticleEmitter.h +// Date: 6/29/05 +// Author: Will Dull +// Purpose: The base particle and emitter +// classes. +///////////////////////////////////////////////////// -#ifndef PARTICLE_EMITTER_H -#define PARTICLE_EMITTER_H +#ifndef _PARTICLEEMITTER_H_ +#define _PARTICLEEMITTER_H_ -//-------------------------------------------------------- -// Includes / Compiler directives -//-------------------------------------------------------- +#include "precompiled.h" +#include +#include +#include "ogl.h" +#include -#include "Particle.h" -#include "Sprite.h" -#include "Vector3D.h" -#include +#define M_PI 3.14159265358979323846f +#define HALF_PI 1.57079632679489661923f +#define DEGTORAD(d) ((d * (float)M_PI) / 180.0f); +#define RADTODEG(r) ((r * 180.0f) /(float)M_PI); -//-------------------------------------------------------- -// Declarations -//-------------------------------------------------------- +const int HALF_RAND = (RAND_MAX / 2); -class CParticleEmitter +struct tVector { -public: - CParticleEmitter(); - ~CParticleEmitter(); - - // must be performed before first frame/render/update call - bool Init(); - - // renders and updates particles - void Frame(); - - // renders without updating particles - void Render(); - - void Update(); - - void SetSprite(CSprite * sprite); - - void SetOrigin(CVector3D origin); - void SetOrigin(float x, float y, float z); - - void SetOriginSpread(CVector3D spread); - void SetOriginSpread(float x, float y, float z); - - void SetGravity(CVector3D gravity); - void SetGravity(float x, float y, float z); - - void SetVelocity(CVector3D direction); - void SetVelocity(float x, float y, float z); - - void SetVelocitySpread(CVector3D spread); - void SetVelocitySpread(float x, float y, float z); - - void SetStartColour(float r, float g, float b, float a); - void SetEndColour(float r, float g, float b, float a); - - // in milliseconds - void SetMaxLifetime(double maxLife); - - // in milliseconds - void SetMinLifetime(double minLife); - - void SetMaxParticles(int maxParticles); - void SetMinParticles(int minParticles); - -private: - CSprite * m_sprite; - - std::vector m_particles; - - CVector3D m_origin; - CVector3D m_originSpread; - - CVector3D m_velocity; - CVector3D m_velocitySpread; - - CVector3D m_gravity; - - float m_startColour[4]; - float m_endColour[4]; - - int m_maxParticles; - int m_minParticles; - int m_numParticles; - - double m_maxLifetime; - double m_minLifetime; - - double m_timeOfLastFrame; - float m_timeSinceLastEmit; + float x,y,z; }; -#endif // PARTICLE_EMITTER_H +class CEmitter +{ +public: + struct tColor + { + unsigned char r, g, b; + }; + + struct tParticle + { + // base stuff + tVector pos; // Current position 12 + tVector dir; // Current direction with speed 12 + float alpha; // Fade value 4 + float alphaDelta; // Change of fade 4 + tColor color; // Current color of particle 3 + tColor deltaColor; // Change of color 3 + short life; // How long it will last 2 + + // particle text stuff + tVector endPos; // For particle texture 12 + bool inPos; // 1 + + tParticle *next; // pointer for link lists 4 + + tParticle() + { + next = 0; + } + }; + + //struct tParticleNode + //{ + // tParticle *pParticle; + // tParticleNode *next; + //}; + +protected: + int texture; // Texture ID + bool isFinished; // tells the engine it's ready to be deleted + + // Transformation Info + tVector pos; // XYZ Position + tVector finalPos; // Final position of the particles (IF IMPLOSION) + float yaw, yawVar; // Yaw and variation + float pitch, pitchVar; // Pitch and variation + float speed, speedVar; // Speed and variation + float updateSpeed; // Controls how fast emitter is updated. + float size; // size of the particles (if point sprites is not enabled) + + // Particle + tParticle *heap; // Pointer to beginning of array + + tParticle *openList; // linked list of unused particles + tParticle *usedList; // linked list of used particles + + int blend_mode; // Method used to blend particles. + int max_particles; // Maximum particles emitter can put out + int particleCount; // Total emitted right now + int emitsPerFrame, emitVar; // Emits per frame and variation + int life, lifeVar; // Life count and variation (in Frames) + int emitterLife; // Life of the emitter + bool decrementLife; // Controls whether or not the particles life is decremented every update. + bool decrementAlpha; // Controls whether or not the particles alpha is decremented every update. + bool renderParticles; // Controls the rendering of the particles. + tColor startColor, startColorVar; // Current color of particle + tColor endColor, endColorVar; // Current color of particle + + // Physics + tVector force; // Forces that affect the particles + +public: + // Constructor + CEmitter(const int MAX_PARTICLES = 4000, const int lifetime = -1, int textureID = 0); + + ////////////////////////////////////////////////////////////// + //Func Name: setupEmitter + //Date: 9/18/05 + //Author: Will Dull + //Notes: Setup emitter. Setup so that a derived class can + // overload this function to suit the specific particles + // needs. + ////////////////////////////////////////////////////////////// + virtual bool setupEmitter() { return false;} + + ////////////////////////////////////////////////////////////// + //Func Name: addParticle + //Date: 9/18/05 + //Author: Will Dull + //Notes: Sets up and adds a particle to an emitter. Setup so + // that a derived class can overload this function to + // suit the specific particles needs. + ////////////////////////////////////////////////////////////// + virtual bool addParticle(); + + ////////////////////////////////////////////////////////////// + //Func Name: updateEmitter + //Date: 9/18/05 + //Author: Will Dull + //Notes: Updates emitter. Setup so that a derived class can + // overload this function to suit the specific particles + // needs. + ////////////////////////////////////////////////////////////// + virtual bool updateEmitter() { return false; } + + ////////////////////////////////////////////////////////////// + //Func Name: renderEmitter + //Date: 9/18/05 + //Author: Will Dull + //Notes: Renders emitter. Setup so that a derived class can + // overload this function to suit the specific particles + // needs. + ////////////////////////////////////////////////////////////// + virtual bool renderEmitter(); + + inline float RandomNum() + { + int rn; + rn = rand(); + return ((float)(rn - HALF_RAND) / (float)HALF_RAND); + } + + inline char RandomChar() + { + return (unsigned char)(rand() >> 24); + } + + inline void RotationToDirection(float pitch, float yaw, tVector *direction) + { + direction->x = (float)(-sin(yaw) * cos(pitch)); + direction->y = (float)sin(pitch); + direction->z = (float)(cos(pitch) * cos(yaw)); + } + + /////////////////////////////////////////////////////////////////// + // + // Accessors + // + /////////////////////////////////////////////////////////////////// + float getPosX() { return pos.x; } + float getPosY() { return pos.y; } + float getPosZ() { return pos.z; } + tVector getPosVec() { return pos; } + float getFinalPosX() { return finalPos.x; } + float getFinalPosY() { return finalPos.y; } + float getFinalPosZ() { return finalPos.z; } + bool getIsFinished(void) { return isFinished; } + int getEmitterLife() { return emitterLife; } + int getParticleCount() { return particleCount; } + float getUpdateSpeed() { return updateSpeed; } + int getMaxParticles(void) { return max_particles; } + tColor getStartColor(void) { return startColor; } + tColor getStartColorVar(void) { return startColorVar; } + tColor getEndColor(void) { return endColor; } + tColor getEndColorVar(void) { return endColorVar; } + int getBlendMode(void) { return blend_mode; } + float getSize(void) { return size; } + float getYaw(void) { return yaw; } + float getYawVar(void) { return yawVar; } + float getPitch(void) { return pitch; } + float getPitchVar(void) { return pitchVar; } + float getSpeed(void) { return speed; } + float getSpeedVar(void) { return speedVar; } + int getEmitsPerFrame(void) { return emitsPerFrame; } + int getEmitVar(void) { return emitVar; } + int getLife(void) { return life; } + int getLifeVar(void) { return lifeVar; } + float getForceX(void) { return force.x; } + float getForceY(void) { return force.y; } + float getForceZ(void) { return force.z; } + + + /////////////////////////////////////////////////////////////////// + // + // Mutators + // + /////////////////////////////////////////////////////////////////// + void setPosX(float posX) { pos.x = posX; } + void setPosY(float posY) { pos.y = posY; } + void setPosZ(float posZ) { pos.z = posZ; } + inline void setPosVec(tVector newPos) + { + pos.x = newPos.x; + pos.y = newPos.y; + pos.z = newPos.z; + } + void setFinalPosX(float finalposX) { finalPos.x = finalposX; } + void setFinalPosY(float finalposY) { finalPos.y = finalposY; } + void setFinalPosZ(float finalposZ) { finalPos.z = finalposZ; } + void setTexture(int id) { texture = id; } + void setIsFinished(bool finished) { isFinished = finished; } + void setEmitterLife(int life) { emitterLife = life; } + void setUpdateSpeed(float speed) { updateSpeed = speed; } + void setLife(int newlife) { life = newlife; } + void setLifeVar(int newlifevar) { lifeVar = newlifevar; } + void setSpeed(float newspeed) { speed = newspeed; } + void setSpeedVar(float newspeedvar) { speedVar = newspeedvar; } + void setYaw(float newyaw) { yaw = newyaw; } + void setYawVar(float newyawvar) { yawVar = newyawvar; } + void setPitch(float newpitch) { pitch = newpitch; } + void setPitchVar(float newpitchvar) { pitchVar = newpitchvar; } + void setStartColor(tColor newColor) { startColor = newColor; } + void setStartColorVar(tColor newColorVar) { startColorVar = newColorVar; } + void setEndColor(tColor newColor) { endColor = newColor; } + void setEndColorVar(tColor newColorVar) { endColorVar = newColorVar; } + void setStartColorR(int newColorR) { startColor.r = newColorR; } + void setStartColorG(int newColorG) { startColor.g = newColorG; } + void setStartColorB(int newColorB) { startColor.b = newColorB; } + void setStartColorVarR(int newColorVarR) { startColorVar.r = newColorVarR; } + void setStartColorVarG(int newColorVarG) { startColorVar.g = newColorVarG; } + void setStartColorVarB(int newColorVarB) { startColorVar.b = newColorVarB; } + void setEndColorR(int newColorR) { endColor.r = newColorR; } + void setEndColorG(int newColorG) { endColor.g = newColorG; } + void setEndColorB(int newColorB) { endColor.b = newColorB; } + void setEndColorVarR(int newColorVarR) { endColorVar.r = newColorVarR; } + void setEndColorVarG(int newColorVarG) { endColorVar.g = newColorVarG; } + void setEndColorVarB(int newColorVarB) { endColorVar.b = newColorVarB; } + inline void setBlendMode(int blendmode) + { + if(blendmode >= 1 && blendmode <= 4) + blend_mode = blendmode; + else + blend_mode = 1; + } + void setEmitsPerFrame(int emitsperframe) { emitsPerFrame = emitsperframe; } + void setEmitVar(int emitvar) { emitVar = emitvar; } + void setForceX(float forceX) { force.x = forceX; } + void setForceY(float forceY) { force.y = forceY; } + void setForceZ(float forceZ) { force.z = forceZ; } + void setSize(float newSize) { size = newSize; } + void setRenderParticles(bool render) { renderParticles = render; } + + + // Destructor + virtual ~CEmitter(void); +}; + +#endif \ No newline at end of file diff --git a/source/graphics/ParticleEngine.cpp b/source/graphics/ParticleEngine.cpp new file mode 100644 index 0000000000..2be1ccd703 --- /dev/null +++ b/source/graphics/ParticleEngine.cpp @@ -0,0 +1,253 @@ +///////////////////////////////////////////////////// +// File Name: ParticleEngine.cpp +// Date: 6/29/05 +// Author: Will Dull +// Purpose: The particle engine system +// implementation. +///////////////////////////////////////////////////// + +#include "precompiled.h" +#include "ParticleEngine.h" +#include +#include "ogl.h" + +CParticleEngine *CParticleEngine::m_pInstance = 0; +CParticleEngine::CParticleEngine(void) +{ + m_pHead = NULL; + totalParticles = 0; +} + +CParticleEngine::~CParticleEngine(void) +{ + //// Release all resources. + //for(int i = 0; i < MAX_TEXTURES; i++) + // glDeleteTextures(1, &idTexture[i]); +} + +void CParticleEngine::cleanup() +{ + tEmitterNode *temp = m_pHead; + totalParticles = 0; + while(temp) + { + tEmitterNode *pTemp = temp->next; + if(!temp->prev) + m_pHead = temp->next; + else + temp->prev->next = temp->next; + + if(pTemp) + temp->next->prev = temp->prev; + + delete temp->pEmitter; + delete temp; + temp = pTemp; + } + + DeleteInstance(); +} + +CParticleEngine *CParticleEngine::GetInstance(void) +{ + // Check to see if one hasn't been made yet. + if (m_pInstance == 0) + m_pInstance = new CParticleEngine; + + // Return the address of the instance. + return m_pInstance; +} + +void CParticleEngine::DeleteInstance() +{ + if (m_pInstance) + delete m_pInstance; + m_pInstance = 0; +} + +bool CParticleEngine::initParticleSystem() +{ + // Texture Loading + + // Needs error checking and testing. + idTexture[DEFAULTTEXT] = ogl_tex_load("art/textures/particles/sprite.tga"); + idTexture[DEFAULTTEXT] = ogl_tex_bind(idTexture[DEFAULTTEXT], 0); + + glBindTexture(GL_TEXTURE_2D, idTexture[DEFAULTTEXT]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE); + //glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL); + //glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_BLEND); + //glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); + + return true; +} + +bool CParticleEngine::addEmitter(CEmitter *emitter, int type, int ID) +{ + // without array you could do: + //emitter->texture = type + idTextureBase; + emitter->setTexture(idTexture[type]); + if(m_pHead == NULL) + { + tEmitterNode *temp = new tEmitterNode; + temp->pEmitter = emitter; + temp->prev = NULL; + temp->next = NULL; + temp->ID = ID; + m_pHead = temp; + return true; + } + else + { + tEmitterNode *temp = new tEmitterNode; + temp->pEmitter = emitter; + temp->next = m_pHead; + temp->prev = NULL; + temp->ID = ID; + m_pHead->prev = temp; + m_pHead = temp; + return true; + } +} + +CEmitter* CParticleEngine::findEmitter(int ID) +{ + tEmitterNode *temp = m_pHead; + while(temp) + { + if(temp->ID < 0 || temp->ID > MAX_EMIT) + continue; + + // NOTE: In the event that there are two different + // emitters with the same ID, this will only + // return the first one that it finds. So + // make sure your emitter has a unique ID + // if you want to use this function. + if(temp->ID == ID) + return temp->pEmitter; + + temp = temp->next; + } + + // NOTE: Just in case this happens, it's VERY important + // that you wrap this function in a if statement + // to check for this condition because you could + // end up with a crash if you try to change an + // emitter when you couldn't find it and returned + // NULL instead. + return NULL; +} + +void CParticleEngine::updateEmitters() +{ + tEmitterNode *temp = m_pHead; + totalParticles = 0; + while(temp) + { + // are we ready for deletion? + if(temp->pEmitter->getIsFinished()) + { + // store a pointer to the next node + tEmitterNode *pTemp = temp->next; + + // check for the head + if(!temp->prev) + m_pHead = pTemp; + else + // forward the previous's pointer + temp->prev->next = pTemp; + + // if there is any next one, + if(pTemp) + // fix the backwards pointer + pTemp->prev = temp->prev; + + delete temp->pEmitter; + delete temp; + temp = pTemp; + } + else + { + temp->pEmitter->updateEmitter(); + + // Add current emitter to particle count + totalParticles += temp->pEmitter->getParticleCount(); + temp = temp->next; + } + } +} + +void CParticleEngine::renderParticles() +{ + EnterParticleContext(); + + tEmitterNode *temp = m_pHead; + while(temp) + { + temp->pEmitter->renderEmitter(); + temp = temp->next; + } + + LeaveParticleContext(); +} + +void CParticleEngine::destroyAllEmitters(bool fade) +{ + tEmitterNode *temp = m_pHead; + while(temp) + { + if(fade) + { + temp->pEmitter->setEmitterLife(0); + temp = temp->next; + } + else + { + // store a pointer to the next node + tEmitterNode *pTemp = temp->next; + + // check for the head + if(!temp->prev) + m_pHead = pTemp; + else + // forward the previous's pointer + temp->prev->next = pTemp; + + // if there is any next one, + if(pTemp) + // fix the backwards pointer + pTemp->prev = temp->prev; + + delete temp->pEmitter; + delete temp; + temp = pTemp; + } + } + m_pHead = NULL; + updateEmitters(); +} + +void CParticleEngine::EnterParticleContext(void) +{ + glEnable(GL_DEPTH_TEST); // Enable depth testing for hidden surface removal. + glDepthMask(false); + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); // Enable texture mapping. + glPushMatrix(); + glEnable(GL_BLEND); +} + +void CParticleEngine::LeaveParticleContext(void) +{ + glDisable(GL_DEPTH_TEST); + glDisable(GL_TEXTURE_2D); + glDepthMask(true); + glPopMatrix(); + glEnable(GL_LIGHTING); + glDisable(GL_BLEND); +} \ No newline at end of file diff --git a/source/graphics/ParticleEngine.h b/source/graphics/ParticleEngine.h new file mode 100644 index 0000000000..0e70f41ee3 --- /dev/null +++ b/source/graphics/ParticleEngine.h @@ -0,0 +1,142 @@ +///////////////////////////////////////////////////// +// File Name: ParticleEngine.h +// Date: 6/29/05 +// Author: Will Dull +// Purpose: The particle engine system. +// controls and maintain particles +// through emitters that are passed +// into each of the main functions. +///////////////////////////////////////////////////// + +#ifndef _PARTICLEENGINE_H_ +#define _PARTICLEENGINE_H_ + +#include "ParticleEmitter.h" +#include "lib/res/graphics/tex.h" +#include "lib/res/graphics/ogl_tex.h" + +#include "ps/CLogger.h" +#include "ps/Loader.h" + +// Different textures +enum PText { DEFAULTTEXT, MAX_TEXTURES }; +// Different emitters +enum PEmit { DEFAULTEMIT, MAX_EMIT }; + +class CParticleEngine +{ +public: + virtual ~CParticleEngine(void); + + + ////////////////////////////////////////////////////////////// + //Func Name: GetInstance + //Date: 8/1/05 + //Author: Will Dull + //Purpose: returns the instance of the singleton class + ////////////////////////////////////////////////////////////// + static CParticleEngine* GetInstance(); + + ////////////////////////////////////////////////////////////// + //Func Name: DeleteInstance + //Date: 8/1/05 + //Author: Will Dull + //Purpose: deletes the instance of the singleton class + ////////////////////////////////////////////////////////////// + static void DeleteInstance(); + + ////////////////////////////////////////////////////////////// + //Func Name: initParticleSystem + //Date: 6/29/05 + //Author: Will Dull + //Out: True if particle system initialized correctly, + // false otherwise + //Purpose: inits particle system. + ////////////////////////////////////////////////////////////// + bool initParticleSystem(void); + + ////////////////////////////////////////////////////////////// + //Func Name: addEmitter + //Date: 7/20/05 + //Author: Will Dull + //In: emitter to add, texture type, id of emitter + //Out: True if emitter added correctly, + // false otherwise + //Purpose: adds the emitter to the engines list + ////////////////////////////////////////////////////////////// + bool addEmitter(CEmitter *emitter, int type = DEFAULTTEXT, int ID = DEFAULTEMIT); + + ////////////////////////////////////////////////////////////// + //Func Name: findEmitter + //Date: 7/21/05 + //Author: Will Dull + //In: id of emitter to find + //Out: the emitter if found, + // NULL otherwise + //Purpose: finds the emitter using its ID + ////////////////////////////////////////////////////////////// + CEmitter* findEmitter(int ID); + + ////////////////////////////////////////////////////////////// + //Func Name: updateEmitters + //Date: 7/20/05 + //Author: Will Dull + //Purpose: Checks if the emitter is ready to be deleted + // and removed. If not it calls the emitters update + // function. + ////////////////////////////////////////////////////////////// + void updateEmitters(); + + ////////////////////////////////////////////////////////////// + //Func Name: renderParticles + //Date: 7/20/05 + //Author: Will Dull + //Purpose: Renders the emitter and all it's particles + ////////////////////////////////////////////////////////////// + void renderParticles(); + + ////////////////////////////////////////////////////////////// + //Func Name: destroyAllEmitters + //Date: 8/1/05 + //Author: Will Dull + //In: fade - if true, will allow the emitter to fade itself out + // if false, emitter and particles will disappear instantly + //Purpose: Destroys every active emitter on screen. + ////////////////////////////////////////////////////////////// + void destroyAllEmitters(bool fade = true); + + ////////////////////////////////////////////////////////////// + //Func Name: cleanup + //Date: 8/3/05 + //Author: Will Dull + //Purpose: Any cleanup not done in the destructor. + ////////////////////////////////////////////////////////////// + void cleanup(); + + void EnterParticleContext(void); + void LeaveParticleContext(void); + + int getTotalParticles() { return totalParticles; } + void SetTotalParticles(int particles) { totalParticles = particles; } + void AddToTotalParticles(int addAmount) { totalParticles += addAmount; } + void SubToTotalParticles(int subAmount) { totalParticles -= subAmount; } + +private: + CParticleEngine(void); + static CParticleEngine* m_pInstance; // The singleton instance + + Handle idTexture[MAX_TEXTURES]; + int totalParticles; // Total Amount of particles of all emitters. + + struct tEmitterNode + { + CEmitter *pEmitter; + tEmitterNode *prev, *next; + int ID; + }; + + tEmitterNode *m_pHead; + friend class CEmitter; +}; + +#endif \ No newline at end of file