com.badlogic.gdx.graphics.g3d.decals.DecalBatch Maven / Gradle / Ivy
/*******************************************************************************
* 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.decals;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.SortedIntList;
/**
* Renderer for {@link Decal} objects.
*
*
* New objects are added using {@link DecalBatch#add(Decal)}, there is no limit on how many decals can be added.
* Once all the decals have been submitted a call to {@link DecalBatch#flush()} will batch them together and send big chunks of
* geometry to the GL.
*
*
* The size of the batch specifies the maximum number of decals that can be batched together before they have to be submitted to
* the graphics pipeline. The default size is {@link DecalBatch#DEFAULT_SIZE}. If it is known before hand that not as many will be
* needed on average the batch can be downsized to save memory. If the game is basically 3d based and decals will only be needed
* for an orthogonal HUD it makes sense to tune the size down.
*
*
* The way the batch handles things depends on the {@link GroupStrategy}. Different strategies can be used to customize shaders,
* states, culling etc. for more details see the {@link GroupStrategy} java doc.
* While it shouldn't be necessary to change strategies, if you have to do so, do it before calling {@link #add(Decal)}, and if
* you already did, call {@link #flush()} first.
*
*/
public class DecalBatch implements Disposable {
private static final int DEFAULT_SIZE = 1000;
private float[] vertices;
private Mesh mesh;
private final SortedIntList> groupList = new SortedIntList>();
private GroupStrategy groupStrategy;
private final Pool> groupPool = new Pool>(16) {
@Override
protected Array newObject () {
return new Array(false, 100);
}
};
private final Array> usedGroups = new Array>(16);
/** Creates a new batch using the {@link DefaultGroupStrategy} */
public DecalBatch () {
this(DEFAULT_SIZE, new DefaultGroupStrategy());
}
public DecalBatch (GroupStrategy groupStrategy) {
this(DEFAULT_SIZE, groupStrategy);
}
public DecalBatch (int size, GroupStrategy groupStrategy) {
initialize(size);
setGroupStrategy(groupStrategy);
}
/** Sets the {@link GroupStrategy} used
* @param groupStrategy Group strategy to use */
public void setGroupStrategy (GroupStrategy groupStrategy) {
this.groupStrategy = groupStrategy;
}
/** Initializes the batch with the given amount of decal objects the buffer is able to hold when full.
*
* @param size Maximum size of decal objects to hold in memory */
public void initialize (int size) {
vertices = new float[size * Decal.SIZE];
mesh = new Mesh(Mesh.VertexDataType.VertexArray, false, size * 4, size * 6, new VertexAttribute(
VertexAttributes.Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE), new VertexAttribute(
VertexAttributes.Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE), new VertexAttribute(
VertexAttributes.Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + "0"));
short[] indices = new short[size * 6];
int v = 0;
for (int i = 0; i < indices.length; i += 6, v += 4) {
indices[i] = (short)(v);
indices[i + 1] = (short)(v + 2);
indices[i + 2] = (short)(v + 1);
indices[i + 3] = (short)(v + 1);
indices[i + 4] = (short)(v + 2);
indices[i + 5] = (short)(v + 3);
}
mesh.setIndices(indices);
}
/** @return maximum amount of decal objects this buffer can hold in memory */
public int getSize () {
return vertices.length / Decal.SIZE;
}
/** Add a decal to the batch, marking it for later rendering
*
* @param decal Decal to add for rendering */
public void add (Decal decal) {
DecalMaterial material = decal.getMaterial();
int groupIndex = groupStrategy.decideGroup(decal);
Array targetGroup = groupList.get(groupIndex);
if (targetGroup == null) {
targetGroup = groupPool.obtain();
targetGroup.clear();
usedGroups.add(targetGroup);
groupList.insert(groupIndex, targetGroup);
}
targetGroup.add(decal);
}
/** Flush this batch sending all contained decals to GL. After flushing the batch is empty once again. */
public void flush () {
render();
clear();
}
/** Renders all decals to the buffer and flushes the buffer to the GL when full/done */
protected void render () {
groupStrategy.beforeGroups();
for (SortedIntList.Node> group : groupList) {
groupStrategy.beforeGroup(group.index, group.value);
ShaderProgram shader = groupStrategy.getGroupShader(group.index);
render(shader, group.value);
groupStrategy.afterGroup(group.index);
}
groupStrategy.afterGroups();
}
/** Renders a group of vertices to the buffer, flushing them to GL when done/full
*
* @param decals Decals to render */
private void render (ShaderProgram shader, Array decals) {
// batch vertices
DecalMaterial lastMaterial = null;
int idx = 0;
for (Decal decal : decals) {
if (lastMaterial == null || !lastMaterial.equals(decal.getMaterial())) {
if (idx > 0) {
flush(shader, idx);
idx = 0;
}
decal.material.set();
lastMaterial = decal.material;
}
decal.update();
System.arraycopy(decal.vertices, 0, vertices, idx, decal.vertices.length);
idx += decal.vertices.length;
// if our batch is full we have to flush it
if (idx == vertices.length) {
flush(shader, idx);
idx = 0;
}
}
// at the end if there is stuff left in the batch we render that
if (idx > 0) {
flush(shader, idx);
}
}
/** Flushes vertices[0,verticesPosition[ to GL verticesPosition % Decal.SIZE must equal 0
*
* @param verticesPosition Amount of elements from the vertices array to flush */
protected void flush (ShaderProgram shader, int verticesPosition) {
mesh.setVertices(vertices, 0, verticesPosition);
if (shader != null) {
mesh.render(shader, GL10.GL_TRIANGLES, 0, verticesPosition / 4);
} else {
mesh.render(GL10.GL_TRIANGLES, 0, verticesPosition / 4);
}
}
/** Remove all decals from batch */
protected void clear () {
groupList.clear();
groupPool.free(usedGroups);
usedGroups.clear();
}
/** Frees up memory by dropping the buffer and underlying resources. If the batch is needed again after disposing it can be
* {@link #initialize(int) initialized} again. */
public void dispose () {
clear();
vertices = null;
mesh.dispose();
}
}