1/******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17package com.badlogic.gdx.graphics.glutils; 18 19import java.nio.ByteBuffer; 20import java.nio.FloatBuffer; 21 22import com.badlogic.gdx.Gdx; 23import com.badlogic.gdx.graphics.GL20; 24import com.badlogic.gdx.graphics.VertexAttribute; 25import com.badlogic.gdx.graphics.VertexAttributes; 26import com.badlogic.gdx.utils.BufferUtils; 27import com.badlogic.gdx.utils.GdxRuntimeException; 28 29/** <p> 30 * A {@link VertexData} implementation based on OpenGL vertex buffer objects. 31 * </p> 32 * 33 * <p> 34 * If the OpenGL ES context was lost you can call {@link #invalidate()} to recreate a new OpenGL vertex buffer object. This class 35 * can be used seamlessly with OpenGL ES 1.x and 2.0. 36 * </p> 37 * 38 * <p> 39 * In case OpenGL ES 2.0 is used in the application the data is bound via glVertexAttribPointer() according to the attribute 40 * aliases specified via {@link VertexAttributes} in the constructor. 41 * </p> 42 * 43 * <p> 44 * Uses indirect Buffers on Android 1.5/1.6 to fix GC invocation due to leaking PlatformAddress instances. 45 * </p> 46 * 47 * <p> 48 * VertexBufferObjects must be disposed via the {@link #dispose()} method when no longer needed 49 * </p> 50 * 51 * @author mzechner */ 52public class VertexBufferObjectSubData implements VertexData { 53 final VertexAttributes attributes; 54 final FloatBuffer buffer; 55 final ByteBuffer byteBuffer; 56 int bufferHandle; 57 final boolean isDirect; 58 final boolean isStatic; 59 final int usage; 60 boolean isDirty = false; 61 boolean isBound = false; 62 63 /** Constructs a new interleaved VertexBufferObject. 64 * 65 * @param isStatic whether the vertex data is static. 66 * @param numVertices the maximum number of vertices 67 * @param attributes the {@link VertexAttribute}s. */ 68 public VertexBufferObjectSubData (boolean isStatic, int numVertices, VertexAttribute... attributes) { 69 this.isStatic = isStatic; 70 this.attributes = new VertexAttributes(attributes); 71 byteBuffer = BufferUtils.newByteBuffer(this.attributes.vertexSize * numVertices); 72 isDirect = true; 73 74 usage = isStatic ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW; 75 buffer = byteBuffer.asFloatBuffer(); 76 bufferHandle = createBufferObject(); 77 buffer.flip(); 78 byteBuffer.flip(); 79 } 80 81 private int createBufferObject () { 82 int result = Gdx.gl20.glGenBuffer(); 83 Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, result); 84 Gdx.gl20.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.capacity(), null, usage); 85 Gdx.gl20.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0); 86 return result; 87 } 88 89 @Override 90 public VertexAttributes getAttributes () { 91 return attributes; 92 } 93 94 @Override 95 public int getNumVertices () { 96 return buffer.limit() * 4 / attributes.vertexSize; 97 } 98 99 @Override 100 public int getNumMaxVertices () { 101 return byteBuffer.capacity() / attributes.vertexSize; 102 } 103 104 @Override 105 public FloatBuffer getBuffer () { 106 isDirty = true; 107 return buffer; 108 } 109 110 private void bufferChanged () { 111 if (isBound) { 112 Gdx.gl20.glBufferSubData(GL20.GL_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer); 113 isDirty = false; 114 } 115 } 116 117 @Override 118 public void setVertices (float[] vertices, int offset, int count) { 119 isDirty = true; 120 if (isDirect) { 121 BufferUtils.copy(vertices, byteBuffer, count, offset); 122 buffer.position(0); 123 buffer.limit(count); 124 } else { 125 buffer.clear(); 126 buffer.put(vertices, offset, count); 127 buffer.flip(); 128 byteBuffer.position(0); 129 byteBuffer.limit(buffer.limit() << 2); 130 } 131 132 bufferChanged(); 133 } 134 135 @Override 136 public void updateVertices (int targetOffset, float[] vertices, int sourceOffset, int count) { 137 isDirty = true; 138 if (isDirect) { 139 final int pos = byteBuffer.position(); 140 byteBuffer.position(targetOffset * 4); 141 BufferUtils.copy(vertices, sourceOffset, count, byteBuffer); 142 byteBuffer.position(pos); 143 } else 144 throw new GdxRuntimeException("Buffer must be allocated direct."); // Should never happen 145 146 bufferChanged(); 147 } 148 149 /** Binds this VertexBufferObject for rendering via glDrawArrays or glDrawElements 150 * 151 * @param shader the shader */ 152 @Override 153 public void bind (final ShaderProgram shader) { 154 bind(shader, null); 155 } 156 157 @Override 158 public void bind (final ShaderProgram shader, final int[] locations) { 159 final GL20 gl = Gdx.gl20; 160 161 gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, bufferHandle); 162 if (isDirty) { 163 byteBuffer.limit(buffer.limit() * 4); 164 gl.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage); 165 isDirty = false; 166 } 167 168 final int numAttributes = attributes.size(); 169 if (locations == null) { 170 for (int i = 0; i < numAttributes; i++) { 171 final VertexAttribute attribute = attributes.get(i); 172 final int location = shader.getAttributeLocation(attribute.alias); 173 if (location < 0) continue; 174 shader.enableVertexAttribute(location); 175 176 shader.setVertexAttribute(location, attribute.numComponents, attribute.type, attribute.normalized, attributes.vertexSize, 177 attribute.offset); 178 } 179 } else { 180 for (int i = 0; i < numAttributes; i++) { 181 final VertexAttribute attribute = attributes.get(i); 182 final int location = locations[i]; 183 if (location < 0) continue; 184 shader.enableVertexAttribute(location); 185 186 shader.setVertexAttribute(location, attribute.numComponents, attribute.type, attribute.normalized, attributes.vertexSize, 187 attribute.offset); 188 } 189 } 190 isBound = true; 191 } 192 193 /** Unbinds this VertexBufferObject. 194 * 195 * @param shader the shader */ 196 @Override 197 public void unbind (final ShaderProgram shader) { 198 unbind(shader, null); 199 } 200 201 @Override 202 public void unbind (final ShaderProgram shader, final int[] locations) { 203 final GL20 gl = Gdx.gl20; 204 final int numAttributes = attributes.size(); 205 if (locations == null) { 206 for (int i = 0; i < numAttributes; i++) { 207 shader.disableVertexAttribute(attributes.get(i).alias); 208 } 209 } else { 210 for (int i = 0; i < numAttributes; i++) { 211 final int location = locations[i]; 212 if (location >= 0) shader.disableVertexAttribute(location); 213 } 214 } 215 gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0); 216 isBound = false; 217 } 218 219 /** Invalidates the VertexBufferObject so a new OpenGL buffer handle is created. Use this in case of a context loss. */ 220 public void invalidate () { 221 bufferHandle = createBufferObject(); 222 isDirty = true; 223 } 224 225 /** Disposes of all resources this VertexBufferObject uses. */ 226 @Override 227 public void dispose () { 228 GL20 gl = Gdx.gl20; 229 gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0); 230 gl.glDeleteBuffer(bufferHandle); 231 bufferHandle = 0; 232 } 233 234 /** Returns the VBO handle 235 * @return the VBO handle */ 236 public int getBufferHandle () { 237 return bufferHandle; 238 } 239} 240