Failing to use VBOs in OpenGl/Android - java

Greetings my fellow programmers,
I've searched the WEB, checked examples online but still can't figure it out. I'm sorry if this was asked previously, I'm tired after a week-long debug of this. I hope you can help me.
Basically the problem is that I try to draw some quads (with triangles) but nothing is drawn. Previously I was drawing without VBOs the way described in 'Triangle example' on official Android website. Everything worked fine, but I decided that updating vertices/indices buffers in Renderer.OnDrawFrame() is not efficient :)
So here is my code:
public class FloorPlanRenderer implements GLSurfaceView.Renderer {
public volatile float mAngle;
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
private GLSurfaceView mGlView;
private GlEngine mGlEngine;
private boolean dataSet = false;
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// Initialize the accumulated rotation matrix
Matrix.setIdentityM(mRotationMatrix, 0);
// Position the eye in front of the origin.
final float eyeX = 0.0f;
final float eyeY = 0.0f;
final float eyeZ = -3.0f;
// We are looking toward the distance
final float lookX = 0.0f;
final float lookY = 0.0f;
final float lookZ = 0.0f; //-5.0f;
// Set our up vector. This is where our head would be pointing were we holding the camera.
final float upX = 0.0f;
final float upY = 1.0f;
final float upZ = 0.0f;
// Set the view matrix. This matrix can be said to represent the camera position.
Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ);
mGlEngine = new GlEngine(10);
mGlEngine.registerQuad(new Wall(-0.5f, 0.4f, -0.2f, 0.4f));
mGlEngine.registerQuad(new Wall(0.5f, 0.4f, 0.2f, 0.4f));
mGlEngine.registerQuad(new Wall(0.0f, 0.0f, 0.0f, 0.3f, 0.02f));
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
// Create a new perspective projection matrix. The height will stay the same
// while the width will vary as per aspect ratio.
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
final float bottom = -1.0f;
final float top = 1.0f;
final float near = 3.0f;
final float far = 7.0f;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far);
}
#Override
public void onDrawFrame(GL10 gl) {
float[] scratch = new float[16];
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, 1.0f);
// Combine the rotation matrix with the projection and camera view
// Note that the mMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
mGlEngine.render(scratch);
}
}
GlEngine class:
public class GlEngine {
public static final int COORDS_PER_VERTEX = 3;
public static final int ORDER_INDICES_PER_QUAD = 6;
public static final int VERTICES_PER_QUAD = 4;
public static final int SIZE_OF_FLOAT = Float.SIZE/Byte.SIZE;
public static final int SIZE_OF_SHORT = Short.SIZE/Byte.SIZE;
private int mQuadsNum = 0;
private int mLastCoordsIndex = 0;
private int mLastOrderIndex = 0;
private final FloatBuffer vertexBuffer;
private final ShortBuffer indexBuffer;
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
// Note that the uMVPMatrix factor *must be first* in order
// for the matrix multiplication product to be correct.
" gl_Position = uMVPMatrix * vPosition;" +
"}";
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 0.0f };
private boolean mDataInitNeeded = true;
public GlEngine(int quadsNum) {
ByteBuffer bb = ByteBuffer.allocateDirect(quadsNum * VERTICES_PER_QUAD *
COORDS_PER_VERTEX * SIZE_OF_FLOAT);
bb.order(ByteOrder.nativeOrder()); // device hardware's native byte order
vertexBuffer = bb.asFloatBuffer();
ByteBuffer bb2 = ByteBuffer.allocateDirect(quadsNum *
ORDER_INDICES_PER_QUAD * SIZE_OF_SHORT);
bb2.order(ByteOrder.nativeOrder());
indexBuffer = bb2.asShortBuffer();
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram);
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
public void registerQuad(Wall quad) {
quad.putCoords(vertexBuffer);
quad.putIndices(indexBuffer);
mQuadsNum++;
}
// This code is dealing with VBO side of things
private final int[] mVerticesBufferId = new int[BUFFERS_COUNT];
private final int[] mIndicesBufferId = new int[BUFFERS_COUNT];
private static final int BUFFERS_COUNT = 1;
public void copyToGpu(FloatBuffer vertices) {
GLES20.glGenBuffers(BUFFERS_COUNT, mVerticesBufferId, 0);
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.capacity() * SIZE_OF_FLOAT, vertices, GLES20.GL_STATIC_DRAW);
// Cleanup buffer
vertices.limit(0);
vertices = null;
}
public void copyToGpu(ShortBuffer indices) {
GLES20.glGenBuffers(BUFFERS_COUNT, mIndicesBufferId, 0);
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, indices.capacity() * SIZE_OF_SHORT, indices, GLES20.GL_STATIC_DRAW);
// Cleanup buffer
indices.limit(0);
indices = null;
}
public void render(float[] mvpMatrix) {
setData();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesBufferId[0]);
GLES20.glUseProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, 0);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
// Draw quads
GLES20.glDrawElements(
GLES20.GL_TRIANGLES, mQuadsNum * ORDER_INDICES_PER_QUAD,
GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
// This method is called on gl thread GlSurfaceView.queueEvent(...)
public void setData() {
if (mDataInitNeeded) {
// Reset positions of buffers for consuming in GL
vertexBuffer.position(0);
indexBuffer.position(0);
copyToGpu(vertexBuffer);
copyToGpu(indexBuffer);
mDataInitNeeded = false;
}
}
public void deallocateGlBuffers() {
if (mVerticesBufferId[0] > 0) {
GLES20.glDeleteBuffers(mVerticesBufferId.length, mVerticesBufferId, 0);
mVerticesBufferId[0] = 0;
}
if (mIndicesBufferId[0] > 0) {
GLES20.glDeleteBuffers(mIndicesBufferId.length, mIndicesBufferId, 0);
mIndicesBufferId[0] = 0;
}
}
}
The Wall class which represents rectangle:
public class Wall {
// number of coordinates per vertex in this array
private static final int COORDS_PER_VERTEX = 3;
private static final int VERTICES_NUM = 4; // it's a rect after all
private static final float DEFAULT_WIDTH = 0.05f;
private static final float DEFAULT_COORDS_SOURCE = 0.5f;
private final float mCoords[] = new float[COORDS_PER_VERTEX * VERTICES_NUM];
private final short mDrawOrder[] = { 0, 1, 2, // first triangle
1, 2, 3 }; // second triangle
private int mVertexBufferPosition;
private int mIndexBufferPosition;
private final PointF mA = new PointF(0, 0);
private final PointF mB = new PointF(0, 0);
private float mWidth;
public Wall() {
init(-DEFAULT_COORDS_SOURCE, DEFAULT_COORDS_SOURCE, DEFAULT_COORDS_SOURCE,
-DEFAULT_COORDS_SOURCE, DEFAULT_WIDTH);
}
public Wall(float x1, float y1, float x2, float y2)
{
init(x1, y1, x2, y2, DEFAULT_WIDTH);
}
public Wall(float x1, float y1, float x2, float y2, float width) {
init(x1, y1, x2, y2, width);
}
private void init(float x1, float y1, float x2, float y2, float width) {
mA.x = x1;
mA.y = y1;
mB.x = x2;
mB.y = y2;
mWidth = width;
calcCoords();
}
private void calcCoords() {
float[] vector = {mA.x - mB.x, mA.y - mB.y};
float magnitude = (float) Math.sqrt(vector[0]*vector[0] + vector[1]*vector[1]);
float[] identityVector = {vector[0]/magnitude, vector[1]/magnitude};
float[] orthogonalIdentityVector = {identityVector[1], -identityVector[0]};
mCoords[0] = mA.x + mWidth * orthogonalIdentityVector[0];
mCoords[1] = mA.y + mWidth * orthogonalIdentityVector[1];
mCoords[3] = mA.x - mWidth * orthogonalIdentityVector[0];
mCoords[4] = mA.y - mWidth * orthogonalIdentityVector[1];
mCoords[6] = mB.x + mWidth * orthogonalIdentityVector[0];
mCoords[7] = mB.y + mWidth * orthogonalIdentityVector[1];
mCoords[9] = mB.x - mWidth * orthogonalIdentityVector[0];
mCoords[10] = mB.y - mWidth * orthogonalIdentityVector[1];
}
public void putCoords(FloatBuffer vertexBuffer) {
mVertexBufferPosition = vertexBuffer.position();
for (int i = 0; i < mDrawOrder.length; i++) {
mDrawOrder[i] += mVertexBufferPosition/GlEngine.COORDS_PER_VERTEX;
}
vertexBuffer.put(mCoords);
}
public void putIndices(ShortBuffer indexBuffer) {
mIndexBufferPosition = indexBuffer.position();
indexBuffer.put(mDrawOrder);
}
public float getWidth() {
return mWidth;
}
public void setWidth(float mWidth) {
this.mWidth = mWidth;
}
public PointF getA() {
return mA;
}
public void setA(float x, float y) {
this.mA.x = x;
this.mA.y = y;
}
public PointF getB() {
return mB;
}
public void setB(float x, float y) {
this.mB.x = x;
this.mB.y = y;
}
}
In Wall class I save offset where it places its vertices and indices because this class will be changing in the future and it intended to update its vertices in the main buffer (the buffer will not be recompiled for each OnDrawFrame).
Thank you. I hope with your help I will somehow overcome this (another) obstacle on my way to OpenGl ES.

Shame on me! I incidentally put indices into wrong array. Instead of this:
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, indices.capacity() * SIZE_OF_SHORT, indices, GLES20.GL_STATIC_DRAW);
There should be:
// Copy vertices data into GPU memory
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesBufferId[0]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.capacity() * SIZE_OF_SHORT, indices, GLES20.GL_STATIC_DRAW);
Why shame? because in the log I saw:
07-23 16:20:05.442 5170-5264/com.example.neutrino.maze W/Adreno-ES20: : GL_INVALID_OPERATION
Just after the second call to glBufferData where I put GL_ARRAY_BUFFER instead of GL_ELEMENT_ARRAY_BUFFER. This was for sure caused by copy-paste as in many cases.

Related

OpenGL Quads Rendering in Black When Rendering Text

So, I'm using LWJGL and OpenGL to render some quads and textures. My quads have been working fine, until the addition of some textures. Specifically, when I added the ability to render text to the screen, all of the quads color has changed to black. I've looked around for a solution but have had no luck, the most common answers are to use glEnable(GL_TEXTURE_2D); only when I draw textures, otherwise keep it disabled when drawing quads. This doesn't seem to be working for me, my quads are still black. I've included my Renderer, Font, and Texture classes below as I'm fairly certain the issue is somewhere in there.
Edit: So I've done a bit more testing, I wasn't even trying to draw normal textures (from .png files) however, now that I've tried to do that as well, I've found they also are drawn completely black. So as of now, the only thing rendering correctly is my font/text, even though I create a texture to render them as well
Renderer.java
public class Renderer {
private VertexArrayObject vao;
private VertexBufferObject vbo;
private ShaderProgram shaderProgram;
private FloatBuffer vertices;
private int numVertices;
private boolean drawing;
private Font fontSketchalot;
public void drawQuad(float x, float y, float width, float height, Colors c) {
glDisable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
begin();
if (vertices.remaining() < 5 * 7) flush();
// Calculate Vertex positions
float x1 = x;
float y1 = y;
float x2 = x + width;
float y2 = y - height;
// Calculate color
float r = c.getR();
float g = c.getG();
float b = c.getB();
// Put data into buffer
vertices.put(x1).put(y1).put(r).put(g).put(b).put(0f).put(1f);
vertices.put(x1).put(y2).put(r).put(g).put(b).put(0f).put(0f);
vertices.put(x2).put(y2).put(r).put(g).put(b).put(1f).put(0f);
vertices.put(x2).put(y1).put(r).put(g).put(b).put(1f).put(1f);
// We drew X vertices
numVertices += 4;
end();
}
public void drawTextureFromTexture(Texture texture, float x, float y, float width, float height) {
drawTexture(texture, x, y, x + width, y - height, 0, 0, 1, 1, Colors.white);
}
public void drawTextureFromFont(Texture texture, float x, float y, float width, float height, float s1, float t1, float sWidth, float sHeight, Colors c) {
drawTexture(texture, x, y, x + width, y - height, s1, t1, s1 + sWidth, t1 + sHeight, c);
}
public void drawTexture(Texture texture, float x1, float y1, float x2, float y2, float s1, float t1, float s2, float t2, Colors c) {
glEnable(GL_TEXTURE_2D);
texture.prepare();
begin();
if (vertices.remaining() < 5 * 7) flush();
// Calculate color
float r = c.getR();
float g = c.getG();
float b = c.getB();
// Put data into buffer
vertices.put(x1).put(y1).put(r).put(g).put(b).put(s1).put(t2);
vertices.put(x1).put(y2).put(r).put(g).put(b).put(s1).put(t1);
vertices.put(x2).put(y2).put(r).put(g).put(b).put(s2).put(t1);
vertices.put(x2).put(y1).put(r).put(g).put(b).put(s2).put(t2);
// We drew X vertices
numVertices += 4;
end();
texture.unbind();
}
public void drawText(String text, float x, float y, float scale, Colors c) {
fontSketchalot.drawText(this, text, x, y, scale, c);
}
// Initialize renderer
public void init(){
// Create font
fontSketchalot = new Font(Fonts.SKETCHALOT);
// Set up shader programs
setupShaderProgram();
// Set wrapping and filtering values
setParameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
setParameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
setParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
setParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Enable blending (?????)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
// Set parameter of texture
private void setParameter(int name, int value) {
glTexParameteri(GL_TEXTURE_2D, name, value);
}
// Clears drawing area
public void clear() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
// Begin rendering
public void begin() {
if (drawing) throw new IllegalStateException("Renderer is already drawing.");
drawing = true;
numVertices = 0;
}
// End rendering
public void end() {
if (!drawing) throw new IllegalStateException("Renderer is not drawing.");
drawing = false;
flush();
}
// Flushes data to GPU to get rendered
public void flush() {
if (numVertices > 0) {
vertices.flip();
if (vao != null) vao.bind();
else vbo.bind(GL_ARRAY_BUFFER);
specifyVertexAttributes();
}
shaderProgram.use();
// Upload the new vertex data
vbo.bind(GL_ARRAY_BUFFER);
vbo.uploadSubData(GL_ARRAY_BUFFER, 0, vertices);
// Draw batch
glDrawArrays(GL_QUADS, 0, numVertices);
// Clear vertex data for next batch
vertices.clear();
numVertices = 0;
}
private void setupShaderProgram() {
// Generate VertexArrayObject
if (Game.is32Supported()) {
vao = new VertexArrayObject();
vao.bind();
} else {
throw new RuntimeException("OpenGL 3.2 not supported.");
}
// Generate VertexBufferObject
vbo = new VertexBufferObject();
vbo.bind(GL_ARRAY_BUFFER);
// Create FloatBuffer
vertices = MemoryUtil.memAllocFloat(4096);
// Upload null data to allocate storage for the VBO
long size = vertices.capacity() * Float.BYTES;
vbo.uploadData(GL_ARRAY_BUFFER, size, GL_DYNAMIC_DRAW);
// Initialize variables
numVertices = 0;
drawing = false;
// Load Shaders:
Shader vertexShader, fragmentShader;
if (Game.is32Supported()) {
vertexShader = Shader.loadShader(GL_VERTEX_SHADER, "res/shaders/default.vert");
fragmentShader = Shader.loadShader(GL_FRAGMENT_SHADER, "res/shaders/default.frag");
} else {
throw new RuntimeException("OpenGL 3.2 not supported.");
}
// Create ShaderProgram
shaderProgram = new ShaderProgram();
shaderProgram.attachShader(vertexShader);
shaderProgram.attachShader(fragmentShader);
if (Game.is32Supported()) {
shaderProgram.bindFragmentDataLocation(0, "fragColor");
}
shaderProgram.link();
shaderProgram.use();
// Delete linked shaders
vertexShader.delete();
fragmentShader.delete();
// Get width & height of framebuffer
long window = GLFW.glfwGetCurrentContext();
int width, height;
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer widthBuffer = stack.mallocInt(1);
IntBuffer heightBuffer = stack.mallocInt(1);
GLFW.glfwGetFramebufferSize(window, widthBuffer, heightBuffer);
width = widthBuffer.get();
height = heightBuffer.get();
}
// Specify vertex pointers
specifyVertexAttributes();
// Set Model Matrix to identity matrix
Matrix4f model = new Matrix4f();
int uniModel = shaderProgram.getUniformLocation("model");
shaderProgram.setUniform(uniModel, model);
// Set View Matrix to identity matrix
Matrix4f view = new Matrix4f();
int uniView = shaderProgram.getUniformLocation("view");
shaderProgram.setUniform(uniView, view);
// Set Projection Matrix to an orthographic projection
Matrix4f projection = Matrix4f.orthographic(0f, width, 0f, height, -1f, 1f);
int uniProjection = shaderProgram.getUniformLocation("projection");
shaderProgram.setUniform(uniProjection, projection);
}
// Specifies the vertex shader pointers (attributes)
private void specifyVertexAttributes() {
int posAttrib = shaderProgram.getAttributeLocation("position");
shaderProgram.enableVertexAttribute(posAttrib);
shaderProgram.pointVertexAttribute(posAttrib, 2, 7 * Float.BYTES, 0);
int colAttrib = shaderProgram.getAttributeLocation("color");
shaderProgram.enableVertexAttribute(colAttrib);
shaderProgram.pointVertexAttribute(colAttrib, 3, 7 * Float.BYTES, 2 * Float.BYTES);
int texAttrib = shaderProgram.getAttributeLocation("texcoord");
shaderProgram.enableVertexAttribute(texAttrib);
shaderProgram.pointVertexAttribute(texAttrib, 2, 7 * Float.BYTES, 5 * Float.BYTES);
int uniTex = shaderProgram.getUniformLocation("texImage");
shaderProgram.setUniform(uniTex, 0);
}
}
Font.java
public class Font {
private String fontPath;
private java.awt.Font font;
private Map<Character, Glyph> glyphs;
private float fontHeight;
private Texture texture;
public Font(String fontPath) {
System.out.println("Creating font...");
this.fontPath = fontPath;
loadFont();
glyphs = new HashMap<>();
texture = createFontTexture();
System.out.println("Font created.");
}
public Font() {
System.out.println("Creating font...");
this.fontPath = "DEFAULT";
font = new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, 30);
glyphs = new HashMap<>();
texture = createFontTexture();
System.out.println("Font created.");
}
private void loadFont() {
try {
System.out.println("Loading font...");
font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, new FileInputStream(fontPath)).deriveFont(java.awt.Font.PLAIN, 30);
System.out.println("Font loaded.");
} catch (Exception e) {
throw new RuntimeException("Could not load font.");
}
}
private Texture createFontTexture() {
int imageWidth = 0;
int imageHeight = 0;
// Add up total width and height
for (int i = 32; i < 256; i++) {
if (i == 127) { // DEL control code
continue;
}
char c = (char) i;
BufferedImage ch = createCharImage(c);
if (ch == null) {
System.out.println("Could not load [CHAR: \"" + c + "\"] from font: " + fontPath);
continue;
}
imageWidth += ch.getWidth();
imageHeight = Math.max(imageHeight, ch.getHeight());
}
fontHeight = Converter.glfwCoordToOpenGLCoord(imageHeight);
/* Image for the texture */
BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
// Set up glyphs (Map<CHAR, GLYPH>)
int xOffsetI = 0;
float xOffsetF = 0;
for (int i = 32; i < 256; i++) {
if (i == 127) { // DEL control code
continue;
}
char c = (char) i;
BufferedImage charImage = createCharImage(c);
if (charImage == null) {
System.out.println("Could not load [CHAR: \"" + c + "\"] from font: " + fontPath);
continue;
}
int charWidth = charImage.getWidth();
int charHeight = charImage.getHeight();
// Create glyph
Glyph ch = new Glyph(Converter.glfwCoordToOpenGLCoord(charWidth), Converter.glfwCoordToOpenGLCoord(charHeight), ((float)charWidth / (float)imageWidth), (float)charHeight / (float)imageHeight, xOffsetF, 0.0f);
// Draw char on image
g.drawImage(charImage, xOffsetI, 0, null);
xOffsetI += charWidth;
xOffsetF += ch.sWidth;
// Put in map
glyphs.put(c, ch);
}
// Flip image Horizontal to get the origin to bottom left
AffineTransform transform = AffineTransform.getScaleInstance(1f, -1f);
transform.translate(0, -image.getHeight());
AffineTransformOp operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
image = operation.filter(image, null);
// Get char width & char height of image
int width = image.getWidth();
int height = image.getHeight();
// Put pixel data into int[] pixels
int[] pixels = new int[width * height];
image.getRGB(0, 0, width, height, pixels, 0, width);
// Put pixel data into byte buffer
ByteBuffer buffer = MemoryUtil.memAlloc(width * height * 4);
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++) {
// Pixel format : OxAARRGGBB
int pixel = pixels[i * width + j];
buffer.put((byte) ((pixel >> 16) & 0xFF)); // 0x000000RR
buffer.put((byte) ((pixel >> 8) & 0xFF)); // 0x000000GG
buffer.put((byte) ((pixel) & 0xFF)); // 0x000000BB
buffer.put((byte) ((pixel >> 24) & 0xFF)); // 0x000000AA
//buffer.put((byte)(0xFF)); // Test
}
}
buffer.flip(); // Set index back to 0
Texture fontTexture = Texture.createTexture(width, height, buffer); // Create texture
MemoryUtil.memFree(buffer); // Free buffer
return fontTexture;
}
private BufferedImage createCharImage(char c) {
// Begin by calculating proper width and height
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
if (Options.ANTIALIAS) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
FontMetrics fontMetrics = g.getFontMetrics();
g.dispose();
int charWidth = fontMetrics.charWidth(c);
int charHeight = fontMetrics.getHeight();
if (charWidth == 0) return null;
// Now set up the image
image = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB);
g = image.createGraphics();
if (Options.ANTIALIAS) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
g.setPaint(Color.WHITE); // Use white so we can set the color while rendering
g.drawString(String.valueOf(c), 0, fontMetrics.getAscent()); // Paint char onto image
g.dispose();
// Finally return the final image
return image;
}
public void drawText(Renderer renderer, CharSequence text, float x, float y, float scale, Colors c) {
float xOffset = 0;
float yOffset = 0;
texture.prepare();
//renderer.begin();
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch == '\n') {
// If we start a new line, change the yOffset to reflect (then continue)
yOffset -= fontHeight;
continue;
}
if (ch == '\r') {
// Skip carriage return
continue;
}
Glyph g = glyphs.get(ch);
renderer.drawTextureFromFont(texture, x + xOffset, y + yOffset, g.width * scale, g.height * scale, g.s, g.t, g.sWidth, g.sHeight, c);
xOffset += g.width * scale;
}
//renderer.end();
texture.unbind();
}
}
Texture.java
public class Texture {
// Store handle
private final int id;
private int width;
private int height;
private ByteBuffer image;
// Draw texture using given renderer
public void draw(Renderer renderer, float x, float y, float scale) {
// Scale texture
float w = ((float)width * scale * 2) / (Game.WIDTH);
float h = ((float)height * scale * 2) / (Game.HEIGHT);
renderer.drawTextureFromTexture(this, x, y, w, h);
}
// Create new texture
public Texture() {
id = glGenTextures();
}
// Prepare texture to be drawn
public void prepare() {
bind();
uploadData(width, height, image);
}
// Bind texture
public void bind() {
glBindTexture(GL_TEXTURE_2D, id);
}
// Set parameter of texture
private void setParameter(int name, int value) {
glTexParameteri(GL_TEXTURE_2D, name, value);
}
// Upload image data with specified width and height
public void uploadData(int width, int height, ByteBuffer data) {
uploadData(GL_RGBA8, width, height, GL_RGBA, data);
}
// Upload image data with specified internal format, width, height, and image format
public void uploadData(int internalFormat, int width, int height, int format, ByteBuffer data) {
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
}
// Delete texture
public void delete() {
glDeleteTextures(id);
}
// Get width
public int getWidth() {
return width;
}
// Set width
public void setWidth(int width) {
if (width > 0) {
this.width = width;
}
}
// Get height
public int getHeight() {
return height;
}
// Set height
public void setHeight(int height) {
if (height > 0) {
this.height = height;
}
}
// Set image
public void setImage(ByteBuffer image) {
this.image = image;
}
public static Texture createTexture(int width, int height, ByteBuffer image) {
Texture texture = new Texture();
texture.setWidth(width);
texture.setHeight(height);
texture.setImage(image);
texture.prepare();
return texture;
}
public static Texture loadTexture(String path) {
ByteBuffer image;
int width, height;
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer w = stack.mallocInt(1);
IntBuffer h = stack.mallocInt(1);
IntBuffer comp = stack.mallocInt(1);
// Load image
stbi_set_flip_vertically_on_load(true);
image = stbi_load(path, w, h, comp, 4);
if (image == null) throw new RuntimeException("Could not load texture.");
width = w.get();
height = h.get();
}
return createTexture(width, height, image);
}
public void unbind() {
glBindTexture(GL_TEXTURE_2D, 0);
}
}

How to draw images fast in OGLES2.0 with android?

I have a little bit knowledge of android and OGLES2.0 but when i am drawing a picture it takes approximately 0.4ms. So I can draw only 40 pictures in 16ms in order to get 60fps for my game.
Here is my GLRenderer code:
The important parts are the Render-method and the Sprite Class.
public class GLRenderer implements Renderer {
// Our matrices
private final float[] mtrxProjection = new float[16];
private final float[] mtrxView = new float[16];
private final float[] mtrxProjectionAndView = new float[16];
// Geometric variables
public static int[] vertices;
public static short[] indices;
public static int[] uvs;
public IntBuffer vertexBuffer;
public ShortBuffer drawListBuffer;
public IntBuffer uvBuffer;
public List<Sprite> sprites;
public int[] texturenames;
public MainActivity mainActivity;
// Our screenresolution
float mScreenWidth = 1280;
float mScreenHeight = 720;
float ssu = 1.0f;
float ssx = 1.0f;
float ssy = 1.0f;
float swp = 1280.0f;
float shp = 720.0f;
Update update;
Images images;
// Misc
Context mContext;
long mLastTime;
int mProgram;
public GLRenderer (MainActivity mainActivity) {
this.mContext = mainActivity;
this.mainActivity = mainActivity;
this.mLastTime = System.currentTimeMillis() + 100;
this.sprites = new ArrayList<Sprite>();
}
public void onPause () {
/* Do stuff to pause the renderer */
}
public void onResume () {
/* Do stuff to resume the renderer */
this.mLastTime = System.currentTimeMillis();
}
#Override
public void onDrawFrame (GL10 unused) {
// Get the current time
long now = System.currentTimeMillis();
// We should make sure we are valid and sane
if (this.mLastTime > now) return;
// Get the amount of time the last frame took.
long elapsed = now - this.mLastTime;
System.out.println(elapsed);
// clear Screen and Depth Buffer, we have set the clear color as black.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Drawing all static things like the background
drawStatics();
for (Opponent opponent: this.update.opponents) {
int position = this.sprites.indexOf(opponent.getSprite());
// Rotate the sprite
this.sprites.get(position).rotate((float) Math.toRadians(opponent.getAngle()));
// Create the image information
SetupImage(opponent.getPicture(), 0);
// Update our example
UpdateSprite(position);
// Render our example
Render(this.mtrxProjectionAndView, 0);
}
for (Tower tower: this.update.towers) {
long a = System.nanoTime();
int position = this.sprites.indexOf(tower.getSprite());
// Rotate the sprite
this.sprites.get(position).rotate((float) Math.toRadians(tower.getAngle()));
// Create the image information
SetupImage(tower.getPicture(), 0);
// Update our example
UpdateSprite(position);
// Render our example
Render(this.mtrxProjectionAndView, 0);
System.out.println("time: " + (System.nanoTime() - a));
}
for (Bullet bullet: this.update.bullets) {
int position = this.sprites.indexOf(bullet.getSprite());
// Rotate the sprite
this.sprites.get(position).rotate((float) Math.toRadians(bullet.getAngle()));
// Create the image information
SetupImage(bullet.getPicture(), 0);
// Update our example
UpdateSprite(position);
// Render our example
Render(this.mtrxProjectionAndView, 0);
}
for (SuperExplosion explosion: this.update.explosions) {
int position = this.sprites.indexOf(explosion.getSprite());
// Rotate the sprite
// sprites.get(position).rotate((float)Math.toRadians(explosion.getAngle()));
// Create the image information
SetupImage(explosion.getPicture(), 0);
// Update our example
UpdateSprite(position);
// Render our example
Render(this.mtrxProjectionAndView, 0);
}
drawStatics2();
// Save the current time to see how long it took :).
this.mLastTime = now;
// System.out.println("höhe "+mScreenHeight+" breite "+ mScreenWidth);
}
private void drawStatics () {
// Ground
// Update our example
long h = System.nanoTime();
UpdateSprite(0);
// for (int i = 0; i < vertices.length; i++) {
// System.out.println("vertices [" + i + "]: " + vertices[i]);
// }
// Render our example
Render(this.mtrxProjectionAndView, 1);
System.out.println("time: " + (System.nanoTime() - h));
// Bar
// Update our example
UpdateSprite(1);
// Render our example
Render(this.mtrxProjectionAndView, 2);
if (!this.update.upgrade) {
if (this.update.normalSpeed) {
// Update our example
UpdateSprite(2);
// Render our example
Render(this.mtrxProjectionAndView, 3);
} else {
// Update our example
UpdateSprite(3);
// Render our example
Render(this.mtrxProjectionAndView, 4);
}
if (!this.update.start) {
// Update our example
UpdateSprite(4);
// Render our example
Render(this.mtrxProjectionAndView, 5);
}
}
// UpgradeBar
if (this.update.upgrade) {
// Update our example
UpdateSprite(6);
// Render our example
Render(this.mtrxProjectionAndView, 7);
}
h = System.nanoTime();
// Bunny1
// Update our example
UpdateSprite(7);
// Render our example
Render(this.mtrxProjectionAndView, 8);
System.out.println("time bunny1 " + (System.nanoTime() - h));
// CarrotTower
// Update our example
UpdateSprite(8);
// Render our example
Render(this.mtrxProjectionAndView, 9);
// MineThrower
// Update our example
UpdateSprite(9);
// Render our example
Render(this.mtrxProjectionAndView, 10);
// BunnyKing
// Update our example
UpdateSprite(10);
// Render our example
Render(this.mtrxProjectionAndView, 11);
}
private void drawStatics2 () {
// Life
// Update our example
UpdateSprite(5);
// Render our example
Render(this.mtrxProjectionAndView, 6);
}
private void Render (float[] m, int index) {
// get handle to vertex shader's vPosition member
int mPositionHandle = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "vPosition");
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_INT, false, 0, this.vertexBuffer);
// Get handle to texture coordinates location
int mTexCoordLoc = GLES20.glGetAttribLocation(riGraphicTools.sp_Image, "a_texCoord");
// Enable generic vertex attribute array
GLES20.glEnableVertexAttribArray(mTexCoordLoc);
// Prepare the texturecoordinates
GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_INT, false, 0, this.uvBuffer);
// Get handle to shape's transformation matrix
int mtrxhandle = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "uMVPMatrix");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, m, 0);
// Get handle to textures locations
int mSamplerLoc = GLES20.glGetUniformLocation(riGraphicTools.sp_Image, "s_texture");
// Set the sampler texture unit to 0, where we have saved the texture.
GLES20.glUniform1i(mSamplerLoc, index);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.texturenames[index]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
// Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT, this.drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordLoc);
}
#Override
public void onSurfaceChanged (GL10 gl, int width, int height) {
// We need to know the current width and height.
this.mScreenWidth = width;
this.mScreenHeight = height;
// Redo the Viewport, making it fullscreen.
GLES20.glViewport(0, 0, (int) this.mScreenWidth, (int) this.mScreenHeight);
// Clear our matrices
for (int i = 0; i < 16; i++) {
this.mtrxProjection[i] = 0.0f;
this.mtrxView[i] = 0.0f;
this.mtrxProjectionAndView[i] = 0.0f;
}
// Setup our screen width and height for normal sprite translation.
Matrix.orthoM(this.mtrxProjection, 0, 0f, this.mScreenWidth, 0.0f, this.mScreenHeight, 0, 50);
// Set the camera position (View matrix)
Matrix.setLookAtM(this.mtrxView, 0, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(this.mtrxProjectionAndView, 0, this.mtrxProjection, 0, this.mtrxView, 0);
// Setup our scaling system
SetupScaling();
// Create the GroundSprite
SetupTriangle(this.images.ground_1.getWidth(), this.images.ground_1.getHeight(), 0, 0);
SetupTriangle(this.images.bar1.getWidth(), this.images.bar1.getHeight(), 0, 0);
SetupTriangle(this.images.speed1.getWidth(), this.images.speed1.getHeight(), 0, 0);
SetupTriangle(this.images.speed2.getWidth(), this.images.speed2.getHeight(), 0, 0);
SetupTriangle(this.images.start.getWidth(), this.images.start.getHeight(), 0, 0);
SetupTriangle(this.images.life.getWidth(), this.images.life.getHeight(), 0, 0);
SetupTriangle(this.images.upgradeBar.getWidth(), this.images.upgradeBar.getHeight(), 0, 0);
SetupTriangle(this.images.bunny1.getWidth(), this.images.bunny1.getHeight(), 0, 0);
SetupTriangle(this.images.carrotTower.getWidth(), this.images.carrotTower.getHeight(), 0, 0);
SetupTriangle(this.images.mineThrower.getWidth(), this.images.mineThrower.getHeight(), 0, 0);
SetupTriangle(this.images.bunnyKing1.getWidth(), this.images.bunnyKing1.getHeight(), 0, 0);
this.sprites.get(0).translate(this.ssx * this.images.ground_1.getWidth() / 2, this.ssy * this.images.ground_1.getHeight() / 2);
this.sprites.get(1).translate(this.ssx * (this.images.bar1.getWidth() / 2) + this.ssx * 1084.0f, this.ssy * this.images.bar1.getHeight() / 2.0f);
this.sprites.get(2).translate(this.ssx * this.images.speed1.getWidth() / 2 + this.ssx * 20, this.mScreenHeight - (this.ssy * this.images.speed1.getHeight() / 2) - this.ssy * 5);
this.sprites.get(3).translate(this.ssx * this.images.speed2.getWidth() / 2 + this.ssx * 20, this.mScreenHeight - (this.ssy * this.images.speed2.getHeight() / 2) - this.ssy * 5);
this.sprites.get(4).translate(this.ssx * this.images.start.getWidth() / 2 + this.ssx * 120, this.mScreenHeight - (this.ssy * this.images.start.getHeight() / 2) - this.ssy * 5);
this.sprites.get(5).translate(this.ssx * this.images.life.getWidth() / 2 + this.ssx * 5, this.ssy * this.images.life.getHeight() / 2 + this.ssy * (20));
this.sprites.get(6).translate(this.ssx * this.images.upgradeBar.getWidth() / 2, this.ssy * this.images.upgradeBar.getHeight() / 2);
this.sprites.get(7).translate(this.ssx * (this.images.bunny1.getWidth()) + this.ssx * (1280 - 143 - (this.images.bunny1.getWidth() / 2)), this.mScreenHeight - (this.ssy * this.images.bunny1.getHeight() / 2) - (this.ssy * (235 + (this.images.bunny1.getHeight() / 2))));
this.sprites.get(8).translate(this.ssx * (this.images.carrotTower.getWidth()) + this.ssx * (1280 - 53 - (this.images.carrotTower.getWidth() / 2)), this.mScreenHeight - (this.ssy * this.images.carrotTower.getHeight() / 2) - (this.ssy * (235 + (this.images.carrotTower.getHeight() / 2))));
this.sprites.get(9).translate(this.ssx * (this.images.mineThrower.getWidth()) + this.ssx * (1280 - 143 - (this.images.mineThrower.getWidth() / 2)), this.mScreenHeight - (this.ssy * this.images.mineThrower.getHeight() / 2) - (this.ssy * (325 + (this.images.mineThrower.getHeight() / 2))));
this.sprites.get(10).translate(this.ssx * (this.images.bunnyKing1.getWidth()) + this.ssx * (1280 - 53 - (this.images.bunnyKing1.getWidth() / 2)), this.mScreenHeight - (this.ssy * this.images.bunnyKing1.getHeight() / 2) - (this.ssy * (325 + (this.images.bunnyKing1.getHeight() / 2))));
}
#Override
public void onSurfaceCreated (GL10 gl, EGLConfig config) {
// Setup our scaling system
SetupScaling();
// Set the clear color to black
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1);
// Blending
GLES20.glEnable(GLES20.GL_BLEND);
// How should opengl blend it
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
// Create the shaders, solid color
int vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_SolidColor);
int fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_SolidColor);
riGraphicTools.sp_SolidColor = GLES20.glCreateProgram(); // create empty
// OpenGL ES
// Program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, vertexShader); // add
// the
// vertex
// shader
// to
// program
GLES20.glAttachShader(riGraphicTools.sp_SolidColor, fragmentShader); // add
// the
// fragment
// shader
// to
// program
GLES20.glLinkProgram(riGraphicTools.sp_SolidColor); // creates OpenGL ES
// program
// executables
// Create the shaders, images
vertexShader = riGraphicTools.loadShader(GLES20.GL_VERTEX_SHADER, riGraphicTools.vs_Image);
fragmentShader = riGraphicTools.loadShader(GLES20.GL_FRAGMENT_SHADER, riGraphicTools.fs_Image);
riGraphicTools.sp_Image = GLES20.glCreateProgram(); // create empty
// OpenGL ES Program
GLES20.glAttachShader(riGraphicTools.sp_Image, vertexShader); // add the
// vertex
// shader
// to
// program
GLES20.glAttachShader(riGraphicTools.sp_Image, fragmentShader); // add
// the
// fragment
// shader
// to
// program
GLES20.glLinkProgram(riGraphicTools.sp_Image); // creates OpenGL ES
// program executables
// Set our shader programm
GLES20.glUseProgram(riGraphicTools.sp_Image);
// Creating a Images Object
this.images = new Images(this.mainActivity.getResources());
// Loading the Bitmaps
this.images.load();
Timer t = new Timer();
t.schedule(this.update = new Update(this.mainActivity, this, this.images), 16);
// Generate Textures, if more needed, alter these numbers.
this.texturenames = new int[24];
GLES20.glGenTextures(24, this.texturenames, 0);
SetupImageStatics();
}
private void SetupImageStatics () {
SetupImage(this.images.ground_1, 1);
SetupImage(this.images.bar1, 2);
SetupImage(this.images.speed1, 3);
SetupImage(this.images.speed2, 4);
SetupImage(this.images.start, 5);
SetupImage(this.images.life, 6);
SetupImage(this.images.upgradeBar, 7);
SetupImage(this.images.bunny1, 8);
SetupImage(this.images.carrotTower, 9);
SetupImage(this.images.mineThrower, 10);
SetupImage(this.images.bunnyKing1, 11);
}
public void SetupScaling () {
// The screen resolutions
this.swp = (this.mContext.getResources().getDisplayMetrics().widthPixels);
this.shp = (this.mContext.getResources().getDisplayMetrics().heightPixels);
// Orientation is assumed portrait
this.ssx = this.swp / 1280.0f;
this.ssy = this.shp / 720.0f;
// Get our uniform scaler
if (this.ssx > this.ssy) this.ssu = this.ssy;
else this.ssu = this.ssx;
}
public void processTouchEvent (MotionEvent me) {
switch (me.getAction()) {
case MotionEvent.ACTION_DOWN:
this.update.x = (int) me.getX();
this.update.y = (int) me.getY();
this.update.getItemOrUpgrade(this.update.x, this.update.y);
this.update.fastForward(this.update.x, this.update.y);
this.update.start(this.update.x, this.update.y);
this.update.nextUpgrade(this.update.x, this.update.y);
break;
case MotionEvent.ACTION_MOVE:
if (this.update.tower != 0) {
this.update.x = (int) me.getX();
this.update.y = (int) me.getY();
this.update.pathPlace(this.update.x, this.update.y);
}
break;
case MotionEvent.ACTION_UP:
this.update.x = (int) me.getX();
this.update.y = (int) me.getY();
this.update.placeOrReleaseItem = true;
break;
}
}
public void UpdateSprite (int location) {
// Get new transformed vertices
vertices = this.sprites.get(location).getTransformedVertices();
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
this.vertexBuffer = bb.asIntBuffer();
this.vertexBuffer.put(vertices);
this.vertexBuffer.position(0);
}
public void SetupImage (Bitmap bmp, int index) {
// Create our UV coordinates.
uvs = new int[] { 0, 0, 0, 1, 1, 1, 1, 0 };
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
this.uvBuffer = bb.asIntBuffer();
this.uvBuffer.put(uvs);
this.uvBuffer.position(0);
// // Retrieve our image from resources.
// int id =
// mContext.getResources().getIdentifier("drawable/ic_launcher", null,
// mContext.getPackageName());
//
// // Temporary create a bitmap
// Bitmap bmp = BitmapFactory.decodeResource(mContext.getResources(),
// id);
// Bind texture to texturename
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + index);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.texturenames[index]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
// // We are done using the bitmap so we should recycle it.
// bmp.recycle();
}
public void SetupTriangle (float width, float height, float x, float y) {
// Get information of sprite.
this.sprites.add(new Sprite(width, height, x, y));
vertices = this.sprites.get(this.sprites.size() - 1).getTransformedVertices();
// The order of vertexrendering for a quad
indices = new short[] { 0, 1, 2, 0, 2, 3 };
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
this.vertexBuffer = bb.asIntBuffer();
this.vertexBuffer.put(vertices);
this.vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
this.drawListBuffer = dlb.asShortBuffer();
this.drawListBuffer.put(indices);
this.drawListBuffer.position(0);
}
class Sprite {
float angle;
float scale;
RectF base;
Point translation;
public Sprite (float width, float height, float x, float y) {
// Initialise our intital size around the 0,0 point
this.base = new RectF(-(width / 2f) * GLRenderer.this.ssu, (height / 2f) * GLRenderer.this.ssu, (width / 2f) * GLRenderer.this.ssu, -(height / 2f) * GLRenderer.this.ssu);
// Initial translation
this.translation = new Point(Math.round(x * GLRenderer.this.ssu), Math.round(y * GLRenderer.this.ssu));
// We start with our inital size
this.scale = 1f;
// We start in our inital angle
this.angle = 0.0f;
}
public void translate (float deltax, float deltay) {
// Update our location.
this.translation.x += Math.round(deltax);
this.translation.y += Math.round(deltay);
}
public void scale (float deltas) {
this.scale += deltas;
}
public void rotate (float deltaa) {
this.angle += deltaa;
}
public int[] getTransformedVertices () {
// Start with scaling
int x1 = Math.round(this.base.left * this.scale);
int x2 = Math.round(this.base.right * this.scale);
int y1 = Math.round(this.base.bottom * this.scale);
int y2 = Math.round(this.base.top * this.scale);
// We now detach from our Rect because when rotating,
// we need the seperate points, so we do so in opengl order
Point one = new Point(x1, y2);
Point two = new Point(x1, y1);
Point three = new Point(x2, y1);
Point four = new Point(x2, y2);
// We create the sin and cos function once,
// so we do not have calculate them each time.
float s = (float) Math.sin(this.angle);
float c = (float) Math.cos(this.angle);
// Then we rotate each point
one.x = Math.round(x1 * c - y2 * s);
one.y = Math.round(x1 * s + y2 * c);
two.x = Math.round(x1 * c - y1 * s);
two.y = Math.round(x1 * s + y1 * c);
three.x = Math.round(x2 * c - y1 * s);
three.y = Math.round(x2 * s + y1 * c);
four.x = Math.round(x2 * c - y2 * s);
four.y = Math.round(x2 * s + y2 * c);
// Finally we translate the sprite to its correct position.
one.x += this.translation.x;
one.y += this.translation.y;
two.x += this.translation.x;
two.y += this.translation.y;
three.x += this.translation.x;
three.y += this.translation.y;
four.x += this.translation.x;
four.y += this.translation.y;
// We now return our float array of vertices.
return new int[] { Math.round(one.x), Math.round(one.y), 0, Math.round(two.x), Math.round(two.y), 0, Math.round(three.x), Math.round(three.y), 0, Math.round(four.x), Math.round(four.y), 0, };
}
}
public class Update extends TimerTask {
Bitmap ball;
int x, y;
int mapStage = StaticVariables.getMap();
int towerCost = 200;
boolean start = false;
int x2 = 0;
int y2 = 0;
public int points = 50000;
public boolean stageClear = false;
boolean everythingSpawned = false;
public int life = 200;
private int space = 40;
public boolean stageLose = false;
public boolean upgrade = false;
List<Opponent> opponents = new ArrayList<Opponent>();
List<Tower> towers = new ArrayList<Tower>();
List<Bullet> bullets = new ArrayList<Bullet>();
List<SuperExplosion> explosions = new ArrayList<SuperExplosion>();
List<Path> paths = new ArrayList<Path>();
List<int[]> stages = new ArrayList<int[]>();
private List<Integer> minesPassive = new ArrayList<Integer>();
private List<Integer> removeBulletsIndex = new ArrayList<Integer>();
List<Integer> removeMinesPassive = new ArrayList<Integer>();
public int time = 16;
public int speed = 1;
public int stageLevel = 1;
public boolean placePossible = true;
private int timeTimes = 0;
private int bubble = 0;
public boolean wait = false;
public int mapLength;
boolean change = false, change1 = false, change2 = false, change3 = false;
double maxX, maxY, minX, minY, abstandX2, abstandX, abstandY2, abstandY, shortestSpacing, shortest, angleMaxX, angleMaxY, angleMinX, angleMinY, angleAbstandX, angleAbstandY;
int shortestIndex, index, spawnCounter = 0;
private MediaPlayer bubblePopSound;
private int tower = 0;
int nextImage = 0;
private boolean normalSpeed = true;
float invisability = 1;
private int upgradeBarDiff = 0;
private boolean hideUpgradeBar = false;
public int iceBallRotation;
private int explosionTime;
private List<MinePlace> minesPlaced = new ArrayList<MinePlace>();
private int gameLocation = 0;
Localizer localizer;
int testCounter;
boolean load = true;
private boolean placeOrReleaseItem = false;
double viewWidth, viewHeight;
MergeSort mergeSort = new MergeSort();
GLRenderer glRenderer2;
public List<Integer> getMinesPassive () {
return this.minesPassive;
}
public void setMinesPassive (int index2, int minesPassive) {
this.minesPassive.set(index2, minesPassive);
}
public void addMinesPassive (int minesPassive) {
this.minesPassive.add(minesPassive);
}
public void addMinesPassive (int i, Integer minesPassive) {
this.minesPassive.add(i, minesPassive);
}
}
I hope you know a method to improve the drawing speed.
Thank you in advance!
It is a bit hard to say how to increase performance in your case. Too much noise in code from my perspective. But generally speaking there are some basic tips on how to increase performance during rendering:
- avoid unnecessary OpenGl state machine changes
- avoid unnecessary OpenGl state querying (e.g. GLES20.glGetAttribLocation())
- avoid memory allocations during rendering
- use simplest shaders possible
- use texture atlases to avoid necessary texture binds
- use VBOs whenever possible
For more information on subject try to read https://www.opengl.org/wiki/Performance. Or see this talk https://www.youtube.com/watch?v=YLVbLVtjDDw.
You may also consider using libGDX http://libgdx.badlogicgames.com/ it will handle drawing sprites efficient way for you.

LibGDX 3D increase perfomance

I'm working on a 3D game.
The game requires around 100 cubes to work, all cube have be dynamic.
I don't really know how much perfomance is required for a game like this, but i'm testing with a tablet with Mali-400 MP2 GPU, 1 GB ram, 1.5 GHz dual core. I know about rendering all of the cubes in one mesh, but then i can't move all of them separately.
This setup gives me a very vacillating fps. Jumping between 20 and 50, mostly under 30. (In emulator 10-15)
When the game starts, i build an arraylist of ModelInstances, all of them is using the same model.
model = new ModelBuilder().createBox(1f, 1f, 1f, new Material(ColorAttribute.createDiffuse(Color.GREEN)), Usage.Position | Usage.Normal);
// width,height,length = 5, creating a total of 125 cubes
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) {
if (this.map[x][y][z] > 0) {
this.modelInstances.add(instance = new ModelInstance(model));
instance.transform.translate(x, -(y * 1.5f), -z);
}
}
}
}
Rendering:
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
mb.begin(camera3D);
mb.render(this.modelInstances);
mb.end();
The camera initializing:
camera3D = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera3D.position.set(0f, 8f, 5f);
camera3D.lookAt(0, 0, 0);
camera3D.near = 1f;
camera3D.far = 300f;
camera3D.update();
What can i do to increase the perfomance?
Is the tablet too weak for a game like this? or the problem is the code?
EDIT:
Did some test with webGL too, same tablet, using chrome, rendering 125 cubes: stable 40-50 fps
You can batch all your cubes into a single Model and ModelInstance like this:
int width = 5;
int height = 5;
int length = 5;
int numCubes = width*height*length;
ModelBuilder mb = new ModelBuilder();
mb.begin();
MeshPartBuilder mpb = mb.part("cubes", GL20.GL_TRIANGLES, (Usage.Position | Usage.Normal), new Material(ColorAttribute.createDiffuse(Color.GREEN)));
for (int i=0; i<numCubes; i++){
mpb.box(1, 1, 1);
}
Model model = mb.end();
mBatchedCubesModelInstance = new ModelInstance(model);
But the tricky part is being able to move each of those cubes to a different location and be able to manipulate them independently.
Here's a Cube class that can manipulate the individual cubes from the above model. I think theoretically it should work with any Mesh you create that uses 24 unique vertices per cube, so you could add texture coordinates for example.
This also relies on position and then normal always being the first two Usage attributes of the mesh, so hopefully that holds true in libGDX's Mesh class.
This works basically by keeping track of it's index number in the base mesh (which cube number it is) so it can pick out the vertices that it needs to update in the vertices array. The vertices need to be copied into the mesh each frame.
public class Cube {
private int index;
int vertexFloatSize;
int posOffset;
int norOffset;
boolean hasColor;
int colOffset;
private Vector3 position = new Vector3();
private Matrix4 rotationTransform = new Matrix4().idt();
private Color color = new Color();
public float halfWidth, halfHeight, halfDepth;
private boolean transformDirty = false;
private boolean colorDirty = false;
static final Vector3 CORNER000 = new Vector3();
static final Vector3 CORNER010 = new Vector3();
static final Vector3 CORNER100 = new Vector3();
static final Vector3 CORNER110 = new Vector3();
static final Vector3 CORNER001 = new Vector3();
static final Vector3 CORNER011 = new Vector3();
static final Vector3 CORNER101 = new Vector3();
static final Vector3 CORNER111 = new Vector3();
static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};
static final Vector3 NORMAL0 = new Vector3();
static final Vector3 NORMAL1 = new Vector3();
static final Vector3 NORMAL2 = new Vector3();
static final Vector3 NORMAL3 = new Vector3();
static final Vector3 NORMAL4 = new Vector3();
static final Vector3 NORMAL5 = new Vector3();
static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};
public Cube(float x, float y, float z, float width, float height, float depth, int index,
VertexAttributes vertexAttributes, float[] meshVertices){
position.set(x,y,z);
this.halfWidth = width/2;
this.halfHeight = height/2;
this.halfDepth = depth/2;
this.index = index;
vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;
VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
hasColor = colorAttribute!=null;
if (hasColor){
colOffset = colorAttribute.offset/4;
this.setColor(Color.WHITE, meshVertices);
}
transformDirty = true;
}
public void setIndex(int index){
this.index = index;
transformDirty = true;
colorDirty = true;
}
/**
* Call this after moving and/or rotating.
*/
public void update(float[] meshVertices){
if (colorDirty && hasColor){
for (int faceIndex= 0; faceIndex<6; faceIndex++){
int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
meshVertices[vertexIndex] = color.r;
meshVertices[++vertexIndex] = color.g;
meshVertices[++vertexIndex] = color.b;
meshVertices[++vertexIndex] = color.a;
}
}
colorDirty = false;
}
if (!transformDirty){
return;
}
transformDirty = false;
CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
NORMAL0.set(0,0,-1).rot(rotationTransform);
NORMAL1.set(0,0,1).rot(rotationTransform);
NORMAL2.set(-1,0,0).rot(rotationTransform);
NORMAL3.set(1,0,0).rot(rotationTransform);
NORMAL4.set(0,-1,0).rot(rotationTransform);
NORMAL5.set(0,1,0).rot(rotationTransform);
for (int faceIndex= 0; faceIndex<6; faceIndex++){
int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;
vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
meshVertices[vertexIndex] = NORMALS[faceIndex].x;
meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
}
}
}
public Cube setColor(Color color){
if (hasColor){
this.color.set(color);
colorDirty = true;
}
return this;
}
public Cube translate(float x, float y, float z){
position.add(x,y,z);
transformDirty = true;
return this;
}
public Cube translateTo(float x, float y, float z){
position.set(x,y,z);
transformDirty = true;
return this;
}
public Cube rotate(float axisX, float axisY, float axisZ, float degrees){
rotationTransform.rotate(axisX, axisY, axisZ, degrees);
transformDirty = true;
return this;
}
public Cube rotateTo(float axisX, float axisY, float axisZ, float degrees){
rotationTransform.idt();
rotationTransform.rotate(axisX, axisY, axisZ, degrees);
transformDirty = true;
return this;
}
public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
int len = attributes.size();
for (int i = 0; i < len; i++)
if (attributes.get(i).usage == usage) return attributes.get(i);
return null;
}
}
To use this, first get a mesh reference and create the cubes:
mBatchedCubesMesh = model.meshes.get(0);
VertexAttributes vertexAttributes = mBatchedCubesMesh.getVertexAttributes();
int vertexFloatSize = vertexAttributes .vertexSize / 4; //4 bytes per float
mBatchedCubesVertices = new float[numCubes * 24 * vertexFloatSize]; //24 unique vertices per cube
mBatchedCubesMesh.getVertices(mBatchedCubesVertices);
mBatchedCubes = new Array<Cube>(numCubes);
int cubeNum = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < length; z++) {
mBatchedCubes.add(new Cube((x-(width/2f))*1.5f, -((y-(height/2f)) * 1.5f), -(z-(length/2f))*1.5f, 1,1,1, cubeNum++, vertexAttributes, mBatchedCubesVertices ));
}
}
}
Then in your render method:
mBatchedCubes.get(0).rotate(1, 1, 1, 180*delta); //example manipulation of a single cube
for (Cube cube : mBatchedCubes){ //must update any changed cubes.
cube.update(mBatchedCubesVertices);
}
mBatchedCubesMesh.setVertices(mBatchedCubesVertices); //apply changes to mesh
...
modelBatch.begin(camera);
modelBatch.render(mBatchedCubesModelInstance);
modelBatch.end();
Now CPU vertex manipulation is not as efficient as shader vertex manipulation, so this may become CPU bound if you're moving all your cubes around every frame. If you aren't rotating them much, it would probably help to create a separate "dirty" variable for rotation and only rotate if necessary in the update method.
EDIT: Update from this question
If you want to have transparency, then the cubes must be sortable, so they can be ordered from far to near for drawing. Their index values must be updated to the new order since that is how they are ordered into the mesh. Here is a Cube class that supports sorting (the color has to be tracked independently now since the cube might be moved to a different part of the mesh).
public class Cube implements Comparable<Cube>{
private int index;
int vertexFloatSize;
int posOffset;
int norOffset;
boolean hasColor;
int colOffset;
private Vector3 position = new Vector3();
private Matrix4 rotationTransform = new Matrix4().idt();
public float halfWidth, halfHeight, halfDepth;
private boolean transformDirty = false;
private boolean colorDirty = false;
private Color color = new Color();
float camDistSquared;
static final Vector3 CORNER000 = new Vector3();
static final Vector3 CORNER010 = new Vector3();
static final Vector3 CORNER100 = new Vector3();
static final Vector3 CORNER110 = new Vector3();
static final Vector3 CORNER001 = new Vector3();
static final Vector3 CORNER011 = new Vector3();
static final Vector3 CORNER101 = new Vector3();
static final Vector3 CORNER111 = new Vector3();
static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};
static final Vector3 NORMAL0 = new Vector3();
static final Vector3 NORMAL1 = new Vector3();
static final Vector3 NORMAL2 = new Vector3();
static final Vector3 NORMAL3 = new Vector3();
static final Vector3 NORMAL4 = new Vector3();
static final Vector3 NORMAL5 = new Vector3();
static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};
public Cube(float x, float y, float z, float width, float height, float depth, int index,
VertexAttributes vertexAttributes, float[] meshVertices){
position.set(x,y,z);
this.halfWidth = width/2;
this.halfHeight = height/2;
this.halfDepth = depth/2;
this.index = index;
vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;
VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
hasColor = colorAttribute!=null;
if (hasColor){
colOffset = colorAttribute.offset/4;
this.setColor(Color.WHITE, meshVertices);
}
transformDirty = true;
}
public void updateCameraDistance(Camera cam){
camDistSquared = cam.position.dst2(position);
}
/**
* Call this after moving and/or rotating.
*/
public void update(float[] meshVertices){
if (transformDirty){
transformDirty = false;
CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
NORMAL0.set(0,0,-1).rot(rotationTransform);
NORMAL1.set(0,0,1).rot(rotationTransform);
NORMAL2.set(-1,0,0).rot(rotationTransform);
NORMAL3.set(1,0,0).rot(rotationTransform);
NORMAL4.set(0,-1,0).rot(rotationTransform);
NORMAL5.set(0,1,0).rot(rotationTransform);
for (int faceIndex= 0; faceIndex<6; faceIndex++){
int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;
vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
meshVertices[vertexIndex] = NORMALS[faceIndex].x;
meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
}
}
}
if (colorDirty){
colorDirty = false;
for (int faceIndex= 0; faceIndex<6; faceIndex++){
int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
meshVertices[vertexIndex] = color.r;
meshVertices[++vertexIndex] = color.g;
meshVertices[++vertexIndex] = color.b;
meshVertices[++vertexIndex] = color.a;
}
}
}
}
public Cube setColor(Color color, float[] meshVertices){
if (hasColor){
this.color.set(color);
colorDirty = true;
}
return this;
}
public void setIndex(int index){
if (this.index != index){
transformDirty = true;
colorDirty = true;
this.index = index;
}
}
public Cube translate(float x, float y, float z){
position.add(x,y,z);
transformDirty = true;
return this;
}
public Cube translateTo(float x, float y, float z){
position.set(x,y,z);
transformDirty = true;
return this;
}
public Cube rotate(float axisX, float axisY, float axisZ, float degrees){
rotationTransform.rotate(axisX, axisY, axisZ, degrees);
transformDirty = true;
return this;
}
public Cube rotateTo(float axisX, float axisY, float axisZ, float degrees){
rotationTransform.idt();
rotationTransform.rotate(axisX, axisY, axisZ, degrees);
transformDirty = true;
return this;
}
public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
int len = attributes.size();
for (int i = 0; i < len; i++)
if (attributes.get(i).usage == usage) return attributes.get(i);
return null;
}
#Override
public int compareTo(Cube other) {
//This is a simple sort based on center point distance to camera. A more
//sophisticated sorting method might be required if the cubes are not all the same
//size (such as calculating which of the 8 vertices is closest to the camera
//and using that instead of the center point).
if (camDistSquared>other.camDistSquared)
return -1;
return camDistSquared<other.camDistSquared ? 1 : 0;
}
}
Here is how you would sort them:
for (Cube cube : mBatchedCubes){
cube.updateCameraDistance(camera);
}
mBatchedCubes.sort();
int index = 0;
for (Cube cube : mBatchedCubes){
cube.setIndex(index++);
}

Android Open GL ES z-buffer not working on GL_POINTS(?)

I'm beginning to think the z-buffer in GL ES (Android) doesn't work on GL_POINTS(?) but I cant find anything in the docs.
Im looking to make a cube primative out of points and render them to a scene using the Z buffer - I have a similar class (Triangle) which renders and does use the z-buffer?
PS: I know my code is messy (I'm getting annoyed with this) I've had a bit of a tidy to post this.
Code listed bellow:
class MyGLRenderer implements GLSurfaceView.Renderer {
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"uniform float vSpatical;" +
"uniform vec3 vCamPos;" +
"float vDistance;" +
"float vDisX;" +
"float vDisY;" +
"float vDisZ;" +
"float vA;" +
"void main() {" +
" gl_Position = vPosition * uMVPMatrix;" +
" vDisX = vCamPos.x-vPosition.x;" +
" vDisY = vCamPos.y-vPosition.y;" +
" vDisZ = vCamPos.z-vPosition.z;" +
" vA = 3.14159265359 * (vSpatical*vSpatical);" +
" vDistance = sqrt((vDisX*vDisX) + (vDisY*vDisY) + (vDisZ*vDisZ));" +
" gl_PointSize = 1.0 * vA / (vDistance*vDistance);" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private final float[] mRotationMatrix = new float[16];
float[] mCamPos = {0f, 0f, 6f};
Triangle mTriangle;
Cube mCube;
int mProgram;
float mAngle = 0;
#Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
mTriangle = new Triangle();
mCube = new Cube();
GLES20.glClearColor(0.0f, 0.4f, 0.4f, 1.0f);
GLES20.glClearDepthf(1.0f);
GLES20.glEnable(GL10.GL_DEPTH_TEST);
GLES20.glDepthFunc(GL10.GL_LEQUAL);
GLES20.glDepthMask(true);
GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
GLES10.glShadeModel(GL10.GL_SMOOTH);
}
#Override
public void onDrawFrame(GL10 unused) {
float[] scratch = new float[16];
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, 0, mCamPos[0], mCamPos[1], mCamPos[2], 0f, 0f, 0f, 0f, 1.0f, 0.0f);
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0f, 1.0f, 1.0f);
mAngle += 0.09f;
Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
mTriangle.draw(mProgram, scratch);
mCube.draw(mProgram, scratch, mCamPos);
}
#Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
public static int loadShader(int type, String shaderCode){
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
class Cube {
private final FloatBuffer vertexBuffer, vBuff2, vBuff3;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
private int vertexCount;
private final int vertexStride = COORDS_PER_VERTEX * 4;
private float mSpaticalExtent[];
static final int COORDS_PER_VERTEX = 3;
static float triangleCoords[];
static float sides[];
static float topbottom[];
float color[] = { 0.93671875f, 0.46953125f, 0.22265625f, 1.0f };
float color2[] = { 0.43671875f, 0.96953125f, 0.22265625f, 1.0f };
float color3[] = { 0.33671875f, 0.46953125f, 0.82265625f, 1.0f };
public Cube() {
MakeCube(0, 0, 0, 10);
ByteBuffer bb = ByteBuffer.allocateDirect(
triangleCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);
ByteBuffer bb2 = ByteBuffer.allocateDirect(
sides.length * 4);
bb2.order(ByteOrder.nativeOrder());
vBuff2 = bb2.asFloatBuffer();
vBuff2.put(sides);
vBuff2.position(0);
ByteBuffer bb3 = ByteBuffer.allocateDirect(
topbottom.length * 4);
bb3.order(ByteOrder.nativeOrder());
vBuff3 = bb3.asFloatBuffer();
vBuff3.put(topbottom);
vBuff3.position(0);
}
private void MakeCube(float x, float y, float z, float width) {
triangleCoords = new float[(int)width*(int)width*6]; // Width * Width * 6 Points * 3 Float per point
sides = new float[(int)width*(int)width*6];
topbottom = new float[(int)width*(int)width*6];
vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
mSpaticalExtent = new float[1];
mSpaticalExtent[0] = width;
x -= width/2f;
y -= width/2f;
z -= width/2f;
int currentArrayPoint = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < width; j++)
{
// FRONT AND BACK WALLS- X AND Y CHANGE Z DOESN'T
//
triangleCoords[currentArrayPoint++] = (x + j) / width;
triangleCoords[currentArrayPoint++] = (y + i) / width;
triangleCoords[currentArrayPoint++] = z / width;
//
triangleCoords[currentArrayPoint++] = (x + j) / width;
triangleCoords[currentArrayPoint++] = (y + i) / width;
triangleCoords[currentArrayPoint++] = (z + width) / width;
}
}
currentArrayPoint = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < width; j++)
{
// SIDE WALLS, Y AND Z CHANGE X DOESN'T
//
sides[currentArrayPoint++] = x / width;
sides[currentArrayPoint++] = (y + i) / width;
sides[currentArrayPoint++] = (z + j) / width;
//
sides[currentArrayPoint++] = (x + width) / width;
sides[currentArrayPoint++] = (y + i) / width;
sides[currentArrayPoint++] = (z + j) / width;
}
}
currentArrayPoint = 0;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < width; j++)
{
// TOP AND BOTTOM WALLS, X AND Z CHANGE Y DOESN'T
//
topbottom[currentArrayPoint++] = (z + j) / width;
topbottom[currentArrayPoint++] = (y) / width;
topbottom[currentArrayPoint++] = (x + i) / width;
//
topbottom[currentArrayPoint++] = (z + j) / width;
topbottom[currentArrayPoint++] = (y + width) / width;
topbottom[currentArrayPoint++] = (x + i) / width;
}
}
}
public void draw(int mProgram, float[] mvpMatrix, float[] mCamPos) {
GLES20.glUseProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
GLES20.glEnableVertexAttribArray(mPositionHandle);
int mCamHandle = GLES20.glGetUniformLocation(mProgram, "vCamPos");
GLES20.glUniform3fv(mCamHandle, 1, mCamPos, 0);
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
int mSpaticalHandle = GLES20.glGetUniformLocation(mProgram, "vSpatical");
GLES20.glUniform1fv(mSpaticalHandle, 1, mSpaticalExtent, 0);
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, vertexCount);
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vBuff2);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color2, 0);
mSpaticalHandle = GLES20.glGetUniformLocation(mProgram, "vSpatical");
GLES20.glUniform1fv(mSpaticalHandle, 1, mSpaticalExtent, 0);
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, vertexCount);
GLES20.glVertexAttribPointer(
mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vBuff3);
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
GLES20.glUniform4fv(mColorHandle, 1, color3, 0);
mSpaticalHandle = GLES20.glGetUniformLocation(mProgram, "vSpatical");
GLES20.glUniform1fv(mSpaticalHandle, 1, mSpaticalExtent, 0);
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, vertexCount);
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
After looking at your shaders, I noticed the following:
gl_Position = vPosition * uMVPMatrix; // D3D convention
This is completely wrong for OpenGL (which uses column-major matrices and post-multiplication).
It should be:
gl_Position = uMVPMatrix * vPosition; // OpenGL convention
I checked the code you use to send your MVP matrix, and it does not transpose the matrix, so your shader is what is backwards.
You could also solve this by setting the transpose parameter in GLES20.glUniformMatrix4fv to true, but it is best to stick with traditional OpenGL convention and use column-major matrices. It is much easier to diagnose these sorts of things if you do.

Moving cube in opengl on android help

After a lot of trial and error I was able to draw a cube and a tile map for the cube to be infront of. I added input so that when you move your finger, the camera moves. But now I want to add the next step in my games progression. I want it to have my cube move to where i pressed my finger in 3d space. It sounds a bit difficult. What I wanted to do is have you use one finger to slide the camera around the map and the other to double tap and move. So far my cube jitters and wobbles in place but doesn't move to where I want it to go. Of course none of my code is perfect and my following of gluunproject might be far from correct. Here is my code so far
The cube creator and update code//its short
public shapemaker(int x, int y, int z,int width)
{
centerx =x;
centery =y;
centerz =z;
mysquare1= new square(centerx,centery,centerz-5,1,.0f,.0f,1f,"vert");
mysquare2= new square(centerx-1,centery,centerz-6f,1,.0f,1.0f,.0f,"side");
mysquare3= new square(centerx,centery,centerz-7,1,1.0f,.0f,.0f,"vert");
mysquare4= new square(centerx+1,centery,centerz-6f,1,.0f,.5f,.5f,"side");
mysquare5= new square(centerx,centery-1,centerz-6f,1,.5f,.5f,.0f,"horiz");
mysquare6= new square(centerx,centery+1,centerz-6f,1,.5f,.0f,.5f,"horiz");
}
public void updatecube(float x, float y, float z)
{
centerx =x;
centery =y;
centerz =z;
mysquare1= new square(centerx,centery,centerz-5,1,.0f,.0f,1f,"vert");
mysquare2= new square(centerx-1,centery,centerz-6f,1,.0f,1.0f,.0f,"side");
mysquare3= new square(centerx,centery,centerz-7,1,1.0f,.0f,.0f,"vert");
mysquare4= new square(centerx+1,centery,centerz-6f,1,.0f,.5f,.5f,"side");
mysquare5= new square(centerx,centery-1,centerz-6f,1,.5f,.5f,.0f,"horiz");
mysquare6= new square(centerx,centery+1,centerz-6f,1,.5f,.0f,.5f,"horiz");
}
public void drawcube(GL10 gl)
{
mysquare1.draw(gl);
mysquare2.draw(gl);
mysquare3.draw(gl);
mysquare4.draw(gl);
mysquare5.draw(gl);
mysquare6.draw(gl);
}
public float getcenterx()
{
return centerx;
}
public float getcentery()
{
return centery;
}
public float getcenterz()
{
return centerz;
}
Here is the actual implementation code , it is based off of googles intro to opengl
class ClearGLSurfaceView extends GLSurfaceView {
private static final String BufferUtils = null;
float x =0;
float y =0;
float z =0f;
float prevx=0;
float prevy=0;
GL10 mygl;
// GL gl;
float mywidth;
float myheight;
public ClearGLSurfaceView(Context context,float width, float height) {
super(context);
mRenderer = new ClearRenderer();
setRenderer(mRenderer);
mywidth = width;
myheight = height;
}
#Override
public boolean onTouchEvent(final MotionEvent event)
{
queueEvent(new Runnable() {
// Find the index of the active pointer and fetch its position
public void run()
{
float curx = event.getX();
float cury = event.getY();
final int action = event.getAction();
if(action == MotionEvent.ACTION_MOVE)
{
if(curx>prevx)
{
x-=.1f;
}
if(curx<prevx)
{
x+=.1f;
}
if(cury>prevy)
{
y+=.1f;
}
if(cury<prevy)
{
y-=.1f;
}
}
if(action == MotionEvent.ACTION_DOWN)
{
// GLU.gluUnProject(winX, winY, winZ, model,
// modelOffset, project, projectOffset, view, viewOffset, obj, objOffset)
vector2d moveto = new vector2d(0,0);
moveto = mRenderer.getworkcoords(curx,cury);
Log.i("move to ", "x "+moveto.x+" y "+ moveto.y+" z " + moveto.z);
mRenderer.updatemoveto(moveto.x, moveto.y);
}
prevx= curx;
prevy = cury;
mRenderer.updatecamera(x, y, z);
}
});
return true;
}
ClearRenderer mRenderer;
}
class ClearRenderer implements GLSurfaceView.Renderer {
GL10 mygl;
GL11 mygl11;
int viewport[] = new int[4];
float modelview[] = new float[16];
float projection[] = new float[16];
float wcoord[] = new float[4];
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // OpenGL docs.
gl.glShadeModel(GL10.GL_SMOOTH);// OpenGL docs.
gl.glClearDepthf(1.0f);// OpenGL docs.
gl.glEnable(GL10.GL_DEPTH_TEST);// OpenGL docs.
gl.glDepthFunc(GL10.GL_LEQUAL);// OpenGL docs.
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs.
GL10.GL_NICEST);
mygl = gl;
mygl11 = (GL11) gl;
int index;
int counter = 0;
float zcoord = -7.0f;
mygl11.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, modelview, 0);
mygl11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, projection, 0);
mygl11.glGetIntegerv(GL11.GL_VIEWPORT, viewport, 0);
for(int x=0;x<11;x++)
{
for(int y =0;y<10;y++)
{
index = tilemap1[y][x];
if(index==0)
{
tiles[counter]=new square(x,y,zcoord,0.5f,1.0f,0.0f,0f,"vert");
}
if(index==1)
{
tiles[counter]=new square(x,y,zcoord,0.5f,0f,1.0f,0f,"vert");
}
if(index==2)
{
tiles[counter]=new square(x,y,zcoord,0.5f,0.0f,0.0f,1f,"vert");
}
counter++;
}
}
}
public vector2d getworkcoords(float width,float height)
{
float[] depth = new float[1];
float winx = width;
float winy =viewport[3]-height;
//vector2d position = new vector2d(0,0);
int test = GLU.gluUnProject(winx, winy,0.0f, modelview, 0, projection,
0, viewport, 0, wcoord, 0);
vector2d v = new vector2d(0,0);
v.x = wcoord[0];
v.y = wcoord[1];
v.z = wcoord[2];
return v ;
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0, 0, w, h);
gl.glMatrixMode(GL10.GL_PROJECTION);// OpenGL docs.
gl.glLoadIdentity();// OpenGL docs.
GLU.gluPerspective(gl, 45.0f,
(float) w / (float) h,
0.1f, 100.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);// OpenGL docs.
gl.glLoadIdentity();// OpenGL docs.
}
public float movetox;
public float movetoy;
float currentposx;
float currentposy;
public void updatemoveto(float x , float y)
{
movetox = x;
movetoy = y;
}
public void onDrawFrame(GL10 gl) {
gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
currentposx = mycube.getcenterx();
currentposy = mycube.getcentery();
float x = movetox -currentposx;
float y = movetoy - currentposy;
double angle = Math.atan2(y, x);
double mx =.5f* Math.cos(angle);
double my = .5f* Math.sin(angle);
double mmx = mx+ currentposx;
double mmy = my+ currentposy;
mycube.updatecube((float)(mmx), (float)(mmy),0);
mycube.drawcube(gl);
for(int i = 0;i<110;i++)
{
tiles[i].draw(gl);
}
}
public void setColor(float r, float g, float b) {
mRed = r;
mGreen = g;
mBlue = b;
}
public void updatecamera(float myx, float myy, float myz)
{
mygl.glLoadIdentity();// OpenGL docs.
GLU.gluLookAt(mygl, myx, myy, myz, myx, myy, myz-10, 0, 1, 0);
}
private float mRed;
private float mGreen;
private float mBlue;
int tilemap1[][] = {
{0,0,0,0,0,0,0,0,0,0,0},
{0,1,2,1,1,1,1,1,1,1,0},
{0,1,2,1,1,1,1,1,1,1,0},
{0,1,2,1,1,1,1,1,1,1,0},
{0,1,2,2,2,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,0},
{0,1,1,1,2,2,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0},
};
square[] tiles = new square[110];
shapemaker mycube = new shapemaker(0,0,0,1);
}
I did not include the on create method and the on pause ones. I would also like advice on designing the code structure because my implementation is far from perfect.
So in short. I would like help figuring out how to modify the code I made to have a cube move to where i press. And how you would improve the code
Thanks
First of all,
you don't need to recreate vertices on every moving. It is very complex task and it will low the framerate (FPS). In you code you have model matrix. Usually it is responsible for translating, scaling rotating of your model(mesh, cube in your example). In common case you have to translate the model matrix and then multiple model, projection and view matrixes, you will get the matrix for your device screen. Then by multiplication of vertex postion and this matrix you'll get the right position of vertex.
you can look at this question/answer

Categories

Resources