Created
January 14, 2014 18:47
-
-
Save xoppa/8423522 to your computer and use it in GitHub Desktop.
Particle 3D test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.badlogic.gdx.tests.g3d; | |
import com.badlogic.gdx.Gdx; | |
import com.badlogic.gdx.graphics.Camera; | |
import com.badlogic.gdx.graphics.Color; | |
import com.badlogic.gdx.graphics.GL10; | |
import com.badlogic.gdx.graphics.Mesh; | |
import com.badlogic.gdx.graphics.Texture; | |
import com.badlogic.gdx.graphics.VertexAttribute; | |
import com.badlogic.gdx.graphics.VertexAttributes; | |
import com.badlogic.gdx.graphics.VertexAttributes.Usage; | |
import com.badlogic.gdx.graphics.g3d.Material; | |
import com.badlogic.gdx.graphics.g3d.ModelBatch; | |
import com.badlogic.gdx.graphics.g3d.ModelInstance; | |
import com.badlogic.gdx.graphics.g3d.Renderable; | |
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute; | |
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; | |
import com.badlogic.gdx.graphics.g3d.attributes.DepthTestAttribute; | |
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute; | |
import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader; | |
import com.badlogic.gdx.graphics.glutils.ShaderProgram; | |
import com.badlogic.gdx.math.MathUtils; | |
import com.badlogic.gdx.math.Vector2; | |
import com.badlogic.gdx.math.Vector3; | |
import com.badlogic.gdx.utils.Array; | |
import com.badlogic.gdx.utils.BufferUtils; | |
import com.badlogic.gdx.utils.GdxRuntimeException; | |
import com.badlogic.gdx.utils.Pool; | |
/** @author Xoppa */ | |
public class Particle3DTest extends BaseG3dHudTest { | |
public static class Particle { | |
private static int Offset = 0; | |
public final static int Life = Offset++; | |
public final static int PositionX = Offset++; | |
public final static int PositionY = Offset++; | |
public final static int PositionZ = Offset++; | |
public final static int SpeedX = Offset++; | |
public final static int SpeedY = Offset++; | |
public final static int SpeedZ = Offset++; | |
public final static int Width = Offset++; | |
public final static int Height = Offset++; | |
public final static int Red = Offset++; | |
public final static int Green = Offset++; | |
public final static int Blue = Offset++; | |
public final static int Alpha = Offset++; | |
public final static int U0 = Offset++; | |
public final static int V0 = Offset++; | |
public final static int U1 = Offset++; | |
public final static int V1 = Offset++; | |
public final static int Position = PositionX; | |
public final static int Speed = SpeedX; | |
public final static int Size = Width; | |
public final static int Color = Red; | |
public final static int UV0 = U0; | |
public final static int UV1 = U1; | |
public final static int Stride = Offset; | |
private float[] data; | |
private int offset; | |
public Particle select(final float[] data, final int index) { | |
this.data = data; | |
this.offset = index * Stride; | |
return this; | |
} | |
public final float get(final int component) { | |
return data[offset + component]; | |
} | |
public final void set1(final int component, final float value) { | |
data[offset + component] = value; | |
} | |
public final void set2(final int component, final float value1, final float value2) { | |
data[offset + component ] = value1; | |
data[offset + component + 1] = value2; | |
} | |
public final void set3(final int component, final float value1, final float value2, final float value3) { | |
data[offset + component ] = value1; | |
data[offset + component + 1] = value2; | |
data[offset + component + 2] = value3; | |
} | |
public final void set4(final int component, final float value1, final float value2, final float value3, final float value4) { | |
data[offset + component ] = value1; | |
data[offset + component + 1] = value2; | |
data[offset + component + 2] = value3; | |
data[offset + component + 3] = value4; | |
} | |
public final void set2(final int component, final Vector2 value) { | |
data[offset + component ] = value.x; | |
data[offset + component + 1] = value.y; | |
} | |
public final void set3(final int component, final Vector3 value) { | |
data[offset + component ] = value.x; | |
data[offset + component + 1] = value.y; | |
data[offset + component + 2] = value.z; | |
} | |
public final void set4(final int component, final Color value) { | |
data[offset + component ] = value.r; | |
data[offset + component + 1] = value.g; | |
data[offset + component + 2] = value.b; | |
data[offset + component + 3] = value.a; | |
} | |
public final void add1(final int component, final float value) { | |
data[offset + component] += value; | |
} | |
public final void add2(final int component, final float value1, final float value2) { | |
data[offset + component ] += value1; | |
data[offset + component + 1] += value2; | |
} | |
public final void add3(final int component, final float value1, final float value2, final float value3) { | |
data[offset + component ] += value1; | |
data[offset + component + 1] += value2; | |
data[offset + component + 2] += value3; | |
} | |
public final void add4(final int component, final float value1, final float value2, final float value3, final float value4) { | |
data[offset + component ] += value1; | |
data[offset + component + 1] += value2; | |
data[offset + component + 2] += value3; | |
data[offset + component + 3] += value4; | |
} | |
public final void add2(final int component, final Vector2 value) { | |
data[offset + component ] += value.x; | |
data[offset + component + 1] += value.y; | |
} | |
public final void add3(final int component, final Vector3 value) { | |
data[offset + component ] += value.x; | |
data[offset + component + 1] += value.y; | |
data[offset + component + 2] += value.z; | |
} | |
public final void add4(final int component, final Color value) { | |
data[offset + component ] += value.r; | |
data[offset + component + 1] += value.g; | |
data[offset + component + 2] += value.b; | |
data[offset + component + 3] += value.a; | |
} | |
public final void mul1(final int component, final float value) { | |
data[offset + component] *= value; | |
} | |
public final void mul2(final int component, final float value1, final float value2) { | |
data[offset + component ] *= value1; | |
data[offset + component + 1] *= value2; | |
} | |
public final void mul3(final int component, final float value1, final float value2, final float value3) { | |
data[offset + component ] *= value1; | |
data[offset + component + 1] *= value2; | |
data[offset + component + 2] *= value3; | |
} | |
public final void mul4(final int component, final float value1, final float value2, final float value3, final float value4) { | |
data[offset + component ] *= value1; | |
data[offset + component + 1] *= value2; | |
data[offset + component + 2] *= value3; | |
data[offset + component + 3] *= value4; | |
} | |
public final void mul2(final int component, final Vector2 value) { | |
data[offset + component ] *= value.x; | |
data[offset + component + 1] *= value.y; | |
} | |
public final void mul3(final int component, final Vector3 value) { | |
data[offset + component ] *= value.x; | |
data[offset + component + 1] *= value.y; | |
data[offset + component + 2] *= value.z; | |
} | |
public final void mul4(final int component, final Color value) { | |
data[offset + component ] *= value.r; | |
data[offset + component + 1] *= value.g; | |
data[offset + component + 2] *= value.b; | |
data[offset + component + 3] *= value.a; | |
} | |
public final void fma1(final int dest, final int source, final float multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier; | |
} | |
public final void fma2(final int dest, final int source, final float multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier; | |
} | |
public final void fma3(final int dest, final int source, final float multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier; | |
data[offset + dest + 2] += data[offset + source + 2] * multiplier; | |
} | |
public final void fma4(final int dest, final int source, final float multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier; | |
data[offset + dest + 2] += data[offset + source + 2] * multiplier; | |
data[offset + dest + 3] += data[offset + source + 3] * multiplier; | |
} | |
public final void fma2(final int dest, final int source, final float multiplier1, final float multiplier2) { | |
data[offset + dest ] += data[offset + source ] * multiplier1; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier2; | |
} | |
public final void fma3(final int dest, final int source, final float multiplier1, final float multiplier2, final float multiplier3) { | |
data[offset + dest ] += data[offset + source ] * multiplier1; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier2; | |
data[offset + dest + 2] += data[offset + source + 2] * multiplier3; | |
} | |
public final void fma4(final int dest, final int source, final float multiplier1, final float multiplier2, final float multiplier3, final float multiplier4) { | |
data[offset + dest ] += data[offset + source ] * multiplier1; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier2; | |
data[offset + dest + 2] += data[offset + source + 2] * multiplier3; | |
data[offset + dest + 3] += data[offset + source + 3] * multiplier4; | |
} | |
public final void fma2(final int dest, final int source, final Vector2 multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier.x; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier.y; | |
} | |
public final void fma3(final int dest, final int source, final Vector3 multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier.x; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier.y; | |
data[offset + dest + 2] += data[offset + source + 2] * multiplier.z; | |
} | |
public final void fma4(final int dest, final int source, final Color multiplier) { | |
data[offset + dest ] += data[offset + source ] * multiplier.r; | |
data[offset + dest + 1] += data[offset + source + 1] * multiplier.g; | |
data[offset + dest + 2] += data[offset + source + 2] * multiplier.b; | |
data[offset + dest + 3] += data[offset + source + 3] * multiplier.a; | |
} | |
} | |
public static class Emitter { | |
public float[] particles; | |
public int numParticles; | |
public Pool<Particle> pool = new Pool<Particle>() { | |
@Override protected Particle newObject () { | |
return new Particle(); | |
} | |
}; | |
final short vertexSize; // vertexsize | |
final short po; // position offset | |
final short co; // color offset | |
final short uo; // uv offset | |
final float[] vertices; | |
final short[] indices; | |
final VertexAttributes attributes; | |
public final Mesh mesh; | |
public int numVertices; | |
public Vector3 position = new Vector3(); | |
public Emitter (int maxSize) { | |
int size = 200; | |
attributes = new VertexAttributes( | |
new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE), | |
new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE), | |
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0")); | |
vertexSize = (short)(attributes.vertexSize / 4); | |
po = (short)(attributes.findByUsage(Usage.Position).offset/4); | |
co = (short)(attributes.findByUsage(Usage.Color).offset/4); | |
uo = (short)(attributes.findByUsage(Usage.TextureCoordinates).offset/4); | |
vertices = new float[size * 4 * vertexSize]; | |
indices = new short[size * 6]; | |
mesh = new Mesh(false, size * 4, size * 6, attributes); | |
particles = new float[maxSize * Particle.Stride]; | |
} | |
Vector3 tmp1 = new Vector3(), tmp2 = new Vector3(); | |
public void emit() { | |
if (numParticles * Particle.Stride >= particles.length) | |
throw new GdxRuntimeException("Too many particles emitted"); | |
tmp2.set(position).nor(); | |
tmp1.set(tmp2.z+MathUtils.random(-0.1f, 0.1f), 1, -tmp2.x+MathUtils.random(-0.1f, 0.1f)).nor().scl(2f); | |
final int offset = numParticles * Particle.Stride; | |
particle.select(particles, numParticles++); | |
particle.set3(Particle.Position, position); | |
particle.set3(Particle.Speed, tmp1); | |
particle.set4(Particle.Color, 1f, 1f, 1f, 1f); | |
particle.set2(Particle.Size, 0.4f, 0.4f); | |
particle.set2(Particle.UV0, 0f, 0f); | |
particle.set2(Particle.UV1, 1f, 1f); | |
particle.set1(Particle.Life, 2.5f); | |
} | |
Vector3 normal = new Vector3(), tangent = new Vector3(), binormal = new Vector3(); | |
Particle particle = new Particle(); | |
public void update(Camera cam, float delta) { | |
normal.set(cam.direction).scl(-1); | |
tangent.set(normal).crs(cam.up).nor(); | |
binormal.set(normal).crs(tangent).nor(); | |
short vo = 0; // the current vertex | |
int fo = 0; // the current offset in the vertex array | |
int io = 0; // the current offset in the indices array | |
for (int i = numParticles-1; i >= 0; i--) { | |
final int offset = i * Particle.Stride; | |
if ((particles[offset + Particle.Life] -= delta) <= 0f) { | |
if (numParticles > 1) { | |
final int last = (numParticles - 1) * Particle.Stride; | |
for (int j = 0; j < Particle.Stride; j++) | |
particles[offset + j] = particles[last + j]; | |
} | |
--numParticles; | |
continue; | |
} | |
particle.select(particles, i); | |
particle.set1(Particle.Alpha, Math.min(1f, particle.get(Particle.Life)/2.5f)); | |
particle.fma3(Particle.Position, Particle.Speed, delta); | |
tmp1.set(tangent).scl(0.5f * particle.get(Particle.Width)); | |
tmp2.set(binormal).scl(0.5f * particle.get(Particle.Height)); | |
// bottom left | |
vertices[fo+po ] = (particles[offset + Particle.PositionX] - tmp1.x) - tmp2.x; | |
vertices[fo+po+1] = (particles[offset + Particle.PositionY] - tmp1.y) - tmp2.y; | |
vertices[fo+po+2] = (particles[offset + Particle.PositionZ] - tmp1.z) - tmp2.z; | |
vertices[fo+co ] = particles[offset + Particle.Red]; | |
vertices[fo+co+1] = particles[offset + Particle.Green]; | |
vertices[fo+co+2] = particles[offset + Particle.Blue]; | |
vertices[fo+co+3] = particles[offset + Particle.Alpha]; | |
vertices[fo+uo ] = particles[offset + Particle.U0]; | |
vertices[fo+uo+1] = particles[offset + Particle.V0]; | |
fo += vertexSize; | |
// top left | |
vertices[fo+po ] = (particles[offset + Particle.PositionX] - tmp1.x) + tmp2.x; | |
vertices[fo+po+1] = (particles[offset + Particle.PositionY] - tmp1.y) + tmp2.y; | |
vertices[fo+po+2] = (particles[offset + Particle.PositionZ] - tmp1.z) + tmp2.z; | |
vertices[fo+co ] = particles[offset + Particle.Red]; | |
vertices[fo+co+1] = particles[offset + Particle.Green]; | |
vertices[fo+co+2] = particles[offset + Particle.Blue]; | |
vertices[fo+co+3] = particles[offset + Particle.Alpha]; | |
vertices[fo+uo ] = particles[offset + Particle.U0]; | |
vertices[fo+uo+1] = particles[offset + Particle.V1]; | |
fo += vertexSize; | |
// top right | |
vertices[fo+po ] = (particles[offset + Particle.PositionX] + tmp1.x) + tmp2.x; | |
vertices[fo+po+1] = (particles[offset + Particle.PositionY] + tmp1.y) + tmp2.y; | |
vertices[fo+po+2] = (particles[offset + Particle.PositionZ] + tmp1.z) + tmp2.z; | |
vertices[fo+co ] = particles[offset + Particle.Red]; | |
vertices[fo+co+1] = particles[offset + Particle.Green]; | |
vertices[fo+co+2] = particles[offset + Particle.Blue]; | |
vertices[fo+co+3] = particles[offset + Particle.Alpha]; | |
vertices[fo+uo ] = particles[offset + Particle.U1]; | |
vertices[fo+uo+1] = particles[offset + Particle.V1]; | |
fo += vertexSize; | |
// bottom right | |
vertices[fo+po ] = (particles[offset + Particle.PositionX] + tmp1.x) - tmp2.x; | |
vertices[fo+po+1] = (particles[offset + Particle.PositionY] + tmp1.y) - tmp2.y; | |
vertices[fo+po+2] = (particles[offset + Particle.PositionZ] + tmp1.z) - tmp2.z; | |
vertices[fo+co ] = particles[offset + Particle.Red]; | |
vertices[fo+co+1] = particles[offset + Particle.Green]; | |
vertices[fo+co+2] = particles[offset + Particle.Blue]; | |
vertices[fo+co+3] = particles[offset + Particle.Alpha]; | |
vertices[fo+uo ] = particles[offset + Particle.U1]; | |
vertices[fo+uo+1] = particles[offset + Particle.V0]; | |
fo += vertexSize; | |
indices[io ] = vo; | |
indices[io+1] = (short)(vo+1); | |
indices[io+2] = (short)(vo+2); | |
indices[io+3] = (short)(vo+2); | |
indices[io+4] = (short)(vo+3); | |
indices[io+5] = vo; | |
vo += 4; | |
io += 6; | |
} | |
numVertices = io; | |
mesh.setVertices(vertices, 0, fo); | |
mesh.setIndices(indices, 0, io); | |
} | |
} | |
Emitter emiter; | |
Renderable renderable; | |
Texture text; | |
@Override | |
public void create () { | |
super.create(); | |
text = new Texture(Gdx.files.internal("data/particle.png")); | |
renderable = new Renderable(); | |
renderable.primitiveType = GL10.GL_TRIANGLES; | |
renderable.meshPartOffset = 0; | |
renderable.material = new Material(ColorAttribute.createDiffuse(1, 0.5f, 0.2f, 1), new BlendingAttribute(GL10.GL_SRC_ALPHA, GL10.GL_ONE, 1f), | |
new DepthTestAttribute(0, false), TextureAttribute.createDiffuse(text)); | |
renderable.worldTransform.idt(); | |
emiter = new Emitter(1000); | |
DefaultShader.defaultCullFace = 0; | |
} | |
@Override | |
protected void onModelClicked (String name) { | |
} | |
float timeLeft; | |
@Override | |
protected void render (ModelBatch batch, Array<ModelInstance> instances) { | |
float delta = Gdx.graphics.getDeltaTime(); | |
if ((timeLeft -= delta) < 0f) { | |
timeLeft = MathUtils.random(0.001f, 0.005f); | |
emiter.emit(); | |
} | |
emiter.update(cam, delta); | |
transform.getTranslation(emiter.position); | |
emiter.position.x = emiter.position.y; | |
emiter.position.y = 0; | |
renderable.mesh = emiter.mesh; | |
renderable.meshPartSize = emiter.numVertices; | |
batch.render(renderable); | |
} | |
@Override | |
public boolean needsGL20 () { | |
return true; | |
} | |
@Override | |
public void dispose () { | |
super.dispose(); | |
text.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment