• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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