--- /dev/null
+package com.badlogic.gdx.graphics.glutils;\r
+\r
+import java.nio.ByteBuffer;\r
+import java.nio.ByteOrder;\r
+import java.nio.FloatBuffer;\r
+import java.nio.IntBuffer;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.Application.ApplicationType;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.GL11;\r
+import com.badlogic.gdx.graphics.GL20;\r
+import com.badlogic.gdx.graphics.VertexAttribute;\r
+import com.badlogic.gdx.graphics.VertexAttributes;\r
+import com.badlogic.gdx.graphics.VertexAttributes.Usage;\r
+import com.badlogic.gdx.utils.BufferUtils;\r
+import com.badlogic.gdx.utils.GdxRuntimeException;\r
+\r
+/**\r
+ * <p>\r
+ * A {@link VertexData} implementation based on OpenGL vertex buffer objects.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * If the OpenGL ES context was lost you can call {@link #invalidate()} to\r
+ * recreate a new OpenGL vertex buffer object. This class can be used seamlessly\r
+ * with OpenGL ES 1.x and 2.0.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * In case OpenGL ES 2.0 is used in the application the data is bound via\r
+ * glVertexAttribPointer() according to the attribute aliases specified via\r
+ * {@link VertexAttributes} in the constructor.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * Uses indirect Buffers on Android 1.5/1.6 to fix GC invocation due to leaking\r
+ * PlatformAddress instances.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * VertexBufferObjects must be disposed via the {@link #dispose()} method when no longer needed\r
+ * </p>\r
+ * \r
+ * @author mzechner\r
+ * \r
+ */\r
+public class VertexBufferObjectSubData implements VertexData {\r
+ final static IntBuffer tmpHandle = BufferUtils.newIntBuffer(1);\r
+\r
+ final VertexAttributes attributes;\r
+ final FloatBuffer buffer;\r
+ final ByteBuffer byteBuffer;\r
+ int bufferHandle;\r
+ final boolean isDirect;\r
+ final boolean isStatic;\r
+ final int usage;\r
+ boolean isDirty = false;\r
+ boolean isBound = false;\r
+\r
+ /**\r
+ * Constructs a new interleaved VertexBufferObject.\r
+ * \r
+ * @param isStatic\r
+ * whether the vertex data is static.\r
+ * @param numVertices\r
+ * the maximum number of vertices\r
+ * @param attributes\r
+ * the {@link VertexAttribute}s.\r
+ */\r
+ public VertexBufferObjectSubData(boolean isStatic, int numVertices,\r
+ VertexAttribute... attributes) {\r
+ this.isStatic = isStatic;\r
+ this.attributes = new VertexAttributes(attributes);\r
+// if (Gdx.app.getType() == ApplicationType.Android\r
+// && Gdx.app.getVersion() < 5) {\r
+// byteBuffer = ByteBuffer.allocate(this.attributes.vertexSize\r
+// * numVertices);\r
+// byteBuffer.order(ByteOrder.nativeOrder());\r
+// isDirect = false;\r
+// } else {\r
+ byteBuffer = ByteBuffer.allocateDirect(this.attributes.vertexSize\r
+ * numVertices);\r
+ byteBuffer.order(ByteOrder.nativeOrder());\r
+ isDirect = true;\r
+// }\r
+ usage = isStatic ? GL11.GL_STATIC_DRAW : GL11.GL_DYNAMIC_DRAW;\r
+ buffer = byteBuffer.asFloatBuffer();\r
+ bufferHandle = createBufferObject();\r
+ }\r
+\r
+ private int createBufferObject() {\r
+ if (Gdx.gl20 != null) { \r
+ Gdx.gl20.glGenBuffers(1, tmpHandle);\r
+ Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, tmpHandle.get(0));\r
+ Gdx.gl20.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.capacity(), null, usage);\r
+ Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);\r
+ }\r
+ else {\r
+ Gdx.gl11.glGenBuffers(1, tmpHandle);\r
+ Gdx.gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, tmpHandle.get(0));\r
+ Gdx.gl11.glBufferData(GL11.GL_ARRAY_BUFFER, byteBuffer.capacity(), null, usage);\r
+ Gdx.gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);\r
+ }\r
+ return tmpHandle.get(0);\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public VertexAttributes getAttributes() {\r
+ return attributes;\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public int getNumVertices() {\r
+ return byteBuffer.limit() / attributes.vertexSize;\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ public int getNumMaxVertices() {\r
+ return byteBuffer.capacity() / attributes.vertexSize;\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public FloatBuffer getBuffer() {\r
+ isDirty = true;\r
+ return buffer;\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public void setVertices(float[] vertices, int offset, int count) {\r
+ isDirty = true;\r
+ if (isDirect) {\r
+ BufferUtils.copy(vertices, byteBuffer, count, offset);\r
+ buffer.position(0);\r
+ buffer.limit(count);\r
+ } else {\r
+ buffer.clear();\r
+ buffer.put(vertices, offset, count);\r
+ buffer.flip();\r
+ byteBuffer.position(0);\r
+ byteBuffer.limit(buffer.limit() << 2);\r
+ }\r
+\r
+ if (isBound) {\r
+ if (Gdx.gl20 != null) {\r
+ GL20 gl = Gdx.gl20; \r
+ gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, byteBuffer.limit(),\r
+ byteBuffer);\r
+ } else {\r
+ GL11 gl = Gdx.gl11; \r
+ gl.glBufferSubData(GL11.GL_ARRAY_BUFFER, 0, byteBuffer.limit(),\r
+ byteBuffer);\r
+ }\r
+ isDirty = false;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public void bind() {\r
+ GL11 gl = Gdx.gl11;\r
+\r
+ gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, bufferHandle);\r
+ if (isDirty) {\r
+ gl.glBufferSubData(GL11.GL_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer);\r
+// gl.glBufferData(GL11.GL_ARRAY_BUFFER, byteBuffer.limit(),\r
+// byteBuffer, usage);\r
+ isDirty = false;\r
+ }\r
+\r
+ int textureUnit = 0;\r
+ int numAttributes = attributes.size();\r
+\r
+ for (int i = 0; i < numAttributes; i++) {\r
+ VertexAttribute attribute = attributes.get(i);\r
+\r
+ switch (attribute.usage) {\r
+ case Usage.Position:\r
+ gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);\r
+ gl.glVertexPointer(attribute.numComponents, GL10.GL_FLOAT,\r
+ attributes.vertexSize, attribute.offset);\r
+ break;\r
+\r
+ case Usage.Color:\r
+ case Usage.ColorPacked:\r
+ int colorType = GL10.GL_FLOAT;\r
+ if (attribute.usage == Usage.ColorPacked)\r
+ colorType = GL11.GL_UNSIGNED_BYTE;\r
+\r
+ gl.glEnableClientState(GL10.GL_COLOR_ARRAY);\r
+ gl.glColorPointer(attribute.numComponents, colorType,\r
+ attributes.vertexSize, attribute.offset);\r
+ break;\r
+\r
+ case Usage.Normal:\r
+ gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);\r
+ gl.glNormalPointer(GL10.GL_FLOAT, attributes.vertexSize,\r
+ attribute.offset);\r
+ break;\r
+\r
+ case Usage.TextureCoordinates:\r
+ gl.glClientActiveTexture(GL10.GL_TEXTURE0 + textureUnit);\r
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);\r
+ gl.glTexCoordPointer(attribute.numComponents, GL10.GL_FLOAT,\r
+ attributes.vertexSize, attribute.offset);\r
+ textureUnit++;\r
+ break;\r
+\r
+ default:\r
+ throw new GdxRuntimeException("unkown vertex attribute type: "\r
+ + attribute.usage);\r
+ }\r
+ }\r
+\r
+ isBound = true;\r
+ }\r
+\r
+ /**\r
+ * Binds this VertexBufferObject for rendering via glDrawArrays or\r
+ * glDrawElements\r
+ * \r
+ * @param shader\r
+ * the shader\r
+ */\r
+ public void bind(ShaderProgram shader) {\r
+ GL20 gl = Gdx.gl20;\r
+\r
+ gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, bufferHandle);\r
+ if (isDirty) {\r
+ gl.glBufferSubData(GL11.GL_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer);\r
+// gl.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(),\r
+// byteBuffer, usage);\r
+ isDirty = false;\r
+ }\r
+\r
+ int numAttributes = attributes.size();\r
+ for (int i = 0; i < numAttributes; i++) {\r
+ VertexAttribute attribute = attributes.get(i);\r
+ shader.enableVertexAttribute(attribute.alias);\r
+ int colorType = GL20.GL_FLOAT;\r
+ boolean normalize = false;\r
+ if (attribute.usage == Usage.ColorPacked) {\r
+ colorType = GL20.GL_UNSIGNED_BYTE;\r
+ normalize = true;\r
+ }\r
+ shader.setVertexAttribute(attribute.alias, attribute.numComponents,\r
+ colorType, normalize, attributes.vertexSize,\r
+ attribute.offset);\r
+ }\r
+ isBound = true;\r
+ }\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public void unbind() {\r
+ GL11 gl = Gdx.gl11;\r
+ int textureUnit = 0;\r
+ int numAttributes = attributes.size(); \r
+\r
+ for (int i = 0; i < numAttributes; i++) {\r
+\r
+ VertexAttribute attribute = attributes.get(i);\r
+ switch (attribute.usage) {\r
+ case Usage.Position:\r
+ break; // no-op, we also need a position bound in gles\r
+ case Usage.Color:\r
+ case Usage.ColorPacked:\r
+ gl.glDisableClientState(GL11.GL_COLOR_ARRAY);\r
+ break;\r
+ case Usage.Normal:\r
+ gl.glDisableClientState(GL11.GL_NORMAL_ARRAY);\r
+ break;\r
+ case Usage.TextureCoordinates:\r
+ gl.glClientActiveTexture(GL11.GL_TEXTURE0 + textureUnit);\r
+ gl.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);\r
+ textureUnit++;\r
+ break;\r
+ default:\r
+ throw new GdxRuntimeException("unkown vertex attribute type: "\r
+ + attribute.usage);\r
+ }\r
+ }\r
+ \r
+ gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);\r
+ isBound = false;\r
+ }\r
+\r
+ /**\r
+ * Unbinds this VertexBufferObject.\r
+ * \r
+ * @param shader\r
+ * the shader\r
+ */\r
+ public void unbind(ShaderProgram shader) {\r
+ GL20 gl = Gdx.gl20;\r
+ int numAttributes = attributes.size();\r
+ for (int i = 0; i < numAttributes; i++) {\r
+ VertexAttribute attribute = attributes.get(i);\r
+ shader.disableVertexAttribute(attribute.alias);\r
+ }\r
+ gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);\r
+ isBound = false;\r
+ }\r
+\r
+ /**\r
+ * Invalidates the VertexBufferObject so a new OpenGL buffer handle is\r
+ * created. Use this in case of a context loss.\r
+ */\r
+ public void invalidate() {\r
+ bufferHandle = createBufferObject();\r
+ isDirty = true;\r
+ }\r
+\r
+ /**\r
+ * Disposes of all resources this VertexBufferObject uses.\r
+ */\r
+ @Override\r
+ public void dispose() {\r
+ if (Gdx.gl20 != null) {\r
+ tmpHandle.clear();\r
+ tmpHandle.put(bufferHandle);\r
+ tmpHandle.flip();\r
+ GL20 gl = Gdx.gl20;\r
+ gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);\r
+ gl.glDeleteBuffers(1, tmpHandle);\r
+ bufferHandle = 0;\r
+ } else {\r
+ tmpHandle.clear();\r
+ tmpHandle.put(bufferHandle);\r
+ tmpHandle.flip();\r
+ GL11 gl = Gdx.gl11;\r
+ gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);\r
+ gl.glDeleteBuffers(1, tmpHandle);\r
+ bufferHandle = 0;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Returns the VBO handle\r
+ * @return the VBO handle\r
+ */\r
+ public int getBufferHandle() {\r
+ return bufferHandle;\r
+ }\r
+}\r