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