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 17 package com.badlogic.gdx.graphics.glutils; 18 19 import java.nio.ByteBuffer; 20 import java.nio.ShortBuffer; 21 22 import com.badlogic.gdx.Gdx; 23 import com.badlogic.gdx.graphics.GL20; 24 import com.badlogic.gdx.utils.BufferUtils; 25 import com.badlogic.gdx.utils.GdxRuntimeException; 26 27 /** <p> 28 * In IndexBufferObject wraps OpenGL's index buffer functionality to be used in conjunction with VBOs. This class can be 29 * seamlessly used with OpenGL ES 1.x and 2.0. 30 * </p> 31 * 32 * <p> 33 * Uses indirect Buffers on Android 1.5/1.6 to fix GC invocation due to leaking PlatformAddress instances. 34 * </p> 35 * 36 * <p> 37 * You can also use this to store indices for vertex arrays. Do not call {@link #bind()} or {@link #unbind()} in this case but 38 * rather use {@link #getBuffer()} to use the buffer directly with glDrawElements. You must also create the IndexBufferObject with 39 * the second constructor and specify isDirect as true as glDrawElements in conjunction with vertex arrays needs direct buffers. 40 * </p> 41 * 42 * <p> 43 * VertexBufferObjects must be disposed via the {@link #dispose()} method when no longer needed 44 * </p> 45 * 46 * @author mzechner */ 47 public class IndexBufferObjectSubData implements IndexData { 48 final ShortBuffer buffer; 49 final ByteBuffer byteBuffer; 50 int bufferHandle; 51 final boolean isDirect; 52 boolean isDirty = true; 53 boolean isBound = false; 54 final int usage; 55 56 /** Creates a new IndexBufferObject. 57 * 58 * @param isStatic whether the index buffer is static 59 * @param maxIndices the maximum number of indices this buffer can hold */ 60 public IndexBufferObjectSubData (boolean isStatic, int maxIndices) { 61 byteBuffer = BufferUtils.newByteBuffer(maxIndices * 2); 62 isDirect = true; 63 64 usage = isStatic ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW; 65 buffer = byteBuffer.asShortBuffer(); 66 buffer.flip(); 67 byteBuffer.flip(); 68 bufferHandle = createBufferObject(); 69 } 70 71 /** Creates a new IndexBufferObject to be used with vertex arrays. 72 * 73 * @param maxIndices the maximum number of indices this buffer can hold */ 74 public IndexBufferObjectSubData (int maxIndices) { 75 byteBuffer = BufferUtils.newByteBuffer(maxIndices * 2); 76 this.isDirect = true; 77 78 usage = GL20.GL_STATIC_DRAW; 79 buffer = byteBuffer.asShortBuffer(); 80 buffer.flip(); 81 byteBuffer.flip(); 82 bufferHandle = createBufferObject(); 83 } 84 85 private int createBufferObject () { 86 int result = Gdx.gl20.glGenBuffer(); 87 Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, result); 88 Gdx.gl20.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, byteBuffer.capacity(), null, usage); 89 Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); 90 return result; 91 } 92 93 /** @return the number of indices currently stored in this buffer */ 94 public int getNumIndices () { 95 return buffer.limit(); 96 } 97 98 /** @return the maximum number of indices this IndexBufferObject can store. */ 99 public int getNumMaxIndices () { 100 return buffer.capacity(); 101 } 102 103 /** <p> 104 * Sets the indices of this IndexBufferObject, discarding the old indices. The count must equal the number of indices to be 105 * copied to this IndexBufferObject. 106 * </p> 107 * 108 * <p> 109 * This can be called in between calls to {@link #bind()} and {@link #unbind()}. The index data will be updated instantly. 110 * </p> 111 * 112 * @param indices the vertex data 113 * @param offset the offset to start copying the data from 114 * @param count the number of floats to copy */ 115 public void setIndices (short[] indices, int offset, int count) { 116 isDirty = true; 117 buffer.clear(); 118 buffer.put(indices, offset, count); 119 buffer.flip(); 120 byteBuffer.position(0); 121 byteBuffer.limit(count << 1); 122 123 if (isBound) { 124 Gdx.gl20.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer); 125 isDirty = false; 126 } 127 } 128 129 public void setIndices (ShortBuffer indices) { 130 int pos = indices.position(); 131 isDirty = true; 132 buffer.clear(); 133 buffer.put(indices); 134 buffer.flip(); 135 indices.position(pos); 136 byteBuffer.position(0); 137 byteBuffer.limit(buffer.limit() << 1); 138 139 if (isBound) { 140 Gdx.gl20.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer); 141 isDirty = false; 142 } 143 } 144 145 @Override 146 public void updateIndices (int targetOffset, short[] indices, int offset, int count) { 147 isDirty = true; 148 final int pos = byteBuffer.position(); 149 byteBuffer.position(targetOffset * 2); 150 BufferUtils.copy(indices, offset, byteBuffer, count); 151 byteBuffer.position(pos); 152 buffer.position(0); 153 154 if (isBound) { 155 Gdx.gl20.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer); 156 isDirty = false; 157 } 158 } 159 160 161 /** <p> 162 * Returns the underlying ShortBuffer. If you modify the buffer contents they wil be uploaded on the call to {@link #bind()}. 163 * If you need immediate uploading use {@link #setIndices(short[], int, int)}. 164 * </p> 165 * 166 * @return the underlying short buffer. */ 167 public ShortBuffer getBuffer () { 168 isDirty = true; 169 return buffer; 170 } 171 172 /** Binds this IndexBufferObject for rendering with glDrawElements. */ 173 public void bind () { 174 if (bufferHandle == 0) throw new GdxRuntimeException("buuh"); 175 176 Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, bufferHandle); 177 if (isDirty) { 178 byteBuffer.limit(buffer.limit() * 2); 179 Gdx.gl20.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, 0, byteBuffer.limit(), byteBuffer); 180 isDirty = false; 181 } 182 isBound = true; 183 } 184 185 /** Unbinds this IndexBufferObject. */ 186 public void unbind () { 187 Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); 188 isBound = false; 189 } 190 191 /** Invalidates the IndexBufferObject so a new OpenGL buffer handle is created. Use this in case of a context loss. */ 192 public void invalidate () { 193 bufferHandle = createBufferObject(); 194 isDirty = true; 195 } 196 197 /** Disposes this IndexBufferObject and all its associated OpenGL resources. */ 198 public void dispose () { 199 GL20 gl = Gdx.gl20; 200 gl.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0); 201 gl.glDeleteBuffer(bufferHandle); 202 bufferHandle = 0; 203 } 204 } 205