1 /* 2 * Copyright (c) 2009-2012 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.jme3.scene; 34 35 import com.jme3.bounding.BoundingBox; 36 import com.jme3.bounding.BoundingVolume; 37 import com.jme3.collision.Collidable; 38 import com.jme3.collision.CollisionResults; 39 import com.jme3.collision.bih.BIHTree; 40 import com.jme3.export.*; 41 import com.jme3.material.RenderState; 42 import com.jme3.math.Matrix4f; 43 import com.jme3.math.Triangle; 44 import com.jme3.math.Vector2f; 45 import com.jme3.math.Vector3f; 46 import com.jme3.scene.VertexBuffer.Format; 47 import com.jme3.scene.VertexBuffer.Type; 48 import com.jme3.scene.VertexBuffer.Usage; 49 import com.jme3.scene.mesh.*; 50 import com.jme3.util.BufferUtils; 51 import com.jme3.util.IntMap; 52 import com.jme3.util.IntMap.Entry; 53 import com.jme3.util.SafeArrayList; 54 import java.io.IOException; 55 import java.nio.*; 56 import java.util.ArrayList; 57 58 /** 59 * <code>Mesh</code> is used to store rendering data. 60 * <p> 61 * All visible elements in a scene are represented by meshes. 62 * Meshes may contain three types of geometric primitives: 63 * <ul> 64 * <li>Points - Every vertex represents a single point in space, 65 * the size of each point is specified via {@link Mesh#setPointSize(float) }. 66 * Points can also be used for {@link RenderState#setPointSprite(boolean) point 67 * sprite} mode.</li> 68 * <li>Lines - 2 vertices represent a line segment, with the width specified 69 * via {@link Mesh#setLineWidth(float) }.</li> 70 * <li>Triangles - 3 vertices represent a solid triangle primitive. </li> 71 * </ul> 72 * 73 * @author Kirill Vainer 74 */ 75 public class Mesh implements Savable, Cloneable { 76 77 /** 78 * The mode of the Mesh specifies both the type of primitive represented 79 * by the mesh and how the data should be interpreted. 80 */ 81 public enum Mode { 82 /** 83 * A primitive is a single point in space. The size of the points 84 * can be specified with {@link Mesh#setPointSize(float) }. 85 */ 86 Points(true), 87 88 /** 89 * A primitive is a line segment. Every two vertices specify 90 * a single line. {@link Mesh#setLineWidth(float) } can be used 91 * to set the width of the lines. 92 */ 93 Lines(true), 94 95 /** 96 * A primitive is a line segment. The first two vertices specify 97 * a single line, while subsequent vertices are combined with the 98 * previous vertex to make a line. {@link Mesh#setLineWidth(float) } can 99 * be used to set the width of the lines. 100 */ 101 LineStrip(false), 102 103 /** 104 * Identical to {@link #LineStrip} except that at the end 105 * the last vertex is connected with the first to form a line. 106 * {@link Mesh#setLineWidth(float) } can be used 107 * to set the width of the lines. 108 */ 109 LineLoop(false), 110 111 /** 112 * A primitive is a triangle. Each 3 vertices specify a single 113 * triangle. 114 */ 115 Triangles(true), 116 117 /** 118 * Similar to {@link #Triangles}, the first 3 vertices 119 * specify a triangle, while subsequent vertices are combined with 120 * the previous two to form a triangle. 121 */ 122 TriangleStrip(false), 123 124 /** 125 * Similar to {@link #Triangles}, the first 3 vertices 126 * specify a triangle, each 2 subsequent vertices are combined 127 * with the very first vertex to make a triangle. 128 */ 129 TriangleFan(false), 130 131 /** 132 * A combination of various triangle modes. It is best to avoid 133 * using this mode as it may not be supported by all renderers. 134 * The {@link Mesh#setModeStart(int[]) mode start points} and 135 * {@link Mesh#setElementLengths(int[]) element lengths} must 136 * be specified for this mode. 137 */ 138 Hybrid(false); 139 140 private boolean listMode = false; 141 Mode(boolean listMode)142 private Mode(boolean listMode){ 143 this.listMode = listMode; 144 } 145 146 /** 147 * Returns true if the specified mode is a list mode (meaning 148 * ,it specifies the indices as a linear list and not some special 149 * format). 150 * Will return true for the types {@link #Points}, {@link #Lines} and 151 * {@link #Triangles}. 152 * 153 * @return true if the mode is a list type mode 154 */ isListMode()155 public boolean isListMode(){ 156 return listMode; 157 } 158 } 159 160 /** 161 * The bounding volume that contains the mesh entirely. 162 * By default a BoundingBox (AABB). 163 */ 164 private BoundingVolume meshBound = new BoundingBox(); 165 166 private CollisionData collisionTree = null; 167 168 private SafeArrayList<VertexBuffer> buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class); 169 private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>(); 170 private VertexBuffer[] lodLevels; 171 private float pointSize = 1; 172 private float lineWidth = 1; 173 174 private transient int vertexArrayID = -1; 175 176 private int vertCount = -1; 177 private int elementCount = -1; 178 private int maxNumWeights = -1; // only if using skeletal animation 179 180 private int[] elementLengths; 181 private int[] modeStart; 182 183 private Mode mode = Mode.Triangles; 184 185 /** 186 * Creates a new mesh with no {@link VertexBuffer vertex buffers}. 187 */ Mesh()188 public Mesh(){ 189 } 190 191 /** 192 * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex 193 * buffers} are shared between this and the clone mesh, the rest 194 * of the data is cloned. 195 * 196 * @return A shallow clone of the mesh 197 */ 198 @Override clone()199 public Mesh clone() { 200 try { 201 Mesh clone = (Mesh) super.clone(); 202 clone.meshBound = meshBound.clone(); 203 clone.collisionTree = collisionTree != null ? collisionTree : null; 204 clone.buffers = buffers.clone(); 205 clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class,buffersList); 206 clone.vertexArrayID = -1; 207 if (elementLengths != null) { 208 clone.elementLengths = elementLengths.clone(); 209 } 210 if (modeStart != null) { 211 clone.modeStart = modeStart.clone(); 212 } 213 return clone; 214 } catch (CloneNotSupportedException ex) { 215 throw new AssertionError(); 216 } 217 } 218 219 /** 220 * Creates a deep clone of this mesh. 221 * The {@link VertexBuffer vertex buffers} and the data inside them 222 * is cloned. 223 * 224 * @return a deep clone of this mesh. 225 */ deepClone()226 public Mesh deepClone(){ 227 try{ 228 Mesh clone = (Mesh) super.clone(); 229 clone.meshBound = meshBound != null ? meshBound.clone() : null; 230 231 // TODO: Collision tree cloning 232 //clone.collisionTree = collisionTree != null ? collisionTree : null; 233 clone.collisionTree = null; // it will get re-generated in any case 234 235 clone.buffers = new IntMap<VertexBuffer>(); 236 clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class); 237 for (VertexBuffer vb : buffersList.getArray()){ 238 VertexBuffer bufClone = vb.clone(); 239 clone.buffers.put(vb.getBufferType().ordinal(), bufClone); 240 clone.buffersList.add(bufClone); 241 } 242 243 clone.vertexArrayID = -1; 244 clone.vertCount = -1; 245 clone.elementCount = -1; 246 247 // although this could change 248 // if the bone weight/index buffers are modified 249 clone.maxNumWeights = maxNumWeights; 250 251 clone.elementLengths = elementLengths != null ? elementLengths.clone() : null; 252 clone.modeStart = modeStart != null ? modeStart.clone() : null; 253 return clone; 254 }catch (CloneNotSupportedException ex){ 255 throw new AssertionError(); 256 } 257 } 258 259 /** 260 * Clone the mesh for animation use. 261 * This creates a shallow clone of the mesh, sharing most 262 * of the {@link VertexBuffer vertex buffer} data, however the 263 * {@link Type#Position}, {@link Type#Normal}, and {@link Type#Tangent} buffers 264 * are deeply cloned. 265 * 266 * @return A clone of the mesh for animation use. 267 */ cloneForAnim()268 public Mesh cloneForAnim(){ 269 Mesh clone = clone(); 270 if (getBuffer(Type.BindPosePosition) != null){ 271 VertexBuffer oldPos = getBuffer(Type.Position); 272 273 // NOTE: creates deep clone 274 VertexBuffer newPos = oldPos.clone(); 275 clone.clearBuffer(Type.Position); 276 clone.setBuffer(newPos); 277 278 if (getBuffer(Type.BindPoseNormal) != null){ 279 VertexBuffer oldNorm = getBuffer(Type.Normal); 280 VertexBuffer newNorm = oldNorm.clone(); 281 clone.clearBuffer(Type.Normal); 282 clone.setBuffer(newNorm); 283 284 if (getBuffer(Type.BindPoseTangent) != null){ 285 VertexBuffer oldTang = getBuffer(Type.Tangent); 286 VertexBuffer newTang = oldTang.clone(); 287 clone.clearBuffer(Type.Tangent); 288 clone.setBuffer(newTang); 289 } 290 } 291 } 292 return clone; 293 } 294 295 /** 296 * Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal}, 297 * and {@link Type#BindPoseTangent} 298 * buffers for this mesh by duplicating them based on the position and normal 299 * buffers already set on the mesh. 300 * This method does nothing if the mesh has no bone weight or index 301 * buffers. 302 * 303 * @param forSoftwareAnim Should be true if the bind pose is to be generated. 304 */ generateBindPose(boolean forSoftwareAnim)305 public void generateBindPose(boolean forSoftwareAnim){ 306 if (forSoftwareAnim){ 307 VertexBuffer pos = getBuffer(Type.Position); 308 if (pos == null || getBuffer(Type.BoneIndex) == null) { 309 // ignore, this mesh doesn't have positional data 310 // or it doesn't have bone-vertex assignments, so its not animated 311 return; 312 } 313 314 VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition); 315 bindPos.setupData(Usage.CpuOnly, 316 3, 317 Format.Float, 318 BufferUtils.clone(pos.getData())); 319 setBuffer(bindPos); 320 321 // XXX: note that this method also sets stream mode 322 // so that animation is faster. this is not needed for hardware skinning 323 pos.setUsage(Usage.Stream); 324 325 VertexBuffer norm = getBuffer(Type.Normal); 326 if (norm != null) { 327 VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal); 328 bindNorm.setupData(Usage.CpuOnly, 329 3, 330 Format.Float, 331 BufferUtils.clone(norm.getData())); 332 setBuffer(bindNorm); 333 norm.setUsage(Usage.Stream); 334 } 335 336 VertexBuffer tangents = getBuffer(Type.Tangent); 337 if (tangents != null) { 338 VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent); 339 bindTangents.setupData(Usage.CpuOnly, 340 4, 341 Format.Float, 342 BufferUtils.clone(tangents.getData())); 343 setBuffer(bindTangents); 344 tangents.setUsage(Usage.Stream); 345 } 346 } 347 } 348 349 /** 350 * Prepares the mesh for software skinning by converting the bone index 351 * and weight buffers to heap buffers. 352 * 353 * @param forSoftwareAnim Should be true to enable the conversion. 354 */ prepareForAnim(boolean forSoftwareAnim)355 public void prepareForAnim(boolean forSoftwareAnim){ 356 if (forSoftwareAnim){ 357 // convert indices 358 VertexBuffer indices = getBuffer(Type.BoneIndex); 359 ByteBuffer originalIndex = (ByteBuffer) indices.getData(); 360 ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity()); 361 originalIndex.clear(); 362 arrayIndex.put(originalIndex); 363 indices.updateData(arrayIndex); 364 365 // convert weights 366 VertexBuffer weights = getBuffer(Type.BoneWeight); 367 FloatBuffer originalWeight = (FloatBuffer) weights.getData(); 368 FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity()); 369 originalWeight.clear(); 370 arrayWeight.put(originalWeight); 371 weights.updateData(arrayWeight); 372 } 373 } 374 375 /** 376 * Set the LOD (level of detail) index buffers on this mesh. 377 * 378 * @param lodLevels The LOD levels to set 379 */ setLodLevels(VertexBuffer[] lodLevels)380 public void setLodLevels(VertexBuffer[] lodLevels){ 381 this.lodLevels = lodLevels; 382 } 383 384 /** 385 * @return The number of LOD levels set on this mesh, including the main 386 * index buffer, returns zero if there are no lod levels. 387 */ getNumLodLevels()388 public int getNumLodLevels(){ 389 return lodLevels != null ? lodLevels.length : 0; 390 } 391 392 /** 393 * Returns the lod level at the given index. 394 * 395 * @param lod The lod level index, this does not include 396 * the main index buffer. 397 * @return The LOD index buffer at the index 398 * 399 * @throws IndexOutOfBoundsException If the index is outside of the 400 * range [0, {@link #getNumLodLevels()}]. 401 * 402 * @see #setLodLevels(com.jme3.scene.VertexBuffer[]) 403 */ getLodLevel(int lod)404 public VertexBuffer getLodLevel(int lod){ 405 return lodLevels[lod]; 406 } 407 408 /** 409 * Get the element lengths for {@link Mode#Hybrid} mesh mode. 410 * 411 * @return element lengths 412 */ getElementLengths()413 public int[] getElementLengths() { 414 return elementLengths; 415 } 416 417 /** 418 * Set the element lengths for {@link Mode#Hybrid} mesh mode. 419 * 420 * @param elementLengths The element lengths to set 421 */ setElementLengths(int[] elementLengths)422 public void setElementLengths(int[] elementLengths) { 423 this.elementLengths = elementLengths; 424 } 425 426 /** 427 * Set the mode start indices for {@link Mode#Hybrid} mesh mode. 428 * 429 * @return mode start indices 430 */ getModeStart()431 public int[] getModeStart() { 432 return modeStart; 433 } 434 435 /** 436 * Get the mode start indices for {@link Mode#Hybrid} mesh mode. 437 * 438 * @return mode start indices 439 */ setModeStart(int[] modeStart)440 public void setModeStart(int[] modeStart) { 441 this.modeStart = modeStart; 442 } 443 444 /** 445 * Returns the mesh mode 446 * 447 * @return the mesh mode 448 * 449 * @see #setMode(com.jme3.scene.Mesh.Mode) 450 */ getMode()451 public Mode getMode() { 452 return mode; 453 } 454 455 /** 456 * Change the Mesh's mode. By default the mode is {@link Mode#Triangles}. 457 * 458 * @param mode The new mode to set 459 * 460 * @see Mode 461 */ setMode(Mode mode)462 public void setMode(Mode mode) { 463 this.mode = mode; 464 updateCounts(); 465 } 466 467 /** 468 * Returns the maximum number of weights per vertex on this mesh. 469 * 470 * @return maximum number of weights per vertex 471 * 472 * @see #setMaxNumWeights(int) 473 */ getMaxNumWeights()474 public int getMaxNumWeights() { 475 return maxNumWeights; 476 } 477 478 /** 479 * Set the maximum number of weights per vertex on this mesh. 480 * Only relevant if this mesh has bone index/weight buffers. 481 * This value should be between 0 and 4. 482 * 483 * @param maxNumWeights 484 */ setMaxNumWeights(int maxNumWeights)485 public void setMaxNumWeights(int maxNumWeights) { 486 this.maxNumWeights = maxNumWeights; 487 } 488 489 /** 490 * Returns the size of points for point meshes 491 * 492 * @return the size of points 493 * 494 * @see #setPointSize(float) 495 */ getPointSize()496 public float getPointSize() { 497 return pointSize; 498 } 499 500 /** 501 * Set the size of points for meshes of mode {@link Mode#Points}. 502 * The point size is specified as on-screen pixels, the default 503 * value is 1.0. The point size 504 * does nothing if {@link RenderState#setPointSprite(boolean) point sprite} 505 * render state is enabled, in that case, the vertex shader must specify the 506 * point size by writing to <code>gl_PointSize</code>. 507 * 508 * @param pointSize The size of points 509 */ setPointSize(float pointSize)510 public void setPointSize(float pointSize) { 511 this.pointSize = pointSize; 512 } 513 514 /** 515 * Returns the line width for line meshes. 516 * 517 * @return the line width 518 */ getLineWidth()519 public float getLineWidth() { 520 return lineWidth; 521 } 522 523 /** 524 * Specify the line width for meshes of the line modes, such 525 * as {@link Mode#Lines}. The line width is specified as on-screen pixels, 526 * the default value is 1.0. 527 * 528 * @param lineWidth The line width 529 */ setLineWidth(float lineWidth)530 public void setLineWidth(float lineWidth) { 531 this.lineWidth = lineWidth; 532 } 533 534 /** 535 * Indicates to the GPU that this mesh will not be modified (a hint). 536 * Sets the usage mode to {@link Usage#Static} 537 * for all {@link VertexBuffer vertex buffers} on this Mesh. 538 */ setStatic()539 public void setStatic() { 540 for (VertexBuffer vb : buffersList.getArray()){ 541 vb.setUsage(Usage.Static); 542 } 543 } 544 545 /** 546 * Indicates to the GPU that this mesh will be modified occasionally (a hint). 547 * Sets the usage mode to {@link Usage#Dynamic} 548 * for all {@link VertexBuffer vertex buffers} on this Mesh. 549 */ setDynamic()550 public void setDynamic() { 551 for (VertexBuffer vb : buffersList.getArray()){ 552 vb.setUsage(Usage.Dynamic); 553 } 554 } 555 556 /** 557 * Indicates to the GPU that this mesh will be modified every frame (a hint). 558 * Sets the usage mode to {@link Usage#Stream} 559 * for all {@link VertexBuffer vertex buffers} on this Mesh. 560 */ setStreamed()561 public void setStreamed(){ 562 for (VertexBuffer vb : buffersList.getArray()){ 563 vb.setUsage(Usage.Stream); 564 } 565 } 566 567 /** 568 * Interleaves the data in this mesh. This operation cannot be reversed. 569 * Some GPUs may prefer the data in this format, however it is a good idea 570 * to <em>avoid</em> using this method as it disables some engine features. 571 */ 572 @Deprecated setInterleaved()573 public void setInterleaved(){ 574 ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(); 575 vbs.addAll(buffersList); 576 577 // ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values()); 578 // index buffer not included when interleaving 579 vbs.remove(getBuffer(Type.Index)); 580 581 int stride = 0; // aka bytes per vertex 582 for (int i = 0; i < vbs.size(); i++){ 583 VertexBuffer vb = vbs.get(i); 584 // if (vb.getFormat() != Format.Float){ 585 // throw new UnsupportedOperationException("Cannot interleave vertex buffer.\n" + 586 // "Contains not-float data."); 587 // } 588 stride += vb.componentsLength; 589 vb.getData().clear(); // reset position & limit (used later) 590 } 591 592 VertexBuffer allData = new VertexBuffer(Type.InterleavedData); 593 ByteBuffer dataBuf = BufferUtils.createByteBuffer(stride * getVertexCount()); 594 allData.setupData(Usage.Static, 1, Format.UnsignedByte, dataBuf); 595 596 // adding buffer directly so that no update counts is forced 597 buffers.put(Type.InterleavedData.ordinal(), allData); 598 buffersList.add(allData); 599 600 for (int vert = 0; vert < getVertexCount(); vert++){ 601 for (int i = 0; i < vbs.size(); i++){ 602 VertexBuffer vb = vbs.get(i); 603 switch (vb.getFormat()){ 604 case Float: 605 FloatBuffer fb = (FloatBuffer) vb.getData(); 606 for (int comp = 0; comp < vb.components; comp++){ 607 dataBuf.putFloat(fb.get()); 608 } 609 break; 610 case Byte: 611 case UnsignedByte: 612 ByteBuffer bb = (ByteBuffer) vb.getData(); 613 for (int comp = 0; comp < vb.components; comp++){ 614 dataBuf.put(bb.get()); 615 } 616 break; 617 case Half: 618 case Short: 619 case UnsignedShort: 620 ShortBuffer sb = (ShortBuffer) vb.getData(); 621 for (int comp = 0; comp < vb.components; comp++){ 622 dataBuf.putShort(sb.get()); 623 } 624 break; 625 case Int: 626 case UnsignedInt: 627 IntBuffer ib = (IntBuffer) vb.getData(); 628 for (int comp = 0; comp < vb.components; comp++){ 629 dataBuf.putInt(ib.get()); 630 } 631 break; 632 case Double: 633 DoubleBuffer db = (DoubleBuffer) vb.getData(); 634 for (int comp = 0; comp < vb.components; comp++){ 635 dataBuf.putDouble(db.get()); 636 } 637 break; 638 } 639 } 640 } 641 642 int offset = 0; 643 for (VertexBuffer vb : vbs){ 644 vb.setOffset(offset); 645 vb.setStride(stride); 646 647 vb.updateData(null); 648 //vb.setupData(vb.usage, vb.components, vb.format, null); 649 offset += vb.componentsLength; 650 } 651 } 652 computeNumElements(int bufSize)653 private int computeNumElements(int bufSize){ 654 switch (mode){ 655 case Triangles: 656 return bufSize / 3; 657 case TriangleFan: 658 case TriangleStrip: 659 return bufSize - 2; 660 case Points: 661 return bufSize; 662 case Lines: 663 return bufSize / 2; 664 case LineLoop: 665 return bufSize; 666 case LineStrip: 667 return bufSize - 1; 668 default: 669 throw new UnsupportedOperationException(); 670 } 671 } 672 673 /** 674 * Update the {@link #getVertexCount() vertex} and 675 * {@link #getTriangleCount() triangle} counts for this mesh 676 * based on the current data. This method should be called 677 * after the {@link Buffer#capacity() capacities} of the mesh's 678 * {@link VertexBuffer vertex buffers} has been altered. 679 * 680 * @throws IllegalStateException If this mesh is in 681 * {@link #setInterleaved() interleaved} format. 682 */ updateCounts()683 public void updateCounts(){ 684 if (getBuffer(Type.InterleavedData) != null) 685 throw new IllegalStateException("Should update counts before interleave"); 686 687 VertexBuffer pb = getBuffer(Type.Position); 688 VertexBuffer ib = getBuffer(Type.Index); 689 if (pb != null){ 690 vertCount = pb.getData().capacity() / pb.getNumComponents(); 691 } 692 if (ib != null){ 693 elementCount = computeNumElements(ib.getData().capacity()); 694 }else{ 695 elementCount = computeNumElements(vertCount); 696 } 697 } 698 699 /** 700 * Returns the triangle count for the given LOD level. 701 * 702 * @param lod The lod level to look up 703 * @return The triangle count for that LOD level 704 */ getTriangleCount(int lod)705 public int getTriangleCount(int lod){ 706 if (lodLevels != null){ 707 if (lod < 0) 708 throw new IllegalArgumentException("LOD level cannot be < 0"); 709 710 if (lod >= lodLevels.length) 711 throw new IllegalArgumentException("LOD level "+lod+" does not exist!"); 712 713 return computeNumElements(lodLevels[lod].getData().capacity()); 714 }else if (lod == 0){ 715 return elementCount; 716 }else{ 717 throw new IllegalArgumentException("There are no LOD levels on the mesh!"); 718 } 719 } 720 721 /** 722 * Returns how many triangles or elements are on this Mesh. 723 * This value is only updated when {@link #updateCounts() } is called. 724 * If the mesh mode is not a triangle mode, then this returns the 725 * number of elements/primitives, e.g. how many lines or how many points, 726 * instead of how many triangles. 727 * 728 * @return how many triangles/elements are on this Mesh. 729 */ getTriangleCount()730 public int getTriangleCount(){ 731 return elementCount; 732 } 733 734 /** 735 * Returns the number of vertices on this mesh. 736 * The value is computed based on the position buffer, which 737 * must be set on all meshes. 738 * 739 * @return Number of vertices on the mesh 740 */ getVertexCount()741 public int getVertexCount(){ 742 return vertCount; 743 } 744 745 /** 746 * Gets the triangle vertex positions at the given triangle index 747 * and stores them into the v1, v2, v3 arguments. 748 * 749 * @param index The index of the triangle. 750 * Should be between 0 and {@link #getTriangleCount()}. 751 * 752 * @param v1 Vector to contain first vertex position 753 * @param v2 Vector to contain second vertex position 754 * @param v3 Vector to contain third vertex position 755 */ getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3)756 public void getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3){ 757 VertexBuffer pb = getBuffer(Type.Position); 758 IndexBuffer ib = getIndicesAsList(); 759 if (pb != null && pb.getFormat() == Format.Float && pb.getNumComponents() == 3){ 760 FloatBuffer fpb = (FloatBuffer) pb.getData(); 761 762 // aquire triangle's vertex indices 763 int vertIndex = index * 3; 764 int vert1 = ib.get(vertIndex); 765 int vert2 = ib.get(vertIndex+1); 766 int vert3 = ib.get(vertIndex+2); 767 768 BufferUtils.populateFromBuffer(v1, fpb, vert1); 769 BufferUtils.populateFromBuffer(v2, fpb, vert2); 770 BufferUtils.populateFromBuffer(v3, fpb, vert3); 771 }else{ 772 throw new UnsupportedOperationException("Position buffer not set or " 773 + " has incompatible format"); 774 } 775 } 776 777 /** 778 * Gets the triangle vertex positions at the given triangle index 779 * and stores them into the {@link Triangle} argument. 780 * Also sets the triangle index to the <code>index</code> argument. 781 * 782 * @param index The index of the triangle. 783 * Should be between 0 and {@link #getTriangleCount()}. 784 * 785 * @param tri The triangle to store the positions in 786 */ getTriangle(int index, Triangle tri)787 public void getTriangle(int index, Triangle tri){ 788 getTriangle(index, tri.get1(), tri.get2(), tri.get3()); 789 tri.setIndex(index); 790 tri.setNormal(null); 791 } 792 793 /** 794 * Gets the triangle vertex indices at the given triangle index 795 * and stores them into the given int array. 796 * 797 * @param index The index of the triangle. 798 * Should be between 0 and {@link #getTriangleCount()}. 799 * 800 * @param indices Indices of the triangle's vertices 801 */ getTriangle(int index, int[] indices)802 public void getTriangle(int index, int[] indices){ 803 IndexBuffer ib = getIndicesAsList(); 804 805 // acquire triangle's vertex indices 806 int vertIndex = index * 3; 807 indices[0] = ib.get(vertIndex); 808 indices[1] = ib.get(vertIndex+1); 809 indices[2] = ib.get(vertIndex+2); 810 } 811 812 /** 813 * Returns the mesh's VAO ID. Internal use only. 814 */ getId()815 public int getId(){ 816 return vertexArrayID; 817 } 818 819 /** 820 * Sets the mesh's VAO ID. Internal use only. 821 */ setId(int id)822 public void setId(int id){ 823 if (vertexArrayID != -1) 824 throw new IllegalStateException("ID has already been set."); 825 826 vertexArrayID = id; 827 } 828 829 /** 830 * Generates a collision tree for the mesh. 831 * Called automatically by {@link #collideWith(com.jme3.collision.Collidable, 832 * com.jme3.math.Matrix4f, 833 * com.jme3.bounding.BoundingVolume, 834 * com.jme3.collision.CollisionResults) }. 835 */ createCollisionData()836 public void createCollisionData(){ 837 BIHTree tree = new BIHTree(this); 838 tree.construct(); 839 collisionTree = tree; 840 } 841 842 /** 843 * Handles collision detection, internal use only. 844 * User code should only use collideWith() on scene 845 * graph elements such as {@link Spatial}s. 846 */ collideWith(Collidable other, Matrix4f worldMatrix, BoundingVolume worldBound, CollisionResults results)847 public int collideWith(Collidable other, 848 Matrix4f worldMatrix, 849 BoundingVolume worldBound, 850 CollisionResults results){ 851 852 if (collisionTree == null){ 853 createCollisionData(); 854 } 855 856 return collisionTree.collideWith(other, worldMatrix, worldBound, results); 857 } 858 859 /** 860 * Sets the {@link VertexBuffer} on the mesh. 861 * This will update the vertex/triangle counts if needed. 862 * 863 * @param vb The buffer to set 864 * @throws IllegalArgumentException If the buffer type is already set 865 */ setBuffer(VertexBuffer vb)866 public void setBuffer(VertexBuffer vb){ 867 if (buffers.containsKey(vb.getBufferType().ordinal())) 868 throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); 869 870 buffers.put(vb.getBufferType().ordinal(), vb); 871 buffersList.add(vb); 872 updateCounts(); 873 } 874 875 /** 876 * Unsets the {@link VertexBuffer} set on this mesh 877 * with the given type. Does nothing if the vertex buffer type is not set 878 * initially. 879 * 880 * @param type The buffer type to remove 881 */ clearBuffer(VertexBuffer.Type type)882 public void clearBuffer(VertexBuffer.Type type){ 883 VertexBuffer vb = buffers.remove(type.ordinal()); 884 if (vb != null){ 885 buffersList.remove(vb); 886 updateCounts(); 887 } 888 } 889 890 /** 891 * Creates a {@link VertexBuffer} for the mesh or modifies 892 * the existing one per the parameters given. 893 * 894 * @param type The type of the buffer 895 * @param components Number of components 896 * @param format Data format 897 * @param buf The buffer data 898 * 899 * @throws UnsupportedOperationException If the buffer already set is 900 * incompatible with the parameters given. 901 */ setBuffer(Type type, int components, Format format, Buffer buf)902 public void setBuffer(Type type, int components, Format format, Buffer buf){ 903 VertexBuffer vb = buffers.get(type.ordinal()); 904 if (vb == null){ 905 vb = new VertexBuffer(type); 906 vb.setupData(Usage.Dynamic, components, format, buf); 907 setBuffer(vb); 908 }else{ 909 if (vb.getNumComponents() != components || vb.getFormat() != format){ 910 throw new UnsupportedOperationException("The buffer already set " 911 + "is incompatible with the given parameters"); 912 } 913 vb.updateData(buf); 914 updateCounts(); 915 } 916 } 917 918 /** 919 * Set a floating point {@link VertexBuffer} on the mesh. 920 * 921 * @param type The type of {@link VertexBuffer}, 922 * e.g. {@link Type#Position}, {@link Type#Normal}, etc. 923 * 924 * @param components Number of components on the vertex buffer, should 925 * be between 1 and 4. 926 * 927 * @param buf The floating point data to contain 928 */ setBuffer(Type type, int components, FloatBuffer buf)929 public void setBuffer(Type type, int components, FloatBuffer buf) { 930 setBuffer(type, components, Format.Float, buf); 931 } 932 setBuffer(Type type, int components, float[] buf)933 public void setBuffer(Type type, int components, float[] buf){ 934 setBuffer(type, components, BufferUtils.createFloatBuffer(buf)); 935 } 936 setBuffer(Type type, int components, IntBuffer buf)937 public void setBuffer(Type type, int components, IntBuffer buf) { 938 setBuffer(type, components, Format.UnsignedInt, buf); 939 } 940 setBuffer(Type type, int components, int[] buf)941 public void setBuffer(Type type, int components, int[] buf){ 942 setBuffer(type, components, BufferUtils.createIntBuffer(buf)); 943 } 944 setBuffer(Type type, int components, ShortBuffer buf)945 public void setBuffer(Type type, int components, ShortBuffer buf) { 946 setBuffer(type, components, Format.UnsignedShort, buf); 947 } 948 setBuffer(Type type, int components, byte[] buf)949 public void setBuffer(Type type, int components, byte[] buf){ 950 setBuffer(type, components, BufferUtils.createByteBuffer(buf)); 951 } 952 setBuffer(Type type, int components, ByteBuffer buf)953 public void setBuffer(Type type, int components, ByteBuffer buf) { 954 setBuffer(type, components, Format.UnsignedByte, buf); 955 } 956 setBuffer(Type type, int components, short[] buf)957 public void setBuffer(Type type, int components, short[] buf){ 958 setBuffer(type, components, BufferUtils.createShortBuffer(buf)); 959 } 960 961 /** 962 * Get the {@link VertexBuffer} stored on this mesh with the given 963 * type. 964 * 965 * @param type The type of VertexBuffer 966 * @return the VertexBuffer data, or null if not set 967 */ getBuffer(Type type)968 public VertexBuffer getBuffer(Type type){ 969 return buffers.get(type.ordinal()); 970 } 971 972 /** 973 * Get the {@link VertexBuffer} data stored on this mesh in float 974 * format. 975 * 976 * @param type The type of VertexBuffer 977 * @return the VertexBuffer data, or null if not set 978 */ getFloatBuffer(Type type)979 public FloatBuffer getFloatBuffer(Type type) { 980 VertexBuffer vb = getBuffer(type); 981 if (vb == null) 982 return null; 983 984 return (FloatBuffer) vb.getData(); 985 } 986 987 /** 988 * Get the {@link VertexBuffer} data stored on this mesh in short 989 * format. 990 * 991 * @param type The type of VertexBuffer 992 * @return the VertexBuffer data, or null if not set 993 */ getShortBuffer(Type type)994 public ShortBuffer getShortBuffer(Type type) { 995 VertexBuffer vb = getBuffer(type); 996 if (vb == null) 997 return null; 998 999 return (ShortBuffer) vb.getData(); 1000 } 1001 1002 /** 1003 * Acquires an index buffer that will read the vertices on the mesh 1004 * as a list. 1005 * 1006 * @return A virtual or wrapped index buffer to read the data as a list 1007 */ getIndicesAsList()1008 public IndexBuffer getIndicesAsList(){ 1009 if (mode == Mode.Hybrid) 1010 throw new UnsupportedOperationException("Hybrid mode not supported"); 1011 1012 IndexBuffer ib = getIndexBuffer(); 1013 if (ib != null){ 1014 if (mode.isListMode()){ 1015 // already in list mode 1016 return ib; 1017 }else{ 1018 // not in list mode but it does have an index buffer 1019 // wrap it so the data is converted to list format 1020 return new WrappedIndexBuffer(this); 1021 } 1022 }else{ 1023 // return a virtual index buffer that will supply 1024 // "fake" indices in list format 1025 return new VirtualIndexBuffer(vertCount, mode); 1026 } 1027 } 1028 1029 /** 1030 * Get the index buffer for this mesh. 1031 * Will return <code>null</code> if no index buffer is set. 1032 * 1033 * @return The index buffer of this mesh. 1034 * 1035 * @see Type#Index 1036 */ getIndexBuffer()1037 public IndexBuffer getIndexBuffer() { 1038 VertexBuffer vb = getBuffer(Type.Index); 1039 if (vb == null) 1040 return null; 1041 1042 Buffer buf = vb.getData(); 1043 if (buf instanceof ByteBuffer) { 1044 return new IndexByteBuffer((ByteBuffer) buf); 1045 } else if (buf instanceof ShortBuffer) { 1046 return new IndexShortBuffer((ShortBuffer) buf); 1047 } else if (buf instanceof IntBuffer) { 1048 return new IndexIntBuffer((IntBuffer) buf); 1049 } else { 1050 throw new UnsupportedOperationException("Index buffer type unsupported: "+ buf.getClass()); 1051 } 1052 } 1053 1054 /** 1055 * Extracts the vertex attributes from the given mesh into 1056 * this mesh, by using this mesh's {@link #getIndexBuffer() index buffer} 1057 * to index into the attributes of the other mesh. 1058 * Note that this will also change this mesh's index buffer so that 1059 * the references to the vertex data match the new indices. 1060 * 1061 * @param other The mesh to extract the vertex data from 1062 */ extractVertexData(Mesh other)1063 public void extractVertexData(Mesh other) { 1064 // Determine the number of unique vertices need to 1065 // be created. Also determine the mappings 1066 // between old indices to new indices (since we avoid duplicating 1067 // vertices, this is a map and not an array). 1068 VertexBuffer oldIdxBuf = getBuffer(Type.Index); 1069 IndexBuffer indexBuf = getIndexBuffer(); 1070 int numIndices = indexBuf.size(); 1071 1072 IntMap<Integer> oldIndicesToNewIndices = new IntMap<Integer>(numIndices); 1073 ArrayList<Integer> newIndicesToOldIndices = new ArrayList<Integer>(); 1074 int newIndex = 0; 1075 1076 for (int i = 0; i < numIndices; i++) { 1077 int oldIndex = indexBuf.get(i); 1078 1079 if (!oldIndicesToNewIndices.containsKey(oldIndex)) { 1080 // this vertex has not been added, so allocate a 1081 // new index for it and add it to the map 1082 oldIndicesToNewIndices.put(oldIndex, newIndex); 1083 newIndicesToOldIndices.add(oldIndex); 1084 1085 // increment to have the next index 1086 newIndex++; 1087 } 1088 } 1089 1090 // Number of unique verts to be created now available 1091 int newNumVerts = newIndicesToOldIndices.size(); 1092 1093 if (newIndex != newNumVerts) { 1094 throw new AssertionError(); 1095 } 1096 1097 // Create the new index buffer. 1098 // Do not overwrite the old one because we might be able to 1099 // convert from int index buffer to short index buffer 1100 IndexBuffer newIndexBuf; 1101 if (newNumVerts >= 65536) { 1102 newIndexBuf = new IndexIntBuffer(BufferUtils.createIntBuffer(numIndices)); 1103 } else { 1104 newIndexBuf = new IndexShortBuffer(BufferUtils.createShortBuffer(numIndices)); 1105 } 1106 1107 for (int i = 0; i < numIndices; i++) { 1108 // Map the old indices to the new indices 1109 int oldIndex = indexBuf.get(i); 1110 newIndex = oldIndicesToNewIndices.get(oldIndex); 1111 1112 newIndexBuf.put(i, newIndex); 1113 } 1114 1115 VertexBuffer newIdxBuf = new VertexBuffer(Type.Index); 1116 newIdxBuf.setupData(oldIdxBuf.getUsage(), 1117 oldIdxBuf.getNumComponents(), 1118 newIndexBuf instanceof IndexIntBuffer ? Format.UnsignedInt : Format.UnsignedShort, 1119 newIndexBuf.getBuffer()); 1120 clearBuffer(Type.Index); 1121 setBuffer(newIdxBuf); 1122 1123 // Now, create the vertex buffers 1124 SafeArrayList<VertexBuffer> oldVertexData = other.getBufferList(); 1125 for (VertexBuffer oldVb : oldVertexData) { 1126 if (oldVb.getBufferType() == VertexBuffer.Type.Index) { 1127 // ignore the index buffer 1128 continue; 1129 } 1130 1131 // Create a new vertex buffer with similar configuration, but 1132 // with the capacity of number of unique vertices 1133 Buffer buffer = VertexBuffer.createBuffer(oldVb.getFormat(), oldVb.getNumComponents(), newNumVerts); 1134 1135 VertexBuffer newVb = new VertexBuffer(oldVb.getBufferType()); 1136 newVb.setNormalized(oldVb.isNormalized()); 1137 newVb.setupData(oldVb.getUsage(), oldVb.getNumComponents(), oldVb.getFormat(), buffer); 1138 1139 // Copy the vertex data from the old buffer into the new buffer 1140 for (int i = 0; i < newNumVerts; i++) { 1141 int oldIndex = newIndicesToOldIndices.get(i); 1142 1143 // Copy the vertex attribute from the old index 1144 // to the new index 1145 oldVb.copyElement(oldIndex, newVb, i); 1146 } 1147 1148 // Set the buffer on the mesh 1149 clearBuffer(newVb.getBufferType()); 1150 setBuffer(newVb); 1151 } 1152 1153 // Copy max weights per vertex as well 1154 setMaxNumWeights(other.getMaxNumWeights()); 1155 1156 // The data has been copied over, update informations 1157 updateCounts(); 1158 updateBound(); 1159 } 1160 1161 /** 1162 * Scales the texture coordinate buffer on this mesh by the given 1163 * scale factor. 1164 * <p> 1165 * Note that values above 1 will cause the 1166 * texture to tile, while values below 1 will cause the texture 1167 * to stretch. 1168 * </p> 1169 * 1170 * @param scaleFactor The scale factor to scale by. Every texture 1171 * coordinate is multiplied by this vector to get the result. 1172 * 1173 * @throws IllegalStateException If there's no texture coordinate 1174 * buffer on the mesh 1175 * @throws UnsupportedOperationException If the texture coordinate 1176 * buffer is not in 2D float format. 1177 */ scaleTextureCoordinates(Vector2f scaleFactor)1178 public void scaleTextureCoordinates(Vector2f scaleFactor){ 1179 VertexBuffer tc = getBuffer(Type.TexCoord); 1180 if (tc == null) 1181 throw new IllegalStateException("The mesh has no texture coordinates"); 1182 1183 if (tc.getFormat() != VertexBuffer.Format.Float) 1184 throw new UnsupportedOperationException("Only float texture coord format is supported"); 1185 1186 if (tc.getNumComponents() != 2) 1187 throw new UnsupportedOperationException("Only 2D texture coords are supported"); 1188 1189 FloatBuffer fb = (FloatBuffer) tc.getData(); 1190 fb.clear(); 1191 for (int i = 0; i < fb.capacity() / 2; i++){ 1192 float x = fb.get(); 1193 float y = fb.get(); 1194 fb.position(fb.position()-2); 1195 x *= scaleFactor.getX(); 1196 y *= scaleFactor.getY(); 1197 fb.put(x).put(y); 1198 } 1199 fb.clear(); 1200 tc.updateData(fb); 1201 } 1202 1203 /** 1204 * Updates the bounding volume of this mesh. 1205 * The method does nothing if the mesh has no {@link Type#Position} buffer. 1206 * It is expected that the position buffer is a float buffer with 3 components. 1207 */ updateBound()1208 public void updateBound(){ 1209 VertexBuffer posBuf = getBuffer(VertexBuffer.Type.Position); 1210 if (meshBound != null && posBuf != null){ 1211 meshBound.computeFromPoints((FloatBuffer)posBuf.getData()); 1212 } 1213 } 1214 1215 /** 1216 * Returns the {@link BoundingVolume} of this Mesh. 1217 * By default the bounding volume is a {@link BoundingBox}. 1218 * 1219 * @return the bounding volume of this mesh 1220 */ getBound()1221 public BoundingVolume getBound() { 1222 return meshBound; 1223 } 1224 1225 /** 1226 * Sets the {@link BoundingVolume} for this Mesh. 1227 * The bounding volume is recomputed by calling {@link #updateBound() }. 1228 * 1229 * @param modelBound The model bound to set 1230 */ setBound(BoundingVolume modelBound)1231 public void setBound(BoundingVolume modelBound) { 1232 meshBound = modelBound; 1233 } 1234 1235 /** 1236 * Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh. 1237 * The integer key for the map is the {@link Enum#ordinal() ordinal} 1238 * of the vertex buffer's {@link Type}. 1239 * Note that the returned map is a reference to the map used internally, 1240 * modifying it will cause undefined results. 1241 * 1242 * @return map of vertex buffers on this mesh. 1243 */ getBuffers()1244 public IntMap<VertexBuffer> getBuffers(){ 1245 return buffers; 1246 } 1247 1248 /** 1249 * Returns a list of all {@link VertexBuffer vertex buffers} on this Mesh. 1250 * Using a list instead an IntMap via the {@link #getBuffers() } method is 1251 * better for iteration as there's no need to create an iterator instance. 1252 * Note that the returned list is a reference to the list used internally, 1253 * modifying it will cause undefined results. 1254 * 1255 * @return list of vertex buffers on this mesh. 1256 */ getBufferList()1257 public SafeArrayList<VertexBuffer> getBufferList(){ 1258 return buffersList; 1259 } 1260 write(JmeExporter ex)1261 public void write(JmeExporter ex) throws IOException { 1262 OutputCapsule out = ex.getCapsule(this); 1263 1264 // HashMap<String, VertexBuffer> map = new HashMap<String, VertexBuffer>(); 1265 // for (Entry<VertexBuffer> buf : buffers){ 1266 // if (buf.getValue() != null) 1267 // map.put(buf.getKey()+"a", buf.getValue()); 1268 // } 1269 // out.writeStringSavableMap(map, "buffers", null); 1270 1271 out.write(meshBound, "modelBound", null); 1272 out.write(vertCount, "vertCount", -1); 1273 out.write(elementCount, "elementCount", -1); 1274 out.write(maxNumWeights, "max_num_weights", -1); 1275 out.write(mode, "mode", Mode.Triangles); 1276 out.write(collisionTree, "collisionTree", null); 1277 out.write(elementLengths, "elementLengths", null); 1278 out.write(modeStart, "modeStart", null); 1279 out.write(pointSize, "pointSize", 1f); 1280 1281 out.writeIntSavableMap(buffers, "buffers", null); 1282 out.write(lodLevels, "lodLevels", null); 1283 } 1284 read(JmeImporter im)1285 public void read(JmeImporter im) throws IOException { 1286 InputCapsule in = im.getCapsule(this); 1287 meshBound = (BoundingVolume) in.readSavable("modelBound", null); 1288 vertCount = in.readInt("vertCount", -1); 1289 elementCount = in.readInt("elementCount", -1); 1290 maxNumWeights = in.readInt("max_num_weights", -1); 1291 mode = in.readEnum("mode", Mode.class, Mode.Triangles); 1292 elementLengths = in.readIntArray("elementLengths", null); 1293 modeStart = in.readIntArray("modeStart", null); 1294 collisionTree = (BIHTree) in.readSavable("collisionTree", null); 1295 elementLengths = in.readIntArray("elementLengths", null); 1296 modeStart = in.readIntArray("modeStart", null); 1297 pointSize = in.readFloat("pointSize", 1f); 1298 1299 // in.readStringSavableMap("buffers", null); 1300 buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null); 1301 for (Entry<VertexBuffer> entry : buffers){ 1302 buffersList.add(entry.getValue()); 1303 } 1304 1305 Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null); 1306 if (lodLevelsSavable != null) { 1307 lodLevels = new VertexBuffer[lodLevelsSavable.length]; 1308 System.arraycopy( lodLevelsSavable, 0, lodLevels, 0, lodLevels.length); 1309 } 1310 } 1311 1312 } 1313