• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, Thorsten Schleinzer */
47 public class IndexBufferObject 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 	// used to work around bug: https://android-review.googlesource.com/#/c/73175/
57 	private final boolean empty;
58 
59 	/** Creates a new static IndexBufferObject to be used with vertex arrays.
60 	 *
61 	 * @param maxIndices the maximum number of indices this buffer can hold */
IndexBufferObject(int maxIndices)62 	public IndexBufferObject (int maxIndices) {
63 		this(true, maxIndices);
64 	}
65 
66 	/** Creates a new IndexBufferObject.
67 	 *
68 	 * @param isStatic whether the index buffer is static
69 	 * @param maxIndices the maximum number of indices this buffer can hold */
IndexBufferObject(boolean isStatic, int maxIndices)70 	public IndexBufferObject (boolean isStatic, int maxIndices) {
71 
72 		empty = maxIndices == 0;
73 		if (empty) {
74 			maxIndices = 1; // avoid allocating a zero-sized buffer because of bug in Android's ART < Android 5.0
75 		}
76 
77 		byteBuffer = BufferUtils.newUnsafeByteBuffer(maxIndices * 2);
78 		isDirect = true;
79 
80 		buffer = byteBuffer.asShortBuffer();
81 		buffer.flip();
82 		byteBuffer.flip();
83 		bufferHandle = Gdx.gl20.glGenBuffer();
84 		usage = isStatic ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW;
85 	}
86 
87 	/** @return the number of indices currently stored in this buffer */
getNumIndices()88 	public int getNumIndices () {
89 		return empty ? 0 : buffer.limit();
90 	}
91 
92 	/** @return the maximum number of indices this IndexBufferObject can store. */
getNumMaxIndices()93 	public int getNumMaxIndices () {
94 		return empty ? 0 : buffer.capacity();
95 	}
96 
97 	/** <p>
98 	 * Sets the indices of this IndexBufferObject, discarding the old indices. The count must equal the number of indices to be
99 	 * copied to this IndexBufferObject.
100 	 * </p>
101 	 *
102 	 * <p>
103 	 * This can be called in between calls to {@link #bind()} and {@link #unbind()}. The index data will be updated instantly.
104 	 * </p>
105 	 *
106 	 * @param indices the vertex data
107 	 * @param offset the offset to start copying the data from
108 	 * @param count the number of shorts to copy */
setIndices(short[] indices, int offset, int count)109 	public void setIndices (short[] indices, int offset, int count) {
110 		isDirty = true;
111 		buffer.clear();
112 		buffer.put(indices, offset, count);
113 		buffer.flip();
114 		byteBuffer.position(0);
115 		byteBuffer.limit(count << 1);
116 
117 		if (isBound) {
118 			Gdx.gl20.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);
119 			isDirty = false;
120 		}
121 	}
122 
setIndices(ShortBuffer indices)123 	public void setIndices (ShortBuffer indices) {
124 		isDirty = true;
125 		int pos = indices.position();
126 		buffer.clear();
127 		buffer.put(indices);
128 		buffer.flip();
129 		indices.position(pos);
130 		byteBuffer.position(0);
131 		byteBuffer.limit(buffer.limit() << 1);
132 
133 		if (isBound) {
134 			Gdx.gl20.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);
135 			isDirty = false;
136 		}
137 	}
138 
139 	@Override
updateIndices(int targetOffset, short[] indices, int offset, int count)140 	public void updateIndices (int targetOffset, short[] indices, int offset, int count) {
141 		isDirty = true;
142 		final int pos = byteBuffer.position();
143 		byteBuffer.position(targetOffset * 2);
144 		BufferUtils.copy(indices, offset, byteBuffer, count);
145 		byteBuffer.position(pos);
146 		buffer.position(0);
147 
148 		if (isBound) {
149 			Gdx.gl20.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);
150 			isDirty = false;
151 		}
152 	}
153 
154 	/** <p>
155 	 * Returns the underlying ShortBuffer. If you modify the buffer contents they wil be uploaded on the call to {@link #bind()}.
156 	 * If you need immediate uploading use {@link #setIndices(short[], int, int)}.
157 	 * </p>
158 	 *
159 	 * @return the underlying short buffer. */
getBuffer()160 	public ShortBuffer getBuffer () {
161 		isDirty = true;
162 		return buffer;
163 	}
164 
165 	/** Binds this IndexBufferObject for rendering with glDrawElements. */
bind()166 	public void bind () {
167 		if (bufferHandle == 0) throw new GdxRuntimeException("No buffer allocated!");
168 
169 		Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, bufferHandle);
170 		if (isDirty) {
171 			byteBuffer.limit(buffer.limit() * 2);
172 			Gdx.gl20.glBufferData(GL20.GL_ELEMENT_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);
173 			isDirty = false;
174 		}
175 		isBound = true;
176 	}
177 
178 	/** Unbinds this IndexBufferObject. */
unbind()179 	public void unbind () {
180 		Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
181 		isBound = false;
182 	}
183 
184 	/** Invalidates the IndexBufferObject so a new OpenGL buffer handle is created. Use this in case of a context loss. */
invalidate()185 	public void invalidate () {
186 		bufferHandle = Gdx.gl20.glGenBuffer();
187 		isDirty = true;
188 	}
189 
190 	/** Disposes this IndexBufferObject and all its associated OpenGL resources. */
dispose()191 	public void dispose () {
192 		Gdx.gl20.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
193 		Gdx.gl20.glDeleteBuffer(bufferHandle);
194 		bufferHandle = 0;
195 
196 		BufferUtils.disposeUnsafeByteBuffer(byteBuffer);
197 	}
198 }
199