Home | History | Annotate | Download | only in batches
      1 package com.badlogic.gdx.graphics.g3d.particles.batches;
      2 
      3 import com.badlogic.gdx.assets.AssetManager;
      4 import com.badlogic.gdx.graphics.GL20;
      5 import com.badlogic.gdx.graphics.Mesh;
      6 import com.badlogic.gdx.graphics.Texture;
      7 import com.badlogic.gdx.graphics.VertexAttribute;
      8 import com.badlogic.gdx.graphics.VertexAttributes;
      9 import com.badlogic.gdx.graphics.VertexAttributes.Usage;
     10 import com.badlogic.gdx.graphics.g3d.Material;
     11 import com.badlogic.gdx.graphics.g3d.Renderable;
     12 import com.badlogic.gdx.graphics.g3d.Shader;
     13 import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
     14 import com.badlogic.gdx.graphics.g3d.attributes.DepthTestAttribute;
     15 import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
     16 import com.badlogic.gdx.graphics.g3d.particles.ParallelArray.FloatChannel;
     17 import com.badlogic.gdx.graphics.g3d.particles.ParticleChannels;
     18 import com.badlogic.gdx.graphics.g3d.particles.ParticleShader;
     19 import com.badlogic.gdx.graphics.g3d.particles.ParticleShader.AlignMode;
     20 import com.badlogic.gdx.graphics.g3d.particles.ResourceData;
     21 import com.badlogic.gdx.graphics.g3d.particles.ResourceData.SaveData;
     22 import com.badlogic.gdx.graphics.g3d.particles.renderers.BillboardControllerRenderData;
     23 import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader;
     24 import com.badlogic.gdx.graphics.glutils.ShaderProgram;
     25 import com.badlogic.gdx.math.MathUtils;
     26 import com.badlogic.gdx.math.Matrix3;
     27 import com.badlogic.gdx.math.Vector3;
     28 import com.badlogic.gdx.utils.Array;
     29 import com.badlogic.gdx.utils.Pool;
     30 
     31 
     32 /** This class is used to render billboard particles.
     33  * @author Inferno */
     34 public class BillboardParticleBatch extends BufferedParticleBatch<BillboardControllerRenderData> {
     35 	protected static final Vector3 TMP_V1 = new Vector3(),
     36 		TMP_V2 = new Vector3(),
     37 		TMP_V3 = new Vector3(),
     38 		TMP_V4 = new Vector3(),
     39 		TMP_V5 = new Vector3(),
     40 		TMP_V6 = new Vector3();
     41 	protected static final Matrix3 TMP_M3 = new Matrix3();
     42 	//Attributes
     43 	protected static final int sizeAndRotationUsage = 1 << 9, directionUsage = 1 << 10;
     44 	private static final VertexAttributes
     45 	GPU_ATTRIBUTES = new VertexAttributes(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
     46 		new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"),
     47 		new VertexAttribute(Usage.ColorUnpacked, 4, ShaderProgram.COLOR_ATTRIBUTE),
     48 		new VertexAttribute(sizeAndRotationUsage, 4, "a_sizeAndRotation")),
     49 		/*
     50 		GPU_EXT_ATTRIBUTES = new VertexAttributes(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
     51 									new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"),
     52 									new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE),
     53 									new VertexAttribute(sizeAndRotationUsage, 4, "a_sizeAndRotation"),
     54 									new VertexAttribute(directionUsage, 3, "a_direction")),
     55 		 */
     56 		CPU_ATTRIBUTES = new VertexAttributes(	new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE),
     57 			new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"),
     58 			new VertexAttribute(Usage.ColorUnpacked, 4, ShaderProgram.COLOR_ATTRIBUTE) );
     59 
     60 	//Offsets
     61 	private static final int 	GPU_POSITION_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(Usage.Position).offset/4),
     62 		GPU_UV_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset/4),
     63 		GPU_SIZE_ROTATION_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(sizeAndRotationUsage).offset/4),
     64 		GPU_COLOR_OFFSET = (short)(GPU_ATTRIBUTES.findByUsage(Usage.ColorUnpacked).offset/4),
     65 		GPU_VERTEX_SIZE = GPU_ATTRIBUTES.vertexSize/4,
     66 
     67 		//Ext
     68 		/*
     69 										GPU_EXT_POSITION_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(Usage.Position).offset/4),
     70 										GPU_EXT_UV_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset/4),
     71 										GPU_EXT_SIZE_ROTATION_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(sizeAndRotationUsage).offset/4),
     72 										GPU_EXT_COLOR_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(Usage.Color).offset/4),
     73 										GPU_EXT_DIRECTION_OFFSET = (short)(GPU_EXT_ATTRIBUTES.findByUsage(directionUsage).offset/4),
     74 										GPU_EXT_VERTEX_SIZE = GPU_EXT_ATTRIBUTES.vertexSize/4,
     75 		 */
     76 
     77 		//Cpu
     78 		CPU_POSITION_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.Position).offset/4),
     79 		CPU_UV_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset/4),
     80 		CPU_COLOR_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.ColorUnpacked).offset/4),
     81 		CPU_VERTEX_SIZE= CPU_ATTRIBUTES.vertexSize/4;
     82 	private final static int 	MAX_PARTICLES_PER_MESH = Short.MAX_VALUE/4,
     83 		MAX_VERTICES_PER_MESH = MAX_PARTICLES_PER_MESH*4;
     84 
     85 	private class RenderablePool extends Pool<Renderable>{
     86 		public RenderablePool () {}
     87 
     88 		@Override
     89 		public Renderable newObject () {
     90 			return allocRenderable();
     91 		}
     92 	}
     93 
     94 	public static class Config{
     95 		public Config(){}
     96 		public Config (boolean useGPU, AlignMode mode) {
     97 			this.useGPU = useGPU;
     98 			this.mode = mode;
     99 		}
    100 		boolean useGPU;
    101 		AlignMode mode;
    102 	}
    103 
    104 	private RenderablePool renderablePool;
    105 	private Array<Renderable> renderables;
    106 	private float[] vertices;
    107 	private short[] indices;
    108 	private int currentVertexSize = 0;
    109 	private VertexAttributes currentAttributes;
    110 	protected boolean useGPU = false;
    111 	protected AlignMode mode = AlignMode.Screen;
    112 	protected Texture texture;
    113 	protected BlendingAttribute blendingAttribute;
    114 	protected DepthTestAttribute depthTestAttribute;
    115 	Shader shader;
    116 
    117 	/**
    118 	 * Create a new BillboardParticleBatch
    119 	 * @param mode
    120 	 * @param useGPU Allow to use GPU instead of CPU
    121 	 * @param capacity Max particle displayed
    122 	 * @param blendingAttribute Blending attribute used by the batch
    123 	 * @param depthTestAttribute DepthTest attribute used by the batch
    124 	 */
    125 	public BillboardParticleBatch(AlignMode mode, boolean useGPU, int capacity,
    126 		BlendingAttribute blendingAttribute,
    127 		DepthTestAttribute depthTestAttribute) {
    128 		super(BillboardControllerRenderData.class);
    129 		renderables = new Array<Renderable>();
    130 		renderablePool = new RenderablePool();
    131 		this.blendingAttribute = blendingAttribute;
    132 		this.depthTestAttribute = depthTestAttribute;
    133 
    134 		if( this.blendingAttribute == null )
    135 			this.blendingAttribute = new BlendingAttribute(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA, 1f);
    136 		if( this.depthTestAttribute == null )
    137 			this.depthTestAttribute = new DepthTestAttribute(GL20.GL_LEQUAL, false);
    138 
    139 		allocIndices();
    140 		initRenderData();
    141 		ensureCapacity(capacity);
    142 		setUseGpu(useGPU);
    143 		setAlignMode(mode);
    144 	}
    145 
    146 	public BillboardParticleBatch(AlignMode mode, boolean useGPU, int capacity){
    147 		this(mode, useGPU, capacity, null, null);
    148 	}
    149 
    150 	public BillboardParticleBatch () {
    151 		this(AlignMode.Screen, false, 100);
    152 	}
    153 
    154 	public BillboardParticleBatch (int capacity) {
    155 		this(AlignMode.Screen, false, capacity);
    156 	}
    157 
    158 	@Override
    159 	public void allocParticlesData(int capacity){
    160 		vertices = new float[currentVertexSize*4*capacity];
    161 		allocRenderables(capacity);
    162 	}
    163 
    164 	protected Renderable allocRenderable(){
    165 		Renderable renderable = new Renderable();
    166 		renderable.meshPart.primitiveType = GL20.GL_TRIANGLES;
    167 		renderable.meshPart.offset = 0;
    168 		renderable.material = new Material(this.blendingAttribute, this.depthTestAttribute,
    169 			TextureAttribute.createDiffuse(texture));
    170 		renderable.meshPart.mesh = new Mesh(false, MAX_VERTICES_PER_MESH, MAX_PARTICLES_PER_MESH*6, currentAttributes);
    171 		renderable.meshPart.mesh.setIndices(indices);
    172 		renderable.shader = shader;
    173 		return renderable;
    174 	}
    175 
    176 	private void allocIndices(){
    177 		int indicesCount = MAX_PARTICLES_PER_MESH * 6;
    178 		indices = new short[indicesCount];
    179 		for(int i=0, vertex = 0; i < indicesCount; i+=6, vertex+=4){
    180 			indices[i] = (short)vertex;
    181 			indices[i+1] = (short)(vertex+1);
    182 			indices[i+2] = (short)(vertex+2);
    183 			indices[i+3] = (short)(vertex+2);
    184 			indices[i+4] = (short)(vertex+3);
    185 			indices[i+5] = (short)vertex;
    186 		}
    187 	}
    188 
    189 	private void allocRenderables(int capacity){
    190 		//Free old meshes
    191 		int 	meshCount = MathUtils.ceil( capacity/MAX_PARTICLES_PER_MESH),
    192 			free = renderablePool.getFree();
    193 		if(free < meshCount){
    194 			for(int i=0, left = meshCount - free; i < left;++i)
    195 				renderablePool.free(renderablePool.newObject());
    196 		}
    197 	}
    198 
    199 	private Shader getShader (Renderable renderable) {
    200 		Shader shader = useGPU 	? 	new ParticleShader(renderable, new ParticleShader.Config(mode)) :
    201 			new DefaultShader(renderable);
    202 		shader.init();
    203 		return shader;
    204 	}
    205 
    206 	private void allocShader () {
    207 		Renderable newRenderable = allocRenderable();
    208 		shader = newRenderable.shader = getShader(newRenderable);
    209 		renderablePool.free(newRenderable);
    210 	}
    211 
    212 	private void clearRenderablesPool(){
    213 		renderablePool.freeAll(renderables);
    214 		for(int i=0, free = renderablePool.getFree(); i < free; ++i){
    215 			Renderable renderable = renderablePool.obtain();
    216 			renderable.meshPart.mesh.dispose();
    217 		}
    218 		renderables.clear();
    219 	}
    220 
    221 	/** Sets vertex attributes and size */
    222 	public void setVertexData(){
    223 		if(useGPU){
    224 			currentAttributes = GPU_ATTRIBUTES;
    225 			currentVertexSize = GPU_VERTEX_SIZE;
    226 			/*
    227 			if(mode == AlignMode.ParticleDirection){
    228 				currentAttributes = GPU_EXT_ATTRIBUTES;
    229 				currentVertexSize = GPU_EXT_VERTEX_SIZE;
    230 			}
    231 			else{
    232 				currentAttributes = GPU_ATTRIBUTES;
    233 				currentVertexSize = GPU_VERTEX_SIZE;
    234 			}
    235 			 */
    236 		}
    237 		else {
    238 			currentAttributes = CPU_ATTRIBUTES;
    239 			currentVertexSize = CPU_VERTEX_SIZE;
    240 		}
    241 	}
    242 
    243 	/** Allocates all the require rendering resources like Renderables,Shaders,Meshes
    244 	 *  according to the current batch configuration.*/
    245 	private void initRenderData () {
    246 		setVertexData();
    247 		clearRenderablesPool();
    248 		allocShader();
    249 		resetCapacity();
    250 	}
    251 
    252 	/** Sets the current align mode.
    253 	 *  It will reallocate internal data, use only when necessary. */
    254 	public void setAlignMode(AlignMode mode){
    255 		if(mode != this.mode){
    256 			this.mode = mode;
    257 			if(useGPU){
    258 				initRenderData();
    259 				allocRenderables(bufferedParticlesCount);
    260 			}
    261 		}
    262 	}
    263 
    264 	public AlignMode getAlignMode(){
    265 		return mode;
    266 	}
    267 
    268 	/** Sets the current align mode.
    269 	 *  It will reallocate internal data, use only when necessary. */
    270 	public void setUseGpu(boolean useGPU){
    271 		if(this.useGPU != useGPU){
    272 			this.useGPU = useGPU;
    273 			initRenderData();
    274 			allocRenderables(bufferedParticlesCount);
    275 		}
    276 	}
    277 
    278 	public boolean isUseGPU(){
    279 		return useGPU;
    280 	}
    281 
    282 	public void setTexture(Texture texture){
    283 		renderablePool.freeAll(renderables);
    284 		renderables.clear();
    285 		for(int i=0, free = renderablePool.getFree(); i < free; ++i){
    286 			Renderable renderable = renderablePool.obtain();
    287 			TextureAttribute attribute = (TextureAttribute) renderable.material.get(TextureAttribute.Diffuse);
    288 			attribute.textureDescription.texture = texture;
    289 		}
    290 		this.texture = texture;
    291 	}
    292 
    293 	public Texture getTexture () {
    294 		return texture;
    295 	}
    296 
    297 	@Override
    298 	public void begin () {
    299 		super.begin();
    300 		renderablePool.freeAll(renderables);
    301 		renderables.clear();
    302 	}
    303 
    304 	//GPU
    305 	//Required + Color + Rotation
    306 	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) {
    307 		//Position
    308 		vertices[offset + GPU_POSITION_OFFSET] = x;
    309 		vertices[offset + GPU_POSITION_OFFSET+1] = y;
    310 		vertices[offset + GPU_POSITION_OFFSET+2] = z;
    311 		//UV
    312 		vertices[offset + GPU_UV_OFFSET] = u;
    313 		vertices[offset + GPU_UV_OFFSET+1] = v;
    314 		//Scale
    315 		vertices[offset + GPU_SIZE_ROTATION_OFFSET] = scaleX;
    316 		vertices[offset + GPU_SIZE_ROTATION_OFFSET+1] = scaleY;
    317 		vertices[offset + GPU_SIZE_ROTATION_OFFSET+2] = cosRotation;
    318 		vertices[offset + GPU_SIZE_ROTATION_OFFSET+3] = sinRotation;
    319 		//Color
    320 		vertices[offset + GPU_COLOR_OFFSET] = r;
    321 		vertices[offset + GPU_COLOR_OFFSET+1] = g;
    322 		vertices[offset + GPU_COLOR_OFFSET+2] = b;
    323 		vertices[offset + GPU_COLOR_OFFSET+3] = a;
    324 	}
    325 
    326 	/*
    327 	//Required + Color + Rotation + Direction
    328 	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) {
    329 		//Position
    330 		vertices[offset + GPU_EXT_POSITION_OFFSET] = x;
    331 		vertices[offset + GPU_EXT_POSITION_OFFSET+1] = y;
    332 		vertices[offset + GPU_EXT_POSITION_OFFSET+2] = z;
    333 		//UV
    334 		vertices[offset + GPU_EXT_UV_OFFSET] = u;
    335 		vertices[offset + GPU_EXT_UV_OFFSET+1] = v;
    336 		//Scale
    337 		vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET] = scaleX;
    338 		vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET+1] = scaleY;
    339 		vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET+2] = cosRotation;
    340 		vertices[offset + GPU_EXT_SIZE_ROTATION_OFFSET+3] = sinRotation;
    341 		//Color
    342 		vertices[offset + GPU_EXT_COLOR_OFFSET] = r;
    343 		vertices[offset + GPU_EXT_COLOR_OFFSET+1] = g;
    344 		vertices[offset + GPU_EXT_COLOR_OFFSET+2] = b;
    345 		vertices[offset + GPU_EXT_COLOR_OFFSET+3] = a;
    346 		//Direction
    347 		vertices[offset + GPU_EXT_DIRECTION_OFFSET] = direction.x;
    348 		vertices[offset + GPU_EXT_DIRECTION_OFFSET +1] = direction.y;
    349 		vertices[offset + GPU_EXT_DIRECTION_OFFSET +2] = direction.z;
    350 	}
    351 	 */
    352 
    353 
    354 	//CPU
    355 	//Required
    356 	private static void putVertex(float[] vertices, int offset, Vector3 p, float u, float v, float r, float g, float b, float a) {
    357 		//Position
    358 		vertices[offset + CPU_POSITION_OFFSET] = p.x;
    359 		vertices[offset + CPU_POSITION_OFFSET+1] = p.y;
    360 		vertices[offset + CPU_POSITION_OFFSET+2] = p.z;
    361 		//UV
    362 		vertices[offset + CPU_UV_OFFSET] = u;
    363 		vertices[offset + CPU_UV_OFFSET+1] = v;
    364 		//Color
    365 		vertices[offset + CPU_COLOR_OFFSET] = r;
    366 		vertices[offset + CPU_COLOR_OFFSET+1] = g;
    367 		vertices[offset + CPU_COLOR_OFFSET+2] = b;
    368 		vertices[offset + CPU_COLOR_OFFSET+3] = a;
    369 	}
    370 
    371 	private void fillVerticesGPU (int[] particlesOffset) {
    372 		int tp=0;
    373 		for(BillboardControllerRenderData data : renderData){
    374 			FloatChannel scaleChannel = data.scaleChannel;
    375 			FloatChannel regionChannel = data.regionChannel;
    376 			FloatChannel positionChannel = data.positionChannel;
    377 			FloatChannel colorChannel = data.colorChannel;
    378 			FloatChannel rotationChannel = data.rotationChannel;
    379 			for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
    380 				int baseOffset = particlesOffset[tp]*currentVertexSize*4;
    381 				float scale = scaleChannel.data[p* scaleChannel.strideSize];
    382 				int regionOffset = p*regionChannel.strideSize;
    383 				int positionOffset = p*positionChannel.strideSize;
    384 				int colorOffset = p*colorChannel.strideSize;
    385 				int rotationOffset = p*rotationChannel.strideSize;
    386 				float 	px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
    387 					py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
    388 					pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
    389 				float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
    390 				float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
    391 				float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
    392 				float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
    393 				float 	sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
    394 					sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset]  * scale;
    395 				float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
    396 				float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
    397 				float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
    398 				float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
    399 				float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
    400 				float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
    401 
    402 				//bottom left, bottom right, top right, top left
    403 				putVertex(vertices, baseOffset, px, py, pz, u, v2, -sx, -sy, cosRotation, sinRotation, r, g, b, a);
    404 				baseOffset += currentVertexSize;
    405 				putVertex(vertices, baseOffset, px, py, pz, u2, v2, sx, -sy, cosRotation, sinRotation, r, g, b, a);
    406 				baseOffset += currentVertexSize;
    407 				putVertex(vertices, baseOffset, px, py, pz, u2, v, sx, sy, cosRotation, sinRotation, r, g, b, a);
    408 				baseOffset += currentVertexSize;
    409 				putVertex(vertices, baseOffset, px, py, pz, u, v, -sx, sy, cosRotation, sinRotation, r, g, b, a);
    410 				baseOffset += currentVertexSize;
    411 			}
    412 		}
    413 	}
    414 
    415 	/*
    416 	private void fillVerticesToParticleDirectionGPU (int[] particlesOffset) {
    417 		int tp=0;
    418 		for(BillboardControllerRenderData data : renderData){
    419 			FloatChannel scaleChannel = data.scaleChannel;
    420 			FloatChannel regionChannel = data.regionChannel;
    421 			FloatChannel positionChannel = data.positionChannel;
    422 			FloatChannel colorChannel = data.colorChannel;
    423 			FloatChannel rotationChannel = data.rotationChannel;
    424 
    425 			for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
    426 				int baseOffset = particlesOffset[tp]*currentVertexSize*4;
    427 				float scale = scaleChannel.data[p* scaleChannel.strideSize];
    428 				int regionOffset = p*regionChannel.strideSize;
    429 				int positionOffset = p*positionChannel.strideSize;
    430 				int colorOffset = p*colorChannel.strideSize;
    431 				int rotationOffset = p*rotationChannel.strideSize;
    432 				int velocityOffset = p* velocityChannel.strideSize;
    433 				float 	px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
    434 					py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
    435 					pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
    436 				float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
    437 				float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
    438 				float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
    439 				float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
    440 				float 	sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
    441 							sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset]  * scale;
    442 				float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
    443 				float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
    444 				float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
    445 				float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
    446 				float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
    447 				float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
    448 				float 	vx = velocityChannel.data[velocityOffset + ParticleChannels.XOffset],
    449 							vy = velocityChannel.data[velocityOffset + ParticleChannels.YOffset],
    450 							vz = velocityChannel.data[velocityOffset + ParticleChannels.ZOffset];
    451 
    452 				//bottom left, bottom right, top right, top left
    453 				TMP_V1.set(vx, vy, vz).nor();
    454 				putVertex(vertices, baseOffset, px, py, pz, u, v2, -sx, -sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
    455 				baseOffset += currentVertexSize;
    456 				putVertex(vertices, baseOffset, px, py, pz, u2, v2, sx, -sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
    457 				baseOffset += currentVertexSize;
    458 				putVertex(vertices, baseOffset, px, py, pz, u2, v, sx, sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
    459 				baseOffset += currentVertexSize;
    460 				putVertex(vertices, baseOffset, px, py, pz, u, v, -sx, sy, cosRotation, sinRotation, r, g, b, a, TMP_V1);
    461 			}
    462 		}
    463 	}
    464 
    465 	private void fillVerticesToParticleDirectionCPU (int[] particlesOffset) {
    466 		int tp=0;
    467 		for(ParticleController controller : renderData){
    468 			FloatChannel scaleChannel = controller.particles.getChannel(ParticleChannels.Scale);
    469 			FloatChannel regionChannel = controller.particles.getChannel(ParticleChannels.TextureRegion);
    470 			FloatChannel positionChannel = controller.particles.getChannel(ParticleChannels.Position);
    471 			FloatChannel colorChannel = controller.particles.getChannel(ParticleChannels.Color);
    472 			FloatChannel rotationChannel = controller.particles.getChannel(ParticleChannels.Rotation2D);
    473 			FloatChannel velocityChannel = controller.particles.getChannel(ParticleChannels.Accelleration);
    474 
    475 			for(int p=0, c = controller.particles.size; p < c; ++p, ++tp){
    476 				int baseOffset = particlesOffset[tp]*currentVertexSize*4;
    477 				float scale = scaleChannel.data[p* scaleChannel.strideSize];
    478 				int regionOffset = p*regionChannel.strideSize;
    479 				int positionOffset = p*positionChannel.strideSize;
    480 				int colorOffset = p*colorChannel.strideSize;
    481 				int rotationOffset = p*rotationChannel.strideSize;
    482 				int velocityOffset = p* velocityChannel.strideSize;
    483 				float 	px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
    484 					py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
    485 					pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
    486 				float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
    487 				float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
    488 				float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
    489 				float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
    490 				float 	sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
    491 							sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset]  * scale;
    492 				float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
    493 				float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
    494 				float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
    495 				float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
    496 				float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
    497 				float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
    498 				float 	vx = velocityChannel.data[velocityOffset + ParticleChannels.XOffset],
    499 							vy = velocityChannel.data[velocityOffset + ParticleChannels.YOffset],
    500 							vz = velocityChannel.data[velocityOffset + ParticleChannels.ZOffset];
    501 				Vector3 up = TMP_V1.set(vx,vy,vz).nor(),
    502 								look = TMP_V3.set(camera.position).sub(px,py,pz).nor(), //normal
    503 								right = TMP_V2.set(up).crs(look).nor(); //tangent
    504 				look.set(right).crs(up).nor();
    505 				right.scl(sx);
    506 				up.scl(sy);
    507 
    508 				if(cosRotation != 1){
    509 					TMP_M3.setToRotation(look, cosRotation, sinRotation);
    510 					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);
    511 					baseOffset += currentVertexSize;
    512 					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);
    513 					baseOffset += currentVertexSize;
    514 					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);
    515 					baseOffset += currentVertexSize;
    516 					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);
    517 				}
    518 				else {
    519 					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);
    520 					baseOffset += currentVertexSize;
    521 					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);
    522 					baseOffset += currentVertexSize;
    523 					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);
    524 					baseOffset += currentVertexSize;
    525 					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);
    526 				}
    527 			}
    528 		}
    529 	}
    530 	 */
    531 
    532 	private void fillVerticesToViewPointCPU (int[] particlesOffset) {
    533 		int tp=0;
    534 		for(BillboardControllerRenderData data : renderData){
    535 			FloatChannel scaleChannel = data.scaleChannel;
    536 			FloatChannel regionChannel = data.regionChannel;
    537 			FloatChannel positionChannel = data.positionChannel;
    538 			FloatChannel colorChannel = data.colorChannel;
    539 			FloatChannel rotationChannel = data.rotationChannel;
    540 
    541 			for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
    542 				int baseOffset = particlesOffset[tp]*currentVertexSize*4;
    543 				float scale = scaleChannel.data[p* scaleChannel.strideSize];
    544 				int regionOffset = p*regionChannel.strideSize;
    545 				int positionOffset = p*positionChannel.strideSize;
    546 				int colorOffset = p*colorChannel.strideSize;
    547 				int rotationOffset = p*rotationChannel.strideSize;
    548 				float 	px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
    549 					py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
    550 					pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
    551 				float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
    552 				float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
    553 				float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
    554 				float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
    555 				float 	sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
    556 					sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset]  * scale;
    557 				float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
    558 				float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
    559 				float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
    560 				float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
    561 				float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
    562 				float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
    563 				Vector3 look = TMP_V3.set(camera.position).sub(px, py, pz).nor(), //normal
    564 					right = TMP_V1.set(camera.up).crs(look).nor(), //tangent
    565 					up = TMP_V2.set(look).crs(right);
    566 				right.scl(sx);
    567 				up.scl(sy);
    568 
    569 				if(cosRotation != 1){
    570 					TMP_M3.setToRotation(look, cosRotation, sinRotation);
    571 					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);
    572 					baseOffset += currentVertexSize;
    573 					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);
    574 					baseOffset += currentVertexSize;
    575 					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);
    576 					baseOffset += currentVertexSize;
    577 					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);
    578 				}
    579 				else {
    580 					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);
    581 					baseOffset += currentVertexSize;
    582 					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);
    583 					baseOffset += currentVertexSize;
    584 					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);
    585 					baseOffset += currentVertexSize;
    586 					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);
    587 				}
    588 			}
    589 		}
    590 	}
    591 
    592 	private void fillVerticesToScreenCPU (int[] particlesOffset) {
    593 		Vector3 look = TMP_V3.set(camera.direction).scl(-1),  //normal
    594 			right = TMP_V4.set(camera.up).crs(look).nor(), //tangent
    595 			up = camera.up;
    596 
    597 		int tp=0;
    598 		for(BillboardControllerRenderData data : renderData){
    599 			FloatChannel scaleChannel = data.scaleChannel;
    600 			FloatChannel regionChannel = data.regionChannel;
    601 			FloatChannel positionChannel = data.positionChannel;
    602 			FloatChannel colorChannel = data.colorChannel;
    603 			FloatChannel rotationChannel = data.rotationChannel;
    604 
    605 			for(int p=0, c = data.controller.particles.size; p < c; ++p, ++tp){
    606 				int baseOffset = particlesOffset[tp]*currentVertexSize*4;
    607 				float scale = scaleChannel.data[p* scaleChannel.strideSize];
    608 				int regionOffset = p*regionChannel.strideSize;
    609 				int positionOffset = p*positionChannel.strideSize;
    610 				int colorOffset = p*colorChannel.strideSize;
    611 				int rotationOffset = p*rotationChannel.strideSize;
    612 				float 	px = positionChannel.data[positionOffset + ParticleChannels.XOffset],
    613 					py = positionChannel.data[positionOffset + ParticleChannels.YOffset],
    614 					pz = positionChannel.data[positionOffset + ParticleChannels.ZOffset];
    615 				float u = regionChannel.data[regionOffset +ParticleChannels.UOffset];
    616 				float v = regionChannel.data[regionOffset +ParticleChannels.VOffset];
    617 				float u2 = regionChannel.data[regionOffset +ParticleChannels.U2Offset];
    618 				float v2 = regionChannel.data[regionOffset +ParticleChannels.V2Offset];
    619 				float 	sx = regionChannel.data[regionOffset +ParticleChannels.HalfWidthOffset] * scale,
    620 					sy = regionChannel.data[regionOffset+ParticleChannels.HalfHeightOffset]  * scale;
    621 				float r = colorChannel.data[colorOffset +ParticleChannels.RedOffset];
    622 				float g = colorChannel.data[colorOffset +ParticleChannels.GreenOffset];
    623 				float b = colorChannel.data[colorOffset +ParticleChannels.BlueOffset];
    624 				float a = colorChannel.data[colorOffset +ParticleChannels.AlphaOffset];
    625 				float cosRotation = rotationChannel.data[rotationOffset +ParticleChannels.CosineOffset];
    626 				float sinRotation = rotationChannel.data[rotationOffset +ParticleChannels.SineOffset];
    627 				TMP_V1.set(right).scl(sx);
    628 				TMP_V2.set(up).scl(sy);
    629 
    630 				if(cosRotation != 1){
    631 					TMP_M3.setToRotation(look, cosRotation, sinRotation);
    632 					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);
    633 					baseOffset += currentVertexSize;
    634 					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);
    635 					baseOffset += currentVertexSize;
    636 					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);
    637 					baseOffset += currentVertexSize;
    638 					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);
    639 				}
    640 				else {
    641 					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);
    642 					baseOffset += currentVertexSize;
    643 					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);
    644 					baseOffset += currentVertexSize;
    645 					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);
    646 					baseOffset += currentVertexSize;
    647 					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);
    648 				}
    649 			}
    650 		}
    651 	}
    652 
    653 	@Override
    654 	protected void flush(int[] offsets){
    655 
    656 		//fill vertices
    657 		if(useGPU){
    658 			//if(mode != AlignMode.ParticleDirection)
    659 			fillVerticesGPU(offsets);
    660 			//else
    661 			//fillVerticesToParticleDirectionGPU(offsets);
    662 		}
    663 		else {
    664 			if(mode == AlignMode.Screen)
    665 				fillVerticesToScreenCPU(offsets);
    666 			else if(mode == AlignMode.ViewPoint)
    667 				fillVerticesToViewPointCPU(offsets);
    668 			//else
    669 			//fillVerticesToParticleDirectionCPU(offsets);
    670 		}
    671 
    672 		//send vertices to meshes
    673 		int addedVertexCount = 0;
    674 		int vCount = bufferedParticlesCount*4;
    675 		for(int v = 0; v < vCount; v += addedVertexCount){
    676 			addedVertexCount = Math.min(vCount-v, MAX_VERTICES_PER_MESH);
    677 			Renderable renderable = renderablePool.obtain();
    678 			renderable.meshPart.size = (addedVertexCount/4)*6;
    679 			renderable.meshPart.mesh.setVertices(vertices, currentVertexSize *v, currentVertexSize * addedVertexCount);
    680 			renderable.meshPart.update();
    681 			renderables.add(renderable);
    682 		}
    683 	}
    684 
    685 	@Override
    686 	public void getRenderables (Array<Renderable> renderables, Pool<Renderable> pool) {
    687 		for(Renderable renderable : this.renderables)
    688 			renderables.add(pool.obtain().set(renderable));
    689 	}
    690 
    691 	@Override
    692 	public void save (AssetManager manager, ResourceData resources) {
    693 		SaveData data = resources.createSaveData("billboardBatch");
    694 		data.save("cfg", new Config(useGPU, mode));
    695 		data.saveAsset(manager.getAssetFileName(texture), Texture.class);
    696 	}
    697 
    698 	@Override
    699 	public void load (AssetManager manager, ResourceData resources) {
    700 		SaveData data = resources.getSaveData("billboardBatch");
    701 		if(data != null){
    702 			setTexture((Texture)manager.get(data.loadAsset()));
    703 			Config cfg = (Config)data.load("cfg");
    704 			setUseGpu(cfg.useGPU);
    705 			setAlignMode(cfg.mode);
    706 		}
    707 	}
    708 }
    709