Просмотр исходного кода

* Optimization to prevent allocation of IntMap$Iterator in Renderer.renderMesh()

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7661 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
sha..rd 14 лет назад
Родитель
Сommit
f9079171f1

+ 319 - 24
engine/src/core/com/jme3/scene/Mesh.java

@@ -46,6 +46,7 @@ import com.jme3.export.JmeImporter;
 import com.jme3.export.InputCapsule;
 import com.jme3.export.OutputCapsule;
 import com.jme3.export.Savable;
+import com.jme3.material.RenderState;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Triangle;
 import com.jme3.math.Vector2f;
@@ -65,22 +66,85 @@ import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
 import java.util.ArrayList;
 
+/**
+ * <code>Mesh</code> is used to store rendering data.
+ * <p>
+ * All visible elements in a scene are represented by meshes.
+ * Meshes may contain three types of geometric primitives:
+ * <ul>
+ * <li>Points</li>
+ * <li>Lines</li>
+ * <li>Triangles</li>
+ * </ul>
+ * 
+ * @author Kirill Vainer
+ */
 public class Mesh implements Savable, Cloneable {
 
-    // TODO: Document this enum
+    /**
+     * The mode of the Mesh specifies both the type of primitive represented
+     * by the mesh and how the data should be interpreted.
+     */
     public enum Mode {
+        /**
+         * A primitive is a single point in space. The size of the points 
+         * can be specified with {@link Mesh#setPointSize(float) }.
+         */
         Points,
+        
+        /**
+         * A primitive is a line segment. Every two vertices specify
+         * a single line. {@link Mesh#setLineWidth(float) } can be used 
+         * to set the width of the lines.
+         */
         Lines,
-        LineLoop,
+        
+        /**
+         * A primitive is a line segment. The first two vertices specify
+         * a single line, while subsequent vertices are combined with the 
+         * previous vertex to make a line. {@link Mesh#setLineWidth(float) } can 
+         * be used to set the width of the lines.
+         */
         LineStrip,
+        
+        /**
+         * Identical to {@link #LineStrip} except that at the end
+         * the last vertex is connected with the first to form a line.
+         * {@link Mesh#setLineWidth(float) } can be used 
+         * to set the width of the lines.
+         */
+        LineLoop,
+        
+        /**
+         * A primitive is a triangle. Each 3 vertices specify a single
+         * triangle.
+         */
         Triangles,
+        
+        /**
+         * Similar to {@link #Triangles}, the first 3 vertices 
+         * specify a triangle, while subsequent vertices are combined with
+         * the previous two to form a triangle. 
+         */
         TriangleStrip,
+        
+        /**
+         * Similar to {@link #Triangles}, the first 3 vertices 
+         * specify a triangle, each 2 subsequent vertices are combined
+         * with the very first vertex to make a triangle.
+         */
         TriangleFan,
-        Hybrid
+        
+        /**
+         * A combination of various triangle modes. It is best to avoid
+         * using this mode as it may not be supported by all renderers.
+         * The {@link Mesh#setModeStart(int[]) mode start points} and
+         * {@link Mesh#setElementLengths(int[]) element lengths} must 
+         * be specified for this mode.
+         */
+        Hybrid;
     }
 
-//    private static final int BUFFERS_SIZE = VertexBuffer.Type.BoneIndex.ordinal() + 1;
-
     /**
      * The bounding volume that contains the mesh entirely.
      * By default a BoundingBox (AABB).
@@ -89,6 +153,7 @@ public class Mesh implements Savable, Cloneable {
 
     private CollisionData collisionTree = null;
 
+    private ArrayList<VertexBuffer> buffersList = new ArrayList<VertexBuffer>(5);
     private IntMap<VertexBuffer> buffers = new IntMap<VertexBuffer>();
     private VertexBuffer[] lodLevels;
     private float pointSize = 1;
@@ -105,27 +170,47 @@ public class Mesh implements Savable, Cloneable {
 
     private Mode mode = Mode.Triangles;
 
+    /**
+     * Creates a new mesh with no {@link VertexBuffer vertex buffers}.
+     */
     public Mesh(){
     }
 
+    /**
+     * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex
+     * buffers} are shared between this and the clone mesh, the rest
+     * of the data is cloned.
+     * 
+     * @return A shallow clone of the mesh
+     */
     @Override
-    public Mesh clone(){
-        try{
+    public Mesh clone() {
+        try {
             Mesh clone = (Mesh) super.clone();
             clone.meshBound = meshBound.clone();
             clone.collisionTree = collisionTree != null ? collisionTree : null;
             clone.buffers = buffers.clone();
+            clone.buffersList = new ArrayList<VertexBuffer>(buffersList);
             clone.vertexArrayID = -1;
-            if (elementLengths != null)
+            if (elementLengths != null) {
                 clone.elementLengths = elementLengths.clone();
-            if (modeStart != null)
+            }
+            if (modeStart != null) {
                 clone.modeStart = modeStart.clone();
+            }
             return clone;
-        }catch (CloneNotSupportedException ex){
+        } catch (CloneNotSupportedException ex) {
             throw new AssertionError();
         }
     }
 
+    /**
+     * Creates a deep clone of this mesh. 
+     * The {@link VertexBuffer vertex buffers} and the data inside them
+     * is cloned.
+     * 
+     * @return a deep clone of this mesh.
+     */
     public Mesh deepClone(){
         try{
             Mesh clone = (Mesh) super.clone();
@@ -136,13 +221,21 @@ public class Mesh implements Savable, Cloneable {
             clone.collisionTree = null; // it will get re-generated in any case
 
             clone.buffers = new IntMap<VertexBuffer>();
+            clone.buffersList = new ArrayList<VertexBuffer>();
             for (Entry<VertexBuffer> ent : buffers){
-                clone.buffers.put(ent.getKey(), ent.getValue().clone());
+                VertexBuffer bufClone = ent.getValue().clone();
+                clone.buffers.put(ent.getKey(), bufClone);
+                clone.buffersList.add(bufClone);
             }
+            
             clone.vertexArrayID = -1;
             clone.vertCount = -1;
             clone.elementCount = -1;
-            clone.maxNumWeights = -1;
+            
+            // although this could change
+            // if the bone weight/index buffers are modified
+            clone.maxNumWeights = maxNumWeights; 
+            
             clone.elementLengths = elementLengths != null ? elementLengths.clone() : null;
             clone.modeStart = modeStart != null ? modeStart.clone() : null;
             return clone;
@@ -151,6 +244,15 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Clone the mesh for animation use.
+     * This creates a shallow clone of the mesh, sharing most
+     * of the {@link VertexBuffer vertex buffer} data, however the
+     * {@link Type#BindPosePosition} and {@link Type#BindPoseNormal} buffers
+     * are deeply cloned.
+     * 
+     * @return A clone of the mesh for animation use.
+     */
     public Mesh cloneForAnim(){
         Mesh clone = clone();
         if (getBuffer(Type.BindPosePosition) != null){
@@ -170,8 +272,17 @@ public class Mesh implements Savable, Cloneable {
         return clone;
     }
 
-    public void generateBindPose(boolean swAnim){
-        if (swAnim){
+    /**
+     * Generates the {@link Type#BindPosePosition} and {@link Type#BindPoseNormal}
+     * buffers for this mesh by duplicating them based on the position and normal
+     * buffers already set on the mesh.
+     * This method does nothing if the mesh has no bone weight or index
+     * buffers.
+     * 
+     * @param forSoftwareAnim Should be true if the bind pose is to be generated.
+     */
+    public void generateBindPose(boolean forSoftwareAnim){
+        if (forSoftwareAnim){
             VertexBuffer pos = getBuffer(Type.Position);
             if (pos == null || getBuffer(Type.BoneIndex) == null) {
                 // ignore, this mesh doesn't have positional data
@@ -200,13 +311,17 @@ public class Mesh implements Savable, Cloneable {
                 setBuffer(bindNorm);
                 norm.setUsage(Usage.Stream);
             }
-
-            norm.setUsage(Usage.Stream);
         }
     }
 
-    public void prepareForAnim(boolean swAnim){
-        if (swAnim){
+    /**
+     * Prepares the mesh for software skinning by converting the bone index
+     * and weight buffers to heap buffers. 
+     * 
+     * @param forSoftwareAnim Should be true to enable the conversion.
+     */
+    public void prepareForAnim(boolean forSoftwareAnim){
+        if (forSoftwareAnim){
             // convert indices
             VertexBuffer indices = getBuffer(Type.BoneIndex);
             ByteBuffer originalIndex = (ByteBuffer) indices.getData();
@@ -225,6 +340,11 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Set the LOD (level of detail) index buffers on this mesh.
+     * 
+     * @param lodLevels The LOD levels to set
+     */
     public void setLodLevels(VertexBuffer[] lodLevels){
         this.lodLevels = lodLevels;
     }
@@ -237,62 +357,152 @@ public class Mesh implements Savable, Cloneable {
         return lodLevels != null ? lodLevels.length : 0;
     }
 
+    /**
+     * Returns the lod level at the given index.
+     * 
+     * @param lod The lod level index, this does not include
+     * the main index buffer.
+     * @return The LOD index buffer at the index
+     * 
+     * @throws IndexOutOfBoundsException If the index is outside of the 
+     * range [0, {@link #getNumLodLevels()}].
+     * 
+     * @see #setLodLevels(com.jme3.scene.VertexBuffer[]) 
+     */
     public VertexBuffer getLodLevel(int lod){
         return lodLevels[lod];
     }
     
+    /**
+     * Get the element lengths for {@link Mode#Hybrid} mesh mode.
+     * 
+     * @return element lengths
+     */
     public int[] getElementLengths() {
         return elementLengths;
     }
 
+    /**
+     * Set the element lengths for {@link Mode#Hybrid} mesh mode.
+     * 
+     * @param elementLengths The element lengths to set
+     */
     public void setElementLengths(int[] elementLengths) {
         this.elementLengths = elementLengths;
     }
 
+    /**
+     * Set the mode start indices for {@link Mode#Hybrid} mesh mode.
+     * 
+     * @return mode start indices
+     */
     public int[] getModeStart() {
         return modeStart;
     }
 
+    /**
+     * Get the mode start indices for {@link Mode#Hybrid} mesh mode.
+     * 
+     * @return mode start indices
+     */
     public void setModeStart(int[] modeStart) {
         this.modeStart = modeStart;
     }
 
+    /**
+     * Returns the mesh mode
+     * 
+     * @return the mesh mode
+     * 
+     * @see #setMode(com.jme3.scene.Mesh.Mode) 
+     */
     public Mode getMode() {
         return mode;
     }
 
+    /**
+     * Change the Mesh's mode. By default the mode is {@link Mode#Triangles}.
+     * 
+     * @param mode The new mode to set
+     * 
+     * @see Mode
+     */
     public void setMode(Mode mode) {
         this.mode = mode;
         updateCounts();
     }
 
+    /**
+     * Returns the maximum number of weights per vertex on this mesh.
+     * 
+     * @return maximum number of weights per vertex
+     * 
+     * @see #setMaxNumWeights(int) 
+     */
     public int getMaxNumWeights() {
         return maxNumWeights;
     }
 
+    /**
+     * Set the maximum number of weights per vertex on this mesh.
+     * Only relevant if this mesh has bone index/weight buffers.
+     * This value should be between 0 and 4.
+     * 
+     * @param maxNumWeights 
+     */
     public void setMaxNumWeights(int maxNumWeights) {
         this.maxNumWeights = maxNumWeights;
     }
 
+    /**
+     * Returns the size of points for point meshes
+     * 
+     * @return the size of points
+     * 
+     * @see #setPointSize(float) 
+     */
     public float getPointSize() {
         return pointSize;
     }
 
+    /**
+     * Set the size of points for meshes of mode {@link Mode#Points}. 
+     * The point size is specified as on-screen pixels, the default
+     * value is 1.0. The point size
+     * does nothing if {@link RenderState#setPointSprite(boolean) point sprite}
+     * render state is enabled, in that case, the vertex shader must specify the 
+     * point size by writing to <code>gl_PointSize</code>.
+     * 
+     * @param pointSize The size of points
+     */
     public void setPointSize(float pointSize) {
         this.pointSize = pointSize;
     }
 
+    /**
+     * Returns the line width for line meshes.
+     * 
+     * @return the line width
+     */
     public float getLineWidth() {
         return lineWidth;
     }
 
+    /**
+     * Specify the line width for meshes of the line modes, such
+     * as {@link Mode#Lines}. The line width is specified as on-screen pixels, 
+     * the default value is 1.0.
+     * 
+     * @param lineWidth The line width
+     */
     public void setLineWidth(float lineWidth) {
         this.lineWidth = lineWidth;
     }
 
     /**
-     * Locks the mesh so it cannot be modified anymore, thus
-     * optimizing its data.
+     * Indicates to the GPU that this mesh will not be modified (a hint). 
+     * Sets the usage mode to {@link Usage#Static}
+     * for all {@link VertexBuffer vertex buffers} on this Mesh.
      */
     public void setStatic() {
         for (Entry<VertexBuffer> entry : buffers){
@@ -301,8 +511,9 @@ public class Mesh implements Savable, Cloneable {
     }
 
     /**
-     * Unlocks the mesh so it can be modified, this
-     * will un-optimize the data!
+     * Indicates to the GPU that this mesh will be modified occasionally (a hint).
+     * Sets the usage mode to {@link Usage#Dynamic}
+     * for all {@link VertexBuffer vertex buffers} on this Mesh.
      */
     public void setDynamic() {
         for (Entry<VertexBuffer> entry : buffers){
@@ -310,12 +521,22 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Indicates to the GPU that this mesh will be modified every frame (a hint).
+     * Sets the usage mode to {@link Usage#Stream}
+     * for all {@link VertexBuffer vertex buffers} on this Mesh.
+     */
     public void setStreamed(){
         for (Entry<VertexBuffer> entry : buffers){
             entry.getValue().setUsage(Usage.Stream);
         }
     }
 
+    /**
+     * Interleaves the data in this mesh. This operation cannot be reversed.
+     * Some GPUs may prefer the data in this format, however it is a good idea
+     * to <em>avoid</em> using this method as it disables some engine features.
+     */
     public void setInterleaved(){
         ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>();
         for (Entry<VertexBuffer> entry : buffers){
@@ -339,8 +560,10 @@ public class Mesh implements Savable, Cloneable {
         VertexBuffer allData = new VertexBuffer(Type.InterleavedData);
         ByteBuffer dataBuf = BufferUtils.createByteBuffer(stride * getVertexCount());
         allData.setupData(Usage.Static, 1, Format.UnsignedByte, dataBuf);
+        
         // adding buffer directly so that no update counts is forced
         buffers.put(Type.InterleavedData.ordinal(), allData);
+        buffersList.add(allData);
 
         for (int vert = 0; vert < getVertexCount(); vert++){
             for (int i = 0; i < vbs.size(); i++){
@@ -415,6 +638,16 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Update the {@link #getVertexCount() vertex} and 
+     * {@link #getTriangleCount() triangle} counts for this mesh
+     * based on the current data. This method should be called
+     * after the {@link Buffer#capacity() capacities} of the mesh's
+     * {@link VertexBuffer vertex buffers} has been altered.
+     * 
+     * @throws IllegalStateException If this mesh is in 
+     * {@link #setInterleaved() interleaved} format.
+     */
     public void updateCounts(){
         if (getBuffer(Type.InterleavedData) != null)
             throw new IllegalStateException("Should update counts before interleave");
@@ -431,6 +664,12 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Returns the triangle count for the given LOD level.
+     * 
+     * @param lod The lod level to look up
+     * @return The triangle count for that LOD level
+     */
     public int getTriangleCount(int lod){
         if (lodLevels != null){
             if (lod < 0)
@@ -447,10 +686,17 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Returns how many triangles are on this Mesh.
+     * This value is only updated when {@link #updateCounts() } is called.
+     * 
+     * @return how many triangles are on this Mesh.
+     */
     public int getTriangleCount(){
         return elementCount;
     }
 
+    
     public int getVertexCount(){
         return vertCount;
     }
@@ -540,6 +786,7 @@ public class Mesh implements Savable, Cloneable {
             vb.setupData(Usage.Dynamic, components, Format.Float, buf);
 //            buffers.put(type, vb);
             buffers.put(type.ordinal(), vb);
+            buffersList.add(vb);
         }else{
             vb.setupData(Usage.Dynamic, components, Format.Float, buf);
         }
@@ -556,6 +803,7 @@ public class Mesh implements Savable, Cloneable {
             vb = new VertexBuffer(type);
             vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf);
             buffers.put(type.ordinal(), vb);
+            buffersList.add(vb);
             updateCounts();
         }
     }
@@ -570,6 +818,7 @@ public class Mesh implements Savable, Cloneable {
             vb = new VertexBuffer(type);
             vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf);
             buffers.put(type.ordinal(), vb);
+            buffersList.add(vb);
             updateCounts();
         }
     }
@@ -584,6 +833,7 @@ public class Mesh implements Savable, Cloneable {
             vb = new VertexBuffer(type);
             vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf);
             buffers.put(type.ordinal(), vb);
+            buffersList.add(vb);
             updateCounts();
         }
     }
@@ -593,12 +843,16 @@ public class Mesh implements Savable, Cloneable {
             throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType());
 
         buffers.put(vb.getBufferType().ordinal(), vb);
+        buffersList.add(vb);
         updateCounts();
     }
 
     public void clearBuffer(VertexBuffer.Type type){
-        buffers.remove(type.ordinal());
-        updateCounts();
+        VertexBuffer vb = buffers.remove(type.ordinal());
+        if (vb != null){
+            buffersList.remove(vb);
+            updateCounts();
+        }
     }
 
     public void setBuffer(Type type, int components, short[] buf){
@@ -642,6 +896,13 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Scales the texture coordinate buffer on this mesh by the given
+     * scale factor.
+     * 
+     * 
+     * @param scaleFactor 
+     */
     public void scaleTextureCoordinates(Vector2f scaleFactor){
         VertexBuffer tc = getBuffer(Type.TexCoord);
         if (tc == null)
@@ -667,6 +928,11 @@ public class Mesh implements Savable, Cloneable {
         tc.updateData(fb);
     }
 
+    /**
+     * Updates the bounding volume of this mesh. 
+     * The method does nothing if the mesh has no {@link Type#Position} buffer.
+     * It is expected that the position buffer is a float buffer with 3 components.
+     */
     public void updateBound(){
         VertexBuffer posBuf = getBuffer(VertexBuffer.Type.Position);
         if (meshBound != null && posBuf != null){
@@ -674,17 +940,42 @@ public class Mesh implements Savable, Cloneable {
         }
     }
 
+    /**
+     * Returns the {@link BoundingVolume} of this Mesh.
+     * By default the bounding volume is a {@link BoundingBox}.
+     * 
+     * @return the bounding volume of this mesh
+     */
     public BoundingVolume getBound() {
         return meshBound;
     }
 
+    /**
+     * Sets the {@link BoundingVolume} for this Mesh.
+     * The bounding volume is recomputed by calling {@link #updateBound() }.
+     * 
+     * @param modelBound The model bound to set
+     */
     public void setBound(BoundingVolume modelBound) {
         meshBound = modelBound;
     }
 
+    /**
+     * Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh.
+     * The integer key for the map is the {@link Enum#ordinal() ordinal}
+     * of the vertex buffer's {@link Type}.
+     * Note that the returned map is a reference to the map used internally, 
+     * modifying it will cause undefined results.
+     * 
+     * @return map of vertex buffers on this mesh.
+     */
     public IntMap<VertexBuffer> getBuffers(){
         return buffers;
     }
+    
+    public ArrayList<VertexBuffer> getBufferList(){
+        return buffersList;
+    }
 
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule out = ex.getCapsule(this);
@@ -726,6 +1017,10 @@ public class Mesh implements Savable, Cloneable {
 
 //        in.readStringSavableMap("buffers", null);
         buffers = (IntMap<VertexBuffer>) in.readIntSavableMap("buffers", null);
+        for (Entry<VertexBuffer> entry : buffers){
+            buffersList.add(entry.getValue());
+        }
+        
         Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null);
         if (lodLevelsSavable != null) {
             lodLevels = new VertexBuffer[lodLevelsSavable.length];

+ 27 - 21
engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java

@@ -75,6 +75,7 @@ import java.nio.DoubleBuffer;
 import java.nio.FloatBuffer;
 import java.nio.IntBuffer;
 import java.nio.ShortBuffer;
+import java.util.ArrayList;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Level;
@@ -478,6 +479,14 @@ public class LwjglRenderer implements Renderer {
     public void setBackgroundColor(ColorRGBA color) {
         glClearColor(color.r, color.g, color.b, color.a);
     }
+    
+    public void setAlphaToCoverage(boolean value) {
+        if (value) {
+            glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
+        } else {
+            glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
+        }
+    }
 
     public void applyRenderState(RenderState state) {
         if (state.isWireframe() && !context.wireframe) {
@@ -2260,8 +2269,10 @@ public class LwjglRenderer implements Renderer {
     }
 
     private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
-        if (mesh.getId() == -1) {
+        if (mesh.getId() == -1){
             updateVertexArray(mesh);
+        }else{
+            // TODO: Check if it was updated
         }
 
         if (context.boundVertexArray != mesh.getId()) {
@@ -2269,18 +2280,17 @@ public class LwjglRenderer implements Renderer {
             context.boundVertexArray = mesh.getId();
         }
 
-        IntMap<VertexBuffer> buffers = mesh.getBuffers();
+//        IntMap<VertexBuffer> buffers = mesh.getBuffers();
         VertexBuffer indices = null;
         if (mesh.getNumLodLevels() > 0) {
             indices = mesh.getLodLevel(lod);
         } else {
-            indices = buffers.get(Type.Index.ordinal());
+            indices = mesh.getBuffer(Type.Index);
         }
         if (indices != null) {
             drawTriangleList(indices, mesh, count);
         } else {
-//            throw new UnsupportedOperationException("Cannot render without index buffer");
-            glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+            drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
         }
         clearVertexAttribs();
         clearTextureUnits();
@@ -2294,15 +2304,19 @@ public class LwjglRenderer implements Renderer {
             updateBufferData(interleavedData);
         }
 
-        IntMap<VertexBuffer> buffers = mesh.getBuffers();
+        //IntMap<VertexBuffer> buffers = mesh.getBuffers();
+        ArrayList<VertexBuffer> buffersList = mesh.getBufferList();
+        
         if (mesh.getNumLodLevels() > 0) {
             indices = mesh.getLodLevel(lod);
         } else {
-            indices = buffers.get(Type.Index.ordinal());
+            indices = mesh.getBuffer(Type.Index);
         }
-        for (Entry<VertexBuffer> entry : buffers) {
-            VertexBuffer vb = entry.getValue();
-
+        //for (Entry<VertexBuffer> entry : buffers) {
+        //     VertexBuffer vb = entry.getValue();
+        for (int i = 0; i < buffersList.size(); i++){
+            VertexBuffer vb = buffersList.get(i);
+            
             if (vb.getBufferType() == Type.InterleavedData
                     || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
                     || vb.getBufferType() == Type.Index) {
@@ -2317,11 +2331,11 @@ public class LwjglRenderer implements Renderer {
                 setVertexAttrib(vb, interleavedData);
             }
         }
+        
         if (indices != null) {
             drawTriangleList(indices, mesh, count);
         } else {
-//            throw new UnsupportedOperationException("Cannot render without index buffer");
-            glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount());
+            drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount());
         }
         clearVertexAttribs();
         clearTextureUnits();
@@ -2344,15 +2358,7 @@ public class LwjglRenderer implements Renderer {
 //        if (GLContext.getCapabilities().GL_ARB_vertex_array_object){
 //            renderMeshVertexArray(mesh, lod, count);
 //        }else{
-        renderMeshDefault(mesh, lod, count);
+            renderMeshDefault(mesh, lod, count);
 //        }
     }
-
-    public void setAlphaToCoverage(boolean value) {
-        if (value) {
-            glEnable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
-        } else {
-            glDisable(ARBMultisample.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
-        }
-    }
 }