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--; } } }