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