19244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames/*******************************************************************************
29244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * Copyright 2011 See AUTHORS file.
3192aca09e83d3f70a712948a7946ace5ee03045dbadlogicgames *
49244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * Licensed under the Apache License, Version 2.0 (the "License");
59244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * you may not use this file except in compliance with the License.
69244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * You may obtain a copy of the License at
7192aca09e83d3f70a712948a7946ace5ee03045dbadlogicgames *
89244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames *   http://www.apache.org/licenses/LICENSE-2.0
9192aca09e83d3f70a712948a7946ace5ee03045dbadlogicgames *
109244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * Unless required by applicable law or agreed to in writing, software
119244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * distributed under the License is distributed on an "AS IS" BASIS,
129244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * See the License for the specific language governing permissions and
149244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames * limitations under the License.
159244abf367bce0e823d8cacc8f2ad15d3c83d154badlogicgames ******************************************************************************/
162f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
17114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgamespackage com.badlogic.gdx.graphics;
18114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames
19114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgamesimport java.nio.FloatBuffer;
20114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgamesimport java.nio.ShortBuffer;
2192ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgamesimport java.util.HashMap;
2292ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgamesimport java.util.Map;
23114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames
2492ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgamesimport com.badlogic.gdx.Application;
25fc2eb8df6aa4fed814c0f809851ad291e9ab7043badlogicgamesimport com.badlogic.gdx.Gdx;
26114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgamesimport com.badlogic.gdx.graphics.VertexAttributes.Usage;
274ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.IndexArray;
284ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.IndexBufferObject;
294ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.IndexBufferObjectSubData;
304ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.IndexData;
314ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.ShaderProgram;
324ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.VertexArray;
334ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.VertexBufferObject;
344ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.VertexBufferObjectSubData;
354ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.VertexBufferObjectWithVAO;
364ed6a0159542602b27e875e1a3a1f419d175148dXoppaimport com.badlogic.gdx.graphics.glutils.VertexData;
37ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppaimport com.badlogic.gdx.math.Matrix3;
3806e0eeb03633afc78536b775f5ef878288b1de02Xoppaimport com.badlogic.gdx.math.Matrix4;
3906e0eeb03633afc78536b775f5ef878288b1de02Xoppaimport com.badlogic.gdx.math.Vector2;
4006e0eeb03633afc78536b775f5ef878288b1de02Xoppaimport com.badlogic.gdx.math.Vector3;
415dc2f5d616354b058897632abb2d080dcde123c8badlogicgamesimport com.badlogic.gdx.math.collision.BoundingBox;
420a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Rennerimport com.badlogic.gdx.utils.Array;
43dd6866e5fe155220c2b8b231f7789dc5ff584b2cbadlogicgamesimport com.badlogic.gdx.utils.Disposable;
445dc2f5d616354b058897632abb2d080dcde123c8badlogicgamesimport com.badlogic.gdx.utils.GdxRuntimeException;
45114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames
462f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet/** <p>
47c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * A Mesh holds vertices composed of attributes specified by a {@link VertexAttributes} instance. The vertices are held either in
48575cf286b878302f75e8513c42877ae254da4886Xoppa * VRAM in form of vertex buffer objects or in RAM in form of vertex arrays. The former variant is more performant and is
49575cf286b878302f75e8513c42877ae254da4886Xoppa * preferred over vertex arrays if hardware supports it.
50847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames * </p>
51847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames *
5230597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet * <p>
53c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * Meshes are automatically managed. If the OpenGL context is lost all vertex buffer objects get invalidated and must be reloaded
54c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * when the context is recreated. This only happens on Android when a user switches to another application or receives an incoming
55c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * call. A managed Mesh will be reloaded automagically so you don't have to do this manually.
56847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames * </p>
57847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames *
58847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames * <p>
59c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * A Mesh consists of vertices and optionally indices which specify which vertices define a triangle. Each vertex is composed of
60c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * attributes such as position, normal, color or texture coordinate. Note that not all of this attributes must be given, except
61c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * 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
62c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet * is used to bind a specific vertex attribute to a shader attribute. The shader source and the alias of the attribute must match
6308711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic * exactly for this to work.
6430597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet * </p>
65114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames *
66575cf286b878302f75e8513c42877ae254da4886Xoppa * @author mzechner, Dave Clayton <contact@redskyforge.com>, Xoppa */
67a2acdc681d9192e68b424f5ca8c28dadae17a100badlogicgamespublic class Mesh implements Disposable {
68938a5b1860782c073367237a0305db43da3289e5badlogicgames	public enum VertexDataType {
69e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin		VertexArray, VertexBufferObject, VertexBufferObjectSubData, VertexBufferObjectWithVAO
70938a5b1860782c073367237a0305db43da3289e5badlogicgames	}
71938a5b1860782c073367237a0305db43da3289e5badlogicgames
72114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	/** list of all meshes **/
730a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner	static final Map<Application, Array<Mesh>> meshes = new HashMap<Application, Array<Mesh>>();
7430597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
75f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	final VertexData vertices;
76938a5b1860782c073367237a0305db43da3289e5badlogicgames	final IndexData indices;
772f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	boolean autoBind = true;
78f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	final boolean isVertexArray;
790fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
80575cf286b878302f75e8513c42877ae254da4886Xoppa	protected Mesh (VertexData vertices, IndexData indices, boolean isVertexArray) {
81575cf286b878302f75e8513c42877ae254da4886Xoppa		this.vertices = vertices;
82575cf286b878302f75e8513c42877ae254da4886Xoppa		this.indices = indices;
83575cf286b878302f75e8513c42877ae254da4886Xoppa		this.isVertexArray = isVertexArray;
84575cf286b878302f75e8513c42877ae254da4886Xoppa
85575cf286b878302f75e8513c42877ae254da4886Xoppa		addManagedMesh(Gdx.app, this);
86575cf286b878302f75e8513c42877ae254da4886Xoppa	}
87575cf286b878302f75e8513c42877ae254da4886Xoppa
882f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Creates a new Mesh with the given attributes.
89114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	 *
90c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
91c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param maxVertices the maximum number of vertices this mesh can hold
92c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param maxIndices the maximum number of indices this mesh can hold
93c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param attributes the {@link VertexAttribute}s. Each vertex attribute defines one property of a vertex such as position,
942f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 *           normal or texture coordinate */
95c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public Mesh (boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) {
96e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin		vertices = makeVertexBuffer(isStatic, maxVertices, new VertexAttributes(attributes));
9708711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic		indices = new IndexBufferObject(isStatic, maxIndices);
9808711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic		isVertexArray = false;
990fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
10092ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		addManagedMesh(Gdx.app, this);
101114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
1022f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
1032f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Creates a new Mesh with the given attributes.
1049bcb0acaadd34d3fc8ccdc01f9b574077cc9f597davedx@gmail.com	 *
1052f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
1062f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param maxVertices the maximum number of vertices this mesh can hold
1072f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param maxIndices the maximum number of indices this mesh can hold
1082f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,
1092f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 *           normal or texture coordinate */
1102f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public Mesh (boolean isStatic, int maxVertices, int maxIndices, VertexAttributes attributes) {
111e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin		vertices = makeVertexBuffer(isStatic, maxVertices, attributes);
11208711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic		indices = new IndexBufferObject(isStatic, maxIndices);
11308711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic		isVertexArray = false;
1149bcb0acaadd34d3fc8ccdc01f9b574077cc9f597davedx@gmail.com
11592ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		addManagedMesh(Gdx.app, this);
1169bcb0acaadd34d3fc8ccdc01f9b574077cc9f597davedx@gmail.com	}
11730597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
1189433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** by jw: Creates a new Mesh with the given attributes. Adds extra optimizations for dynamic (frequently modified) meshes.
1197d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	 *
1207d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	 * @param staticVertices whether vertices of this mesh are static or not. Allows for internal optimizations.
1217d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	 * @param staticIndices whether indices of this mesh are static or not. Allows for internal optimizations.
1227d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	 * @param maxVertices the maximum number of vertices this mesh can hold
1237d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	 * @param maxIndices the maximum number of indices this mesh can hold
1247d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	 * @param attributes the {@link VertexAttributes}. Each vertex attribute defines one property of a vertex such as position,
1259433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 *           normal or texture coordinate
1269433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 *
1279433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @author Jaroslaw Wisniewski <j.wisniewski@appsisle.com> **/
1287d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	public Mesh (boolean staticVertices, boolean staticIndices, int maxVertices, int maxIndices, VertexAttributes attributes) {
129e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin		vertices = makeVertexBuffer(staticVertices, maxVertices, attributes);
13008711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic		indices = new IndexBufferObject(staticIndices, maxIndices);
13108711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic		isVertexArray = false;
1327d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski
1337d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski		addManagedMesh(Gdx.app, this);
1347d9423bce885a999053d1da46f5e312284f844d6Jaroslaw Wisniewski	}
1359433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
136d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa	private VertexData makeVertexBuffer (boolean isStatic, int maxVertices, VertexAttributes vertexAttributes) {
137d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		if (Gdx.gl30 != null) {
138e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin			return new VertexBufferObjectWithVAO(isStatic, maxVertices, vertexAttributes);
139e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin		} else {
140e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin			return new VertexBufferObject(isStatic, maxVertices, vertexAttributes);
141e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin		}
142e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin	}
143e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin
1442f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Creates a new Mesh with the given attributes. This is an expert method with no error checking. Use at your own risk.
145938a5b1860782c073367237a0305db43da3289e5badlogicgames	 *
146c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param type the {@link VertexDataType} to be used, VBO or VA.
147c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param isStatic whether this mesh is static or not. Allows for internal optimizations.
148c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param maxVertices the maximum number of vertices this mesh can hold
149c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param maxIndices the maximum number of indices this mesh can hold
150c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param attributes the {@link VertexAttribute}s. Each vertex attribute defines one property of a vertex such as position,
1512f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 *           normal or texture coordinate */
152c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public Mesh (VertexDataType type, boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) {
153d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		switch (type) {
154d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		case VertexBufferObject:
155d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			vertices = new VertexBufferObject(isStatic, maxVertices, attributes);
156d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			indices = new IndexBufferObject(isStatic, maxIndices);
157d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			isVertexArray = false;
158d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			break;
159d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		case VertexBufferObjectSubData:
160d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			vertices = new VertexBufferObjectSubData(isStatic, maxVertices, attributes);
161d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			indices = new IndexBufferObjectSubData(isStatic, maxIndices);
162d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			isVertexArray = false;
163d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			break;
164d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		case VertexBufferObjectWithVAO:
165d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			vertices = new VertexBufferObjectWithVAO(isStatic, maxVertices, attributes);
166d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			indices = new IndexBufferObjectSubData(isStatic, maxIndices);
167d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			isVertexArray = false;
168d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			break;
169d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		case VertexArray:
170d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		default:
171d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			vertices = new VertexArray(maxVertices, attributes);
172d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			indices = new IndexArray(maxIndices);
173d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			isVertexArray = true;
174d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			break;
175938a5b1860782c073367237a0305db43da3289e5badlogicgames		}
176e4f285b4a85ce8a1a1da9fb38106da6924d6879bNateAustin
1772f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		addManagedMesh(Gdx.app, this);
178938a5b1860782c073367237a0305db43da3289e5badlogicgames	}
1799433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
1800b066287d2300050e41fa341bc24c722a775e7f2badlogic	/** Sets the vertices of this Mesh. The attributes are assumed to be given in float format.
181114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	 *
18202ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	 * @param vertices the vertices.
1839433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @return the mesh for invocation chaining. */
18402ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	public Mesh setVertices (float[] vertices) {
185f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		this.vertices.setVertices(vertices, 0, vertices.length);
1869433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
18702ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle		return this;
188114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
18930597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
1900b066287d2300050e41fa341bc24c722a775e7f2badlogic	/** Sets the vertices of this Mesh. The attributes are assumed to be given in float format.
191114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	 *
192c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param vertices the vertices.
193c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param offset the offset into the vertices array
1949433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the number of floats to use
1959433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @return the mesh for invocation chaining. */
19602ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	public Mesh setVertices (float[] vertices, int offset, int count) {
197f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		this.vertices.setVertices(vertices, offset, count);
1989433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
19902ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle		return this;
200114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
2012f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
2027a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Update (a portion of) the vertices. Does not resize the backing buffer.
2037a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param targetOffset the offset in number of floats of the mesh part.
2049433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param source the vertex data to update the mesh part with */
2057a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	public Mesh updateVertices (int targetOffset, float[] source) {
2067a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return updateVertices(targetOffset, source, 0, source.length);
2079433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	}
2089433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
2097a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Update (a portion of) the vertices. Does not resize the backing buffer.
2107a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param targetOffset the offset in number of floats of the mesh part.
2119433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param source the vertex data to update the mesh part with
2127a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param sourceOffset the offset in number of floats within the source array
2139433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the number of floats to update */
2147a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	public Mesh updateVertices (int targetOffset, float[] source, int sourceOffset, int count) {
2157a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		this.vertices.updateVertices(targetOffset, source, sourceOffset, count);
2167a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return this;
2177a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
2187a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa
2192f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Copies the vertices from the Mesh to the float array. The float array must be large enough to hold all the Mesh's vertices.
2202f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param vertices the array to copy the vertices to */
2217a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	public float[] getVertices (float[] vertices) {
2227a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return getVertices(0, -1, vertices);
22306e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
2249433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
2259433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Copies the the remaining vertices from the Mesh to the float array. The float array must be large enough to hold the
2269433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * remaining vertices.
22706e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
22806e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param vertices the array to copy the vertices to */
2297a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	public float[] getVertices (int srcOffset, float[] vertices) {
2307a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return getVertices(srcOffset, -1, vertices);
23106e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
23206e0eeb03633afc78536b775f5ef878288b1de02Xoppa
23306e0eeb03633afc78536b775f5ef878288b1de02Xoppa	/** Copies the specified vertices from the Mesh to the float array. The float array must be large enough to hold count vertices.
23406e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
23506e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param count the amount of floats to copy
23606e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param vertices the array to copy the vertices to */
2377a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	public float[] getVertices (int srcOffset, int count, float[] vertices) {
2387a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return getVertices(srcOffset, count, vertices, 0);
23906e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
2409433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
2419433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Copies the specified vertices from the Mesh to the float array. The float array must be large enough to hold
2429433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * destOffset+count vertices.
24306e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param srcOffset the offset (in number of floats) of the vertices in the mesh to copy
24406e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param count the amount of floats to copy
24506e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param vertices the array to copy the vertices to
24606e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param destOffset the offset (in floats) in the vertices array to start copying */
2477a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	public float[] getVertices (int srcOffset, int count, float[] vertices, int destOffset) {
24806e0eeb03633afc78536b775f5ef878288b1de02Xoppa		// TODO: Perhaps this method should be vertexSize aware??
24906e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int max = getNumVertices() * getVertexSize() / 4;
25006e0eeb03633afc78536b775f5ef878288b1de02Xoppa		if (count == -1) {
25106e0eeb03633afc78536b775f5ef878288b1de02Xoppa			count = max - srcOffset;
2529433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet			if (count > vertices.length - destOffset) count = vertices.length - destOffset;
25306e0eeb03633afc78536b775f5ef878288b1de02Xoppa		}
25406e0eeb03633afc78536b775f5ef878288b1de02Xoppa		if (srcOffset < 0 || count <= 0 || (srcOffset + count) > max || destOffset < 0 || destOffset >= vertices.length)
25506e0eeb03633afc78536b775f5ef878288b1de02Xoppa			throw new IndexOutOfBoundsException();
25606e0eeb03633afc78536b775f5ef878288b1de02Xoppa		if ((vertices.length - destOffset) < count)
2579433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet			throw new IllegalArgumentException("not enough room in vertices array, has " + vertices.length + " floats, needs "
2589433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				+ count);
2592f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		int pos = getVerticesBuffer().position();
26006e0eeb03633afc78536b775f5ef878288b1de02Xoppa		getVerticesBuffer().position(srcOffset);
26106e0eeb03633afc78536b775f5ef878288b1de02Xoppa		getVerticesBuffer().get(vertices, destOffset, count);
2624732e63a3c5a351cef799716735039019c3bd364badlogicgames		getVerticesBuffer().position(pos);
2637a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return vertices;
2644732e63a3c5a351cef799716735039019c3bd364badlogicgames	}
26530597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
2662f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Sets the indices of this Mesh
267114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	 *
26802ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	 * @param indices the indices
26902ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	 * @return the mesh for invocation chaining. */
27002ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	public Mesh setIndices (short[] indices) {
271f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		this.indices.setIndices(indices, 0, indices.length);
2729433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
27302ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle		return this;
274f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	}
2750fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
2762f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Sets the indices of this Mesh.
277f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	 *
278c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param indices the indices
279c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param offset the offset into the indices array
28002ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	 * @param count the number of indices to copy
28102ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	 * @return the mesh for invocation chaining. */
28202ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle	public Mesh setIndices (short[] indices, int offset, int count) {
283f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		this.indices.setIndices(indices, offset, count);
2849433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
28502ccb3898475086fc1d53a8c71be542af5b2e2a0Peter LaValle		return this;
286114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
2872f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
2882f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold all the Mesh's indices.
2892f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param indices the array to copy the indices to */
2902f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public void getIndices (short[] indices) {
29106e0eeb03633afc78536b775f5ef878288b1de02Xoppa		getIndices(indices, 0);
29206e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
29306e0eeb03633afc78536b775f5ef878288b1de02Xoppa
2949433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold destOffset + all the
2959433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * Mesh's indices.
2969433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param indices the array to copy the indices to
29706e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param destOffset the offset in the indices array to start copying */
2989433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public void getIndices (short[] indices, int destOffset) {
299b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		getIndices(0, indices, destOffset);
300b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	}
301b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa
302b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	/** Copies the remaining indices from the Mesh to the short array. The short array must be large enough to hold destOffset + all
303b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * the remaining indices.
304b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param srcOffset the zero-based offset of the first index to fetch
305b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param indices the array to copy the indices to
306b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param destOffset the offset in the indices array to start copying */
307b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	public void getIndices (int srcOffset, short[] indices, int destOffset) {
308b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		getIndices(srcOffset, -1, indices, destOffset);
309b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	}
310b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa
311b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	/** Copies the indices from the Mesh to the short array. The short array must be large enough to hold destOffset + count
312b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * indices.
313b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param srcOffset the zero-based offset of the first index to fetch
314b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param count the total amount of indices to copy
315b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param indices the array to copy the indices to
316b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	 * @param destOffset the offset in the indices array to start copying */
317b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa	public void getIndices (int srcOffset, int count, short[] indices, int destOffset) {
318b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		int max = getNumIndices();
319b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		if (count < 0) count = max - srcOffset;
320b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		if (srcOffset < 0 || srcOffset >= max || srcOffset + count > max)
321b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa			throw new IllegalArgumentException("Invalid range specified, offset: " + srcOffset + ", count: " + count + ", max: "
322b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa				+ max);
323b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		if ((indices.length - destOffset) < count)
324b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa			throw new IllegalArgumentException("not enough room in indices array, has " + indices.length + " shorts, needs " + count);
3252f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		int pos = getIndicesBuffer().position();
326b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		getIndicesBuffer().position(srcOffset);
327b1e5f16aadd6d8481c7b749ecccf8a8210c0cc55Xoppa		getIndicesBuffer().get(indices, destOffset, count);
3284732e63a3c5a351cef799716735039019c3bd364badlogicgames		getIndicesBuffer().position(pos);
3294732e63a3c5a351cef799716735039019c3bd364badlogicgames	}
33030597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
3312f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the number of defined indices */
332c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public int getNumIndices () {
333f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return indices.getNumIndices();
334114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
33530597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
3362f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the number of defined vertices */
337c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public int getNumVertices () {
338f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return vertices.getNumVertices();
339f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	}
3400fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
3412f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the maximum number of vertices this mesh can hold */
342c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public int getMaxVertices () {
343f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return vertices.getNumMaxVertices();
344f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	}
345f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames
3462f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the maximum number of indices this mesh can hold */
347c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public int getMaxIndices () {
348f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return indices.getNumMaxIndices();
349114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
35030597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
3512f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the size of a single vertex in bytes */
352c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public int getVertexSize () {
353f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return vertices.getAttributes().vertexSize;
354f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	}
355f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames
3562f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Sets whether to bind the underlying {@link VertexArray} or {@link VertexBufferObject} automatically on a call to one of the
3579433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * render methods. Usually you want to use autobind. Manual binding is an expert functionality. There is a driver bug on the
3589433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * MSM720xa chips that will fuck up memory if you manipulate the vertices and indices of a Mesh multiple times while it is
3599433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * bound. Keep this in mind.
3600c2f4f311d59a0dc53b2e074f5e583b640566956badlogicgames	 *
3612f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param autoBind whether to autobind meshes. */
362c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public void setAutoBind (boolean autoBind) {
3630c2f4f311d59a0dc53b2e074f5e583b640566956badlogicgames		this.autoBind = autoBind;
3649433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	}
3650fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
3662f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Binds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} if indices where given. Use this with OpenGL
367c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * ES 2.0 and when auto-bind is disabled.
3680fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames	 *
3692f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param shader the shader (does not bind the shader) */
370e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	public void bind (final ShaderProgram shader) {
371e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa		bind(shader, null);
372e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	}
373e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa
374e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	/** Binds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} if indices where given. Use this with OpenGL
375e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * ES 2.0 and when auto-bind is disabled.
376e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 *
3779433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param shader the shader (does not bind the shader)
37893642c20846d2342e9d75c02292d9031e48316cbXoppa	 * @param locations array containing the attribute locations. */
37993642c20846d2342e9d75c02292d9031e48316cbXoppa	public void bind (final ShaderProgram shader, final int[] locations) {
380e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa		vertices.bind(shader, locations);
381c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet		if (indices.getNumIndices() > 0) indices.bind();
382f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	}
3830fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
3842f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Unbinds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} is indices were given. Use this with OpenGL
385c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * ES 1.x and when auto-bind is disabled.
3860fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames	 *
3872f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param shader the shader (does not unbind the shader) */
388e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	public void unbind (final ShaderProgram shader) {
389e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa		unbind(shader, null);
390e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	}
3919433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
392e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	/** Unbinds the underlying {@link VertexBufferObject} and {@link IndexBufferObject} is indices were given. Use this with OpenGL
393e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * ES 1.x and when auto-bind is disabled.
394e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 *
395e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * @param shader the shader (does not unbind the shader)
39693642c20846d2342e9d75c02292d9031e48316cbXoppa	 * @param locations array containing the attribute locations. */
39793642c20846d2342e9d75c02292d9031e48316cbXoppa	public void unbind (final ShaderProgram shader, final int[] locations) {
398e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa		vertices.unbind(shader, locations);
399c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet		if (indices.getNumIndices() > 0) indices.unbind();
400f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames	}
4010fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
4022f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** <p>
403c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * Renders the mesh using the given primitive type. If indices are set for this mesh then getNumIndices() / #vertices per
404c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * primitive primitives are rendered. If no indices are set then getNumVertices() / #vertices per primitive are rendered.
405847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
406114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	 *
407847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * <p>
408c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
409c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
410847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
411847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
412847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * <p>
413c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
414847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
415847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
416847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * <p>
417c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
418847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
419847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
4202f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param primitiveType the primitive type */
421c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public void render (ShaderProgram shader, int primitiveType) {
422e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa		render(shader, primitiveType, 0, indices.getNumMaxIndices() > 0 ? getNumIndices() : getNumVertices(), autoBind);
423114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
424847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames
4252f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** <p>
426c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * Renders the mesh using the given primitive type. offset specifies the offset into either the vertex buffer or the index
427c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * buffer depending on whether indices are defined. count specifies the number of vertices or indices to use thus count /
428c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * #vertices per primitive primitives are rendered.
429847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
430847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
431847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * <p>
432c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
433c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
434847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
435847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
436847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * <p>
437c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
438847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
439847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
440847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * <p>
441c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
442847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 * </p>
443847c371a0012580e84fa65d7348b930a7aa4e805badlogicgames	 *
444c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param shader the shader to be used
445c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param primitiveType the primitive type
446c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param offset the offset into the vertex or index buffer
4472f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param count number of vertices or indices to use */
448c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public void render (ShaderProgram shader, int primitiveType, int offset, int count) {
4499433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		render(shader, primitiveType, offset, count, autoBind);
450e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	}
4519433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
452e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	/** <p>
453e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * Renders the mesh using the given primitive type. offset specifies the offset into either the vertex buffer or the index
454e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * buffer depending on whether indices are defined. count specifies the number of vertices or indices to use thus count /
455e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * #vertices per primitive primitives are rendered.
456e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * </p>
457e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 *
458e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * <p>
459e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * This method will automatically bind each vertex attribute as specified at construction time via {@link VertexAttributes} to
460e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * the respective shader attributes. The binding is based on the alias defined for each VertexAttribute.
461e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * </p>
462e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 *
463e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * <p>
464e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * This method must only be called after the {@link ShaderProgram#begin()} method has been called!
465e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * </p>
466e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 *
467e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * <p>
468e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * This method is intended for use with OpenGL ES 2.0 and will throw an IllegalStateException when OpenGL ES 1.x is used.
469e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * </p>
470e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 *
471e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * @param shader the shader to be used
472e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * @param primitiveType the primitive type
473e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * @param offset the offset into the vertex or index buffer
474e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * @param count number of vertices or indices to use
475e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	 * @param autoBind overrides the autoBind member of this Mesh */
476e3286411b0bf0d72eca4462a1468bdf4c0b885cfXoppa	public void render (ShaderProgram shader, int primitiveType, int offset, int count, boolean autoBind) {
4772c9030de2ddb983cfaeb5736089453b32c559cc1Nathan Sweet		if (count == 0) return;
4782c9030de2ddb983cfaeb5736089453b32c559cc1Nathan Sweet
479c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet		if (autoBind) bind(shader);
4800fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
481f693d47c8859c9fb3039441d6da01289564b8726badlogicgames		if (isVertexArray) {
482f693d47c8859c9fb3039441d6da01289564b8726badlogicgames			if (indices.getNumIndices() > 0) {
483f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				ShortBuffer buffer = indices.getBuffer();
484f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				int oldPosition = buffer.position();
485f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				int oldLimit = buffer.limit();
486f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				buffer.position(offset);
487f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				buffer.limit(offset + count);
48808711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic				Gdx.gl20.glDrawElements(primitiveType, count, GL20.GL_UNSIGNED_SHORT, buffer);
489f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				buffer.position(oldPosition);
490f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				buffer.limit(oldLimit);
49159787375f8b2ebe19189a355a281199688d5a5e9badlogicgames			} else {
49259787375f8b2ebe19189a355a281199688d5a5e9badlogicgames				Gdx.gl20.glDrawArrays(primitiveType, offset, count);
49359787375f8b2ebe19189a355a281199688d5a5e9badlogicgames			}
494f693d47c8859c9fb3039441d6da01289564b8726badlogicgames		} else {
495f693d47c8859c9fb3039441d6da01289564b8726badlogicgames			if (indices.getNumIndices() > 0)
49608711a2152eda79f20c2db6064b4f1b444b6ea0cbadlogic				Gdx.gl20.glDrawElements(primitiveType, count, GL20.GL_UNSIGNED_SHORT, offset * 2);
497f693d47c8859c9fb3039441d6da01289564b8726badlogicgames			else
498f693d47c8859c9fb3039441d6da01289564b8726badlogicgames				Gdx.gl20.glDrawArrays(primitiveType, offset, count);
499f693d47c8859c9fb3039441d6da01289564b8726badlogicgames		}
5000fb217903b1b0c5a4a3a63c604e912a3a2e6d4e2badlogicgames
501c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet		if (autoBind) unbind(shader);
502114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
50330597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
5042f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Frees all resources associated with this Mesh */
505c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public void dispose () {
5060a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner		if (meshes.get(Gdx.app) != null) meshes.get(Gdx.app).removeValue(this, true);
507f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		vertices.dispose();
508f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		indices.dispose();
509114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
510114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames
5112f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Returns the first {@link VertexAttribute} having the given {@link Usage}.
512114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	 *
513c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	 * @param usage the Usage.
5142f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @return the VertexAttribute or null if no attribute with that usage was found. */
515c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public VertexAttribute getVertexAttribute (int usage) {
516f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		VertexAttributes attributes = vertices.getAttributes();
517f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		int len = attributes.size();
518f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		for (int i = 0; i < len; i++)
519c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet			if (attributes.get(i).usage == usage) return attributes.get(i);
52030597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
521114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames		return null;
522114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
52330597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
5242f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the vertex attributes of this Mesh */
525c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public VertexAttributes getVertexAttributes () {
526f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return vertices.getAttributes();
527114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
52830597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
5292f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the backing FloatBuffer holding the vertices. Does not have to be a direct buffer on Android! */
530c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public FloatBuffer getVerticesBuffer () {
531f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return vertices.getBuffer();
5325836b479b519807d4a6fab91600fe6943d637505badlogicgames	}
5332f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
5342f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Calculates the {@link BoundingBox} of the vertices contained in this mesh. In case no vertices are defined yet a
5352f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * {@link GdxRuntimeException} is thrown. This method creates a new BoundingBox instance.
5365dc2f5d616354b058897632abb2d080dcde123c8badlogicgames	 *
5372f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @return the bounding box. */
5382f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public BoundingBox calculateBoundingBox () {
5396c11e3667fcc1328385f17ac08ba57d3432e9380badlogicgames		BoundingBox bbox = new BoundingBox();
5406c11e3667fcc1328385f17ac08ba57d3432e9380badlogicgames		calculateBoundingBox(bbox);
5416c11e3667fcc1328385f17ac08ba57d3432e9380badlogicgames		return bbox;
5426c11e3667fcc1328385f17ac08ba57d3432e9380badlogicgames	}
5432f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
5442f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Calculates the {@link BoundingBox} of the vertices contained in this mesh. In case no vertices are defined yet a
5452f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * {@link GdxRuntimeException} is thrown.
5466c11e3667fcc1328385f17ac08ba57d3432e9380badlogicgames	 *
5472f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param bbox the bounding box to store the result in. */
5482f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public void calculateBoundingBox (BoundingBox bbox) {
5495dc2f5d616354b058897632abb2d080dcde123c8badlogicgames		final int numVertices = getNumVertices();
5502f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		if (numVertices == 0) throw new GdxRuntimeException("No vertices defined");
5512f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
5525dc2f5d616354b058897632abb2d080dcde123c8badlogicgames		final FloatBuffer verts = vertices.getBuffer();
5535dc2f5d616354b058897632abb2d080dcde123c8badlogicgames		bbox.inf();
5545dc2f5d616354b058897632abb2d080dcde123c8badlogicgames		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
5552f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		final int offset = posAttrib.offset / 4;
5565dc2f5d616354b058897632abb2d080dcde123c8badlogicgames		final int vertexSize = vertices.getAttributes().vertexSize / 4;
5575dc2f5d616354b058897632abb2d080dcde123c8badlogicgames		int idx = offset;
5582f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
5592f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		switch (posAttrib.numComponents) {
5602f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		case 1:
5612f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			for (int i = 0; i < numVertices; i++) {
5622f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				bbox.ext(verts.get(idx), 0, 0);
5632f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				idx += vertexSize;
5642f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			}
5652f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			break;
5662f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		case 2:
5672f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			for (int i = 0; i < numVertices; i++) {
5682f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				bbox.ext(verts.get(idx), verts.get(idx + 1), 0);
5692f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				idx += vertexSize;
5702f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			}
5712f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			break;
5722f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		case 3:
5732f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			for (int i = 0; i < numVertices; i++) {
5742f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				bbox.ext(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
5752f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				idx += vertexSize;
5762f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			}
5772f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			break;
5782f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		}
5792f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	}
5809433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
581ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	/** Calculate the {@link BoundingBox} of the specified part.
5829433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param out the bounding box to store the result in.
583ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param offset the start index of the part.
5849433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
585ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @return the value specified by out. */
5869433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public BoundingBox calculateBoundingBox (final BoundingBox out, int offset, int count) {
587ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		return extendBoundingBox(out.inf(), offset, count);
588ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	}
5899433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
590c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	/** Calculate the {@link BoundingBox} of the specified part.
5919433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param out the bounding box to store the result in.
592c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	 * @param offset the start index of the part.
5939433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
594c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	 * @return the value specified by out. */
5959433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public BoundingBox calculateBoundingBox (final BoundingBox out, int offset, int count, final Matrix4 transform) {
596c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa		return extendBoundingBox(out.inf(), offset, count, transform);
597c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	}
598c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa
599ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	/** Extends the specified {@link BoundingBox} with the specified part.
6009433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param out the bounding box to store the result in.
601ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param offset the start index of the part.
6029433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
603ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @return the value specified by out. */
6049433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public BoundingBox extendBoundingBox (final BoundingBox out, int offset, int count) {
605c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa		return extendBoundingBox(out, offset, count, null);
606c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	}
6079433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
608c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	private final Vector3 tmpV = new Vector3();
6099433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
610c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	/** Extends the specified {@link BoundingBox} with the specified part.
6119433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param out the bounding box to store the result in.
612d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa	 * @param offset the start of the part.
613d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa	 * @param count the size of the part.
614c92ddcf1b1795845789dbcbaee455be4540e7d42Xoppa	 * @return the value specified by out. */
6159433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public BoundingBox extendBoundingBox (final BoundingBox out, int offset, int count, final Matrix4 transform) {
616d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		final int numIndices = getNumIndices();
617d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		final int numVertices = getNumVertices();
618d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		final int max = numIndices == 0 ? numVertices : numIndices;
619d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa		if (offset < 0 || count < 1 || offset + count > max)
620d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			throw new GdxRuntimeException("Invalid part specified ( offset=" + offset + ", count=" + count + ", max=" + max + " )");
6219433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
622ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final FloatBuffer verts = vertices.getBuffer();
623ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final ShortBuffer index = indices.getBuffer();
624ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
625ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final int posoff = posAttrib.offset / 4;
626ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final int vertexSize = vertices.getAttributes().vertexSize / 4;
627ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final int end = offset + count;
6289433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
629ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		switch (posAttrib.numComponents) {
630ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		case 1:
631d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			if (numIndices > 0) {
632d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				for (int i = offset; i < end; i++) {
633d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					final int idx = index.get(i) * vertexSize + posoff;
634d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					tmpV.set(verts.get(idx), 0, 0);
635d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					if (transform != null) tmpV.mul(transform);
636d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					out.ext(tmpV);
637d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				}
638d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			} else {
639d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				for (int i = offset; i < end; i++) {
640d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					final int idx = i * vertexSize + posoff;
641d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					tmpV.set(verts.get(idx), 0, 0);
642d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					if (transform != null) tmpV.mul(transform);
643d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					out.ext(tmpV);
644d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				}
645ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			}
646ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			break;
647ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		case 2:
648d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			if (numIndices > 0) {
649d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				for (int i = offset; i < end; i++) {
650d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					final int idx = index.get(i) * vertexSize + posoff;
651d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
652d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					if (transform != null) tmpV.mul(transform);
653d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					out.ext(tmpV);
654d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				}
655d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			} else {
656d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				for (int i = offset; i < end; i++) {
657d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					final int idx = i * vertexSize + posoff;
658d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
659d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					if (transform != null) tmpV.mul(transform);
660d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					out.ext(tmpV);
661d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				}
662ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			}
663ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			break;
664ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		case 3:
665d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			if (numIndices > 0) {
666d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				for (int i = offset; i < end; i++) {
667d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					final int idx = index.get(i) * vertexSize + posoff;
668d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
669d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					if (transform != null) tmpV.mul(transform);
670d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					out.ext(tmpV);
671d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				}
672d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa			} else {
673d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				for (int i = offset; i < end; i++) {
674d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					final int idx = i * vertexSize + posoff;
675d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
676d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					if (transform != null) tmpV.mul(transform);
677d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa					out.ext(tmpV);
678d7fa8a0037ceb94f45b1a1877f74b70fb3751ed9Xoppa				}
679ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			}
680ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			break;
681ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		}
682ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		return out;
683ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	}
6849433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
6857a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
6867a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerX The X coordinate of the center of the bounding sphere
6877a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerY The Y coordinate of the center of the bounding sphere
6887a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerZ The Z coordinate of the center of the bounding sphere
6897a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param offset the start index of the part.
6909433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
6917a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the squared radius of the bounding sphere. */
6929433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadiusSquared (final float centerX, final float centerY, final float centerZ, int offset, int count,
6939433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		final Matrix4 transform) {
6947a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		int numIndices = getNumIndices();
6959433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		if (offset < 0 || count < 1 || offset + count > numIndices) throw new GdxRuntimeException("Not enough indices");
6969433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
6977a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final FloatBuffer verts = vertices.getBuffer();
6987a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final ShortBuffer index = indices.getBuffer();
6997a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final VertexAttribute posAttrib = getVertexAttribute(Usage.Position);
7007a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final int posoff = posAttrib.offset / 4;
7017a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final int vertexSize = vertices.getAttributes().vertexSize / 4;
7027a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final int end = offset + count;
7039433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7047a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		float result = 0;
7059433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7067a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		switch (posAttrib.numComponents) {
7077a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		case 1:
7087a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			for (int i = offset; i < end; i++) {
7097a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				final int idx = index.get(i) * vertexSize + posoff;
7107a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				tmpV.set(verts.get(idx), 0, 0);
7119433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				if (transform != null) tmpV.mul(transform);
7127a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				final float r = tmpV.sub(centerX, centerY, centerZ).len2();
7139433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				if (r > result) result = r;
7147a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			}
7157a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			break;
7167a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		case 2:
7177a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			for (int i = offset; i < end; i++) {
7187a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				final int idx = index.get(i) * vertexSize + posoff;
7197a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				tmpV.set(verts.get(idx), verts.get(idx + 1), 0);
7209433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				if (transform != null) tmpV.mul(transform);
7217a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				final float r = tmpV.sub(centerX, centerY, centerZ).len2();
7229433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				if (r > result) result = r;
7237a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			}
7247a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			break;
7257a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		case 3:
7267a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			for (int i = offset; i < end; i++) {
7277a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				final int idx = index.get(i) * vertexSize + posoff;
7287a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				tmpV.set(verts.get(idx), verts.get(idx + 1), verts.get(idx + 2));
7299433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				if (transform != null) tmpV.mul(transform);
7307a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa				final float r = tmpV.sub(centerX, centerY, centerZ).len2();
7319433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				if (r > result) result = r;
7327a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			}
7337a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa			break;
7347a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		}
7357a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return result;
7367a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7379433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7387a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the radius of the bounding sphere around the specified center for the specified part.
7397a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerX The X coordinate of the center of the bounding sphere
7407a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerY The Y coordinate of the center of the bounding sphere
7417a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerZ The Z coordinate of the center of the bounding sphere
7427a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param offset the start index of the part.
7439433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
7447a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the radius of the bounding sphere. */
7459433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadius (final float centerX, final float centerY, final float centerZ, int offset, int count,
7469433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		final Matrix4 transform) {
7477a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return (float)Math.sqrt(calculateRadiusSquared(centerX, centerY, centerZ, offset, count, transform));
7487a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7499433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7507a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
7517a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param center The center of the bounding sphere
7527a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param offset the start index of the part.
7539433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
7547a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the squared radius of the bounding sphere. */
7559433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadius (final Vector3 center, int offset, int count, final Matrix4 transform) {
7567a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return calculateRadius(center.x, center.y, center.z, offset, count, transform);
7577a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7589433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7597a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
7607a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerX The X coordinate of the center of the bounding sphere
7617a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerY The Y coordinate of the center of the bounding sphere
7627a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerZ The Z coordinate of the center of the bounding sphere
7637a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param offset the start index of the part.
7649433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
7657a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the squared radius of the bounding sphere. */
7669433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadius (final float centerX, final float centerY, final float centerZ, int offset, int count) {
7677a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return calculateRadius(centerX, centerY, centerZ, offset, count, null);
7687a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7699433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7707a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
7717a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param center The center of the bounding sphere
7727a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param offset the start index of the part.
7739433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of indices the part contains.
7747a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the squared radius of the bounding sphere. */
7759433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadius (final Vector3 center, int offset, int count) {
7767a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return calculateRadius(center.x, center.y, center.z, offset, count, null);
7777a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7789433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7797a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
7807a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerX The X coordinate of the center of the bounding sphere
7817a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerY The Y coordinate of the center of the bounding sphere
7827a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param centerZ The Z coordinate of the center of the bounding sphere
7837a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the squared radius of the bounding sphere. */
7849433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadius (final float centerX, final float centerY, final float centerZ) {
7857a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return calculateRadius(centerX, centerY, centerZ, 0, getNumIndices(), null);
7867a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7879433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7887a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	/** Calculates the squared radius of the bounding sphere around the specified center for the specified part.
7897a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @param center The center of the bounding sphere
7907a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	 * @return the squared radius of the bounding sphere. */
7919433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public float calculateRadius (final Vector3 center) {
7927a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		return calculateRadius(center.x, center.y, center.z, 0, getNumIndices(), null);
7937a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa	}
7949433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
7952f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** @return the backing shortbuffer holding the indices. Does not have to be a direct buffer on Android! */
796c1a20432e96c5ceb32a62b52627aaa6f93e703aenathan.sweet	public ShortBuffer getIndicesBuffer () {
797f1fa40492f7ea283f5ded3d671ce685277405dc3badlogicgames		return indices.getBuffer();
798114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
79930597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
8002f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	private static void addManagedMesh (Application app, Mesh mesh) {
8010a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner		Array<Mesh> managedResources = meshes.get(app);
8020a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner		if (managedResources == null) managedResources = new Array<Mesh>();
80392ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		managedResources.add(mesh);
80492ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		meshes.put(app, managedResources);
80592ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames	}
8062f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
8072f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Invalidates all meshes so the next time they are rendered new VBO handles are generated.
8082f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param app */
80992ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames	public static void invalidateAllMeshes (Application app) {
8100a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner		Array<Mesh> meshesArray = meshes.get(app);
8110a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner		if (meshesArray == null) return;
8120a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner		for (int i = 0; i < meshesArray.size; i++) {
8135f4eac0a477e0e9f952ceab25c89dd9da51d3e03Xoppa			meshesArray.get(i).vertices.invalidate();
8140a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner			meshesArray.get(i).indices.invalidate();
815114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames		}
816114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames	}
81730597377f5f74e1e402481d423a8ecc933e65ed2nathan.sweet
8182f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Will clear the managed mesh cache. I wouldn't use this if i was you :) */
8192f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public static void clearAllMeshes (Application app) {
82092ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		meshes.remove(app);
82192ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames	}
8222f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
8232f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public static String getManagedStatus () {
82492ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		StringBuilder builder = new StringBuilder();
82592ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		int i = 0;
82692ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		builder.append("Managed meshes/app: { ");
8272f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		for (Application app : meshes.keySet()) {
8280a5c1025d1f147746db4f92bfd3c1ba4abea58b9Jon Renner			builder.append(meshes.get(app).size);
82992ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames			builder.append(" ");
83092ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		}
83192ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		builder.append("}");
83292ed4d69842a61ff92fabe3167305fbf82bb5e94badlogicgames		return builder.toString();
833fc2eb8df6aa4fed814c0f809851ad291e9ab7043badlogicgames	}
8342f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
8352f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	/** Method to scale the positions in the mesh. Normals will be kept as is. This is a potentially slow operation, use with care.
8362f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * It will also create a temporary float[] which will be garbage collected.
83707e23dedf55a93606dff1c5069e379c053b73a78badlogicgames	 *
83807e23dedf55a93606dff1c5069e379c053b73a78badlogicgames	 * @param scaleX scale on x
83907e23dedf55a93606dff1c5069e379c053b73a78badlogicgames	 * @param scaleY scale on y
8402f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	 * @param scaleZ scale on z */
8412f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	public void scale (float scaleX, float scaleY, float scaleZ) {
84206e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final VertexAttribute posAttr = getVertexAttribute(Usage.Position);
84306e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int offset = posAttr.offset / 4;
84406e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int numComponents = posAttr.numComponents;
84506e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int numVertices = getNumVertices();
84606e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int vertexSize = getVertexSize() / 4;
8472f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
84806e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final float[] vertices = new float[numVertices * vertexSize];
84907e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		getVertices(vertices);
8502f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
85107e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		int idx = offset;
8522f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet		switch (numComponents) {
85307e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		case 1:
8542f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			for (int i = 0; i < numVertices; i++) {
8552f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				vertices[idx] *= scaleX;
8562f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				idx += vertexSize;
85707e23dedf55a93606dff1c5069e379c053b73a78badlogicgames			}
85807e23dedf55a93606dff1c5069e379c053b73a78badlogicgames			break;
85907e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		case 2:
8602f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			for (int i = 0; i < numVertices; i++) {
86107e23dedf55a93606dff1c5069e379c053b73a78badlogicgames				vertices[idx] *= scaleX;
8622f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				vertices[idx + 1] *= scaleY;
8632f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				idx += vertexSize;
86407e23dedf55a93606dff1c5069e379c053b73a78badlogicgames			}
86507e23dedf55a93606dff1c5069e379c053b73a78badlogicgames			break;
86607e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		case 3:
8672f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet			for (int i = 0; i < numVertices; i++) {
86807e23dedf55a93606dff1c5069e379c053b73a78badlogicgames				vertices[idx] *= scaleX;
8692f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				vertices[idx + 1] *= scaleY;
8702f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				vertices[idx + 2] *= scaleZ;
8712f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet				idx += vertexSize;
87207e23dedf55a93606dff1c5069e379c053b73a78badlogicgames			}
87307e23dedf55a93606dff1c5069e379c053b73a78badlogicgames			break;
87407e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		}
8752f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet
87607e23dedf55a93606dff1c5069e379c053b73a78badlogicgames		setVertices(vertices);
8772f412de0a867ffffe70909d9bb69434e3ac6b16bnathan.sweet	}
8789433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
8799433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Method to transform the positions in the mesh. Normals will be kept as is. This is a potentially slow operation, use with
8809433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * care. It will also create a temporary float[] which will be garbage collected.
88106e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 *
88206e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param matrix the transformation matrix */
8839433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public void transform (final Matrix4 matrix) {
88406e0eeb03633afc78536b775f5ef878288b1de02Xoppa		transform(matrix, 0, getNumVertices());
88506e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
8869433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
88706e0eeb03633afc78536b775f5ef878288b1de02Xoppa	// TODO: Protected for now, because transforming a portion works but still copies all vertices
8889433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public void transform (final Matrix4 matrix, final int start, final int count) {
88906e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final VertexAttribute posAttr = getVertexAttribute(Usage.Position);
8907a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final int posOffset = posAttr.offset / 4;
8917a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final int stride = getVertexSize() / 4;
89206e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int numComponents = posAttr.numComponents;
89306e0eeb03633afc78536b775f5ef878288b1de02Xoppa		final int numVertices = getNumVertices();
8949433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
8957a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		final float[] vertices = new float[count * stride];
8967a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		getVertices(start * stride, count * stride, vertices);
8977a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		// getVertices(0, vertices.length, vertices);
8987a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		transform(matrix, vertices, stride, posOffset, numComponents, 0, count);
8999433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		// setVertices(vertices, 0, vertices.length);
9007a0073fb6e1d9cf0f56101489abe2d4780a780e6Xoppa		updateVertices(start * stride, vertices);
90106e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
9029433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
9039433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Method to transform the positions in the float array. Normals will be kept as is. This is a potentially slow operation, use
9049433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * with care.
90506e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param matrix the transformation matrix
90606e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param vertices the float array
90706e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param vertexSize the number of floats in each vertex
90806e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param offset the offset within a vertex to the position
90906e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param dimensions the size of the position
91006e0eeb03633afc78536b775f5ef878288b1de02Xoppa	 * @param start the vertex to start with
9119433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of vertices to transform */
9129433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public static void transform (final Matrix4 matrix, final float[] vertices, int vertexSize, int offset, int dimensions,
9139433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		int start, int count) {
9149433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		if (offset < 0 || dimensions < 1 || (offset + dimensions) > vertexSize) throw new IndexOutOfBoundsException();
91506e0eeb03633afc78536b775f5ef878288b1de02Xoppa		if (start < 0 || count < 1 || ((start + count) * vertexSize) > vertices.length)
9169433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet			throw new IndexOutOfBoundsException("start = " + start + ", count = " + count + ", vertexSize = " + vertexSize
9179433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				+ ", length = " + vertices.length);
9189433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
919ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final Vector3 tmp = new Vector3();
9209433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
92106e0eeb03633afc78536b775f5ef878288b1de02Xoppa		int idx = offset + (start * vertexSize);
9229433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		switch (dimensions) {
92306e0eeb03633afc78536b775f5ef878288b1de02Xoppa		case 1:
92406e0eeb03633afc78536b775f5ef878288b1de02Xoppa			for (int i = 0; i < count; i++) {
92506e0eeb03633afc78536b775f5ef878288b1de02Xoppa				tmp.set(vertices[idx], 0, 0).mul(matrix);
92606e0eeb03633afc78536b775f5ef878288b1de02Xoppa				vertices[idx] = tmp.x;
92706e0eeb03633afc78536b775f5ef878288b1de02Xoppa				idx += vertexSize;
92806e0eeb03633afc78536b775f5ef878288b1de02Xoppa			}
92906e0eeb03633afc78536b775f5ef878288b1de02Xoppa			break;
93006e0eeb03633afc78536b775f5ef878288b1de02Xoppa		case 2:
93106e0eeb03633afc78536b775f5ef878288b1de02Xoppa			for (int i = 0; i < count; i++) {
93206e0eeb03633afc78536b775f5ef878288b1de02Xoppa				tmp.set(vertices[idx], vertices[idx + 1], 0).mul(matrix);
93306e0eeb03633afc78536b775f5ef878288b1de02Xoppa				vertices[idx] = tmp.x;
9349433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				vertices[idx + 1] = tmp.y;
93506e0eeb03633afc78536b775f5ef878288b1de02Xoppa				idx += vertexSize;
93606e0eeb03633afc78536b775f5ef878288b1de02Xoppa			}
93706e0eeb03633afc78536b775f5ef878288b1de02Xoppa			break;
93806e0eeb03633afc78536b775f5ef878288b1de02Xoppa		case 3:
93906e0eeb03633afc78536b775f5ef878288b1de02Xoppa			for (int i = 0; i < count; i++) {
94006e0eeb03633afc78536b775f5ef878288b1de02Xoppa				tmp.set(vertices[idx], vertices[idx + 1], vertices[idx + 2]).mul(matrix);
94106e0eeb03633afc78536b775f5ef878288b1de02Xoppa				vertices[idx] = tmp.x;
9429433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				vertices[idx + 1] = tmp.y;
9439433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				vertices[idx + 2] = tmp.z;
94406e0eeb03633afc78536b775f5ef878288b1de02Xoppa				idx += vertexSize;
94506e0eeb03633afc78536b775f5ef878288b1de02Xoppa			}
94606e0eeb03633afc78536b775f5ef878288b1de02Xoppa			break;
94706e0eeb03633afc78536b775f5ef878288b1de02Xoppa		}
94806e0eeb03633afc78536b775f5ef878288b1de02Xoppa	}
9499433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
9509433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Method to transform the texture coordinates in the mesh. This is a potentially slow operation, use with care. It will also
9519433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * create a temporary float[] which will be garbage collected.
952ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 *
953ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param matrix the transformation matrix */
9549433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public void transformUV (final Matrix3 matrix) {
955ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		transformUV(matrix, 0, getNumVertices());
956ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	}
9579433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
958ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	// TODO: Protected for now, because transforming a portion works but still copies all vertices
9599433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	protected void transformUV (final Matrix3 matrix, final int start, final int count) {
960ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final VertexAttribute posAttr = getVertexAttribute(Usage.TextureCoordinates);
961ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final int offset = posAttr.offset / 4;
962ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final int vertexSize = getVertexSize() / 4;
963ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final int numVertices = getNumVertices();
9649433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
965ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final float[] vertices = new float[numVertices * vertexSize];
966ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		// TODO: getVertices(vertices, start * vertexSize, count * vertexSize);
967ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		getVertices(0, vertices.length, vertices);
968ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		transformUV(matrix, vertices, vertexSize, offset, start, count);
969ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		setVertices(vertices, 0, vertices.length);
970ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		// TODO: setVertices(start * vertexSize, vertices, 0, vertices.length);
971ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	}
9729433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
9739433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	/** Method to transform the texture coordinates (UV) in the float array. This is a potentially slow operation, use with care.
974ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param matrix the transformation matrix
975ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param vertices the float array
976ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param vertexSize the number of floats in each vertex
977ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param offset the offset within a vertex to the texture location
978ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	 * @param start the vertex to start with
9799433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @param count the amount of vertices to transform */
9809433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public static void transformUV (final Matrix3 matrix, final float[] vertices, int vertexSize, int offset, int start, int count) {
981ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		if (start < 0 || count < 1 || ((start + count) * vertexSize) > vertices.length)
9829433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet			throw new IndexOutOfBoundsException("start = " + start + ", count = " + count + ", vertexSize = " + vertexSize
9839433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet				+ ", length = " + vertices.length);
9849433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
985ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		final Vector2 tmp = new Vector2();
9869433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
987ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		int idx = offset + (start * vertexSize);
988ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		for (int i = 0; i < count; i++) {
9899433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet			tmp.set(vertices[idx], vertices[idx + 1]).mul(matrix);
990ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			vertices[idx] = tmp.x;
9919433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet			vertices[idx + 1] = tmp.y;
992ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa			idx += vertexSize;
993ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa		}
994ba5b19207d55f30c027b4a5ac3deb50d05bf8992Xoppa	}
9959433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
9968a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	/** Copies this mesh optionally removing duplicate vertices and/or reducing the amount of attributes.
9978a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	 * @param isStatic whether the new mesh is static or not. Allows for internal optimizations.
9988a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	 * @param removeDuplicates whether to remove duplicate vertices if possible. Only the vertices specified by usage are checked.
9998a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	 * @param usage which attributes (if available) to copy
10009433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @return the copy of this mesh */
10019433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public Mesh copy (boolean isStatic, boolean removeDuplicates, final int[] usage) {
10028a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		// TODO move this to a copy constructor?
10038a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		// TODO duplicate the buffers without double copying the data if possible.
10048a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		// TODO perhaps move this code to JNI if it turns out being too slow.
10058a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		final int vertexSize = getVertexSize() / 4;
10068a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		int numVertices = getNumVertices();
10078a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		float[] vertices = new float[numVertices * vertexSize];
10088a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		getVertices(0, vertices.length, vertices);
10098a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		short[] checks = null;
10108a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		VertexAttribute[] attrs = null;
10118a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		int newVertexSize = 0;
10128a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		if (usage != null) {
10138a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			int size = 0;
10148a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			int as = 0;
10158a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			for (int i = 0; i < usage.length; i++)
10168a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				if (getVertexAttribute(usage[i]) != null) {
10178a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					size += getVertexAttribute(usage[i]).numComponents;
10188a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					as++;
10198a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				}
10208a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			if (size > 0) {
10218a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				attrs = new VertexAttribute[as];
10228a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				checks = new short[size];
10238a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				int idx = -1;
10248a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				int ai = -1;
10258a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				for (int i = 0; i < usage.length; i++) {
10268a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					VertexAttribute a = getVertexAttribute(usage[i]);
10279433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet					if (a == null) continue;
10288a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					for (int j = 0; j < a.numComponents; j++)
10298a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						checks[++idx] = (short)(a.offset + j);
10308a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					attrs[++ai] = new VertexAttribute(a.usage, a.numComponents, a.alias);
10318a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					newVertexSize += a.numComponents;
10328a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				}
10338a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			}
10348a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		}
10358a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		if (checks == null) {
10368a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			checks = new short[vertexSize];
10378a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			for (short i = 0; i < vertexSize; i++)
10388a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				checks[i] = i;
10398a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			newVertexSize = vertexSize;
10408a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		}
10419433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
10428a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		int numIndices = getNumIndices();
10439433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet		short[] indices = null;
10448a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		if (numIndices > 0) {
10458a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			indices = new short[numIndices];
10468a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			getIndices(indices);
10478a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			if (removeDuplicates || newVertexSize != vertexSize) {
10488a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				float[] tmp = new float[vertices.length];
10498a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				int size = 0;
10508a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				for (int i = 0; i < numIndices; i++) {
10518a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					final int idx1 = indices[i] * vertexSize;
10528a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					short newIndex = -1;
10538a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					if (removeDuplicates) {
10548a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						for (short j = 0; j < size && newIndex < 0; j++) {
10559433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet							final int idx2 = j * newVertexSize;
10568a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa							boolean found = true;
10578a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa							for (int k = 0; k < checks.length && found; k++) {
10589433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet								if (tmp[idx2 + k] != vertices[idx1 + checks[k]]) found = false;
10598a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa							}
10609433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet							if (found) newIndex = j;
10618a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						}
10628a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					}
10638a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					if (newIndex > 0)
10648a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						indices[i] = newIndex;
10658a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					else {
10668a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						final int idx = size * newVertexSize;
10678a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						for (int j = 0; j < checks.length; j++)
10689433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet							tmp[idx + j] = vertices[idx1 + checks[j]];
10698a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						indices[i] = (short)size;
10708a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa						size++;
10718a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa					}
10728a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				}
10738a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				vertices = tmp;
10748a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa				numVertices = size;
10758a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			}
10768a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		}
10779433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
10788a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		Mesh result;
10798a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		if (attrs == null)
10808a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, getVertexAttributes());
10818a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		else
10828a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa			result = new Mesh(isStatic, numVertices, indices == null ? 0 : indices.length, attrs);
10838a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		result.setVertices(vertices, 0, numVertices * newVertexSize);
10848a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		result.setIndices(indices);
10858a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		return result;
10868a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	}
10879433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet
10888a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	/** Copies this mesh.
10898a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	 * @param isStatic whether the new mesh is static or not. Allows for internal optimizations.
10909433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	 * @return the copy of this mesh */
10919433d7435548a2583fa50137ea0dc9c424f8acd6NathanSweet	public Mesh copy (boolean isStatic) {
10928a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa		return copy(isStatic, false, null);
10938a78a4a9b44d8c24c8afd90945dbc2cb570226edXoppa	}
1094114c5c91c13f74b04c08c0816d649da267bdcbbdbadlogicgames}
1095