Home | History | Annotate | Download | only in graphics
      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 (at) 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 
     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 */
     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 */
    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 (at) appsisle.com> **/
    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 
    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 */
    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. */
    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. */
    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 */
    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 */
    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 */
    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 */
    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 */
    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 */
    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. */
    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. */
    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 */
    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 */
    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 */
    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 */
    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 */
    332 	public int getNumIndices () {
    333 		return indices.getNumIndices();
    334 	}
    335 
    336 	/** @return the number of defined vertices */
    337 	public int getNumVertices () {
    338 		return vertices.getNumVertices();
    339 	}
    340 
    341 	/** @return the maximum number of vertices this mesh can hold */
    342 	public int getMaxVertices () {
    343 		return vertices.getNumMaxVertices();
    344 	}
    345 
    346 	/** @return the maximum number of indices this mesh can hold */
    347 	public int getMaxIndices () {
    348 		return indices.getNumMaxIndices();
    349 	}
    350 
    351 	/** @return the size of a single vertex in bytes */
    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. */
    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) */
    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. */
    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) */
    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. */
    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 */
    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 */
    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 */
    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 */
    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. */
    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 */
    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! */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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! */
    796 	public ShortBuffer getIndicesBuffer () {
    797 		return indices.getBuffer();
    798 	}
    799 
    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 */
    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 :) */
    819 	public static void clearAllMeshes (Application app) {
    820 		meshes.remove(app);
    821 	}
    822 
    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 */
    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 */
    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
    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 */
    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 */
    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
    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 */
    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 */
   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 */
   1091 	public Mesh copy (boolean isStatic) {
   1092 		return copy(isStatic, false, null);
   1093 	}
   1094 }
   1095