/** * Copyright 2008 - 2015 The Loon Game Engine Authors * * 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. * * @project loon * @author cping * @email:javachenpeng@yahoo.com * @version 0.5 */ package loon.opengl; import loon.LRelease; import loon.LSystem; import loon.canvas.LColor; import loon.geom.Affine2f; import loon.opengl.VertexAttributes.Usage; import loon.utils.TArray; public class GLBatch implements LRelease { private int primitiveType; private int vertexIdx; private int numSetTexCoords; private final int maxVertices; private int numVertices; private Mesh mesh; private ShaderProgram shader; private boolean ownsShader; private int numTexCoords; private int vertexSize; private int normalOffset; private int colorOffset; private int texCoordOffset; private final Affine2f projModelView = new Affine2f(); float[] vertices; private String[] shaderUniformNames; public GLBatch(boolean hasNormals, boolean hasColors, int numTexCoords) { this(5000, hasNormals, hasColors, numTexCoords, null); ownsShader = true; } public GLBatch(int maxVertices, boolean hasNormals, boolean hasColors, int numTexCoords) { this(maxVertices, hasNormals, hasColors, numTexCoords, null); ownsShader = true; } private boolean hasNormals, hasColors; public GLBatch(int maxVertices, boolean hasNormals, boolean hasColors, int numTexCoords, ShaderProgram shader) { this.maxVertices = maxVertices; this.numTexCoords = numTexCoords; this.shader = shader; this.hasNormals = hasNormals; this.hasColors = hasColors; } private VertexAttribute[] buildVertexAttributes(boolean hasNormals, boolean hasColor, int numTexCoords) { TArray<VertexAttribute> attribs = new TArray<VertexAttribute>( numTexCoords + 2); attribs.add(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE)); if (hasNormals) { attribs.add(new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE)); } if (hasColor) { attribs.add(new VertexAttribute(Usage.ColorPacked, 4, ShaderProgram.COLOR_ATTRIBUTE)); } for (int i = 0; i < numTexCoords; i++) { attribs.add(new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + i)); } final int size = attribs.size; final VertexAttribute[] array = new VertexAttribute[size]; for (int i = 0; i < size; i++) { array[i] = attribs.get(i); } return array; } public void setShader(ShaderProgram shader) { if (ownsShader) { this.shader.close(); } this.shader = shader; ownsShader = false; } public void begin(Affine2f projModelView, int primitiveType) { if (shader == null) { VertexAttribute[] attribs = buildVertexAttributes(hasNormals, hasColors, numTexCoords); mesh = new Mesh(false, maxVertices, 0, attribs); vertices = new float[maxVertices * (mesh.getVertexAttributes().vertexSize / 4)]; vertexSize = mesh.getVertexAttributes().vertexSize / 4; normalOffset = mesh.getVertexAttribute(Usage.Normal) != null ? mesh .getVertexAttribute(Usage.Normal).offset / 4 : 0; colorOffset = mesh.getVertexAttribute(Usage.ColorPacked) != null ? mesh .getVertexAttribute(Usage.ColorPacked).offset / 4 : 0; texCoordOffset = mesh.getVertexAttribute(Usage.TextureCoordinates) != null ? mesh .getVertexAttribute(Usage.TextureCoordinates).offset / 4 : 0; shaderUniformNames = new String[numTexCoords]; for (int i = 0; i < numTexCoords; i++) { shaderUniformNames[i] = "u_sampler" + i; } shader = createDefaultShader(hasNormals, hasColors, numTexCoords); } this.numSetTexCoords = 0; this.vertexIdx = 0; this.numVertices = 0; this.projModelView.set(projModelView); this.primitiveType = primitiveType; } public void color(float color) { vertices[vertexIdx + colorOffset] = color; } public void color(LColor color) { vertices[vertexIdx + colorOffset] = color.toFloatBits(); } public void color(float r, float g, float b, float a) { vertices[vertexIdx + colorOffset] = LColor.toFloatBits(r, g, b, a); } public void texCoord(float u, float v) { final int idx = vertexIdx + texCoordOffset; vertices[idx + numSetTexCoords] = u; vertices[idx + numSetTexCoords + 1] = v; numSetTexCoords += 2; } public void normal(float x, float y, float z) { final int idx = vertexIdx + normalOffset; vertices[idx] = x; vertices[idx + 1] = y; vertices[idx + 2] = z; } public void vertex(float x, float y) { vertex(x, y, 0); } public void vertex(float x, float y, float z) { final int idx = vertexIdx; vertices[idx] = x; vertices[idx + 1] = y; vertices[idx + 2] = z; numSetTexCoords = 0; vertexIdx += vertexSize; numVertices++; } public void flush() { if (numVertices == 0) { return; } shader.begin(); shader.setUniformMatrix("u_projModelView", projModelView.toViewMatrix4()); for (int i = 0; i < numTexCoords; i++) { shader.setUniformi(shaderUniformNames[i], i); } mesh.setVertices(vertices, 0, vertexIdx); mesh.render(shader, primitiveType); shader.end(); } public void end() { flush(); } public int getNumVertices() { return numVertices; } public int getMaxVertices() { return maxVertices; } @Override public void close() { if (ownsShader && shader != null) { shader.close(); } mesh.close(); } static public ShaderProgram createDefaultShader(boolean hasNormals, boolean hasColors, int numTexCoords) { String vertexShader = LSystem.createVertexShader(hasNormals, hasColors, numTexCoords); String fragmentShader = LSystem.createFragmentShader(hasNormals, hasColors, numTexCoords); ShaderProgram program = new ShaderProgram(vertexShader, fragmentShader); return program; } }