private boolean loadParticleXML() {
    InputStream is = null;
    try {
      SAXParserFactory spf = SAXParserFactory.newInstance();
      SAXParser sp = spf.newSAXParser();

      QuickTiGame2dParticleParser handler = new QuickTiGame2dParticleParser(this);

      XMLReader xr = sp.getXMLReader();
      xr.setContentHandler(handler);

      is = QuickTiGame2dUtil.getFileInputStream(image);
      xr.parse(new InputSource(new BufferedInputStream(is)));

      emissionRate = maxParticles / particleLifespan;

      sourcePosition.x = x;
      sourcePosition.y = y;

    } catch (Exception e) {
      if (debug)
        Log.w(Quicktigame2dModule.LOG_TAG, String.format("failed to load particle: %s", image), e);
      return false;
    } finally {
      if (is != null) {
        try {
          is.close();
        } catch (IOException e) {
          // nothing to do
        }
      }
    }

    return true;
  }
  public void onDrawFrame(GL10 gl10, boolean fpsTimeElapsed) {
    GL11 gl = (GL11) gl10;

    updateWithDelta(1.0f / MAXIMUM_UPDATE_RATE);

    synchronized (transforms) {
      if (fpsTimeElapsed) {
        onTransform();
      }
    }
    sourcePosition.x = x;
    sourcePosition.y = y;

    gl.glMatrixMode(GL11.GL_MODELVIEW);
    gl.glLoadIdentity();

    gl.glEnableClientState(GL11.GL_COLOR_ARRAY);

    // unbind all buffers
    gl.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
    gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
    gl.glBindTexture(GL11.GL_TEXTURE_2D, 0);

    gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, verticesID[0]);

    // Using glBufferSubData means that a copy is done from the quads array to the buffer rather
    // than recreating the buffer which
    // would be an allocation and copy. The copy also only takes over the number of live particles.
    // This provides a nice performance
    // boost.
    quadsBuffer.put(quads);
    quadsBuffer.position(0);

    gl.glBufferSubData(GL11.GL_ARRAY_BUFFER, 0, 128 * particleIndex, quadsBuffer);

    // Configure the vertex pointer which will use the currently bound VBO for its data
    gl.glVertexPointer(2, GL11.GL_FLOAT, 32, 0);
    gl.glColorPointer(4, GL11.GL_FLOAT, 32, (4 * 4));
    gl.glTexCoordPointer(2, GL11.GL_FLOAT, 32, (4 * 2));

    if (hasTexture) {
      gl.glEnable(GL11.GL_TEXTURE_2D);
      gl.glBindTexture(GL11.GL_TEXTURE_2D, getTexture().getTextureId());
    }

    gl.glBlendFunc(srcBlendFactor, dstBlendFactor);

    gl.glDrawElements(GL11.GL_TRIANGLES, particleIndex * 6, GL11.GL_UNSIGNED_SHORT, indicesBuffer);

    gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
    gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);

    gl.glDisableClientState(GL11.GL_COLOR_ARRAY);
  }
  private void initParticle(Particle particle) {

    // Init the position of the particle.  This is based on the source position of the particle
    // emitter
    // plus a configured variance.  The random_minus_1_to_1 macro allows the number to be both
    // positive
    // and negative
    particle.position.x =
        (float) (sourcePosition.x + sourcePositionVariance.x * random_minus_1_to_1());
    particle.position.y =
        (float) (sourcePosition.y + sourcePositionVariance.y * random_minus_1_to_1());
    particle.startPos.x = sourcePosition.x;
    particle.startPos.y = sourcePosition.y;

    // Init the direction of the particle.  The newAngle is calculated using the angle passed in and
    // the
    // angle variance.
    float newAngle =
        (float) degreesToRadians((float) (angle + angleVariance * random_minus_1_to_1()));

    // Create a new Vector2 using the newAngle
    Vector2 vector = new Vector2((float) Math.cos(newAngle), (float) Math.sin(newAngle));

    // Calculate the vectorSpeed using the speed and speedVariance which has been passed in
    float vectorSpeed = (float) (speed + speedVariance * random_minus_1_to_1());

    // The particles direction vector is calculated by taking the vector calculated above and
    // multiplying that by the speed
    particle.direction = Vector2.multiply(vector, vectorSpeed);

    // Set the default diameter of the particle from the source position
    particle.radius = (float) (maxRadius + maxRadiusVariance * random_minus_1_to_1());
    particle.radiusDelta = (maxRadius / particleLifespan) * (1.0f / MAXIMUM_UPDATE_RATE);
    particle.angle = degreesToRadians((float) (angle + angleVariance * random_minus_1_to_1()));
    particle.degreesPerSecond =
        degreesToRadians(
            (float) (rotatePerSecond + rotatePerSecondVariance * random_minus_1_to_1()));

    particle.radialAcceleration = radialAcceleration;
    particle.tangentialAcceleration = tangentialAcceleration;

    // Calculate the particles life span using the life span and variance passed in
    particle.timeToLive =
        (float) Math.max(0, particleLifespan + particleLifespanVariance * random_minus_1_to_1());

    // Calculate the particle size using the start and finish particle sizes
    float particleStartSize =
        (float) (startParticleSize + startParticleSizeVariance * random_minus_1_to_1());
    float particleFinishSize =
        (float) (finishParticleSize + finishParticleSizeVariance * random_minus_1_to_1());
    particle.particleSizeDelta =
        ((particleFinishSize - particleStartSize) / particle.timeToLive)
            * (1.0f / MAXIMUM_UPDATE_RATE);
    particle.particleSize = Math.max(0, particleStartSize);

    // Calculate the color the particle should have when it starts its life.  All the elements
    // of the start color passed in along with the variance are used to calculate the star color
    Color4f start = new Color4f(0, 0, 0, 0);
    start.red = (float) (startColor.red + startColorVariance.red * random_minus_1_to_1());
    start.green = (float) (startColor.green + startColorVariance.green * random_minus_1_to_1());
    start.blue = (float) (startColor.blue + startColorVariance.blue * random_minus_1_to_1());
    start.alpha = (float) (startColor.alpha + startColorVariance.alpha * random_minus_1_to_1());

    // Calculate the color the particle should be when its life is over.  This is done the same
    // way as the start color above
    Color4f end = new Color4f(0, 0, 0, 0);
    end.red = (float) (finishColor.red + finishColorVariance.red * random_minus_1_to_1());
    end.green = (float) (finishColor.green + finishColorVariance.green * random_minus_1_to_1());
    end.blue = (float) (finishColor.blue + finishColorVariance.blue * random_minus_1_to_1());
    end.alpha = (float) (finishColor.alpha + finishColorVariance.alpha * random_minus_1_to_1());

    // Calculate the delta which is to be applied to the particles color during each cycle of its
    // life.  The delta calculation uses the life span of the particle to make sure that the
    // particles color will transition from the start to end color during its life time.  As the
    // game
    // loop is using a fixed delta value we can calculate the delta color once saving cycles in the
    // update method
    particle.color = start;
    particle.deltaColor.red =
        ((end.red - start.red) / particle.timeToLive) * (1.0f / MAXIMUM_UPDATE_RATE);
    particle.deltaColor.green =
        ((end.green - start.green) / particle.timeToLive) * (1.0f / MAXIMUM_UPDATE_RATE);
    particle.deltaColor.blue =
        ((end.blue - start.blue) / particle.timeToLive) * (1.0f / MAXIMUM_UPDATE_RATE);
    particle.deltaColor.alpha =
        ((end.alpha - start.alpha) / particle.timeToLive) * (1.0f / MAXIMUM_UPDATE_RATE);

    // Calculate the rotation
    float startA = (float) (rotationStart + rotationStartVariance * random_minus_1_to_1());
    float endA = (float) (rotationEnd + rotationEndVariance * random_minus_1_to_1());
    particle.rotation = startA;
    particle.rotationDelta = (endA - startA) / particle.timeToLive;
  }
  /*
   * update with delta (aDelta=delta seconds)
   */
  private void updateWithDelta(float aDelta) {

    // If the emitter is active and the emission rate is greater than zero then emit
    // particles
    if (active && emissionRate > 0) {
      float rate = 1.0f / emissionRate;
      emitCounter += aDelta;
      while (particleCount < maxParticles && emitCounter > rate) {
        addParticle();
        emitCounter -= rate;
      }

      elapsedTime += aDelta;
      if (duration != -1 && duration < elapsedTime) stopParticleEmitter();
    }

    // Reset the particle index before updating the particles in this emitter
    particleIndex = 0;

    // Loop through all the particles updating their location and color
    while (particleIndex < particleCount) {

      // Get the particle for the current particle index
      Particle currentParticle = particles[particleIndex];

      // FIX 1
      // Reduce the life span of the particle
      currentParticle.timeToLive -= aDelta;

      // If the current particle is alive then update it
      if (currentParticle.timeToLive > 0) {

        // If maxRadius is greater than 0 then the particles are going to spin otherwise
        // they are effected by speed and gravity
        if (emitterType == kParticleTypeRadial) {

          // FIX 2
          // Update the angle of the particle from the sourcePosition and the radius.  This is only
          // done of the particles are rotating
          currentParticle.angle += currentParticle.degreesPerSecond * aDelta;
          currentParticle.radius -= currentParticle.radiusDelta;

          Vector2 tmp = new Vector2();
          tmp.x =
              (float) (sourcePosition.x - Math.cos(currentParticle.angle) * currentParticle.radius);
          tmp.y =
              (float) (sourcePosition.y - Math.sin(currentParticle.angle) * currentParticle.radius);
          currentParticle.position = tmp;

          if (currentParticle.radius < minRadius) currentParticle.timeToLive = 0;
        } else {
          Vector2 tmp, radial;
          Vector2 tangential = new Vector2();

          radial = Vector2.makeZeroVector();
          Vector2 diff = Vector2.sub(currentParticle.startPos, Vector2.makeZeroVector());

          currentParticle.position = Vector2.sub(currentParticle.position, diff);

          if (currentParticle.position.x > 0 || currentParticle.position.y > 0)
            radial = Vector2.normalize(currentParticle.position);

          tangential.x = radial.x;
          tangential.y = radial.y;
          radial = Vector2.multiply(radial, currentParticle.radialAcceleration);

          float newy = tangential.x;
          tangential.x = -tangential.y;
          tangential.y = newy;
          tangential = Vector2.multiply(tangential, currentParticle.tangentialAcceleration);

          tmp = Vector2.add(Vector2.add(radial, tangential), gravity);
          tmp = Vector2.multiply(tmp, aDelta);
          currentParticle.direction = Vector2.add(currentParticle.direction, tmp);
          tmp = Vector2.multiply(currentParticle.direction, aDelta);
          currentParticle.position = Vector2.add(currentParticle.position, tmp);
          currentParticle.position = Vector2.add(currentParticle.position, diff);
        }

        // Update the particles color
        currentParticle.color.red += currentParticle.deltaColor.red;
        currentParticle.color.green += currentParticle.deltaColor.green;
        currentParticle.color.blue += currentParticle.deltaColor.blue;
        currentParticle.color.alpha += currentParticle.deltaColor.alpha;

        // Update the particle size
        currentParticle.particleSize += currentParticle.particleSizeDelta;

        // Update the rotation of the particle
        currentParticle.rotation += (currentParticle.rotationDelta * aDelta);

        // As we are rendering the particles as quads, we need to define 6 vertices for each
        // particle
        float halfSize = currentParticle.particleSize * 0.5f;

        // If a rotation has been defined for this particle then apply the rotation to the vertices
        // that define
        // the particle
        if (currentParticle.rotation > 0) {
          float x1 = -halfSize;
          float y1 = -halfSize;
          float x2 = halfSize;
          float y2 = halfSize;
          float lx = currentParticle.position.x;
          float ly = currentParticle.position.y;
          float r = (float) degreesToRadians(currentParticle.rotation);
          float cr = (float) Math.cos(r);
          float sr = (float) Math.sin(r);
          float ax = x1 * cr - y1 * sr + lx;
          float ay = x1 * sr + y1 * cr + ly;
          float bx = x2 * cr - y1 * sr + lx;
          float by = x2 * sr + y1 * cr + ly;
          float cx = x2 * cr - y2 * sr + lx;
          float cy = x2 * sr + y2 * cr + ly;
          float dx = x1 * cr - y2 * sr + lx;
          float dy = x1 * sr + y2 * cr + ly;

          updateQuad(particleIndex, 0, ax, ay, currentParticle.color);
          updateQuad(particleIndex, 1, dx, dy, currentParticle.color);
          updateQuad(particleIndex, 2, cx, cy, currentParticle.color);
          updateQuad(particleIndex, 3, bx, by, currentParticle.color);
        } else {
          updateQuad(
              particleIndex,
              0,
              currentParticle.position.x - halfSize,
              currentParticle.position.y - halfSize,
              currentParticle.color);
          updateQuad(
              particleIndex,
              1,
              currentParticle.position.x - halfSize,
              currentParticle.position.y + halfSize,
              currentParticle.color);
          updateQuad(
              particleIndex,
              2,
              currentParticle.position.x + halfSize,
              currentParticle.position.y + halfSize,
              currentParticle.color);
          updateQuad(
              particleIndex,
              3,
              currentParticle.position.x + halfSize,
              currentParticle.position.y - halfSize,
              currentParticle.color);
        }
        particleIndex++;
      } else {

        // As the particle is not alive anymore replace it with the last active particle
        // in the array and reduce the count of particles by one.  This causes all active particles
        // to be packed together at the start of the array so that a particle which has run out of
        // life will only drop into this clause once
        if (particleIndex != particleCount - 1)
          particles[particleIndex] = particles[particleCount - 1].copy();
        particleCount--;
      }
    }
  }