package org.anddev.andengine.entity.particle;
import static org.anddev.andengine.util.MathUtils.RANDOM;
import static org.anddev.andengine.util.constants.Constants.VERTEX_INDEX_X;
import static org.anddev.andengine.util.constants.Constants.VERTEX_INDEX_Y;
import java.util.ArrayList;
import javax.microedition.khronos.opengles.GL10;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.entity.Entity;
import org.anddev.andengine.entity.particle.emitter.IParticleEmitter;
import org.anddev.andengine.entity.particle.emitter.RectangleParticleEmitter;
import org.anddev.andengine.entity.particle.initializer.IParticleInitializer;
import org.anddev.andengine.entity.particle.modifier.IParticleModifier;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.vertex.RectangleVertexBuffer;
import android.util.FloatMath;
/**
* TODO Check if SpriteBatch can be used here to improve performance.
*
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @since 19:42:27 - 14.03.2010
*/
public class ParticleSystem extends Entity {
// ===========================================================
// Constants
// ===========================================================
private static final int BLENDFUNCTION_SOURCE_DEFAULT = GL10.GL_ONE;
private static final int BLENDFUNCTION_DESTINATION_DEFAULT = GL10.GL_ONE_MINUS_SRC_ALPHA;
private final float[] POSITION_OFFSET = new float[2];
// ===========================================================
// Fields
// ===========================================================
private final IParticleEmitter mParticleEmitter;
private final Particle[] mParticles;
private int mSourceBlendFunction = BLENDFUNCTION_SOURCE_DEFAULT;
private int mDestinationBlendFunction = BLENDFUNCTION_DESTINATION_DEFAULT;
private final ArrayList<IParticleInitializer> mParticleInitializers = new ArrayList<IParticleInitializer>();
private final ArrayList<IParticleModifier> mParticleModifiers = new ArrayList<IParticleModifier>();
private final float mRateMinimum;
private final float mRateMaximum;
private final TextureRegion mTextureRegion;
private boolean mParticlesSpawnEnabled = true;
private final int mParticlesMaximum;
private int mParticlesAlive;
private float mParticlesDueToSpawn;
private int mParticleModifierCount;
private int mParticleInitializerCount;
private RectangleVertexBuffer mSharedParticleVertexBuffer;
// ===========================================================
// Constructors
// ===========================================================
/**
* Creates a ParticleSystem with a {@link RectangleParticleEmitter}.
* @deprecated Instead use {@link ParticleSystem#ParticleSystem(IParticleEmitter, float, float, int, TextureRegion)}.
*/
@Deprecated
public ParticleSystem(final float pX, final float pY, final float pWidth, final float pHeight, final float pRateMinimum, final float pRateMaximum, final int pParticlesMaximum, final TextureRegion pTextureRegion) {
this(new RectangleParticleEmitter(pX + pWidth * 0.5f, pY + pHeight * 0.5f, pWidth, pHeight), pRateMinimum, pRateMaximum, pParticlesMaximum, pTextureRegion);
}
public ParticleSystem(final IParticleEmitter pParticleEmitter, final float pRateMinimum, final float pRateMaximum, final int pParticlesMaximum, final TextureRegion pTextureRegion) {
super(0, 0);
this.mParticleEmitter = pParticleEmitter;
this.mParticles = new Particle[pParticlesMaximum];
this.mRateMinimum = pRateMinimum;
this.mRateMaximum = pRateMaximum;
this.mParticlesMaximum = pParticlesMaximum;
this.mTextureRegion = pTextureRegion;
this.registerUpdateHandler(this.mParticleEmitter);
}
// ===========================================================
// Getter & Setter
// ===========================================================
public boolean isParticlesSpawnEnabled() {
return this.mParticlesSpawnEnabled;
}
public void setParticlesSpawnEnabled(final boolean pParticlesSpawnEnabled) {
this.mParticlesSpawnEnabled = pParticlesSpawnEnabled;
}
public void setBlendFunction(final int pSourceBlendFunction, final int pDestinationBlendFunction) {
this.mSourceBlendFunction = pSourceBlendFunction;
this.mDestinationBlendFunction = pDestinationBlendFunction;
}
public IParticleEmitter getParticleEmitter() {
return this.mParticleEmitter;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void reset() {
super.reset();
this.mParticlesDueToSpawn = 0;
this.mParticlesAlive = 0;
}
@Override
protected void onManagedDraw(final GL10 pGL, final Camera pCamera) {
final Particle[] particles = this.mParticles;
for(int i = this.mParticlesAlive - 1; i >= 0; i--) {
particles[i].onDraw(pGL, pCamera);
}
}
@Override
protected void onManagedUpdate(final float pSecondsElapsed) {
super.onManagedUpdate(pSecondsElapsed);
if(this.mParticlesSpawnEnabled) {
this.spawnParticles(pSecondsElapsed);
}
final Particle[] particles = this.mParticles;
final ArrayList<IParticleModifier> particleModifiers = this.mParticleModifiers;
final int particleModifierCountMinusOne = this.mParticleModifierCount - 1;
for(int i = this.mParticlesAlive - 1; i >= 0; i--) {
final Particle particle = particles[i];
/* Apply all particleModifiers */
for(int j = particleModifierCountMinusOne; j >= 0; j--) {
particleModifiers.get(j).onUpdateParticle(particle);
}
particle.onUpdate(pSecondsElapsed);
if(particle.mDead){
this.mParticlesAlive--;
final int particlesAlive = this.mParticlesAlive;
particles[i] = particles[particlesAlive];
particles[particlesAlive] = particle;
}
}
}
// ===========================================================
// Methods
// ===========================================================
public void addParticleModifier(final IParticleModifier pParticleModifier) {
this.mParticleModifiers.add(pParticleModifier);
this.mParticleModifierCount++;
}
public void removeParticleModifier(final IParticleModifier pParticleModifier) {
this.mParticleModifierCount--;
this.mParticleModifiers.remove(pParticleModifier);
}
public void addParticleInitializer(final IParticleInitializer pParticleInitializer) {
this.mParticleInitializers.add(pParticleInitializer);
this.mParticleInitializerCount++;
}
public void removeParticleInitializer(final IParticleInitializer pParticleInitializer) {
this.mParticleInitializerCount--;
this.mParticleInitializers.remove(pParticleInitializer);
}
private void spawnParticles(final float pSecondsElapsed) {
final float currentRate = this.determineCurrentRate();
final float newParticlesThisFrame = currentRate * pSecondsElapsed;
this.mParticlesDueToSpawn += newParticlesThisFrame;
final int particlesToSpawnThisFrame = Math.min(this.mParticlesMaximum - this.mParticlesAlive, (int)FloatMath.floor(this.mParticlesDueToSpawn));
this.mParticlesDueToSpawn -= particlesToSpawnThisFrame;
for(int i = 0; i < particlesToSpawnThisFrame; i++){
this.spawnParticle();
}
}
private void spawnParticle() {
final Particle[] particles = this.mParticles;
final int particlesAlive = this.mParticlesAlive;
if(particlesAlive < this.mParticlesMaximum){
Particle particle = particles[particlesAlive];
/* New particle needs to be created. */
this.mParticleEmitter.getPositionOffset(this.POSITION_OFFSET);
final float x = this.POSITION_OFFSET[VERTEX_INDEX_X];
final float y = this.POSITION_OFFSET[VERTEX_INDEX_Y];
if(particle != null) {
particle.reset();
particle.setPosition(x, y);
} else {
if(particlesAlive == 0) {
/* This is the very first particle. */
particle = new Particle(x, y, this.mTextureRegion);
this.mSharedParticleVertexBuffer = particle.getVertexBuffer();
} else {
particle = new Particle(x, y, this.mTextureRegion, this.mSharedParticleVertexBuffer);
}
particles[particlesAlive] = particle;
}
particle.setBlendFunction(this.mSourceBlendFunction, this.mDestinationBlendFunction);
/* Apply particle initializers. */
{
final ArrayList<IParticleInitializer> particleInitializers = this.mParticleInitializers;
for(int i = this.mParticleInitializerCount - 1; i >= 0; i--) {
particleInitializers.get(i).onInitializeParticle(particle);
}
final ArrayList<IParticleModifier> particleModifiers = this.mParticleModifiers;
for(int i = this.mParticleModifierCount - 1; i >= 0; i--) {
particleModifiers.get(i).onInitializeParticle(particle);
}
}
this.mParticlesAlive++;
}
}
private float determineCurrentRate() {
if(this.mRateMinimum == this.mRateMaximum){
return this.mRateMinimum;
} else {
return (RANDOM.nextFloat() * (this.mRateMaximum - this.mRateMinimum)) + this.mRateMinimum;
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}