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