Home | History | Annotate | Download | only in scene
      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 
    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