Skip to content

Instantly share code, notes, and snippets.

@xoppa
Created January 14, 2014 18:47
Show Gist options
  • Save xoppa/8423522 to your computer and use it in GitHub Desktop.
Save xoppa/8423522 to your computer and use it in GitHub Desktop.
Particle 3D test
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