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