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
33package com.jme3.scene;
34
35import com.jme3.bounding.BoundingBox;
36import com.jme3.bounding.BoundingVolume;
37import com.jme3.collision.Collidable;
38import com.jme3.collision.CollisionResults;
39import com.jme3.collision.bih.BIHTree;
40import com.jme3.export.*;
41import com.jme3.material.RenderState;
42import com.jme3.math.Matrix4f;
43import com.jme3.math.Triangle;
44import com.jme3.math.Vector2f;
45import com.jme3.math.Vector3f;
46import com.jme3.scene.VertexBuffer.Format;
47import com.jme3.scene.VertexBuffer.Type;
48import com.jme3.scene.VertexBuffer.Usage;
49import com.jme3.scene.mesh.*;
50import com.jme3.util.BufferUtils;
51import com.jme3.util.IntMap;
52import com.jme3.util.IntMap.Entry;
53import com.jme3.util.SafeArrayList;
54import java.io.IOException;
55import java.nio.*;
56import 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 */
75public 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
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         */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
815    public int getId(){
816        return vertexArrayID;
817    }
818
819    /**
820     * Sets the mesh's VAO ID. Internal use only.
821     */
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     */
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     */
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     */
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     */
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     */
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     */
929    public void setBuffer(Type type, int components, FloatBuffer buf) {
930        setBuffer(type, components, Format.Float, buf);
931    }
932
933    public void setBuffer(Type type, int components, float[] buf){
934        setBuffer(type, components, BufferUtils.createFloatBuffer(buf));
935    }
936
937    public void setBuffer(Type type, int components, IntBuffer buf) {
938        setBuffer(type, components, Format.UnsignedInt, buf);
939    }
940
941    public void setBuffer(Type type, int components, int[] buf){
942        setBuffer(type, components, BufferUtils.createIntBuffer(buf));
943    }
944
945    public void setBuffer(Type type, int components, ShortBuffer buf) {
946        setBuffer(type, components, Format.UnsignedShort, buf);
947    }
948
949    public void setBuffer(Type type, int components, byte[] buf){
950        setBuffer(type, components, BufferUtils.createByteBuffer(buf));
951    }
952
953    public void setBuffer(Type type, int components, ByteBuffer buf) {
954        setBuffer(type, components, Format.UnsignedByte, buf);
955    }
956
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
1257    public SafeArrayList<VertexBuffer> getBufferList(){
1258        return buffersList;
1259    }
1260
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
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