//============================================================================== // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //============================================================================== //============================================================================== // File: cParticleSystem.cpp // Project: Shooting Star // Author: // Copyrights (c) 2003 2ndPoint ry (www.2ndpoint.fi) //------------------------------------------------------------------------------ // Revision history //============================================================================== //============================================================================== // Includes #include "cParticleSystem.hpp" #include #include #include #include "Debug.hpp" #include "cTextureManager.hpp" #include "cParticleEmitter.hpp" //------------------------------------------------------------------------------ // Namespaces using namespace ShootingStar; //============================================================================== //! Constructor cParticleSystem::cParticleSystem (int size): mpEmitter (NULL), mParticleCount (0), mFreeParticle (0), mLastEmit (0), mEmitDelay (10), mActive (false), mKillEmptySystem (false), mAcceleration (0.0f, 0.0f), mAngleVar (0.0f), mTexture (0), mBlend (false), mSpeed (0.3f), mSpeedVar (0.3f), mStartSize (16.0f), mEndSize (128.0f), mEnergy (1500), mEnergyVar (0.3f) { dbg::assertion (DBG_ASSERTION (size != 0)); // Set default colors mStartColor[0] = mStartColor[1] = mStartColor[2] = mStartColor[3] = 1.0f; mEndColor[0] = mEndColor[1] = mEndColor[2] = 1.0f; mEndColor[3] = 0.0f; // Create particles mpParticles = new tParticle[size]; mNumberOfParticles = size; // TEMP memset (mpParticles, 0, mNumberOfParticles * sizeof (tParticle)); }; //! Destructor cParticleSystem::~cParticleSystem (void) { delete [] mpParticles; }; void cParticleSystem::Update (Uint32 deltaTime) { mActive = false; // Update particles for ( int i = 0; i < mNumberOfParticles; i++ ) { if ( mpParticles[i].alive ) UpdateParticle (deltaTime, mpParticles[i]); if ( !mpParticles[i].alive ) mFreeParticle = i; } // Emit new particles if ( mEmitDelay != 0 ) { mLastEmit += deltaTime; int emits = mLastEmit / mEmitDelay; Uint32 updateTime = mEmitDelay; for ( int i = 0; i < emits; i++ ) EmitParticle (updateTime += mEmitDelay); if ( emits != 0 ) mLastEmit = 0; } // Kill if system isn't active and empty system killing is enabled if ( !mActive && mKillEmptySystem ) Kill (); } void cParticleSystem::Render (Uint32 deltaTime) { glPopMatrix (); glPushMatrix (); glTranslatef (0.0f, 0.0f, 1.0f); glPushAttrib (GL_COLOR_BUFFER_BIT|GL_ENABLE_BIT); if ( mBlend ) glBlendFunc (GL_SRC_ALPHA, GL_ONE); if ( mTexture != 0 ) glBindTexture (GL_TEXTURE_2D, mTexture); else glDisable (GL_TEXTURE_2D); glBegin (GL_QUADS); for ( int i = 0; i < mNumberOfParticles; i++ ) { tParticle &particle = mpParticles[i]; glColor4fv (particle.color); if ( particle.alive ) { glTexCoord2f (0.0f, 0.0f); glVertex2f (particle.position.mX + -0.5f * particle.size, particle.position.mY + -0.5f * particle.size); glTexCoord2f (0.0f, 1.0f); glVertex2f (particle.position.mX + -0.5f * particle.size, particle.position.mY + 0.5f * particle.size); glTexCoord2f (1.0f, 1.0f); glVertex2f (particle.position.mX + 0.5f * particle.size, particle.position.mY + 0.5f * particle.size); glTexCoord2f (1.0f, 0.0f); glVertex2f (particle.position.mX + 0.5f * particle.size, particle.position.mY + -0.5f * particle.size); } } glEnd (); glPopAttrib (); } void cParticleSystem::EmitParticle (Uint32 deltaTime) { float angle = mAngleVar * -0.5f + mAngleVar * rand () / (RAND_MAX + 1.0f); if ( mpEmitter != NULL ) { cVector2f position = mpEmitter->GetEmittingPosition (); cVector2f direction = mpEmitter->GetEmittingDirection (angle); EmitParticle (deltaTime, position, direction); } else { cVector2f position = mEmittingPosition; cVector2f direction (cos (D2R (angle)), sin (D2R (angle))); EmitParticle (deltaTime, position, direction); } } void cParticleSystem::EmitParticle (Uint32 deltaTime, cVector2f position, cVector2f direction) { dbg::check_ptr (dbg::error, mpParticles, DBG_HERE); if ( mFreeParticle == -1 ) return; mActive = true; dbg::assertion (DBG_ASSERTION (!mpParticles[mFreeParticle].alive)); // Get free particle tParticle &particle = mpParticles[mFreeParticle]; // Set position & velocity particle.position = position; float speed = mSpeed + (-mSpeedVar * 0.5f + mSpeedVar * rand () / (RAND_MAX + 1.0f)) * mSpeed; particle.velocity = direction * speed; // Set energy int energy = mEnergy + int ((-mEnergyVar * 0.5f + mEnergyVar * rand () / (RAND_MAX + 1.0f)) * float (mEnergy)); particle.energy = energy; // Set size float startSize = 1.0f / (speed / mSpeed) * mStartSize; float endSize = 1.0f / (speed / mSpeed) * mEndSize; particle.size = startSize; particle.deltaSize = (endSize - startSize) / energy; // Set color particle.color[0] = mStartColor[0]; particle.color[1] = mStartColor[1]; particle.color[2] = mStartColor[2]; particle.color[3] = mStartColor[3]; particle.deltaColor[0] = (mEndColor[0] - mStartColor[0]) / float (energy); particle.deltaColor[1] = (mEndColor[1] - mStartColor[1]) / float (energy); particle.deltaColor[2] = (mEndColor[2] - mStartColor[2]) / float (energy); particle.deltaColor[3] = (mEndColor[3] - mStartColor[3]) / float (energy); particle.alive = true; mParticleCount++; if ( deltaTime != 0 ) UpdateParticle (deltaTime, particle); FindFreeParticle (); } void cParticleSystem::UpdateParticle (Uint32 deltaTime, tParticle &particle) { mActive = true; if ( particle.energy <= deltaTime ) { particle.energy = 0; particle.alive = false; mParticleCount--; return; } particle.energy -= deltaTime; particle.velocity += mAcceleration * deltaTime; particle.position += particle.velocity * deltaTime; particle.size += particle.deltaSize * deltaTime; particle.color[0] += particle.deltaColor[0] * deltaTime; particle.color[1] += particle.deltaColor[1] * deltaTime; particle.color[2] += particle.deltaColor[2] * deltaTime; particle.color[3] += particle.deltaColor[3] * deltaTime; } void cParticleSystem::FindFreeParticle (void) { mFreeParticle = -1; // SO SLOW!!! for ( int i = 0; i < mNumberOfParticles; i++ ) { if ( !mpParticles[i].alive ) { mFreeParticle = i; break; } } } void cParticleSystem::EmitAll (void) { while ( mParticleCount < mNumberOfParticles ) EmitParticle (); } void cParticleSystem::EmitParticles (int count) { for ( int i = 0; i <= count; i++ ) EmitParticle (); } void cParticleSystem::EmitParticles (int count, cVector2f position, cVector2f direction) { for ( int i = 0; i <= count; i++ ) EmitParticle (0, position, direction); } //============================================================================== // EOF //==============================================================================