• 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;
18 
19 import java.nio.FloatBuffer;
20 import java.nio.ShortBuffer;
21 import java.util.HashMap;
22 import java.util.Map;
23 
24 import com.badlogic.gdx.Application;
25 import com.badlogic.gdx.Gdx;
26 import com.badlogic.gdx.graphics.VertexAttributes.Usage;
27 import com.badlogic.gdx.graphics.glutils.IndexArray;
28 import com.badlogic.gdx.graphics.glutils.IndexBufferObject;
29 import com.badlogic.gdx.graphics.glutils.IndexBufferObjectSubData;
30 import com.badlogic.gdx.graphics.glutils.IndexData;
31 import com.badlogic.gdx.graphics.glutils.ShaderProgram;
32 import com.badlogic.gdx.graphics.glutils.VertexArray;
33 import com.badlogic.gdx.graphics.glutils.VertexBufferObject;
34 import com.badlogic.gdx.graphics.glutils.VertexBufferObjectSubData;
35 import com.badlogic.gdx.graphics.glutils.VertexBufferObjectWithVAO;
36 import com.badlogic.gdx.graphics.glutils.VertexData;
37 import com.badlogic.gdx.math.Matrix3;
38 import com.badlogic.gdx.math.Matrix4;
39 import com.badlogic.gdx.math.Vector2;
40 import com.badlogic.gdx.math.Vector3;
41 import com.badlogic.gdx.math.collision.BoundingBox;
42 import com.badlogic.gdx.utils.Array;
43 import com.badlogic.gdx.utils.Disposable;
44 import com.badlogic.gdx.utils.GdxRuntimeException;
45 
46 /** <p>
47  * A Mesh holds vertices composed of attributes specified by a {@link VertexAttributes} instance. The vertices are held either in
48  * VRAM in form of vertex buffer objects or in RAM in form of vertex arrays. The former variant is more performant and is
49  * preferred over vertex arrays if hardware supports it.
50  * </p>
51  *
52  * <p>
53  * Meshes are automatically managed. If the OpenGL context is lost all vertex buffer objects get invalidated and must be reloaded
54  * when the context is recreated. This only happens on Android when a user switches to another application or receives an incoming
55  * call. A managed Mesh will be reloaded automagically so you don't have to do this manually.
56  * </p>
57  *
58  * <p>
59  * A Mesh consists of vertices and optionally indices which specify which vertices define a triangle. Each vertex is composed of
60  * attributes such as position, normal, color or texture coordinate. Note that not all of this attributes must be given, except
61  * for position which is non-optional. Each attribute has an alias which is used when rendering a Mesh in OpenGL ES 2.0. The alias
62  * is used to bind a specific vertex attribute to a shader attribute. The shader source and the alias of the attribute must match
63  * exactly for this to work.
64  * </p>
65  *
66  * @author mzechner, Dave Clayton <contact@redskyforge.com>, Xoppa */
67 public class Mesh implements Disposable {
68 	public enum VertexDataType {
69 		VertexArray, VertexBufferObject, VertexBufferObjectSubData, VertexBufferObjectWithVAO
70 	}
71 
72 	/** list of all meshes **/
73 	static final Map<Application, Array<Mesh>> meshes = new HashMap<Application, Array<Mesh>>();
74 
75 	final VertexData vertices;
76 	final IndexData indices;
77 	boolean autoBind = true;
78 	final boolean isVertexArray;
79 
Mesh(VertexData vertices, IndexData indices, boolean isVertexArray)80 	protected Mesh (VertexData vertices, IndexData indices, boolean isVertexArray) {
81 		this.vertices = vertices;
82 		this.indices = indices;
83 		this.isVertexArray = isVertexArray;
84 
85 		addManagedMesh(Gdx.app, this);
86 	}
87 
88 	/** Creates a new Mesh with the given attributes.
89 	 *
90 	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
91 	 * @param maxVertices the maximum number of vertices this mesh can hold
92 	 * @param maxIndices the maximum number of indices this mesh can hold
93 	 * @param attributes the {@link VertexAttribute}s. Each vertex attribute defines one property of a vertex such as position,
94 	 *           normal or texture coordinate */
Mesh(boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes)95 	public Mesh (boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) {
96 		vertices = makeVertexBuffer(isStatic, maxVertices, new VertexAttributes(attributes));
97 		indices = new IndexBufferObject(isStatic, maxIndices);
98 		isVertexArray = false;
99 
100 		addManagedMesh(Gdx.app, this);
101 	}
102 
103 	/** Creates a new Mesh with the given attributes.
104 	 *
105 	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
106 	 * @param maxVertices the maximum number of vertices this mesh can hold
107 	 * @param maxIndices the maximum number of indices this mesh can hold
108 	 * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,
109 	 *           normal or texture coordinate */
Mesh(boolean isStatic, int maxVertices, int maxIndices, VertexAttributes attributes)110 	public Mesh (boolean isStatic, int maxVertices, int maxIndices, VertexAttributes attributes) {
111 		vertices = makeVertexBuffer(isStatic, maxVertices, attributes);
112 		indices = new IndexBufferObject(isStatic, maxIndices);
113 		isVertexArray = false;
114 
115 		addManagedMesh(Gdx.app, this);
116 	}
117 
118 	/** by jw: Creates a new Mesh with the given attributes. Adds extra optimizations for dynamic (frequently modified) meshes.
119 	 *
120 	 * @param staticVertices whether vertices of this mesh are static or not. Allows for internal optimizations.
121 	 * @param staticIndices whether indices of this mesh are static or not. Allows for internal optimizations.
122 	 * @param maxVertices the maximum number of vertices this mesh can hold
123 	 * @param maxIndices the maximum number of indices this mesh can hold
124 	 * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,
125 	 *           normal or texture coordinate
126 	 *
127 	 * @author Jaroslaw Wisniewski <j.wisniewski@appsisle.com> **/
Mesh(boolean staticVertices, boolean staticIndices, int maxVertices, int maxIndices, VertexAttributes attributes)128 	public Mesh (boolean staticVertices, boolean staticIndices, int maxVertices, int maxIndices, VertexAttributes attributes) {
129 		vertices = makeVertexBuffer(staticVertices, maxVertices, attributes);
130 		indices = new IndexBufferObject(staticIndices, maxIndices);
131 		isVertexArray = false;
132 
133 		addManagedMesh(Gdx.app, this);
134 	}
135 
makeVertexBuffer(boolean isStatic, int maxVertices, VertexAttributes vertexAttributes)136 	private VertexData makeVertexBuffer (boolean isStatic, int maxVertices, VertexAttributes vertexAttributes) {
137 		if (Gdx.gl30 != null) {
138 			return new VertexBufferObjectWithVAO(isStatic, maxVertices, vertexAttributes);
139 		} else {
140 			return new VertexBufferObject(isStatic, maxVertices, vertexAttributes);
141 		}
142 	}
143 
144 	/** Creates a new Mesh with the given attributes. This is an expert method with no error checking. Use at your own risk.
145 	 *
146 	 * @param type the {@link VertexDataType} to be used, VBO or VA.
147 	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
148 	 * @param maxVertices the maximum number of vertices this mesh can hold
149 	 * @param maxIndices the maximum number of indices this mesh can hold
150 	 * @param attributes the {@link VertexAttribute}s. Each vertex attribute defines one property of a vertex such as position,
151 	 *           normal or texture coordinate */
Mesh(VertexDataType type, boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes)152 	public Mesh (VertexDataType type, boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) {
153 		switch (type) {
154 		case VertexBufferObject:
155 			vertices = new VertexBufferObject(isStatic, maxVertices, attributes);
156 			indices = new IndexBufferObject(isStatic, maxIndices);
157 			isVertexArray = false;
158 			break;
159 		case VertexBufferObjectSubData:
160 			vertices = new VertexBufferObjectSubData(isStatic, maxVertices, attributes);
161 			indices = new IndexBufferObjectSubData(isStatic, maxIndices);
162 			isVertexArray = false;
163 			break;
164 		case VertexBufferObjectWithVAO:
165 			vertices = new VertexBufferObjectWithVAO(isStatic, maxVertices, attributes);
166 			indices = new IndexBufferObjectSubData(isStatic, maxIndices);
167 			isVertexArray = false;
168 			break;
169 		case VertexArray:
170 		default:
171 			vertices = new VertexArray(maxVertices, attributes);
172 			indices = new IndexArray(maxIndices);
173 			isVertexArray = true;
174 			break;
175 		}
176 
177 		addManagedMesh(Gdx.app, this);
178 	}
179 
180 	/** Sets the vertices of this Mesh. The attributes are assumed to be given in float format.
181 	 *
182 	 * @param vertices the vertices.
183 	 * @return the mesh for invocation chaining. */
setVertices(float[] vertices)184 	public Mesh setVertices (float[] vertices) {
185 		this.vertices.setVertices(vertices, 0, vertices.length);
186 
187 		return this;
188 	}
189 
190 	/** Sets the vertices of this Mesh. The attributes are assumed to be given in float format.
191 	 *
192 	 * @param vertices the vertices.
193 	 * @param offset the offset into the vertices array
194 	 * @param count the number of floats to use
195 	 * @return the mesh for invocation chaining. */
setVertices(float[] vertices, int offset, int count)196 	public Mesh setVertices (float[] vertices, int offset, int count) {
197 		this.vertices.setVertices(vertices, offset, count);
198 
199 		return this;
200 	}
201 
202 	/** Update (a portion of) the vertices. Does not resize the backing buffer.
203 	 * @param targetOffset the offset in number of floats of the mesh part.
204 	 * @param source the vertex data to update the mesh part with */
updateVertices(int targetOffset, float[] source)205 	public Mesh updateVertices (int targetOffset, float[] source) {
206 		return updateVertices(targetOffset, source, 0, source.length);
207 	}
208 
209 	/** Update (a portion of) the vertices. Does not resize the backing buffer.
210 	 * @param targetOffset the offset in number of floats of the mesh part.
211 	 * @param source the vertex data to update the mesh part with
212 	 * @param sourceOffset the offset in number of floats within the source array
213 	 * @param count the number of floats to update */
updateVertices(int targetOffset, float[] source, int sourceOffset, int count)214 	public Mesh updateVertices (int targetOffset, float[] source, int sourceOffset, int count) {
215 		this.vertices.updateVertices(targetOffset, source, sourceOffset, count);
216 		return this;
217 	}
218 
219 	/** Copies the vertices from the Mesh to the float array. The float array must be large enough to hold all the Mesh's vertices.
220 	 * @param vertices the array to copy the vertices to */
getVertices(float[] vertices)221 	public float[] getVertices (float[] vertices) {
222 		return getVertices(0, -1, vertices);
223 	}
224 
225 	/** Copies the the remaining vertices from the Mesh to the float array. The float array must be large enough to hold the
226 	 * remaining vertices.
227 	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
228 	 * @param vertices the array to copy the vertices to */
getVertices(int srcOffset, float[] vertices)229 	public float[] getVertices (int srcOffset, float[] vertices) {
230 		return getVertices(srcOffset, -1, vertices);
231 	}
232 
233 	/** Copies the specified vertices from the Mesh to the float array. The float array must be large enough to hold count vertices.
234 	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
235 	 * @param count the amount of floats to copy
236 	 * @param vertices the array to copy the vertices to */
getVertices(int srcOffset, int count, float[] vertices)237 	public float[] getVertices (int srcOffset, int count, float[] vertices) {
238 		return getVertices(srcOffset, count, vertices, 0);
239 	}
240 
241 	/** Copies the specified vertices from the Mesh to the float array. The float array must be large enough to hold
242 	 * destOffset+count vertices.
243 	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
244 	 * @param count the amount of floats to copy
245 	 * @param vertices the array to copy the vertices to
246 	 * @param destOffset the offset (in floats) in the vertices array to start copying */
getVertices(int srcOffset, int count, float[] vertices, int destOffset)247 	public float[] getVertices (int srcOffset, int count, float[] vertices, int destOffset) {
248 		// TODO: Perhaps this method should be vertexSize aware??
249 		final int max = getNumVertices() * getVertexSize() / 4;
250 		if (count == -1) {
251 			count = max - srcOffset;
252 			if (count > vertices.length - destOffset) count = vertices.length - destOffset;
253 		}
254 		if (srcOffset < 0 || count <= 0 || (srcOffset + count) > max || destOffset < 0 || destOffset >= vertices.length)
255 			throw new IndexOutOfBoundsException();
256 		if ((vertices.length - destOffset) < count)
257 			throw new IllegalArgumentException("not enough room in vertices array, has " + vertices.length + " floats, needs "
258 				+ count);
259 		int pos = getVerticesBuffer().position();
260 		getVerticesBuffer().position(srcOffset);
261 		getVerticesBuffer().get(vertices, destOffset, count);
262 		getVerticesBuffer().position(pos);
263 		return vertices;
264 	}
265 
266 	/** Sets the indices of this Mesh
267 	 *
268 	 * @param indices the indices
269 	 * @return the mesh for invocation chaining. */
setIndices(short[] indices)270 	public Mesh setIndices (short[] indices) {
271 		this.indices.setIndices(indices, 0, indices.length);
272 
273 		return this;
274 	}
275 
276 	/** Sets the indices of this Mesh.
277 	 *
278 	 * @param indices the indices
279 	 * @param offset the offset into the indices array
280 	 * @param count the number of indices to copy
281 	 * @return the mesh for invocation chaining. */
setIndices(short[] indices, int offset, int count)282 	public Mesh setIndices (short[] indices, int offset, int count) {
283 		this.indices.setIndices(indices, offset, count);
284 
285 		return this;
286 	}
287 
288 	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold all the Mesh's indices.
289 	 * @param indices the array to copy the indices to */
getIndices(short[] indices)290 	public void getIndices (short[] indices) {
291 		getIndices(indices, 0);
292 	}
293 
294 	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold destOffset + all the
295 	 * Mesh's indices.
296 	 * @param indices the array to copy the indices to
297 	 * @param destOffset the offset in the indices array to start copying */
getIndices(short[] indices, int destOffset)298 	public void getIndices (short[] indices, int destOffset) {
299 		getIndices(0, indices, destOffset);
300 	}
301 
302 	/** Copies the remaining indices from the Mesh to the short array. The short array must be large enough to hold destOffset + all
303 	 * the remaining indices.
304 	 * @param srcOffset the zero-based offset of the first index to fetch
305 	 * @param indices the array to copy the indices to
306 	 * @param destOffset the offset in the indices array to start copying */
getIndices(int srcOffset, short[] indices, int destOffset)307 	public void getIndices (int srcOffset, short[] indices, int destOffset) {
308 		getIndices(srcOffset, -1, indices, destOffset);
309 	}
310 
311 	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold destOffset + count
312 	 * indices.
313 	 * @param srcOffset the zero-based offset of the first index to fetch
314 	 * @param count the total amount of indices to copy
315 	 * @param indices the array to copy the indices to
316 	 * @param destOffset the offset in the indices array to start copying */
getIndices(int srcOffset, int count, short[] indices, int destOffset)317 	public void getIndices (int srcOffset, int count, short[] indices, int destOffset) {
318 		int max = getNumIndices();
319 		if (count < 0) count = max - srcOffset;
320 		if (srcOffset < 0 || srcOffset >= max || srcOffset + count > max)
321 			throw new IllegalArgumentException("Invalid range specified, offset: " + srcOffset + ", count: " + count + ", max: "
322 				+ max);
323 		if ((indices.length - destOffset) < count)
324 			throw new IllegalArgumentException("not enough room in indices array, has " + indices.length + " shorts, needs " + count);
325 		int pos = getIndicesBuffer().position();
326 		getIndicesBuffer().position(srcOffset);
327 		getIndicesBuffer().get(indices, destOffset, count);
328 		getIndicesBuffer().position(pos);
329 	}
330 
331 	/** @return the number of defined indices */
getNumIndices()332 	public int getNumIndices () {
333 		return indices.getNumIndices();
334 	}
335 
336 	/** @return the number of defined vertices */
getNumVertices()337 	public int getNumVertices () {
338 		return vertices.getNumVertices();
339 	}
340 
341 	/** @return the maximum number of vertices this mesh can hold */
getMaxVertices()342 	public int getMaxVertices () {
343 		return vertices.getNumMaxVertices();
344 	}
345 
346 	/** @return the maximum number of indices this mesh can hold */
getMaxIndices()347 	public int getMaxIndices () {
348 		return indices.getNumMaxIndices();
349 	}
350 
351 	/** @return the size of a single vertex in bytes */
getVertexSize()352 	public int getVertexSize () {
353 		return vertices.getAttributes().vertexSize;
354 	}
355 
356 	/** Sets whether to bind the underlying {@link VertexArray} or {@link VertexBufferObject} automatically on a call to one of the
357 	 * render methods. Usually you want to use autobind. Manual binding is an expert functionality. There is a driver bug on the
358 	 * MSM720xa chips that will fuck up memory if you manipulate the vertices and indices of a Mesh multiple times while it is
359 	 * bound. Keep this in mind.
360 	 *
361 	 * @param autoBind whether to autobind meshes. */
setAutoBind(boolean autoBind)362 	public void setAutoBind (boolean autoBind) {
363 		this.autoBind = autoBind;
364 	}
365 
366 	/** Binds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} if indices where given. Use this with OpenGL
367 	 * ES 2.0 and when auto-bind is disabled.
368 	 *
369 	 * @param shader the shader (does not bind the shader) */
bind(final ShaderProgram shader)370 	public void bind (final ShaderProgram shader) {
371 		bind(shader, null);
372 	}
373 
374 	/** Binds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} if indices where given. Use this with OpenGL
375 	 * ES 2.0 and when auto-bind is disabled.
376 	 *
377 	 * @param shader the shader (does not bind the shader)
378 	 * @param locations array containing the attribute locations. */
bind(final ShaderProgram shader, final int[] locations)379 	public void bind (final ShaderProgram shader, final int[] locations) {
380 		vertices.bind(shader, locations);
381 		if (indices.getNumIndices() > 0) indices.bind();
382 	}
383 
384 	/** Unbinds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} is indices were given. Use this with OpenGL
385 	 * ES 1.x and when auto-bind is disabled.
386 	 *
387 	 * @param shader the shader (does not unbind the shader) */
unbind(final ShaderProgram shader)388 	public void unbind (final ShaderProgram shader) {
389 		unbind(shader, null);
390 	}
391 
392 	/** Unbinds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} is indices were given. Use this with OpenGL
393 	 * ES 1.x and when auto-bind is disabled.
394 	 *
395 	 * @param shader the shader (does not unbind the shader)
396 	 * @param locations array containing the attribute locations. */
unbind(final ShaderProgram shader, final int[] locations)397 	public void unbind (final ShaderProgram shader, final int[] locations) {
398 		vertices.unbind(shader, locations);
399 		if (indices.getNumIndices() > 0) indices.unbind();
400 	}
401 
402 	/** <p>
403 	 * Renders the mesh using the given primitive type. If indices are set for this mesh then getNumIndices() / #vertices per
404 	 * primitive primitives are rendered. If no indices are set then getNumVertices() / #vertices per primitive are rendered.
405 	 * </p>
406 	 *
407 	 * <p>
408 	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
409 	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
410 	 * </p>
411 	 *
412 	 * <p>
413 	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
414 	 * </p>
415 	 *
416 	 * <p>
417 	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
418 	 * </p>
419 	 *
420 	 * @param primitiveType the primitive type */
render(ShaderProgram shader, int primitiveType)421 	public void render (ShaderProgram shader, int primitiveType) {
422 		render(shader, primitiveType, 0, indices.getNumMaxIndices() > 0 ? getNumIndices() : getNumVertices(), autoBind);
423 	}
424 
425 	/** <p>
426 	 * Renders the mesh using the given primitive type. offset specifies the offset into either the vertex buffer or the index
427 	 * buffer depending on whether indices are defined. count specifies the number of vertices or indices to use thus count /
428 	 * #vertices per primitive primitives are rendered.
429 	 * </p>
430 	 *
431 	 * <p>
432 	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
433 	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
434 	 * </p>
435 	 *
436 	 * <p>
437 	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
438 	 * </p>
439 	 *
440 	 * <p>
441 	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
442 	 * </p>
443 	 *
444 	 * @param shader the shader to be used
445 	 * @param primitiveType the primitive type
446 	 * @param offset the offset into the vertex or index buffer
447 	 * @param count number of vertices or indices to use */
render(ShaderProgram shader, int primitiveType, int offset, int count)448 	public void render (ShaderProgram shader, int primitiveType, int offset, int count) {
449 		render(shader, primitiveType, offset, count, autoBind);
450 	}
451 
452 	/** <p>
453 	 * Renders the mesh using the given primitive type. offset specifies the offset into either the vertex buffer or the index
454 	 * buffer depending on whether indices are defined. count specifies the number of vertices or indices to use thus count /
455 	 * #vertices per primitive primitives are rendered.
456 	 * </p>
457 	 *
458 	 * <p>
459 	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
460 	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
461 	 * </p>
462 	 *
463 	 * <p>
464 	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
465 	 * </p>
466 	 *
467 	 * <p>
468 	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
469 	 * </p>
470 	 *
471 	 * @param shader the shader to be used
472 	 * @param primitiveType the primitive type
473 	 * @param offset the offset into the vertex or index buffer
474 	 * @param count number of vertices or indices to use
475 	 * @param autoBind overrides the autoBind member of this Mesh */
render(ShaderProgram shader, int primitiveType, int offset, int count, boolean autoBind)476 	public void render (ShaderProgram shader, int primitiveType, int offset, int count, boolean autoBind) {
477 		if (count == 0) return;
478 
479 		if (autoBind) bind(shader);
480 
481 		if (isVertexArray) {
482 			if (indices.getNumIndices() > 0) {
483 				ShortBuffer buffer = indices.getBuffer();
484 				int oldPosition = buffer.position();
485 				int oldLimit = buffer.limit();
486 				buffer.position(offset);
487 				buffer.limit(offset + count);
488 				Gdx.gl20.glDrawElements(primitiveType, count, GL20.GL_UNSIGNED_SHORT, buffer);
489 				buffer.position(oldPosition);
490 				buffer.limit(oldLimit);
491 			} else {
492 				Gdx.gl20.glDrawArrays(primitiveType, offset, count);
493 			}
494 		} else {
495 			if (indices.getNumIndices() > 0)
496 				Gdx.gl20.glDrawElements(primitiveType, count, GL20.GL_UNSIGNED_SHORT, offset * 2);
497 			else
498 				Gdx.gl20.glDrawArrays(primitiveType, offset, count);
499 		}
500 
501 		if (autoBind) unbind(shader);
502 	}
503 
504 	/** Frees all resources associated with this Mesh */
dispose()505 	public void dispose () {
506 		if (meshes.get(Gdx.app) != null) meshes.get(Gdx.app).removeValue(this, true);
507 		vertices.dispose();
508 		indices.dispose();
509 	}
510 
511 	/** Returns the first {@link VertexAttribute} having the given {@link Usage}.
512 	 *
513 	 * @param usage the Usage.
514 	 * @return the VertexAttribute or null if no attribute with that usage was found. */
getVertexAttribute(int usage)515 	public VertexAttribute getVertexAttribute (int usage) {
516 		VertexAttributes attributes = vertices.getAttributes();
517 		int len = attributes.size();
518 		for (int i = 0; i < len; i++)
519 			if (attributes.get(i).usage == usage) return attributes.get(i);
520 
521 		return null;
522 	}
523 
524 	/** @return the vertex attributes of this Mesh */
getVertexAttributes()525 	public VertexAttributes getVertexAttributes () {
526 		return vertices.getAttributes();
527 	}
528 
529 	/** @return the backing FloatBuffer holding the vertices. Does not have to be a direct buffer on Android! */
getVerticesBuffer()530 	public FloatBuffer getVerticesBuffer () {
531 		return vertices.getBuffer();
532 	}
533 
534 	/** Calculates the {@link BoundingBox} of the vertices contained in this mesh. In case no vertices are defined yet a
535 	 * {@link GdxRuntimeException} is thrown. This method creates a new BoundingBox instance.
536 	 *
537 	 * @return the bounding box. */
calculateBoundingBox()538 	public BoundingBox calculateBoundingBox () {
539 		BoundingBox bbox = new BoundingBox();
540 		calculateBoundingBox(bbox);
541 		return bbox;
542 	}
543 
544 	/** Calculates the {@link BoundingBox} of the vertices contained in this mesh. In case no vertices are defined yet a
545 	 * {@link GdxRuntimeException} is thrown.
546 	 *
547 	 * @param bbox the bounding box to store the result in. */
calculateBoundingBox(BoundingBox bbox)548 	public void calculateBoundingBox (BoundingBox bbox) {
549 		final int numVertices = getNumVertices();
550 		if (numVertices == 0) throw new GdxRuntimeException("No vertices defined");
551 
552 		final FloatBuffer verts = vertices.getBuffer();
553 		bbox.inf();
554 		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
555 		final int offset = posAttrib.offset / 4;
556 		final int vertexSize = vertices.getAttributes().vertexSize / 4;
557 		int idx = offset;
558 
559 		switch (posAttrib.numComponents) {
560 		case 1:
561 			for (int i = 0; i < numVertices; i++) {
562 				bbox.ext(verts.get(idx), 0, 0);
563 				idx += vertexSize;
564 			}
565 			break;
566 		case 2:
567 			for (int i = 0; i < numVertices; i++) {
568 				bbox.ext(verts.get(idx), verts.get(idx + 1), 0);
569 				idx += vertexSize;
570 			}
571 			break;
572 		case 3:
573 			for (int i = 0; i < numVertices; i++) {
574 				bbox.ext(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
575 				idx += vertexSize;
576 			}
577 			break;
578 		}
579 	}
580 
581 	/** Calculate the {@link BoundingBox} of the specified part.
582 	 * @param out the bounding box to store the result in.
583 	 * @param offset the start index of the part.
584 	 * @param count the amount of indices the part contains.
585 	 * @return the value specified by out. */
calculateBoundingBox(final BoundingBox out, int offset, int count)586 	public BoundingBox calculateBoundingBox (final BoundingBox out, int offset, int count) {
587 		return extendBoundingBox(out.inf(), offset, count);
588 	}
589 
590 	/** Calculate the {@link BoundingBox} of the specified part.
591 	 * @param out the bounding box to store the result in.
592 	 * @param offset the start index of the part.
593 	 * @param count the amount of indices the part contains.
594 	 * @return the value specified by out. */
calculateBoundingBox(final BoundingBox out, int offset, int count, final Matrix4 transform)595 	public BoundingBox calculateBoundingBox (final BoundingBox out, int offset, int count, final Matrix4 transform) {
596 		return extendBoundingBox(out.inf(), offset, count, transform);
597 	}
598 
599 	/** Extends the specified {@link BoundingBox} with the specified part.
600 	 * @param out the bounding box to store the result in.
601 	 * @param offset the start index of the part.
602 	 * @param count the amount of indices the part contains.
603 	 * @return the value specified by out. */
extendBoundingBox(final BoundingBox out, int offset, int count)604 	public BoundingBox extendBoundingBox (final BoundingBox out, int offset, int count) {
605 		return extendBoundingBox(out, offset, count, null);
606 	}
607 
608 	private final Vector3 tmpV = new Vector3();
609 
610 	/** Extends the specified {@link BoundingBox} with the specified part.
611 	 * @param out the bounding box to store the result in.
612 	 * @param offset the start of the part.
613 	 * @param count the size of the part.
614 	 * @return the value specified by out. */
extendBoundingBox(final BoundingBox out, int offset, int count, final Matrix4 transform)615 	public BoundingBox extendBoundingBox (final BoundingBox out, int offset, int count, final Matrix4 transform) {
616 		final int numIndices = getNumIndices();
617 		final int numVertices = getNumVertices();
618 		final int max = numIndices == 0 ? numVertices : numIndices;
619 		if (offset < 0 || count < 1 || offset + count > max)
620 			throw new GdxRuntimeException("Invalid part specified ( offset=" + offset + ", count=" + count + ", max=" + max + " )");
621 
622 		final FloatBuffer verts = vertices.getBuffer();
623 		final ShortBuffer index = indices.getBuffer();
624 		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
625 		final int posoff = posAttrib.offset / 4;
626 		final int vertexSize = vertices.getAttributes().vertexSize / 4;
627 		final int end = offset + count;
628 
629 		switch (posAttrib.numComponents) {
630 		case 1:
631 			if (numIndices > 0) {
632 				for (int i = offset; i < end; i++) {
633 					final int idx = index.get(i) * vertexSize + posoff;
634 					tmpV.set(verts.get(idx), 0, 0);
635 					if (transform != null) tmpV.mul(transform);
636 					out.ext(tmpV);
637 				}
638 			} else {
639 				for (int i = offset; i < end; i++) {
640 					final int idx = i * vertexSize + posoff;
641 					tmpV.set(verts.get(idx), 0, 0);
642 					if (transform != null) tmpV.mul(transform);
643 					out.ext(tmpV);
644 				}
645 			}
646 			break;
647 		case 2:
648 			if (numIndices > 0) {
649 				for (int i = offset; i < end; i++) {
650 					final int idx = index.get(i) * vertexSize + posoff;
651 					tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
652 					if (transform != null) tmpV.mul(transform);
653 					out.ext(tmpV);
654 				}
655 			} else {
656 				for (int i = offset; i < end; i++) {
657 					final int idx = i * vertexSize + posoff;
658 					tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
659 					if (transform != null) tmpV.mul(transform);
660 					out.ext(tmpV);
661 				}
662 			}
663 			break;
664 		case 3:
665 			if (numIndices > 0) {
666 				for (int i = offset; i < end; i++) {
667 					final int idx = index.get(i) * vertexSize + posoff;
668 					tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
669 					if (transform != null) tmpV.mul(transform);
670 					out.ext(tmpV);
671 				}
672 			} else {
673 				for (int i = offset; i < end; i++) {
674 					final int idx = i * vertexSize + posoff;
675 					tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
676 					if (transform != null) tmpV.mul(transform);
677 					out.ext(tmpV);
678 				}
679 			}
680 			break;
681 		}
682 		return out;
683 	}
684 
685 	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
686 	 * @param centerX The X coordinate of the center of the bounding sphere
687 	 * @param centerY The Y coordinate of the center of the bounding sphere
688 	 * @param centerZ The Z coordinate of the center of the bounding sphere
689 	 * @param offset the start index of the part.
690 	 * @param count the amount of indices the part contains.
691 	 * @return the squared radius of the bounding sphere. */
calculateRadiusSquared(final float centerX, final float centerY, final float centerZ, int offset, int count, final Matrix4 transform)692 	public float calculateRadiusSquared (final float centerX, final float centerY, final float centerZ, int offset, int count,
693 		final Matrix4 transform) {
694 		int numIndices = getNumIndices();
695 		if (offset < 0 || count < 1 || offset + count > numIndices) throw new GdxRuntimeException("Not enough indices");
696 
697 		final FloatBuffer verts = vertices.getBuffer();
698 		final ShortBuffer index = indices.getBuffer();
699 		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
700 		final int posoff = posAttrib.offset / 4;
701 		final int vertexSize = vertices.getAttributes().vertexSize / 4;
702 		final int end = offset + count;
703 
704 		float result = 0;
705 
706 		switch (posAttrib.numComponents) {
707 		case 1:
708 			for (int i = offset; i < end; i++) {
709 				final int idx = index.get(i) * vertexSize + posoff;
710 				tmpV.set(verts.get(idx), 0, 0);
711 				if (transform != null) tmpV.mul(transform);
712 				final float r = tmpV.sub(centerX, centerY, centerZ).len2();
713 				if (r > result) result = r;
714 			}
715 			break;
716 		case 2:
717 			for (int i = offset; i < end; i++) {
718 				final int idx = index.get(i) * vertexSize + posoff;
719 				tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
720 				if (transform != null) tmpV.mul(transform);
721 				final float r = tmpV.sub(centerX, centerY, centerZ).len2();
722 				if (r > result) result = r;
723 			}
724 			break;
725 		case 3:
726 			for (int i = offset; i < end; i++) {
727 				final int idx = index.get(i) * vertexSize + posoff;
728 				tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
729 				if (transform != null) tmpV.mul(transform);
730 				final float r = tmpV.sub(centerX, centerY, centerZ).len2();
731 				if (r > result) result = r;
732 			}
733 			break;
734 		}
735 		return result;
736 	}
737 
738 	/** Calculates the radius of the bounding sphere around the specified center for the specified part.
739 	 * @param centerX The X coordinate of the center of the bounding sphere
740 	 * @param centerY The Y coordinate of the center of the bounding sphere
741 	 * @param centerZ The Z coordinate of the center of the bounding sphere
742 	 * @param offset the start index of the part.
743 	 * @param count the amount of indices the part contains.
744 	 * @return the radius of the bounding sphere. */
calculateRadius(final float centerX, final float centerY, final float centerZ, int offset, int count, final Matrix4 transform)745 	public float calculateRadius (final float centerX, final float centerY, final float centerZ, int offset, int count,
746 		final Matrix4 transform) {
747 		return (float)Math.sqrt(calculateRadiusSquared(centerX, centerY, centerZ, offset, count, transform));
748 	}
749 
750 	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
751 	 * @param center The center of the bounding sphere
752 	 * @param offset the start index of the part.
753 	 * @param count the amount of indices the part contains.
754 	 * @return the squared radius of the bounding sphere. */
calculateRadius(final Vector3 center, int offset, int count, final Matrix4 transform)755 	public float calculateRadius (final Vector3 center, int offset, int count, final Matrix4 transform) {
756 		return calculateRadius(center.x, center.y, center.z, offset, count, transform);
757 	}
758 
759 	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
760 	 * @param centerX The X coordinate of the center of the bounding sphere
761 	 * @param centerY The Y coordinate of the center of the bounding sphere
762 	 * @param centerZ The Z coordinate of the center of the bounding sphere
763 	 * @param offset the start index of the part.
764 	 * @param count the amount of indices the part contains.
765 	 * @return the squared radius of the bounding sphere. */
calculateRadius(final float centerX, final float centerY, final float centerZ, int offset, int count)766 	public float calculateRadius (final float centerX, final float centerY, final float centerZ, int offset, int count) {
767 		return calculateRadius(centerX, centerY, centerZ, offset, count, null);
768 	}
769 
770 	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
771 	 * @param center The center of the bounding sphere
772 	 * @param offset the start index of the part.
773 	 * @param count the amount of indices the part contains.
774 	 * @return the squared radius of the bounding sphere. */
calculateRadius(final Vector3 center, int offset, int count)775 	public float calculateRadius (final Vector3 center, int offset, int count) {
776 		return calculateRadius(center.x, center.y, center.z, offset, count, null);
777 	}
778 
779 	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
780 	 * @param centerX The X coordinate of the center of the bounding sphere
781 	 * @param centerY The Y coordinate of the center of the bounding sphere
782 	 * @param centerZ The Z coordinate of the center of the bounding sphere
783 	 * @return the squared radius of the bounding sphere. */
calculateRadius(final float centerX, final float centerY, final float centerZ)784 	public float calculateRadius (final float centerX, final float centerY, final float centerZ) {
785 		return calculateRadius(centerX, centerY, centerZ, 0, getNumIndices(), null);
786 	}
787 
788 	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
789 	 * @param center The center of the bounding sphere
790 	 * @return the squared radius of the bounding sphere. */
calculateRadius(final Vector3 center)791 	public float calculateRadius (final Vector3 center) {
792 		return calculateRadius(center.x, center.y, center.z, 0, getNumIndices(), null);
793 	}
794 
795 	/** @return the backing shortbuffer holding the indices. Does not have to be a direct buffer on Android! */
getIndicesBuffer()796 	public ShortBuffer getIndicesBuffer () {
797 		return indices.getBuffer();
798 	}
799 
addManagedMesh(Application app, Mesh mesh)800 	private static void addManagedMesh (Application app, Mesh mesh) {
801 		Array<Mesh> managedResources = meshes.get(app);
802 		if (managedResources == null) managedResources = new Array<Mesh>();
803 		managedResources.add(mesh);
804 		meshes.put(app, managedResources);
805 	}
806 
807 	/** Invalidates all meshes so the next time they are rendered new VBO handles are generated.
808 	 * @param app */
invalidateAllMeshes(Application app)809 	public static void invalidateAllMeshes (Application app) {
810 		Array<Mesh> meshesArray = meshes.get(app);
811 		if (meshesArray == null) return;
812 		for (int i = 0; i < meshesArray.size; i++) {
813 			meshesArray.get(i).vertices.invalidate();
814 			meshesArray.get(i).indices.invalidate();
815 		}
816 	}
817 
818 	/** Will clear the managed mesh cache. I wouldn't use this if i was you :) */
clearAllMeshes(Application app)819 	public static void clearAllMeshes (Application app) {
820 		meshes.remove(app);
821 	}
822 
getManagedStatus()823 	public static String getManagedStatus () {
824 		StringBuilder builder = new StringBuilder();
825 		int i = 0;
826 		builder.append("Managed meshes/app: { ");
827 		for (Application app : meshes.keySet()) {
828 			builder.append(meshes.get(app).size);
829 			builder.append(" ");
830 		}
831 		builder.append("}");
832 		return builder.toString();
833 	}
834 
835 	/** Method to scale the positions in the mesh. Normals will be kept as is. This is a potentially slow operation, use with care.
836 	 * It will also create a temporary float[] which will be garbage collected.
837 	 *
838 	 * @param scaleX scale on x
839 	 * @param scaleY scale on y
840 	 * @param scaleZ scale on z */
scale(float scaleX, float scaleY, float scaleZ)841 	public void scale (float scaleX, float scaleY, float scaleZ) {
842 		final VertexAttribute posAttr = getVertexAttribute(Usage.Position);
843 		final int offset = posAttr.offset / 4;
844 		final int numComponents = posAttr.numComponents;
845 		final int numVertices = getNumVertices();
846 		final int vertexSize = getVertexSize() / 4;
847 
848 		final float[] vertices = new float[numVertices * vertexSize];
849 		getVertices(vertices);
850 
851 		int idx = offset;
852 		switch (numComponents) {
853 		case 1:
854 			for (int i = 0; i < numVertices; i++) {
855 				vertices[idx] *= scaleX;
856 				idx += vertexSize;
857 			}
858 			break;
859 		case 2:
860 			for (int i = 0; i < numVertices; i++) {
861 				vertices[idx] *= scaleX;
862 				vertices[idx + 1] *= scaleY;
863 				idx += vertexSize;
864 			}
865 			break;
866 		case 3:
867 			for (int i = 0; i < numVertices; i++) {
868 				vertices[idx] *= scaleX;
869 				vertices[idx + 1] *= scaleY;
870 				vertices[idx + 2] *= scaleZ;
871 				idx += vertexSize;
872 			}
873 			break;
874 		}
875 
876 		setVertices(vertices);
877 	}
878 
879 	/** Method to transform the positions in the mesh. Normals will be kept as is. This is a potentially slow operation, use with
880 	 * care. It will also create a temporary float[] which will be garbage collected.
881 	 *
882 	 * @param matrix the transformation matrix */
transform(final Matrix4 matrix)883 	public void transform (final Matrix4 matrix) {
884 		transform(matrix, 0, getNumVertices());
885 	}
886 
887 	// TODO: Protected for now, because transforming a portion works but still copies all vertices
transform(final Matrix4 matrix, final int start, final int count)888 	public void transform (final Matrix4 matrix, final int start, final int count) {
889 		final VertexAttribute posAttr = getVertexAttribute(Usage.Position);
890 		final int posOffset = posAttr.offset / 4;
891 		final int stride = getVertexSize() / 4;
892 		final int numComponents = posAttr.numComponents;
893 		final int numVertices = getNumVertices();
894 
895 		final float[] vertices = new float[count * stride];
896 		getVertices(start * stride, count * stride, vertices);
897 		// getVertices(0, vertices.length, vertices);
898 		transform(matrix, vertices, stride, posOffset, numComponents, 0, count);
899 		// setVertices(vertices, 0, vertices.length);
900 		updateVertices(start * stride, vertices);
901 	}
902 
903 	/** Method to transform the positions in the float array. Normals will be kept as is. This is a potentially slow operation, use
904 	 * with care.
905 	 * @param matrix the transformation matrix
906 	 * @param vertices the float array
907 	 * @param vertexSize the number of floats in each vertex
908 	 * @param offset the offset within a vertex to the position
909 	 * @param dimensions the size of the position
910 	 * @param start the vertex to start with
911 	 * @param count the amount of vertices to transform */
transform(final Matrix4 matrix, final float[] vertices, int vertexSize, int offset, int dimensions, int start, int count)912 	public static void transform (final Matrix4 matrix, final float[] vertices, int vertexSize, int offset, int dimensions,
913 		int start, int count) {
914 		if (offset < 0 || dimensions < 1 || (offset + dimensions) > vertexSize) throw new IndexOutOfBoundsException();
915 		if (start < 0 || count < 1 || ((start + count) * vertexSize) > vertices.length)
916 			throw new IndexOutOfBoundsException("start = " + start + ", count = " + count + ", vertexSize = " + vertexSize
917 				+ ", length = " + vertices.length);
918 
919 		final Vector3 tmp = new Vector3();
920 
921 		int idx = offset + (start * vertexSize);
922 		switch (dimensions) {
923 		case 1:
924 			for (int i = 0; i < count; i++) {
925 				tmp.set(vertices[idx], 0, 0).mul(matrix);
926 				vertices[idx] = tmp.x;
927 				idx += vertexSize;
928 			}
929 			break;
930 		case 2:
931 			for (int i = 0; i < count; i++) {
932 				tmp.set(vertices[idx], vertices[idx + 1], 0).mul(matrix);
933 				vertices[idx] = tmp.x;
934 				vertices[idx + 1] = tmp.y;
935 				idx += vertexSize;
936 			}
937 			break;
938 		case 3:
939 			for (int i = 0; i < count; i++) {
940 				tmp.set(vertices[idx], vertices[idx + 1], vertices[idx + 2]).mul(matrix);
941 				vertices[idx] = tmp.x;
942 				vertices[idx + 1] = tmp.y;
943 				vertices[idx + 2] = tmp.z;
944 				idx += vertexSize;
945 			}
946 			break;
947 		}
948 	}
949 
950 	/** Method to transform the texture coordinates in the mesh. This is a potentially slow operation, use with care. It will also
951 	 * create a temporary float[] which will be garbage collected.
952 	 *
953 	 * @param matrix the transformation matrix */
transformUV(final Matrix3 matrix)954 	public void transformUV (final Matrix3 matrix) {
955 		transformUV(matrix, 0, getNumVertices());
956 	}
957 
958 	// TODO: Protected for now, because transforming a portion works but still copies all vertices
transformUV(final Matrix3 matrix, final int start, final int count)959 	protected void transformUV (final Matrix3 matrix, final int start, final int count) {
960 		final VertexAttribute posAttr = getVertexAttribute(Usage.TextureCoordinates);
961 		final int offset = posAttr.offset / 4;
962 		final int vertexSize = getVertexSize() / 4;
963 		final int numVertices = getNumVertices();
964 
965 		final float[] vertices = new float[numVertices * vertexSize];
966 		// TODO: getVertices(vertices, start * vertexSize, count * vertexSize);
967 		getVertices(0, vertices.length, vertices);
968 		transformUV(matrix, vertices, vertexSize, offset, start, count);
969 		setVertices(vertices, 0, vertices.length);
970 		// TODO: setVertices(start * vertexSize, vertices, 0, vertices.length);
971 	}
972 
973 	/** Method to transform the texture coordinates (UV) in the float array. This is a potentially slow operation, use with care.
974 	 * @param matrix the transformation matrix
975 	 * @param vertices the float array
976 	 * @param vertexSize the number of floats in each vertex
977 	 * @param offset the offset within a vertex to the texture location
978 	 * @param start the vertex to start with
979 	 * @param count the amount of vertices to transform */
transformUV(final Matrix3 matrix, final float[] vertices, int vertexSize, int offset, int start, int count)980 	public static void transformUV (final Matrix3 matrix, final float[] vertices, int vertexSize, int offset, int start, int count) {
981 		if (start < 0 || count < 1 || ((start + count) * vertexSize) > vertices.length)
982 			throw new IndexOutOfBoundsException("start = " + start + ", count = " + count + ", vertexSize = " + vertexSize
983 				+ ", length = " + vertices.length);
984 
985 		final Vector2 tmp = new Vector2();
986 
987 		int idx = offset + (start * vertexSize);
988 		for (int i = 0; i < count; i++) {
989 			tmp.set(vertices[idx], vertices[idx + 1]).mul(matrix);
990 			vertices[idx] = tmp.x;
991 			vertices[idx + 1] = tmp.y;
992 			idx += vertexSize;
993 		}
994 	}
995 
996 	/** Copies this mesh optionally removing duplicate vertices and/or reducing the amount of attributes.
997 	 * @param isStatic whether the new mesh is static or not. Allows for internal optimizations.
998 	 * @param removeDuplicates whether to remove duplicate vertices if possible. Only the vertices specified by usage are checked.
999 	 * @param usage which attributes (if available) to copy
1000 	 * @return the copy of this mesh */
copy(boolean isStatic, boolean removeDuplicates, final int[] usage)1001 	public Mesh copy (boolean isStatic, boolean removeDuplicates, final int[] usage) {
1002 		// TODO move this to a copy constructor?
1003 		// TODO duplicate the buffers without double copying the data if possible.
1004 		// TODO perhaps move this code to JNI if it turns out being too slow.
1005 		final int vertexSize = getVertexSize() / 4;
1006 		int numVertices = getNumVertices();
1007 		float[] vertices = new float[numVertices * vertexSize];
1008 		getVertices(0, vertices.length, vertices);
1009 		short[] checks = null;
1010 		VertexAttribute[] attrs = null;
1011 		int newVertexSize = 0;
1012 		if (usage != null) {
1013 			int size = 0;
1014 			int as = 0;
1015 			for (int i = 0; i < usage.length; i++)
1016 				if (getVertexAttribute(usage[i]) != null) {
1017 					size += getVertexAttribute(usage[i]).numComponents;
1018 					as++;
1019 				}
1020 			if (size > 0) {
1021 				attrs = new VertexAttribute[as];
1022 				checks = new short[size];
1023 				int idx = -1;
1024 				int ai = -1;
1025 				for (int i = 0; i < usage.length; i++) {
1026 					VertexAttribute a = getVertexAttribute(usage[i]);
1027 					if (a == null) continue;
1028 					for (int j = 0; j < a.numComponents; j++)
1029 						checks[++idx] = (short)(a.offset + j);
1030 					attrs[++ai] = new VertexAttribute(a.usage, a.numComponents, a.alias);
1031 					newVertexSize += a.numComponents;
1032 				}
1033 			}
1034 		}
1035 		if (checks == null) {
1036 			checks = new short[vertexSize];
1037 			for (short i = 0; i < vertexSize; i++)
1038 				checks[i] = i;
1039 			newVertexSize = vertexSize;
1040 		}
1041 
1042 		int numIndices = getNumIndices();
1043 		short[] indices = null;
1044 		if (numIndices > 0) {
1045 			indices = new short[numIndices];
1046 			getIndices(indices);
1047 			if (removeDuplicates || newVertexSize != vertexSize) {
1048 				float[] tmp = new float[vertices.length];
1049 				int size = 0;
1050 				for (int i = 0; i < numIndices; i++) {
1051 					final int idx1 = indices[i] * vertexSize;
1052 					short newIndex = -1;
1053 					if (removeDuplicates) {
1054 						for (short j = 0; j < size && newIndex < 0; j++) {
1055 							final int idx2 = j * newVertexSize;
1056 							boolean found = true;
1057 							for (int k = 0; k < checks.length && found; k++) {
1058 								if (tmp[idx2 + k] != vertices[idx1 + checks[k]]) found = false;
1059 							}
1060 							if (found) newIndex = j;
1061 						}
1062 					}
1063 					if (newIndex > 0)
1064 						indices[i] = newIndex;
1065 					else {
1066 						final int idx = size * newVertexSize;
1067 						for (int j = 0; j < checks.length; j++)
1068 							tmp[idx + j] = vertices[idx1 + checks[j]];
1069 						indices[i] = (short)size;
1070 						size++;
1071 					}
1072 				}
1073 				vertices = tmp;
1074 				numVertices = size;
1075 			}
1076 		}
1077 
1078 		Mesh result;
1079 		if (attrs == null)
1080 			result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, getVertexAttributes());
1081 		else
1082 			result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, attrs);
1083 		result.setVertices(vertices, 0, numVertices * newVertexSize);
1084 		result.setIndices(indices);
1085 		return result;
1086 	}
1087 
1088 	/** Copies this mesh.
1089 	 * @param isStatic whether the new mesh is static or not. Allows for internal optimizations.
1090 	 * @return the copy of this mesh */
copy(boolean isStatic)1091 	public Mesh copy (boolean isStatic) {
1092 		return copy(isStatic, false, null);
1093 	}
1094 }
1095