Android Open Source - opengl-particles Particle System






From Project

Back to project page opengl-particles.

License

The source code is released under:

Apache License

If you think the Android project opengl-particles listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*******************************************************************************
 * Copyright 2014 Kenneth Maffei//from  w w  w.  j  ava 2 s. co m
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

package com.kennethmaffei.particles;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.opengl.GLES11;
import android.opengl.GLUtils;

/**
 * Abstract class for a particle system. Defines system and individual particle properties.
 * Also handles creation and destruction of particles
 * 
 * @author Kenneth Maffei
 *
 */
public abstract class ParticleSystem {
  ArrayList<Particle> particles = new ArrayList<Particle>();
  //We don't rely on particles.size() for the number of particles in the system.
  //For efficiency, the particles array is always built for maxParticles.
  //Particles are swapped in the array at particle death (see the emit() function).
  //numParticles keeps track of how many we are rendering at a given time.
  protected int numParticles; 
  int maxParticles;
  
  protected PointF startSize = new PointF();              //Particle start size
  protected PointF endSize = new PointF();                //Particle end size
  protected float particlesPerSec;                        //Particle birth rate
  protected Vector3 velocity = new Vector3();             //Particle starting velocity      
  protected Vector3 velocityVariation = new Vector3();    //Variation in the particle starting velocity
  protected Vector3 acceleration = new Vector3();         //Particle staring acceleration
  protected Vector3 origin = new Vector3();               //The origin for the system
  protected float height, width, depth;                   //Volume parameters for particle generation
  protected float radius;                                 //For radial systems
  protected float accumulatedTime;                        //For transient systems, the time the system has been alive
  protected float lifeTime;                               //Particle lifetime
  protected float lifeTimeVar;                            //Lifetime variation
  protected float startTime;                              //Delay before actually generating any particles
  protected float timeBeforeStartTime;                    //The accumulated time before the start time
  protected boolean started;                              //Indicates that the system has been started
  protected boolean fixed;                                //Fixed system or transient
  protected float duration = -1.0f;                       //Duration of particle system; duration = -1 means keep going once started
  protected boolean destroying;                           //If the system is shutting down. Generally will decrease life for graceful shut down
  protected boolean draw;                                 //Whether or not to actually render the system (for transient systems, update can be true, but draw false)
  //If elapsedTime is such that numNewParticles is 0 (because of rounding down),
  //then hold over this elapsed time and add it to the next until we get some particle production!
  protected float timeHeldOver;
  protected float numParticlesHeldOver;
  
  protected Vector3 emitterVelocity = new Vector3();      //Allows the particle emitter to move
  protected Vector3 emitterAcceleration = new Vector3();  //Acceleration for the emitter

  protected boolean radial;                               //Sets this as for radial particle production. Velocity is interpreted as radial velocity.

  protected boolean facing = true;                        //Particles always face the camera. This is usually the case, but not always.
  
  protected float rEff;                                   //Effective radius used for frustum culling

  protected Vector3 scale = new Vector3(1.0f, 1.0f, 1.0f);//Scaling of the entire system as a whole
  protected Vector3 rotate = new Vector3();               //Rotation of the entire system as a whole
  
  protected int[] glTexture = new int[1];
  
  /**
   * Initializes a given particle with all its "start" values
   * 
   * @param index - the index into the particle array
   */
  abstract void InitializeParticle(int index);
  
  /**
   * Iterates over the particles and updates their attributes
   * This function must also call updateSystem()
   * 
   * @param elapsedTime - the time since the last frame
   */
  abstract void update(float elapsedTime);
  
  /**
   * Renders the particle system to the screen
   * 
   * @param gl - the openGL context
   */
  abstract void draw(GL10 gl);
  
  /**
     * Load a masking texture for the particle system
     * 
     * @param gl - the openGL context
     * @param file - the masking texture file name
     * @return - success or failure
     */
  boolean loadTexture(GL10 gl, String file) {
    try{
      InputStream is = Globals.context.getAssets().open(file); 
      int size = is.available(); 
      byte[] buffer = new byte[size]; 
      is.read(buffer, 0, size);
      is.close(); 
      
      //Check if png or jpg
      BitmapFactory.Options opt = new BitmapFactory.Options(); 
      opt.inDither = false;
      opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
      
      Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, size, opt);
      
      //Generate one texture pointer 
      GLES11.glGenTextures(1, glTexture, 0); 
      // ...and bind it to our array 
      GLES11.glBindTexture(GL10.GL_TEXTURE_2D, glTexture[0]); 
      
      //Create nearest filtered texture 
      GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 
      GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 
      GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); 
      GLES11.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
      
      //Use Android GLUtils to specify a two-dimensional texture image from our bitmap 
      GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); 
    }
    catch(IOException IOerror) {
      return false;
    }
    return true;
    }
  
  /**
   * Resets the particle array
   */
  public void initializeSystem() {
    particles.clear();
    numParticles = 0;
    for(int i=0; i<maxParticles; i++) {
      Particle p = new Particle();
      particles.add(p);
    }
  }
  
  /**
   * Sets this to a radially emitting systems
   */
  public void setRadial() {
    radial = true;
  }

  /**
   * Particles will not rotate to face the camera
   */
  public void facingOff() {
    facing = false;
  }

  /**
   * Sets the volume in which particles are produced
   * 
   * @param width - production extent along x
   * @param depth - production extent along y
   * @param height - production extent along z
   */
  public void setEmitterVolume(float width, float depth, float height) {
    this.width = width;
    this.depth = depth;
    this.height = height;
  }

  /**
   * Sets the motion of the emitter volume
   * 
   * @param velocity - starting velocity
   * @param acceleration - acceleration
   */
  public void setEmitterMotion(Vector3 velocity, Vector3 acceleration) {
    emitterVelocity.copy(velocity);
    emitterAcceleration.copy(acceleration);
  }

  /**
   * Sets the initial origin of the system
   * 
   * @param position - x, y and z coordinates of the origin
   */
  public void setEmitterPosition(Vector3 position) {
    origin.copy(position);
  }

  /**
   * Sets the scale factor for the system as a whole.
   * Can be used to dynamically scale the system
   * 
   * @param scale - the scale factor
   */
  public void setScale(Vector3 scale) {
    this.scale = scale;
  }

  /**
   * For rotation of the system
   * 
   * @param axis - which axis to rotate
   * @param angle - the amount to increase rotation by
   */
  public void setRotate(int axis, float deltaAngle) {
    switch (axis)
    {
      case 0:
        rotate.x+= deltaAngle;
        break;
      case 1:
        rotate.y+= deltaAngle;
        break;
      case 2:
        rotate.z+= deltaAngle;
        break;
      default:
        break;
    }
  }
  
  /**
   * Sets the particle attributes at creation and destruction
   * 
   * @param startSize - x and y start sizes
   * @param endSize - x and y end sizes
   */
  public void setParticleSize(PointF startSize, PointF endSize) {
    this.startSize = startSize;
    this.endSize = endSize;
  }

  /**
   * Sets the attributes for particle creation and life
   * 
   * @param maxParticles - maximum allowed number of particles at any given time
   * @param particlesPerSec - particle birth rate
   * @param lifeTime - how long a particle lives before it is removed from the system
   * @param lifeTimeVar - for a given particle, a variation parameter for the lifetime for randomness
   */
  public void setParticleLife(int maxParticles, float particlesPerSec, float lifeTime, float lifeTimeVar) {
    this.maxParticles = maxParticles;
    this.particlesPerSec = particlesPerSec;
    this.lifeTime = lifeTime;
    this.lifeTimeVar = lifeTimeVar;
  }

  /**
   * Sets the motion attributes for particles
   * 
   * @param velocity - the start velocity for a particle
   * @param velocityVariation - a variation in the start velocity for randomness
   * @param acceleration - particle acceleration
   */
  public void setMotion(Vector3 velocity, Vector3 velocityVariation, Vector3 acceleration) {
    this.velocity.copy(velocity);
    this.velocityVariation.copy(velocityVariation);
    this.acceleration.copy(acceleration);
  }

  
  /**
   * Starts a system
   * 
   * @param origin - the x, y and z coordinates of the system position
   * @param duration - how long the system lives. A value of -1.0f means the system does not stop
   */
  public void startSystem(Vector3 origin, float duration) {
    this.origin.copy(origin);

    this.duration = duration;
    initializeSystem();

    float test;
    float sizex = startSize.x > endSize.x? startSize.x:endSize.x;
    float sizey = startSize.y > endSize.y? startSize.y:endSize.y;

    //Calculate an effective radius for frustum culling
    if(radius > 0.0f)
    {
      rEff = sizex + radius + velocity.x*(lifeTime + lifeTimeVar);
      test = sizey + height/2.0f + velocity.z*(lifeTime + lifeTimeVar);
      if(test > rEff)
        test = rEff;
    }
    else
    {
      float size = (sizex > sizey)? sizex:sizey;
      float dim = (width > depth)? width:depth;
      dim = (dim > height)? dim:height;
      //By using the factor of 2.0, it helps to start the system before the camera fully gets around to it.
      rEff = 2.0f*(size + dim + velocity.length()*(lifeTime + lifeTimeVar));
    }
    
    started = draw = true;
    if(duration == -1.0f)
      fixed = true;
  }
  
  /**
   * Moves the system if necessary.
   * Adjusts our particle array when emitting particles
   * 
   * @param numParticlesToCreate - how many particles need to be created this frame
   * @param deltaTime - time increment since the last frame
   * @return
   */
  protected void updateSystem(int numParticlesToCreate, float deltaTime) {
    //Create new particles
    origin.add(emitterVelocity.scaled(deltaTime));

    emitterVelocity.add(emitterAcceleration.scaled(deltaTime));
    while(numParticlesToCreate > 0 && numParticles < maxParticles) {
      InitializeParticle(numParticles++);
      --numParticlesToCreate;
    }
  }

  /**
   * Causes an immediate killing and reset of the system
   */
  public void killSystem() {
    if(numParticles != 0) {
      particles.clear();
      numParticles = 0;
    }
  }
}




Java Source Code List

com.kennethmaffei.particles.BlackSmoke.java
com.kennethmaffei.particles.Fire.java
com.kennethmaffei.particles.GLRenderer.java
com.kennethmaffei.particles.GenericParticleSystem.java
com.kennethmaffei.particles.Globals.java
com.kennethmaffei.particles.MainActivity.java
com.kennethmaffei.particles.ParticleSystem.java
com.kennethmaffei.particles.Particle.java
com.kennethmaffei.particles.Quad.java
com.kennethmaffei.particles.Vector3.java