2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
33 package com.jme3.scene;
35 import com.jme3.scene.mesh.IndexShortBuffer;
36 import com.jme3.scene.mesh.IndexIntBuffer;
37 import com.jme3.scene.mesh.IndexBuffer;
38 import com.jme3.scene.mesh.IndexByteBuffer;
39 import com.jme3.bounding.BoundingBox;
40 import com.jme3.bounding.BoundingVolume;
41 import com.jme3.collision.Collidable;
42 import com.jme3.collision.CollisionResults;
43 import com.jme3.collision.bih.BIHTree;
44 import com.jme3.export.JmeExporter;
45 import com.jme3.export.JmeImporter;
46 import com.jme3.export.InputCapsule;
47 import com.jme3.export.OutputCapsule;
48 import com.jme3.export.Savable;
49 import com.jme3.material.RenderState;
50 import com.jme3.math.Matrix4f;
51 import com.jme3.math.Triangle;
52 import com.jme3.math.Vector2f;
53 import com.jme3.math.Vector3f;
54 import com.jme3.scene.VertexBuffer.*;
55 import com.jme3.scene.mesh.VirtualIndexBuffer;
56 import com.jme3.scene.mesh.WrappedIndexBuffer;
57 import com.jme3.util.BufferUtils;
58 import com.jme3.util.IntMap;
59 import com.jme3.util.IntMap.Entry;
60 import java.io.IOException;
61 import java.nio.Buffer;
62 import java.nio.ByteBuffer;
63 import java.nio.DoubleBuffer;
64 import java.nio.FloatBuffer;
65 import java.nio.IntBuffer;
66 import java.nio.ShortBuffer;
67 import java.util.ArrayList;
70 * <code>Mesh</code> is used to store rendering data.
72 * All visible elements in a scene are represented by meshes.
73 * Meshes may contain three types of geometric primitives:
75 * <li>Points - Every vertex represents a single point in space,
76 * the size of each point is specified via {@link Mesh#setPointSize(float) }.
77 * Points can also be used for {@link RenderState#setPointSprite(boolean) point
79 * <li>Lines - 2 vertices represent a line segment, with the width specified
80 * via {@link Mesh#setLineWidth(float) }.</li>
81 * <li>Triangles - 3 vertices represent a solid triangle primitive. </li>
84 * @author Kirill Vainer
86 public class Mesh implements Savable, Cloneable {
89 * The mode of the Mesh specifies both the type of primitive represented
90 * by the mesh and how the data should be interpreted.
94 * A primitive is a single point in space. The size of the points
95 * can be specified with {@link Mesh#setPointSize(float) }.
100 * A primitive is a line segment. Every two vertices specify
101 * a single line. {@link Mesh#setLineWidth(float) } can be used
102 * to set the width of the lines.
107 * A primitive is a line segment. The first two vertices specify
108 * a single line, while subsequent vertices are combined with the
109 * previous vertex to make a line. {@link Mesh#setLineWidth(float) } can
110 * be used to set the width of the lines.
115 * Identical to {@link #LineStrip} except that at the end
116 * the last vertex is connected with the first to form a line.
117 * {@link Mesh#setLineWidth(float) } can be used
118 * to set the width of the lines.
123 * A primitive is a triangle. Each 3 vertices specify a single
129 * Similar to {@link #Triangles}, the first 3 vertices
130 * specify a triangle, while subsequent vertices are combined with
131 * the previous two to form a triangle.
133 TriangleStrip(false),
136 * Similar to {@link #Triangles}, the first 3 vertices
137 * specify a triangle, each 2 subsequent vertices are combined
138 * with the very first vertex to make a triangle.
143 * A combination of various triangle modes. It is best to avoid
144 * using this mode as it may not be supported by all renderers.
145 * The {@link Mesh#setModeStart(int[]) mode start points} and
146 * {@link Mesh#setElementLengths(int[]) element lengths} must
147 * be specified for this mode.
151 private boolean listMode = false;
153 private Mode(boolean listMode){
154 this.listMode = listMode;
158 * Returns true if the specified mode is a list mode (meaning
159 * ,it specifies the indices as a linear list and not some special
161 * Will return true for the types {@link #Points}, {@link #Lines} and
162 * {@link #Triangles}.
164 * @return true if the mode is a list type mode
166 public boolean isListMode(){
172 * The bounding volume that contains the mesh entirely.
173 * By default a BoundingBox (AABB).
175 private BoundingVolume meshBound = new BoundingBox();
177 private CollisionData collisionTree = null;
179 private ArrayList<VertexBuffer> buffersList = new ArrayList<VertexBuffer>(5);
180 private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
181 private VertexBuffer[] lodLevels;
182 private float pointSize = 1;
183 private float lineWidth = 1;
185 private transient int vertexArrayID = -1;
187 private int vertCount = -1;
188 private int elementCount = -1;
189 private int maxNumWeights = -1; // only if using skeletal animation
191 private int[] elementLengths;
192 private int[] modeStart;
194 private Mode mode = Mode.Triangles;
197 * Creates a new mesh with no {@link VertexBuffer vertex buffers}.
203 * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex
204 * buffers} are shared between this and the clone mesh, the rest
205 * of the data is cloned.
207 * @return A shallow clone of the mesh
210 public Mesh clone() {
212 Mesh clone = (Mesh) super.clone();
213 clone.meshBound = meshBound.clone();
214 clone.collisionTree = collisionTree != null ? collisionTree : null;
215 clone.buffers = buffers.clone();
216 clone.buffersList = new ArrayList<VertexBuffer>(buffersList);
217 clone.vertexArrayID = -1;
218 if (elementLengths != null) {
219 clone.elementLengths = elementLengths.clone();
221 if (modeStart != null) {
222 clone.modeStart = modeStart.clone();
225 } catch (CloneNotSupportedException ex) {
226 throw new AssertionError();
231 * Creates a deep clone of this mesh.
232 * The {@link VertexBuffer vertex buffers} and the data inside them
235 * @return a deep clone of this mesh.
237 public Mesh deepClone(){
239 Mesh clone = (Mesh) super.clone();
240 clone.meshBound = meshBound != null ? meshBound.clone() : null;
242 // TODO: Collision tree cloning
243 //clone.collisionTree = collisionTree != null ? collisionTree : null;
244 clone.collisionTree = null; // it will get re-generated in any case
246 clone.buffers = new IntMap<VertexBuffer>();
247 clone.buffersList = new ArrayList<VertexBuffer>();
248 for (Entry<VertexBuffer> ent : buffers){
249 VertexBuffer bufClone = ent.getValue().clone();
250 clone.buffers.put(ent.getKey(), bufClone);
251 clone.buffersList.add(bufClone);
254 clone.vertexArrayID = -1;
255 clone.vertCount = -1;
256 clone.elementCount = -1;
258 // although this could change
259 // if the bone weight/index buffers are modified
260 clone.maxNumWeights = maxNumWeights;
262 clone.elementLengths = elementLengths != null ? elementLengths.clone() : null;
263 clone.modeStart = modeStart != null ? modeStart.clone() : null;
265 }catch (CloneNotSupportedException ex){
266 throw new AssertionError();
271 * Clone the mesh for animation use.
272 * This creates a shallow clone of the mesh, sharing most
273 * of the {@link VertexBuffer vertex buffer} data, however the
274 * {@link Type#BindPosePosition} and {@link Type#BindPoseNormal} buffers
277 * @return A clone of the mesh for animation use.
279 public Mesh cloneForAnim(){
280 Mesh clone = clone();
281 if (getBuffer(Type.BindPosePosition) != null){
282 VertexBuffer oldPos = getBuffer(Type.Position);
283 // NOTE: creates deep clone
284 VertexBuffer newPos = oldPos.clone();
285 clone.clearBuffer(Type.Position);
286 clone.setBuffer(newPos);
288 if (getBuffer(Type.BindPoseNormal) != null){
289 VertexBuffer oldNorm = getBuffer(Type.Normal);
290 VertexBuffer newNorm = oldNorm.clone();
291 clone.clearBuffer(Type.Normal);
292 clone.setBuffer(newNorm);
299 * Generates the {@link Type#BindPosePosition} and {@link Type#BindPoseNormal}
300 * buffers for this mesh by duplicating them based on the position and normal
301 * buffers already set on the mesh.
302 * This method does nothing if the mesh has no bone weight or index
305 * @param forSoftwareAnim Should be true if the bind pose is to be generated.
307 public void generateBindPose(boolean forSoftwareAnim){
308 if (forSoftwareAnim){
309 VertexBuffer pos = getBuffer(Type.Position);
310 if (pos == null || getBuffer(Type.BoneIndex) == null) {
311 // ignore, this mesh doesn't have positional data
312 // or it doesn't have bone-vertex assignments, so its not animated
316 VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition);
317 bindPos.setupData(Usage.CpuOnly,
320 BufferUtils.clone(pos.getData()));
323 // XXX: note that this method also sets stream mode
324 // so that animation is faster. this is not needed for hardware skinning
325 pos.setUsage(Usage.Stream);
327 VertexBuffer norm = getBuffer(Type.Normal);
329 VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal);
330 bindNorm.setupData(Usage.CpuOnly,
333 BufferUtils.clone(norm.getData()));
335 norm.setUsage(Usage.Stream);
341 * Prepares the mesh for software skinning by converting the bone index
342 * and weight buffers to heap buffers.
344 * @param forSoftwareAnim Should be true to enable the conversion.
346 public void prepareForAnim(boolean forSoftwareAnim){
347 if (forSoftwareAnim){
349 VertexBuffer indices = getBuffer(Type.BoneIndex);
350 ByteBuffer originalIndex = (ByteBuffer) indices.getData();
351 ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity());
352 originalIndex.clear();
353 arrayIndex.put(originalIndex);
354 indices.updateData(arrayIndex);
357 VertexBuffer weights = getBuffer(Type.BoneWeight);
358 FloatBuffer originalWeight = (FloatBuffer) weights.getData();
359 FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity());
360 originalWeight.clear();
361 arrayWeight.put(originalWeight);
362 weights.updateData(arrayWeight);
367 * Set the LOD (level of detail) index buffers on this mesh.
369 * @param lodLevels The LOD levels to set
371 public void setLodLevels(VertexBuffer[] lodLevels){
372 this.lodLevels = lodLevels;
376 * @return The number of LOD levels set on this mesh, including the main
377 * index buffer, returns zero if there are no lod levels.
379 public int getNumLodLevels(){
380 return lodLevels != null ? lodLevels.length : 0;
384 * Returns the lod level at the given index.
386 * @param lod The lod level index, this does not include
387 * the main index buffer.
388 * @return The LOD index buffer at the index
390 * @throws IndexOutOfBoundsException If the index is outside of the
391 * range [0, {@link #getNumLodLevels()}].
393 * @see #setLodLevels(com.jme3.scene.VertexBuffer[])
395 public VertexBuffer getLodLevel(int lod){
396 return lodLevels[lod];
400 * Get the element lengths for {@link Mode#Hybrid} mesh mode.
402 * @return element lengths
404 public int[] getElementLengths() {
405 return elementLengths;
409 * Set the element lengths for {@link Mode#Hybrid} mesh mode.
411 * @param elementLengths The element lengths to set
413 public void setElementLengths(int[] elementLengths) {
414 this.elementLengths = elementLengths;
418 * Set the mode start indices for {@link Mode#Hybrid} mesh mode.
420 * @return mode start indices
422 public int[] getModeStart() {
427 * Get the mode start indices for {@link Mode#Hybrid} mesh mode.
429 * @return mode start indices
431 public void setModeStart(int[] modeStart) {
432 this.modeStart = modeStart;
436 * Returns the mesh mode
438 * @return the mesh mode
440 * @see #setMode(com.jme3.scene.Mesh.Mode)
442 public Mode getMode() {
447 * Change the Mesh's mode. By default the mode is {@link Mode#Triangles}.
449 * @param mode The new mode to set
453 public void setMode(Mode mode) {
459 * Returns the maximum number of weights per vertex on this mesh.
461 * @return maximum number of weights per vertex
463 * @see #setMaxNumWeights(int)
465 public int getMaxNumWeights() {
466 return maxNumWeights;
470 * Set the maximum number of weights per vertex on this mesh.
471 * Only relevant if this mesh has bone index/weight buffers.
472 * This value should be between 0 and 4.
474 * @param maxNumWeights
476 public void setMaxNumWeights(int maxNumWeights) {
477 this.maxNumWeights = maxNumWeights;
481 * Returns the size of points for point meshes
483 * @return the size of points
485 * @see #setPointSize(float)
487 public float getPointSize() {
492 * Set the size of points for meshes of mode {@link Mode#Points}.
493 * The point size is specified as on-screen pixels, the default
494 * value is 1.0. The point size
495 * does nothing if {@link RenderState#setPointSprite(boolean) point sprite}
496 * render state is enabled, in that case, the vertex shader must specify the
497 * point size by writing to <code>gl_PointSize</code>.
499 * @param pointSize The size of points
501 public void setPointSize(float pointSize) {
502 this.pointSize = pointSize;
506 * Returns the line width for line meshes.
508 * @return the line width
510 public float getLineWidth() {
515 * Specify the line width for meshes of the line modes, such
516 * as {@link Mode#Lines}. The line width is specified as on-screen pixels,
517 * the default value is 1.0.
519 * @param lineWidth The line width
521 public void setLineWidth(float lineWidth) {
522 this.lineWidth = lineWidth;
526 * Indicates to the GPU that this mesh will not be modified (a hint).
527 * Sets the usage mode to {@link Usage#Static}
528 * for all {@link VertexBuffer vertex buffers} on this Mesh.
530 public void setStatic() {
531 for (Entry<VertexBuffer> entry : buffers){
532 entry.getValue().setUsage(Usage.Static);
537 * Indicates to the GPU that this mesh will be modified occasionally (a hint).
538 * Sets the usage mode to {@link Usage#Dynamic}
539 * for all {@link VertexBuffer vertex buffers} on this Mesh.
541 public void setDynamic() {
542 for (Entry<VertexBuffer> entry : buffers){
543 entry.getValue().setUsage(Usage.Dynamic);
548 * Indicates to the GPU that this mesh will be modified every frame (a hint).
549 * Sets the usage mode to {@link Usage#Stream}
550 * for all {@link VertexBuffer vertex buffers} on this Mesh.
552 public void setStreamed(){
553 for (Entry<VertexBuffer> entry : buffers){
554 entry.getValue().setUsage(Usage.Stream);
559 * Interleaves the data in this mesh. This operation cannot be reversed.
560 * Some GPUs may prefer the data in this format, however it is a good idea
561 * to <em>avoid</em> using this method as it disables some engine features.
563 public void setInterleaved(){
564 ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
565 for (Entry<VertexBuffer> entry : buffers){
566 vbs.add(entry.getValue());
568 // ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values());
569 // index buffer not included when interleaving
570 vbs.remove(getBuffer(Type.Index));
572 int stride = 0; // aka bytes per vertex
573 for (int i = 0; i < vbs.size(); i++){
574 VertexBuffer vb = vbs.get(i);
575 // if (vb.getFormat() != Format.Float){
576 // throw new UnsupportedOperationException("Cannot interleave vertex buffer.\n" +
577 // "Contains not-float data.");
579 stride += vb.componentsLength;
580 vb.getData().clear(); // reset position & limit (used later)
583 VertexBuffer allData = new VertexBuffer(Type.InterleavedData);
584 ByteBuffer dataBuf = BufferUtils.createByteBuffer(stride * getVertexCount());
585 allData.setupData(Usage.Static, 1, Format.UnsignedByte, dataBuf);
587 // adding buffer directly so that no update counts is forced
588 buffers.put(Type.InterleavedData.ordinal(), allData);
589 buffersList.add(allData);
591 for (int vert = 0; vert < getVertexCount(); vert++){
592 for (int i = 0; i < vbs.size(); i++){
593 VertexBuffer vb = vbs.get(i);
594 switch (vb.getFormat()){
596 FloatBuffer fb = (FloatBuffer) vb.getData();
597 for (int comp = 0; comp < vb.components; comp++){
598 dataBuf.putFloat(fb.get());
603 ByteBuffer bb = (ByteBuffer) vb.getData();
604 for (int comp = 0; comp < vb.components; comp++){
605 dataBuf.put(bb.get());
611 ShortBuffer sb = (ShortBuffer) vb.getData();
612 for (int comp = 0; comp < vb.components; comp++){
613 dataBuf.putShort(sb.get());
618 IntBuffer ib = (IntBuffer) vb.getData();
619 for (int comp = 0; comp < vb.components; comp++){
620 dataBuf.putInt(ib.get());
624 DoubleBuffer db = (DoubleBuffer) vb.getData();
625 for (int comp = 0; comp < vb.components; comp++){
626 dataBuf.putDouble(db.get());
634 for (VertexBuffer vb : vbs){
635 vb.setOffset(offset);
636 vb.setStride(stride);
639 //vb.setupData(vb.usage, vb.components, vb.format, null);
640 offset += vb.componentsLength;
644 private int computeNumElements(int bufSize){
660 throw new UnsupportedOperationException();
665 * Update the {@link #getVertexCount() vertex} and
666 * {@link #getTriangleCount() triangle} counts for this mesh
667 * based on the current data. This method should be called
668 * after the {@link Buffer#capacity() capacities} of the mesh's
669 * {@link VertexBuffer vertex buffers} has been altered.
671 * @throws IllegalStateException If this mesh is in
672 * {@link #setInterleaved() interleaved} format.
674 public void updateCounts(){
675 // if (getBuffer(Type.InterleavedData) != null)
676 // throw new IllegalStateException("Should update counts before interleave");
677 VertexBuffer pb = getBuffer(Type.Position);
678 VertexBuffer ib = getBuffer(Type.Index);
679 if (pb != null && pb.getData() != null){
680 vertCount = pb.getData().capacity() / pb.getNumComponents();
683 elementCount = computeNumElements(ib.getData().capacity());
685 elementCount = computeNumElements(vertCount);
690 * Returns the triangle count for the given LOD level.
692 * @param lod The lod level to look up
693 * @return The triangle count for that LOD level
695 public int getTriangleCount(int lod){
696 if (lodLevels != null){
698 throw new IllegalArgumentException("LOD level cannot be < 0");
700 if (lod >= lodLevels.length)
701 throw new IllegalArgumentException("LOD level "+lod+" does not exist!");
703 return computeNumElements(lodLevels[lod].getData().capacity());
707 throw new IllegalArgumentException("There are no LOD levels on the mesh!");
712 * Returns how many triangles or elements are on this Mesh.
713 * This value is only updated when {@link #updateCounts() } is called.
714 * If the mesh mode is not a triangle mode, then this returns the
715 * number of elements/primitives, e.g. how many lines or how many points,
716 * instead of how many triangles.
718 * @return how many triangles/elements are on this Mesh.
720 public int getTriangleCount(){
725 * Returns the number of vertices on this mesh.
726 * The value is computed based on the position buffer, which
727 * must be set on all meshes.
729 * @return Number of vertices on the mesh
731 public int getVertexCount(){
734 public void setVertCount(int vertCount) {
735 this.vertCount = vertCount;
738 * Gets the triangle vertex positions at the given triangle index
739 * and stores them into the v1, v2, v3 arguments.
741 * @param index The index of the triangle.
742 * Should be between 0 and {@link #getTriangleCount()}.
744 * @param v1 Vector to contain first vertex position
745 * @param v2 Vector to contain second vertex position
746 * @param v3 Vector to contain third vertex position
748 public void getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3){
749 VertexBuffer pb = getBuffer(Type.Position);
750 IndexBuffer ib = getIndicesAsList();
751 if (pb != null && pb.getFormat() == Format.Float && pb.getNumComponents() == 3){
752 FloatBuffer fpb = (FloatBuffer) pb.getData();
754 // aquire triangle's vertex indices
755 int vertIndex = index * 3;
756 int vert1 = ib.get(vertIndex);
757 int vert2 = ib.get(vertIndex+1);
758 int vert3 = ib.get(vertIndex+2);
760 BufferUtils.populateFromBuffer(v1, fpb, vert1);
761 BufferUtils.populateFromBuffer(v2, fpb, vert2);
762 BufferUtils.populateFromBuffer(v3, fpb, vert3);
764 throw new UnsupportedOperationException("Position buffer not set or "
765 + " has incompatible format");
770 * Gets the triangle vertex positions at the given triangle index
771 * and stores them into the {@link Triangle} argument.
772 * Also sets the triangle index to the <code>index</code> argument.
774 * @param index The index of the triangle.
775 * Should be between 0 and {@link #getTriangleCount()}.
777 * @param tri The triangle to store the positions in
779 public void getTriangle(int index, Triangle tri){
780 getTriangle(index, tri.get1(), tri.get2(), tri.get3());
786 * Gets the triangle vertex indices at the given triangle index
787 * and stores them into the given int array.
789 * @param index The index of the triangle.
790 * Should be between 0 and {@link #getTriangleCount()}.
792 * @param indices Indices of the triangle's vertices
794 public void getTriangle(int index, int[] indices){
795 IndexBuffer ib = getIndicesAsList();
797 // acquire triangle's vertex indices
798 int vertIndex = index * 3;
799 indices[0] = ib.get(vertIndex);
800 indices[1] = ib.get(vertIndex+1);
801 indices[2] = ib.get(vertIndex+2);
805 * Returns the mesh's VAO ID. Internal use only.
808 return vertexArrayID;
812 * Sets the mesh's VAO ID. Internal use only.
814 public void setId(int id){
815 if (vertexArrayID != -1)
816 throw new IllegalStateException("ID has already been set.");
822 * Generates a collision tree for the mesh.
823 * Called automatically by {@link #collideWith(com.jme3.collision.Collidable,
824 * com.jme3.math.Matrix4f,
825 * com.jme3.bounding.BoundingVolume,
826 * com.jme3.collision.CollisionResults) }.
828 public void createCollisionData(){
829 BIHTree tree = new BIHTree(this);
831 collisionTree = tree;
835 * Handles collision detection, internal use only.
836 * User code should only use collideWith() on scene
837 * graph elements such as {@link Spatial}s.
839 public int collideWith(Collidable other,
840 Matrix4f worldMatrix,
841 BoundingVolume worldBound,
842 CollisionResults results){
844 if (collisionTree == null){
845 createCollisionData();
848 return collisionTree.collideWith(other, worldMatrix, worldBound, results);
852 * Set a floating point {@link VertexBuffer} on the mesh.
854 * @param type The type of {@link VertexBuffer},
855 * e.g. {@link Type#Position}, {@link Type#Normal}, etc.
857 * @param components Number of components on the vertex buffer, should
858 * be between 1 and 4.
860 * @param buf The floating point data to contain
862 public void setBuffer(Type type, int components, FloatBuffer buf) {
863 // VertexBuffer vb = buffers.get(type);
864 VertexBuffer vb = buffers.get(type.ordinal());
869 vb = new VertexBuffer(type);
870 vb.setupData(Usage.Dynamic, components, Format.Float, buf);
871 // buffers.put(type, vb);
872 buffers.put(type.ordinal(), vb);
875 vb.setupData(Usage.Dynamic, components, Format.Float, buf);
880 public void setBuffer(Type type, int components, float[] buf){
881 setBuffer(type, components, BufferUtils.createFloatBuffer(buf));
884 public void setBuffer(Type type, int components, IntBuffer buf) {
885 VertexBuffer vb = buffers.get(type.ordinal());
887 vb = new VertexBuffer(type);
888 vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf);
889 buffers.put(type.ordinal(), vb);
895 public void setBuffer(Type type, int components, int[] buf){
896 setBuffer(type, components, BufferUtils.createIntBuffer(buf));
899 public void setBuffer(Type type, int components, ShortBuffer buf) {
900 VertexBuffer vb = buffers.get(type.ordinal());
902 vb = new VertexBuffer(type);
903 vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf);
904 buffers.put(type.ordinal(), vb);
910 public void setBuffer(Type type, int components, byte[] buf){
911 setBuffer(type, components, BufferUtils.createByteBuffer(buf));
914 public void setBuffer(Type type, int components, ByteBuffer buf) {
915 VertexBuffer vb = buffers.get(type.ordinal());
917 vb = new VertexBuffer(type);
918 vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf);
919 buffers.put(type.ordinal(), vb);
925 public void setBuffer(VertexBuffer vb){
926 // if (buffers.containsKey(vb.getBufferType().ordinal()))
927 // throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
929 VertexBuffer vb2 = buffers.put(vb.getBufferType().ordinal(), vb);
931 buffersList.remove(vb2);
938 * Clears or unsets the {@link VertexBuffer} set on this mesh
939 * with the given type.
940 * Does nothing if the vertex buffer type is not set initially
942 * @param type The type to remove
944 public void clearBuffer(VertexBuffer.Type type){
945 VertexBuffer vb = buffers.remove(type.ordinal());
947 buffersList.remove(vb);
952 public void setBuffer(Type type, int components, short[] buf){
953 setBuffer(type, components, BufferUtils.createShortBuffer(buf));
957 * Get the {@link VertexBuffer} stored on this mesh with the given
960 * @param type The type of VertexBuffer
961 * @return the VertexBuffer data, or null if not set
963 public VertexBuffer getBuffer(Type type){
964 return buffers.get(type.ordinal());
968 * Get the {@link VertexBuffer} data stored on this mesh in float
971 * @param type The type of VertexBuffer
972 * @return the VertexBuffer data, or null if not set
974 public FloatBuffer getFloatBuffer(Type type) {
975 VertexBuffer vb = getBuffer(type);
979 return (FloatBuffer) vb.getData();
983 * Get the {@link VertexBuffer} data stored on this mesh in short
986 * @param type The type of VertexBuffer
987 * @return the VertexBuffer data, or null if not set
989 public ShortBuffer getShortBuffer(Type type) {
990 VertexBuffer vb = getBuffer(type);
994 return (ShortBuffer) vb.getData();
998 * Acquires an index buffer that will read the vertices on the mesh
1001 * @param mesh The mesh to read from
1002 * @return A virtual or wrapped index buffer to read the data as a list
1004 public IndexBuffer getIndicesAsList(){
1005 if (mode == Mode.Hybrid)
1006 throw new UnsupportedOperationException("Hybrid mode not supported");
1008 IndexBuffer ib = getIndexBuffer();
1010 if (mode.isListMode()){
1011 // already in list mode
1014 // not in list mode but it does have an index buffer
1015 // wrap it so the data is converted to list format
1016 return new WrappedIndexBuffer(this);
1019 // return a virtual index buffer that will supply
1020 // "fake" indices in list format
1021 return new VirtualIndexBuffer(vertCount, mode);
1026 * Get the index buffer for this mesh.
1027 * Will return <code>null</code> if no index buffer is set.
1029 * @return The index buffer of this mesh.
1033 public IndexBuffer getIndexBuffer() {
1034 VertexBuffer vb = getBuffer(Type.Index);
1038 Buffer buf = vb.getData();
1039 if (buf instanceof ByteBuffer) {
1040 return new IndexByteBuffer((ByteBuffer) buf);
1041 } else if (buf instanceof ShortBuffer) {
1042 return new IndexShortBuffer((ShortBuffer) buf);
1043 } else if (buf instanceof IntBuffer) {
1044 return new IndexIntBuffer((IntBuffer) buf);
1046 throw new UnsupportedOperationException("Index buffer type unsupported: "+ buf.getClass());
1051 * Scales the texture coordinate buffer on this mesh by the given
1054 * Note that values above 1 will cause the
1055 * texture to tile, while values below 1 will cause the texture
1059 * @param scaleFactor The scale factor to scale by. Every texture
1060 * coordinate is multiplied by this vector to get the result.
1062 * @throws IllegalStateException If there's no texture coordinate
1063 * buffer on the mesh
1064 * @throws UnsupportedOperationException If the texture coordinate
1065 * buffer is not in 2D float format.
1067 public void scaleTextureCoordinates(Vector2f scaleFactor){
1068 VertexBuffer tc = getBuffer(Type.TexCoord);
1070 throw new IllegalStateException("The mesh has no texture coordinates");
1072 if (tc.getFormat() != VertexBuffer.Format.Float)
1073 throw new UnsupportedOperationException("Only float texture coord format is supported");
1075 if (tc.getNumComponents() != 2)
1076 throw new UnsupportedOperationException("Only 2D texture coords are supported");
1078 FloatBuffer fb = (FloatBuffer) tc.getData();
1080 for (int i = 0; i < fb.capacity() / 2; i++){
1083 fb.position(fb.position()-2);
1084 x *= scaleFactor.getX();
1085 y *= scaleFactor.getY();
1093 * Updates the bounding volume of this mesh.
1094 * The method does nothing if the mesh has no {@link Type#Position} buffer.
1095 * It is expected that the position buffer is a float buffer with 3 components.
1097 public void updateBound(){
1098 VertexBuffer posBuf = getBuffer(VertexBuffer.Type.Position);
1099 if (meshBound != null && posBuf != null){
1100 meshBound.computeFromPoints((FloatBuffer)posBuf.getData());
1105 * Returns the {@link BoundingVolume} of this Mesh.
1106 * By default the bounding volume is a {@link BoundingBox}.
1108 * @return the bounding volume of this mesh
1110 public BoundingVolume getBound() {
1115 * Sets the {@link BoundingVolume} for this Mesh.
1116 * The bounding volume is recomputed by calling {@link #updateBound() }.
1118 * @param modelBound The model bound to set
1120 public void setBound(BoundingVolume modelBound) {
1121 meshBound = modelBound;
1125 * Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh.
1126 * The integer key for the map is the {@link Enum#ordinal() ordinal}
1127 * of the vertex buffer's {@link Type}.
1128 * Note that the returned map is a reference to the map used internally,
1129 * modifying it will cause undefined results.
1131 * @return map of vertex buffers on this mesh.
1133 public IntMap<VertexBuffer> getBuffers(){
1137 public ArrayList<VertexBuffer> getBufferList(){
1141 public void write(JmeExporter ex) throws IOException {
1142 OutputCapsule out = ex.getCapsule(this);
1144 // HashMap<String, VertexBuffer> map = new HashMap<String, VertexBuffer>();
1145 // for (Entry<VertexBuffer> buf : buffers){
1146 // if (buf.getValue() != null)
1147 // map.put(buf.getKey()+"a", buf.getValue());
1149 // out.writeStringSavableMap(map, "buffers", null);
1151 out.write(meshBound, "modelBound", null);
1152 out.write(vertCount, "vertCount", -1);
1153 out.write(elementCount, "elementCount", -1);
1154 out.write(maxNumWeights, "max_num_weights", -1);
1155 out.write(mode, "mode", Mode.Triangles);
1156 out.write(collisionTree, "collisionTree", null);
1157 out.write(elementLengths, "elementLengths", null);
1158 out.write(modeStart, "modeStart", null);
1159 out.write(pointSize, "pointSize", 1f);
1161 out.writeIntSavableMap(buffers, "buffers", null);
1162 out.write(lodLevels, "lodLevels", null);
1165 public void read(JmeImporter im) throws IOException {
1166 InputCapsule in = im.getCapsule(this);
1167 meshBound = (BoundingVolume) in.readSavable("modelBound", null);
1168 vertCount = in.readInt("vertCount", -1);
1169 elementCount = in.readInt("elementCount", -1);
1170 maxNumWeights = in.readInt("max_num_weights", -1);
1171 mode = in.readEnum("mode", Mode.class, Mode.Triangles);
1172 elementLengths = in.readIntArray("elementLengths", null);
1173 modeStart = in.readIntArray("modeStart", null);
1174 collisionTree = (BIHTree) in.readSavable("collisionTree", null);
1175 elementLengths = in.readIntArray("elementLengths", null);
1176 modeStart = in.readIntArray("modeStart", null);
1177 pointSize = in.readFloat("pointSize", 1f);
1179 // in.readStringSavableMap("buffers", null);
1180 buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null);
1181 for (Entry<VertexBuffer> entry : buffers){
1182 buffersList.add(entry.getValue());
1185 Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null);
1186 if (lodLevelsSavable != null) {
1187 lodLevels = new VertexBuffer[lodLevelsSavable.length];
1188 System.arraycopy( lodLevelsSavable, 0, lodLevels, 0, lodLevels.length);