/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ 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.ColorUnpacked, 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.ColorUnpacked, 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.ColorUnpacked).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.ColorUnpacked).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; protected BlendingAttribute blendingAttribute; protected DepthTestAttribute depthTestAttribute; Shader shader; /** Create a new BillboardParticleBatch * @param mode * @param useGPU Allow to use GPU instead of CPU * @param capacity Max particle displayed * @param blendingAttribute Blending attribute used by the batch * @param depthTestAttribute DepthTest attribute used by the batch */ public BillboardParticleBatch (AlignMode mode, boolean useGPU, int capacity, BlendingAttribute blendingAttribute, DepthTestAttribute depthTestAttribute) { super(BillboardControllerRenderData.class); renderables = new Array<Renderable>(); renderablePool = new RenderablePool(); this.blendingAttribute = blendingAttribute; this.depthTestAttribute = depthTestAttribute; if (this.blendingAttribute == null) this.blendingAttribute = new BlendingAttribute(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA, 1f); if (this.depthTestAttribute == null) this.depthTestAttribute = new DepthTestAttribute(GL20.GL_LEQUAL, false); allocIndices(); initRenderData(); ensureCapacity(capacity); setUseGpu(useGPU); setAlignMode(mode); } public BillboardParticleBatch (AlignMode mode, boolean useGPU, int capacity) { this(mode, useGPU, capacity, null, null); } 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.meshPart.primitiveType = GL20.GL_TRIANGLES; renderable.meshPart.offset = 0; renderable.material = new Material(this.blendingAttribute, this.depthTestAttribute, TextureAttribute.createDiffuse(texture)); renderable.meshPart.mesh = new Mesh(false, MAX_VERTICES_PER_MESH, MAX_PARTICLES_PER_MESH * 6, currentAttributes); renderable.meshPart.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()); } } protected 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.meshPart.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.meshPart.size = (addedVertexCount / 4) * 6; renderable.meshPart.mesh.setVertices(vertices, currentVertexSize * v, currentVertexSize * addedVertexCount); renderable.meshPart.update(); 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); } } }