/*******************************************************************************
* Copyright (c) 2015
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.graphics.map.draw;
import go.graphics.IllegalBufferException;
import go.graphics.TextureHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jsettlers.graphics.map.IGLProvider;
/**
* This class buffers triangle draw calls and sends them to opengl in bratches. It should only be used during one frame.
*
* @author Michael Zangl
*
*/
public class DrawBuffer {
private static final float Z_OFFSET_PER_ITEM = .00001f;
/**
* Number of buffers to hold. So many textures may be used simultaneously.
*/
private static final int BUFFERS = 5;
private final IGLProvider context;
private float z;
/**
* This is a buffer for a single texture.
*
* @author Michael Zangl
*
*/
public class Buffer {
/**
* Bytes we need for one vertex.
*/
private static final int VERTEX_LENGTH = 5 * 4 + 4;
private static final int TRIAMGLE_LENGTH = 3 * VERTEX_LENGTH;
private static final int BUFFER_TRIANGLES = 1000;
/**
* The last texture we set.
*/
private TextureHandle currentTexture = null;
private int currentTriangles = 0;
protected final ByteBuffer byteBuffer;
protected Buffer() {
byteBuffer =
ByteBuffer.allocateDirect(BUFFER_TRIANGLES * TRIAMGLE_LENGTH);
byteBuffer.order(ByteOrder.nativeOrder());
}
protected void setForTexture(TextureHandle texture) throws IllegalBufferException {
if (currentTexture != texture) {
if (texture != null && !texture.isValid()) {
throw new IllegalBufferException("The texture " + texture + " is not valid.");
}
if (currentTriangles != 0) {
draw();
}
currentTexture = texture;
}
}
protected void draw() {
try {
byteBuffer.rewind();
context.getGl().drawTrianglesWithTextureColored(currentTexture, byteBuffer, currentTriangles);
} catch (IllegalBufferException e) {
// TODO: Crash report. Should not happen since we check texture in advance.
e.printStackTrace();
}
byteBuffer.rewind();
currentTriangles = 0;
}
protected void addImage(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, int activeColor) {
if (currentTriangles >= BUFFER_TRIANGLES - 2) {
draw();
}
addPointPrimitive(x1, y1, u1, v1, activeColor);
addPointPrimitive(x1, y2, u1, v2, activeColor);
addPointPrimitive(x2, y1, u2, v1, activeColor);
addPointPrimitive(x2, y1, u2, v1, activeColor);
addPointPrimitive(x1, y2, u1, v2, activeColor);
addPointPrimitive(x2, y2, u2, v2, activeColor);
currentTriangles += 2;
}
public void addTriangle(float x1, float y1, float x2, float y2, float x3, float y3,
float u1, float v1, float u2, float v2, float u3, float v3, int activeColor) {
if (currentTriangles >= BUFFER_TRIANGLES - 1) {
draw();
}
addPointPrimitive(x1, y1, u1, v1, activeColor);
addPointPrimitive(x2, y2, u2, v2, activeColor);
addPointPrimitive(x3, y3, u3, v3, activeColor);
currentTriangles += 1;
}
private void addPointPrimitive(float x1, float y1, float u, float v, int activeColor) {
byteBuffer.putFloat(x1);
byteBuffer.putFloat(y1);
byteBuffer.putFloat(getZ());
byteBuffer.putFloat(u);
byteBuffer.putFloat(v);
byteBuffer.putInt(activeColor);
}
}
private int lastFreedBuffer = 0;
private final Buffer[] drawBuffers;
public DrawBuffer(IGLProvider context) {
this.context = context;
drawBuffers = new Buffer[BUFFERS];
for (int i = 0; i < BUFFERS; i++) {
drawBuffers[i] = new Buffer();
}
}
/**
* Add a new image to this buffer
*
* @param texture
* The texture. May be null.
* @param x1
* The lower, bottom corner on the screen.
* @param y1
* The lower, bottom corner on the screen.
* @param x2
* The upper upper corner on the screen.
* @param y2
* The upper upper corner on the screen.
* @param u1
* u-coordinate for bottom left corner on the screen.
* @param v1
* v-coordinate for bottom left corner on the screen.
* @param u2
* u-coordinate for upper right corner on the screen.
* @param v2
* v-coordinate for upper right corner on the screen.
* @param activeColor
* The color the image should be premultiplied with.
* @throws IllegalBufferException
* If the texture could not be used.
*/
public void addImage(TextureHandle texture, float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2,
int activeColor)
throws IllegalBufferException {
Buffer buffer = getBuffer(texture); // get first to make it fail.
setZ(getZ() + Z_OFFSET_PER_ITEM);
buffer.addImage(x1, y1, x2, y2, u1, v1, u2, v2, activeColor);
}
/**
* Gets a buffer to use for the given texture.
*
* @param texture
* The texture.
* @return The buffer.
* @throws IllegalBufferException
* If the texture could not be used.
*/
public Buffer getBuffer(TextureHandle texture) throws IllegalBufferException {
for (int i = 0; i < BUFFERS; i++) {
if (drawBuffers[i].currentTexture == texture) {
return drawBuffers[i];
}
}
lastFreedBuffer++;
if (lastFreedBuffer >= BUFFERS) {
lastFreedBuffer = 0;
}
Buffer buffer = drawBuffers[lastFreedBuffer];
buffer.setForTexture(texture);
return buffer;
}
/**
* Draw all pending buffers to the screen.
*/
public void flush() {
for (int i = 0; i < BUFFERS; i++) {
drawBuffers[i].draw();
}
setZ(0);
}
/**
* Gets the Z coordinate to add new images at.
*
* @return z
*/
public float getZ() {
return z;
}
/**
* Sets the Z coordinate to add new images at.
*
* @param z
* z
*/
public void setZ(float z) {
this.z = z;
}
}