package com.badlogic.gdx.graphics.g3d.particles.batches;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.GL20;
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.Renderable;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.DepthTestAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.particles.ParallelArray.FloatChannel;
import com.badlogic.gdx.graphics.g3d.particles.ParticleChannels;
import com.badlogic.gdx.graphics.g3d.particles.ParticleShader;
import com.badlogic.gdx.graphics.g3d.particles.ParticleShader.AlignMode;
import com.badlogic.gdx.graphics.g3d.particles.ResourceData;
import com.badlogic.gdx.graphics.g3d.particles.ResourceData.SaveData;
import com.badlogic.gdx.graphics.g3d.particles.renderers.BillboardControllerRenderData;
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.Matrix3;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;
/** This class is used to render billboard particles.
* @author Inferno */
public class BillboardParticleBatch extends BufferedParticleBatch<BillboardControllerRenderData> {
protected static final Vector3 TMP_V1 = new Vector3(),
TMP_V2 = new Vector3(),
TMP_V3 = new Vector3(),
TMP_V4 = new Vector3(),
TMP_V5 = new Vector3(),
TMP_V6 = new Vector3();
protected static final Matrix3 TMP_M3 = new Matrix3();
//Attributes
protected static final int sizeAndRotationUsage = 1 << 9, directionUsage = 1 << 10;
private static final VertexAttributes
GPU_ATTRIBUTES = new VertexAttributes(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"),
new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE),
new VertexAttribute(sizeAndRotationUsage, 4, "a_sizeAndRotation")),
/*
GPU_EXT_ATTRIBUTES = new VertexAttributes(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"),
new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE),
new VertexAttribute(sizeAndRotationUsage, 4, "a_sizeAndRotation"),
new VertexAttribute(directionUsage, 3, "a_direction")),
*/
CPU_ATTRIBUTES = new VertexAttributes( new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"),
new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE) );
//Offsets
private static final int GPU_POSITION_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(Usage.Position).offset/4),
GPU_UV_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset/4),
GPU_SIZE_ROTATION_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(sizeAndRotationUsage).offset/4),
GPU_COLOR_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(Usage.Color).offset/4),
GPU_VERTEX_SIZE = GPU_ATTRIBUTES.vertexSize/4,
//Ext
/*
GPU_EXT_POSITION_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(Usage.Position).offset/4),
GPU_EXT_UV_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset/4),
GPU_EXT_SIZE_ROTATION_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(sizeAndRotationUsage).offset/4),
GPU_EXT_COLOR_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(Usage.Color).offset/4),
GPU_EXT_DIRECTION_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(directionUsage).offset/4),
GPU_EXT_VERTEX_SIZE = GPU_EXT_ATTRIBUTES.vertexSize/4,
*/
//Cpu
CPU_POSITION_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.Position).offset/4),
CPU_UV_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset/4),
CPU_COLOR_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.Color).offset/4),
CPU_VERTEX_SIZE= CPU_ATTRIBUTES.vertexSize/4;
private final static int MAX_PARTICLES_PER_MESH = Short.MAX_VALUE/4,
MAX_VERTICES_PER_MESH = MAX_PARTICLES_PER_MESH*4;
private class RenderablePool extends Pool<Renderable>{
public RenderablePool () {}
@Override
public Renderable newObject () {
return allocRenderable();
}
}
public static class Config{
public Config(){}
public Config (boolean useGPU, AlignMode mode) {
this.useGPU = useGPU;
this.mode = mode;
}
boolean useGPU;
AlignMode mode;
}
private RenderablePool renderablePool;
private Array<Renderable> renderables;
private float[] vertices;
private short[] indices;
private int currentVertexSize = 0;
private VertexAttributes currentAttributes;
protected boolean useGPU = false;
protected AlignMode mode = AlignMode.Screen;
protected Texture texture;
Shader shader;
public BillboardParticleBatch(AlignMode mode, boolean useGPU, int capacity){
super(BillboardControllerRenderData.class);
renderables = new Array<Renderable>();
renderablePool = new RenderablePool();
allocIndices();
initRenderData();
ensureCapacity(capacity);
setUseGpu(useGPU);
setAlignMode(mode);
}
public BillboardParticleBatch () {
this(AlignMode.Screen, false, 100);
}
public BillboardParticleBatch (int capacity) {
this(AlignMode.Screen, false, capacity);
}
@Override
public void allocParticlesData(int capacity){
vertices = new float[currentVertexSize*4*capacity];
allocRenderables(capacity);
}
protected Renderable allocRenderable(){
Renderable renderable = new Renderable();
renderable.primitiveType = GL20.GL_TRIANGLES;
renderable.meshPartOffset = 0;
renderable.material = new Material( new BlendingAttribute(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA, 1f),
new DepthTestAttribute(GL20.GL_LEQUAL, false),
TextureAttribute.createDiffuse(texture));
renderable.mesh = new Mesh(false, MAX_VERTICES_PER_MESH, MAX_PARTICLES_PER_MESH*6, currentAttributes);
renderable.mesh.setIndices(indices);
renderable.shader = shader;
return renderable;
}
private void allocIndices(){
int indicesCount = MAX_PARTICLES_PER_MESH * 6;
indices = new short[indicesCount];
for(int i=0, vertex = 0; i < indicesCount; i+=6, vertex+=4){
indices[i] = (short)vertex;
indices[i+1] = (short)(vertex+1);
indices[i+2] = (short)(vertex+2);
indices[i+3] = (short)(vertex+2);
indices[i+4] = (short)(vertex+3);
indices[i+5] = (short)vertex;
}
}
private void allocRenderables(int capacity){
//Free old meshes
int meshCount = MathUtils.ceil( capacity/MAX_PARTICLES_PER_MESH),
free = renderablePool.getFree();
if(free < meshCount){
for(int i=0, left = meshCount - free; i < left;++i)
renderablePool.free(renderablePool.newObject());
}
}
private Shader getShader (Renderable renderable) {
Shader shader = useGPU ? new ParticleShader(renderable, new ParticleShader.Config(mode)) :
new DefaultShader(renderable);
shader.init();
return shader;
}
private void allocShader () {
Renderable newRenderable = allocRenderable();
shader = newRenderable.shader = getShader(newRenderable);
renderablePool.free(newRenderable);
}
private void clearRenderablesPool(){
renderablePool.freeAll(renderables);
for(int i=0, free = renderablePool.getFree(); i < free; ++i){
Renderable renderable = renderablePool.obtain();
renderable.mesh.dispose();
}
renderables.clear();
}
/** Sets vertex attributes and size */
public void setVertexData(){
if(useGPU){
currentAttributes = GPU_ATTRIBUTES;
currentVertexSize = GPU_VERTEX_SIZE;
/*
if(mode == AlignMode.ParticleDirection){
currentAttributes = GPU_EXT_ATTRIBUTES;
currentVertexSize = GPU_EXT_VERTEX_SIZE;
}
else{
currentAttributes = GPU_ATTRIBUTES;
currentVertexSize = GPU_VERTEX_SIZE;
}
*/
}
else {
currentAttributes = CPU_ATTRIBUTES;
currentVertexSize = CPU_VERTEX_SIZE;
}
}
/** Allocates all the require rendering resources like Renderables,Shaders,Meshes
* according to the current batch configuration.*/
private void initRenderData () {
setVertexData();
clearRenderablesPool();
allocShader();
resetCapacity();
}
/** Sets the current align mode.
* It will reallocate internal data, use only when necessary. */
public void setAlignMode(AlignMode mode){
if(mode != this.mode){
this.mode = mode;
if(useGPU){
initRenderData();
allocRenderables(bufferedParticlesCount);
}
}
}
public AlignMode getAlignMode(){
return mode;
}
/** Sets the current align mode.
* It will reallocate internal data, use only when necessary. */
public void setUseGpu(boolean useGPU){
if(this.useGPU != useGPU){
this.useGPU = useGPU;
initRenderData();
allocRenderables(bufferedParticlesCount);
}
}
public boolean isUseGPU(){
return useGPU;
}
public void setTexture(Texture texture){
renderablePool.freeAll(renderables);
renderables.clear();
for(int i=0, free = renderablePool.getFree(); i < free; ++i){
Renderable renderable = renderablePool.obtain();
TextureAttribute attribute = (TextureAttribute) renderable.material.get(TextureAttribute.Diffuse);
attribute.textureDescription.texture = texture;
}
this.texture = texture;
}
public Texture getTexture () {
return texture;
}
@Override
public void begin () {
super.begin();
renderablePool.freeAll(renderables);
renderables.clear();
}
//GPU
//Required + Color + Rotation
private static void putVertex( float[] vertices, int offset, float x, float y, float z, float u, float v, float scaleX, float scaleY, float cosRotation, float sinRotation, float r, float g, float b, float a) {
//Position
vertices[offset + GPU_POSITION_OFFSET] = x;
vertices[offset + GPU_POSITION_OFFSET+1] = y;
vertices[offset + GPU_POSITION_OFFSET+2] = z;
//UV
vertices[offset + GPU_UV_OFFSET] = u;
vertices[offset + GPU_UV_OFFSET+1] = v;
//Scale
vertices[offset + GPU_SIZE_ROTATION_OFFSET] = scaleX;
vertices[offset + GPU_SIZE_ROTATION_OFFSET+1] = scaleY;
vertices[offset + GPU_SIZE_ROTATION_OFFSET+2] = cosRotation;
vertices[offset + GPU_SIZE_ROTATION_OFFSET+3] = sinRotation;
//Color
vertices[offset + GPU_COLOR_OFFSET] = r;
vertices[offset + GPU_COLOR_OFFSET+1] = g;
vertices[offset + GPU_COLOR_OFFSET+2] = b;
vertices[offset + GPU_COLOR_OFFSET+3] = a;
}
/*
//Required + Color + Rotation + Direction
private static void putVertex( float[] vertices, int offset, float x, float y, float z, float u, float v, float scaleX, float scaleY, float cosRotation, float sinRotation, float r, float g, float b, float a, Vector3 direction) {
//Position
vertices[offset + GPU_EXT_POSITION_OFFSET] = x;
vertices[offset + GPU_EXT_POSITION_OFFSET+1] = y;
vertices[offset + GPU_EXT_POSITION_OFFSET+2] = z;
//UV
vertices[offset + GPU_EXT_UV_OFFSET] = u;
vertices[offset + GPU_EXT_UV_OFFSET+1] = v;
//Scale
vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET] = scaleX;
vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET+1] = scaleY;
vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET+2] = cosRotation;
vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET+3] = sinRotation;
//Color
vertices[offset + GPU_EXT_COLOR_OFFSET] = r;
vertices[offset + GPU_EXT_COLOR_OFFSET+1] = g;
vertices[offset + GPU_EXT_COLOR_OFFSET+2] = b;
vertices[offset + GPU_EXT_COLOR_OFFSET+3] = a;
//Direction
vertices[offset + GPU_EXT_DIRECTION_OFFSET] = direction.x;
vertices[offset + GPU_EXT_DIRECTION_OFFSET +1] = direction.y;
vertices[offset + GPU_EXT_DIRECTION_OFFSET +2] = direction.z;
}
*/
//CPU
//Required
private static void putVertex(float[] vertices, int offset, Vector3 p, float u, float v, float r, float g, float b, float a) {
//Position
vertices[offset + CPU_POSITION_OFFSET] = p.x;
vertices[offset + CPU_POSITION_OFFSET+1] = p.y;
vertices[offset + CPU_POSITION_OFFSET+2] = p.z;
//UV
vertices[offset + CPU_UV_OFFSET] = u;
vertices[offset + CPU_UV_OFFSET+1] = v;
//Color
vertices[offset + CPU_COLOR_OFFSET] = r;
vertices[offset + CPU_COLOR_OFFSET+1] = g;
vertices[offset + CPU_COLOR_OFFSET+2] = b;
vertices[offset + CPU_COLOR_OFFSET+3] = a;
}
private void fillVerticesGPU (int[] particlesOffset) {
int tp=0;
for(BillboardControllerRenderData data : renderData){
FloatChannel scaleChannel = data.scaleChannel;
FloatChannel regionChannel = data.regionChannel;
FloatChannel positionChannel = data.positionChannel;
FloatChannel colorChannel = data.colorChannel;
FloatChannel rotationChannel = data.rotationChannel;
for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
int baseOffset = particlesOffset[tp]*currentVertexSize*4;
float scale = scaleChannel.data[p* scaleChannel.strideSize];
int regionOffset = p*regionChannel.strideSize;
int positionOffset = p*positionChannel.strideSize;
int colorOffset = p*colorChannel.strideSize;
int rotationOffset = p*rotationChannel.strideSize;
float px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
float sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset] * scale;
float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
//bottom left, bottom right, top right, top left
putVertex(vertices, baseOffset, px, py, pz, u, v2, -sx, -sy, cosRotation, sinRotation, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, px, py, pz, u2, v2, sx, -sy, cosRotation, sinRotation, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, px, py, pz, u2, v, sx, sy, cosRotation, sinRotation, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, px, py, pz, u, v, -sx, sy, cosRotation, sinRotation, r, g, b, a);
baseOffset += currentVertexSize;
}
}
}
/*
private void fillVerticesToParticleDirectionGPU (int[] particlesOffset) {
int tp=0;
for(BillboardControllerRenderData data : renderData){
FloatChannel scaleChannel = data.scaleChannel;
FloatChannel regionChannel = data.regionChannel;
FloatChannel positionChannel = data.positionChannel;
FloatChannel colorChannel = data.colorChannel;
FloatChannel rotationChannel = data.rotationChannel;
for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
int baseOffset = particlesOffset[tp]*currentVertexSize*4;
float scale = scaleChannel.data[p* scaleChannel.strideSize];
int regionOffset = p*regionChannel.strideSize;
int positionOffset = p*positionChannel.strideSize;
int colorOffset = p*colorChannel.strideSize;
int rotationOffset = p*rotationChannel.strideSize;
int velocityOffset = p* velocityChannel.strideSize;
float px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
float sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset] * scale;
float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
float vx = velocityChannel.data[velocityOffset + ParticleChannels.XOffset],
vy = velocityChannel.data[velocityOffset + ParticleChannels.YOffset],
vz = velocityChannel.data[velocityOffset + ParticleChannels.ZOffset];
//bottom left, bottom right, top right, top left
TMP_V1.set(vx, vy, vz).nor();
putVertex(vertices, baseOffset, px, py, pz, u, v2, -sx, -sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, px, py, pz, u2, v2, sx, -sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, px, py, pz, u2, v, sx, sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, px, py, pz, u, v, -sx, sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
}
}
}
private void fillVerticesToParticleDirectionCPU (int[] particlesOffset) {
int tp=0;
for(ParticleController controller : renderData){
FloatChannel scaleChannel = controller.particles.getChannel(ParticleChannels.Scale);
FloatChannel regionChannel = controller.particles.getChannel(ParticleChannels.TextureRegion);
FloatChannel positionChannel = controller.particles.getChannel(ParticleChannels.Position);
FloatChannel colorChannel = controller.particles.getChannel(ParticleChannels.Color);
FloatChannel rotationChannel = controller.particles.getChannel(ParticleChannels.Rotation2D);
FloatChannel velocityChannel = controller.particles.getChannel(ParticleChannels.Accelleration);
for(int p=0, c = controller.particles.size; p < c; ++p, ++tp){
int baseOffset = particlesOffset[tp]*currentVertexSize*4;
float scale = scaleChannel.data[p* scaleChannel.strideSize];
int regionOffset = p*regionChannel.strideSize;
int positionOffset = p*positionChannel.strideSize;
int colorOffset = p*colorChannel.strideSize;
int rotationOffset = p*rotationChannel.strideSize;
int velocityOffset = p* velocityChannel.strideSize;
float px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
float sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset] * scale;
float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
float vx = velocityChannel.data[velocityOffset + ParticleChannels.XOffset],
vy = velocityChannel.data[velocityOffset + ParticleChannels.YOffset],
vz = velocityChannel.data[velocityOffset + ParticleChannels.ZOffset];
Vector3 up = TMP_V1.set(vx,vy,vz).nor(),
look = TMP_V3.set(camera.position).sub(px,py,pz).nor(), //normal
right = TMP_V2.set(up).crs(look).nor(); //tangent
look.set(right).crs(up).nor();
right.scl(sx);
up.scl(sy);
if(cosRotation != 1){
TMP_M3.setToRotation(look, cosRotation, sinRotation);
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x-TMP_V2.x, -TMP_V1.y-TMP_V2.y, -TMP_V1.z-TMP_V2.z).mul(TMP_M3).add(px, py, pz), u, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset,TMP_V6.set(TMP_V1.x-TMP_V2.x, TMP_V1.y-TMP_V2.y, TMP_V1.z-TMP_V2.z).mul(TMP_M3).add(px, py, pz), u2, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset,TMP_V6.set(TMP_V1.x+TMP_V2.x, TMP_V1.y+TMP_V2.y, TMP_V1.z+TMP_V2.z).mul(TMP_M3).add(px, py, pz), u2, v, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x+TMP_V2.x, -TMP_V1.y+TMP_V2.y, -TMP_V1.z+TMP_V2.z).mul(TMP_M3).add(px, py, pz), u, v, r, g, b, a);
}
else {
putVertex(vertices, baseOffset,TMP_V6.set(-TMP_V1.x-TMP_V2.x+px, -TMP_V1.y-TMP_V2.y+py, -TMP_V1.z-TMP_V2.z+pz), u, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset,TMP_V6.set(TMP_V1.x-TMP_V2.x+px, TMP_V1.y-TMP_V2.y+py, TMP_V1.z-TMP_V2.z+pz), u2, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset,TMP_V6.set(TMP_V1.x+TMP_V2.x+px, TMP_V1.y+TMP_V2.y+py, TMP_V1.z+TMP_V2.z+pz), u2, v, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x+TMP_V2.x+px, -TMP_V1.y+TMP_V2.y+py, -TMP_V1.z+TMP_V2.z+pz), u, v, r, g, b, a);
}
}
}
}
*/
private void fillVerticesToViewPointCPU (int[] particlesOffset) {
int tp=0;
for(BillboardControllerRenderData data : renderData){
FloatChannel scaleChannel = data.scaleChannel;
FloatChannel regionChannel = data.regionChannel;
FloatChannel positionChannel = data.positionChannel;
FloatChannel colorChannel = data.colorChannel;
FloatChannel rotationChannel = data.rotationChannel;
for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
int baseOffset = particlesOffset[tp]*currentVertexSize*4;
float scale = scaleChannel.data[p* scaleChannel.strideSize];
int regionOffset = p*regionChannel.strideSize;
int positionOffset = p*positionChannel.strideSize;
int colorOffset = p*colorChannel.strideSize;
int rotationOffset = p*rotationChannel.strideSize;
float px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
float sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset] * scale;
float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
Vector3 look = TMP_V3.set(camera.position).sub(px, py, pz).nor(), //normal
right = TMP_V1.set(camera.up).crs(look).nor(), //tangent
up = TMP_V2.set(look).crs(right);
right.scl(sx);
up.scl(sy);
if(cosRotation != 1){
TMP_M3.setToRotation(look, cosRotation, sinRotation);
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x-TMP_V2.x, -TMP_V1.y-TMP_V2.y, -TMP_V1.z-TMP_V2.z).mul(TMP_M3).add(px, py, pz), u, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x-TMP_V2.x, TMP_V1.y-TMP_V2.y, TMP_V1.z-TMP_V2.z).mul(TMP_M3).add(px, py, pz), u2, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x+TMP_V2.x, TMP_V1.y+TMP_V2.y, TMP_V1.z+TMP_V2.z).mul(TMP_M3).add(px, py, pz), u2, v, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x+TMP_V2.x, -TMP_V1.y+TMP_V2.y, -TMP_V1.z+TMP_V2.z).mul(TMP_M3).add(px, py, pz), u, v, r, g, b, a);
}
else {
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x-TMP_V2.x+px, -TMP_V1.y-TMP_V2.y+py, -TMP_V1.z-TMP_V2.z+pz), u, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x-TMP_V2.x+px, TMP_V1.y-TMP_V2.y+py, TMP_V1.z-TMP_V2.z+pz), u2, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x+TMP_V2.x+px, TMP_V1.y+TMP_V2.y+py, TMP_V1.z+TMP_V2.z+pz), u2, v, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x+TMP_V2.x+px, -TMP_V1.y+TMP_V2.y+py, -TMP_V1.z+TMP_V2.z+pz), u, v, r, g, b, a);
}
}
}
}
private void fillVerticesToScreenCPU (int[] particlesOffset) {
Vector3 look = TMP_V3.set(camera.direction).scl(-1), //normal
right = TMP_V4.set(camera.up).crs(look).nor(), //tangent
up = camera.up;
int tp=0;
for(BillboardControllerRenderData data : renderData){
FloatChannel scaleChannel = data.scaleChannel;
FloatChannel regionChannel = data.regionChannel;
FloatChannel positionChannel = data.positionChannel;
FloatChannel colorChannel = data.colorChannel;
FloatChannel rotationChannel = data.rotationChannel;
for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
int baseOffset = particlesOffset[tp]*currentVertexSize*4;
float scale = scaleChannel.data[p* scaleChannel.strideSize];
int regionOffset = p*regionChannel.strideSize;
int positionOffset = p*positionChannel.strideSize;
int colorOffset = p*colorChannel.strideSize;
int rotationOffset = p*rotationChannel.strideSize;
float px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
float sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset] * scale;
float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
TMP_V1.set(right).scl(sx);
TMP_V2.set(up).scl(sy);
if(cosRotation != 1){
TMP_M3.setToRotation(look, cosRotation, sinRotation);
putVertex( vertices, baseOffset, TMP_V6.set(-TMP_V1.x-TMP_V2.x, -TMP_V1.y-TMP_V2.y, -TMP_V1.z-TMP_V2.z).mul(TMP_M3).add(px, py, pz), u, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x-TMP_V2.x, TMP_V1.y-TMP_V2.y, TMP_V1.z-TMP_V2.z).mul(TMP_M3).add(px, py, pz), u2, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x+TMP_V2.x, TMP_V1.y+TMP_V2.y, TMP_V1.z+TMP_V2.z).mul(TMP_M3).add(px, py, pz), u2, v, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x+TMP_V2.x, -TMP_V1.y+TMP_V2.y, -TMP_V1.z+TMP_V2.z).mul(TMP_M3).add(px, py, pz), u, v, r, g, b, a);
}
else {
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x-TMP_V2.x+px, -TMP_V1.y-TMP_V2.y+py, -TMP_V1.z-TMP_V2.z+pz), u, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x-TMP_V2.x+px, TMP_V1.y-TMP_V2.y+py, TMP_V1.z-TMP_V2.z+pz), u2, v2, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(TMP_V1.x+TMP_V2.x+px, TMP_V1.y+TMP_V2.y+py, TMP_V1.z+TMP_V2.z+pz), u2, v, r, g, b, a);
baseOffset += currentVertexSize;
putVertex(vertices, baseOffset, TMP_V6.set(-TMP_V1.x+TMP_V2.x+px, -TMP_V1.y+TMP_V2.y+py, -TMP_V1.z+TMP_V2.z+pz), u, v, r, g, b, a);
}
}
}
}
@Override
protected void flush(int[] offsets){
//fill vertices
if(useGPU){
//if(mode != AlignMode.ParticleDirection)
fillVerticesGPU(offsets);
//else
//fillVerticesToParticleDirectionGPU(offsets);
}
else {
if(mode == AlignMode.Screen)
fillVerticesToScreenCPU(offsets);
else if(mode == AlignMode.ViewPoint)
fillVerticesToViewPointCPU(offsets);
//else
//fillVerticesToParticleDirectionCPU(offsets);
}
//send vertices to meshes
int addedVertexCount = 0;
int vCount = bufferedParticlesCount*4;
for(int v = 0; v < vCount; v += addedVertexCount){
addedVertexCount = Math.min(vCount-v, MAX_VERTICES_PER_MESH);
Renderable renderable = renderablePool.obtain();
renderable.meshPartSize = (addedVertexCount/4)*6;
renderable.mesh.setVertices(vertices, currentVertexSize *v, currentVertexSize * addedVertexCount);
renderables.add(renderable);
}
}
@Override
public void getRenderables (Array<Renderable> renderables, Pool<Renderable> pool) {
for(Renderable renderable : this.renderables)
renderables.add(pool.obtain().set(renderable));
}
@Override
public void save (AssetManager manager, ResourceData resources) {
SaveData data = resources.createSaveData("billboardBatch");
data.save("cfg", new Config(useGPU, mode));
data.saveAsset(manager.getAssetFileName(texture), Texture.class);
}
@Override
public void load (AssetManager manager, ResourceData resources) {
SaveData data = resources.getSaveData("billboardBatch");
if(data != null){
setTexture((Texture)manager.get(data.loadAsset()));
Config cfg = (Config)data.load("cfg");
setUseGpu(cfg.useGPU);
setAlignMode(cfg.mode);
}
}
}