/******************************************************************************* * 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 go.graphics; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; public abstract class GlCachingDrawer implements GLDrawContext { private static final int GL_BUFFERS = 4; private static final int GL_BUFFER_TRIANGLES = 100; private static final byte[] WHITE = new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255 }; private static final byte[] activeColor = WHITE; private class GLBuffer { /** * Bytes we need for one vertex */ private static final int VERTEX_LENGTH = 5 * 4 + 4; private static final int TRIAMGLE_LENGTH = 3 * VERTEX_LENGTH; protected final ByteBuffer byteBuffer; protected final FloatBuffer reuseableBuffer; protected final FloatBuffer reuseableBufferDuplicate; protected GLBuffer() { byteBuffer = ByteBuffer.allocateDirect(GL_BUFFER_TRIANGLES * TRIAMGLE_LENGTH); byteBuffer.order(ByteOrder.nativeOrder()); reuseableBuffer = byteBuffer.asFloatBuffer(); reuseableBufferDuplicate = reuseableBuffer.duplicate(); } /** * The last texture we set. */ private TextureHandle currentTexture = null; private int currentTriangles = 0; protected void addTriangles(float[] triangles) { for (int i = 0; i < triangles.length; i += 5 * 3) { addTexturedTriangle(triangles, i); } } private void addTexturedTriangle(float[] triangles, int offset) { for (int i = 0; i < 3; i++) { addTexturedPoint(triangles, offset + i * 5); } currentTriangles++; } private void addTexturedPoint(float[] triangles, int offset) { for (int i = 0; i < 5; i++) { byteBuffer.putFloat(triangles[offset + i]); } byteBuffer.put(activeColor); } protected void setForTexture(TextureHandle texture) { if (currentTexture != texture) { if (currentTriangles != 0) { draw(); } currentTexture = texture; } } private void draw() { setTexture(currentTexture); reuseableBuffer.rewind(); reuseableBufferDuplicate.rewind(); drawTriangles(reuseableBuffer, reuseableBufferDuplicate, currentTriangles); byteBuffer.rewind(); currentTriangles = 0; } public void addQuad(float[] geometry) { addTexturedPoint(geometry, 0); addTexturedPoint(geometry, 5); addTexturedPoint(geometry, 10); addTexturedPoint(geometry, 5); addTexturedPoint(geometry, 10); addTexturedPoint(geometry, 15); currentTriangles += 2; } public void addQuadPrimitive(float x1, float y1, float x2, float y2) { addPointPrimitive(x1, y1); addPointPrimitive(x1, y2); addPointPrimitive(x2, y1); addPointPrimitive(x2, y1); addPointPrimitive(x1, y2); addPointPrimitive(x2, y2); currentTriangles += 2; } private void addPointPrimitive(float x1, float y1) { byteBuffer.putFloat(x1); byteBuffer.putFloat(y1); byteBuffer.putFloat(0); byteBuffer.putFloat(0); byteBuffer.putFloat(0); byteBuffer.put(activeColor); } } private int lastFreedBuffer = 0; private final GLBuffer[] drawBuffers; protected GlCachingDrawer() { drawBuffers = new GLBuffer[GL_BUFFERS]; for (int i = 0; i < GL_BUFFERS; i++) { drawBuffers[i] = new GLBuffer(); } } public void flush() { for (int i = 0; i < GL_BUFFERS; i++) { drawBuffers[i].draw(); } } protected abstract void drawTriangles(FloatBuffer buffer, FloatBuffer bufferDuplicate, int tris); protected abstract void setTexture(TextureHandle index); protected GLBuffer getBuffer(TextureHandle texture) { for (int i = 0; i < GL_BUFFERS; i++) { if (drawBuffers[i].currentTexture == texture) { return drawBuffers[i]; } } lastFreedBuffer++; if (lastFreedBuffer >= GL_BUFFERS) { lastFreedBuffer = 0; } GLBuffer buffer = drawBuffers[lastFreedBuffer]; buffer.setForTexture(texture); return buffer; } @Override public void drawTrianglesWithTexture(TextureHandle textureid, float[] geometry) { GLBuffer buffer = getBuffer(textureid); buffer.addTriangles(geometry); } @Override public void color(float red, float green, float blue, float alpha) { activeColor[0] = (byte) (red * 255); activeColor[1] = (byte) (green * 255); activeColor[2] = (byte) (blue * 255); activeColor[3] = (byte) (alpha * 255); } @Override public void drawQuadWithTexture(TextureHandle textureid, float[] geometry) { GLBuffer buffer = getBuffer(textureid); buffer.addQuad(geometry); } @Override public void fillQuad(float x1, float y1, float x2, float y2) { GLBuffer buffer = getBuffer(null); buffer.addQuadPrimitive(x1, y1, x2, y2); } @Override public void drawTrianglesWithTextureColored(TextureHandle textureid, float[] geometry) { // UNUSED } }