/******************************************************************************* * 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.Application.ApplicationType; import com.badlogic.gdx.Gdx; 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.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.ParticleType; 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.PointSpriteControllerRenderData; import com.badlogic.gdx.graphics.glutils.ShaderProgram; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Pool; /** This class is used to draw particles as point sprites. * @author Inferno */ public class PointSpriteParticleBatch extends BufferedParticleBatch<PointSpriteControllerRenderData> { private static boolean pointSpritesEnabled = false; protected static final Vector3 TMP_V1 = new Vector3(); protected static final int sizeAndRotationUsage = 1 << 9; protected static final VertexAttributes CPU_ATTRIBUTES = new VertexAttributes(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(Usage.ColorUnpacked, 4, ShaderProgram.COLOR_ATTRIBUTE), new VertexAttribute(Usage.TextureCoordinates, 4, "a_region"), new VertexAttribute(sizeAndRotationUsage, 3, "a_sizeAndRotation")); protected static final int CPU_VERTEX_SIZE = (short)(CPU_ATTRIBUTES.vertexSize / 4), CPU_POSITION_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.Position).offset / 4), CPU_COLOR_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.ColorUnpacked).offset / 4), CPU_REGION_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(Usage.TextureCoordinates).offset / 4), CPU_SIZE_AND_ROTATION_OFFSET = (short)(CPU_ATTRIBUTES.findByUsage(sizeAndRotationUsage).offset / 4); private static void enablePointSprites () { Gdx.gl.glEnable(GL20.GL_VERTEX_PROGRAM_POINT_SIZE); if (Gdx.app.getType() == ApplicationType.Desktop) { Gdx.gl.glEnable(0x8861); // GL_POINT_OES } pointSpritesEnabled = true; } private float[] vertices; Renderable renderable; public PointSpriteParticleBatch () { this(1000); } public PointSpriteParticleBatch (int capacity) { this(capacity, new ParticleShader.Config(ParticleType.Point)); } public PointSpriteParticleBatch (int capacity, ParticleShader.Config shaderConfig) { super(PointSpriteControllerRenderData.class); if (!pointSpritesEnabled) enablePointSprites(); allocRenderable(); ensureCapacity(capacity); renderable.shader = new ParticleShader(renderable, shaderConfig); renderable.shader.init(); } @Override protected void allocParticlesData (int capacity) { vertices = new float[capacity * CPU_VERTEX_SIZE]; if (renderable.meshPart.mesh != null) renderable.meshPart.mesh.dispose(); renderable.meshPart.mesh = new Mesh(false, capacity, 0, CPU_ATTRIBUTES); } protected void allocRenderable () { renderable = new Renderable(); renderable.meshPart.primitiveType = GL20.GL_POINTS; renderable.meshPart.offset = 0; renderable.material = new Material(new BlendingAttribute(GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA, 1f), new DepthTestAttribute(GL20.GL_LEQUAL, false), TextureAttribute.createDiffuse((Texture)null)); } public void setTexture (Texture texture) { TextureAttribute attribute = (TextureAttribute)renderable.material.get(TextureAttribute.Diffuse); attribute.textureDescription.texture = texture; } public Texture getTexture () { TextureAttribute attribute = (TextureAttribute)renderable.material.get(TextureAttribute.Diffuse); return attribute.textureDescription.texture; } @Override protected void flush (int[] offsets) { int tp = 0; for (PointSpriteControllerRenderData 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; p < data.controller.particles.size; ++p, ++tp) { int offset = offsets[tp] * CPU_VERTEX_SIZE; int regionOffset = p * regionChannel.strideSize; int positionOffset = p * positionChannel.strideSize; int colorOffset = p * colorChannel.strideSize; int rotationOffset = p * rotationChannel.strideSize; vertices[offset + CPU_POSITION_OFFSET] = positionChannel.data[positionOffset + ParticleChannels.XOffset]; vertices[offset + CPU_POSITION_OFFSET + 1] = positionChannel.data[positionOffset + ParticleChannels.YOffset]; vertices[offset + CPU_POSITION_OFFSET + 2] = positionChannel.data[positionOffset + ParticleChannels.ZOffset]; vertices[offset + CPU_COLOR_OFFSET] = colorChannel.data[colorOffset + ParticleChannels.RedOffset]; vertices[offset + CPU_COLOR_OFFSET + 1] = colorChannel.data[colorOffset + ParticleChannels.GreenOffset]; vertices[offset + CPU_COLOR_OFFSET + 2] = colorChannel.data[colorOffset + ParticleChannels.BlueOffset]; vertices[offset + CPU_COLOR_OFFSET + 3] = colorChannel.data[colorOffset + ParticleChannels.AlphaOffset]; vertices[offset + CPU_SIZE_AND_ROTATION_OFFSET] = scaleChannel.data[p * scaleChannel.strideSize]; vertices[offset + CPU_SIZE_AND_ROTATION_OFFSET + 1] = rotationChannel.data[rotationOffset + ParticleChannels.CosineOffset]; vertices[offset + CPU_SIZE_AND_ROTATION_OFFSET + 2] = rotationChannel.data[rotationOffset + ParticleChannels.SineOffset]; vertices[offset + CPU_REGION_OFFSET] = regionChannel.data[regionOffset + ParticleChannels.UOffset]; vertices[offset + CPU_REGION_OFFSET + 1] = regionChannel.data[regionOffset + ParticleChannels.VOffset]; vertices[offset + CPU_REGION_OFFSET + 2] = regionChannel.data[regionOffset + ParticleChannels.U2Offset]; vertices[offset + CPU_REGION_OFFSET + 3] = regionChannel.data[regionOffset + ParticleChannels.V2Offset]; } } renderable.meshPart.size = bufferedParticlesCount; renderable.meshPart.mesh.setVertices(vertices, 0, bufferedParticlesCount * CPU_VERTEX_SIZE); renderable.meshPart.update(); } @Override public void getRenderables (Array<Renderable> renderables, Pool<Renderable> pool) { if (bufferedParticlesCount > 0) renderables.add(pool.obtain().set(renderable)); } @Override public void save (AssetManager manager, ResourceData resources) { SaveData data = resources.createSaveData("pointSpriteBatch"); data.saveAsset(manager.getAssetFileName(getTexture()), Texture.class); } @Override public void load (AssetManager manager, ResourceData resources) { SaveData data = resources.getSaveData("pointSpriteBatch"); if (data != null) setTexture((Texture)manager.get(data.loadAsset())); } }