Sfoglia il codice sorgente

Issue 466: Blender loader duplicates vertices
Done. Hope it does not break anything ... too much :)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9509 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

Kae..pl 13 anni fa
parent
commit
d63683d240

+ 44 - 13
engine/src/blender/com/jme3/scene/plugins/blender/curves/CurvesHelper.java

@@ -1,31 +1,40 @@
 package com.jme3.scene.plugins.blender.curves;
 
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
 import com.jme3.material.Material;
 import com.jme3.material.RenderState.FaceCullMode;
-import com.jme3.math.*;
+import com.jme3.math.FastMath;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Spline;
 import com.jme3.math.Spline.SplineType;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
 import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
-import com.jme3.scene.plugins.blender.file.*;
+import com.jme3.scene.plugins.blender.file.BlenderInputStream;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.FileBlockHeader;
+import com.jme3.scene.plugins.blender.file.Pointer;
+import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.materials.MaterialContext;
 import com.jme3.scene.plugins.blender.materials.MaterialHelper;
-import com.jme3.scene.plugins.blender.meshes.MeshHelper;
 import com.jme3.scene.plugins.blender.objects.Properties;
 import com.jme3.scene.shape.Curve;
 import com.jme3.scene.shape.Surface;
 import com.jme3.util.BufferUtils;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.logging.Logger;
 
 /**
  * A class that is used in mesh calculations.
@@ -396,7 +405,6 @@ public class CurvesHelper extends AbstractBlenderHelper {
     protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject,
             boolean smooth, BlenderContext blenderContext) {
         float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position));
-        MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
         float curveLength = curve.getLength();
         //TODO: use the smooth var
 
@@ -512,7 +520,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
             int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]);
             for (int i = 0; i < allIndices.length - 3; i += 3) {
                 Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
-                meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
+                this.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]);
             }
             if (normalBuffers[geomIndex] == null) {
                 normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3);
@@ -541,6 +549,29 @@ public class CurvesHelper extends AbstractBlenderHelper {
         return result;
     }
 
+    /**
+     * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
+     * 
+     * @param normalToAdd
+     *            a normal to be added
+     * @param normalMap
+     *            merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
+     * @param smooth
+     *            the variable that indicates wheather to merge normals (creating the smooth mesh) or not
+     * @param vertices
+     *            a list of vertices read from the blender file
+     */
+    private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
+        for (Vector3f v : vertices) {
+            Vector3f n = normalMap.get(v);
+            if (!smooth || n == null) {
+                normalMap.put(v, normalToAdd.clone());
+            } else {
+                n.addLocal(normalToAdd).normalizeLocal();
+            }
+        }
+    }
+    
     /**
      * This method loads the taper object.
      * @param taperStructure

+ 7 - 0
engine/src/blender/com/jme3/scene/plugins/blender/file/Pointer.java

@@ -103,6 +103,13 @@ public class Pointer {
                     } else {
                         structures.addAll(p.fetchData(inputStream));
                     }
+                } else {
+                	//it is necessary to put null's if the pointer is null, ie. in materials array that is attached to the mesh, the index
+                	//of the material is important, that is why we need null's to indicate that some materials' slots are empty
+                	if(structures == null) {
+                		structures = new ArrayList<Structure>();
+                	}
+                	structures.add(null);
                 }
             }
         } else {

+ 53 - 37
engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialContext.java

@@ -195,43 +195,45 @@ public final class MaterialContext {
 		}
 		
 		//applying textures
-		for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
-			CombinedTexture combinedTexture = entry.getValue();
-			combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
-			VertexBuffer.Type uvCoordinatesType = null;
-			
-			switch(entry.getKey().intValue()) {
-				case MTEX_COL:
-					uvCoordinatesType = VertexBuffer.Type.TexCoord;
-					material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, 
-							combinedTexture.getResultTexture());
-					break;
-				case MTEX_NOR:
-					uvCoordinatesType = VertexBuffer.Type.TexCoord2;
-					material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, combinedTexture.getResultTexture());
-					break;
-				case MTEX_SPEC:
-					uvCoordinatesType = VertexBuffer.Type.TexCoord3;
-					material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, combinedTexture.getResultTexture());
-					break;
-				case MTEX_EMIT:
-					uvCoordinatesType = VertexBuffer.Type.TexCoord4;
-					material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, combinedTexture.getResultTexture());
-					break;
-				case MTEX_ALPHA:
-					uvCoordinatesType = VertexBuffer.Type.TexCoord5;
-					material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, combinedTexture.getResultTexture());
-					break;
-				default:
-					LOGGER.severe("Unknown mapping type: " + entry.getKey().intValue());
-			}
-			
-			//applying texture coordinates
-			if(uvCoordinatesType != null) {
-				VertexBuffer uvCoordsBuffer = new VertexBuffer(uvCoordinatesType);
-	            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
-	                    BufferUtils.createFloatBuffer(combinedTexture.getResultUVS().toArray(new Vector2f[combinedTexture.getResultUVS().size()])));
-				geometry.getMesh().setBuffer(uvCoordsBuffer);
+		if(!noTextures) {
+			for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
+				CombinedTexture combinedTexture = entry.getValue();
+				combinedTexture.flatten(geometry, geometriesOMA, userDefinedUVCoordinates, blenderContext);
+				VertexBuffer.Type uvCoordinatesType = null;
+				
+				switch(entry.getKey().intValue()) {
+					case MTEX_COL:
+						uvCoordinatesType = VertexBuffer.Type.TexCoord;
+						material.setTexture(shadeless ? MaterialHelper.TEXTURE_TYPE_COLOR : MaterialHelper.TEXTURE_TYPE_DIFFUSE, 
+								combinedTexture.getResultTexture());
+						break;
+					case MTEX_NOR:
+						uvCoordinatesType = VertexBuffer.Type.TexCoord2;
+						material.setTexture(MaterialHelper.TEXTURE_TYPE_NORMAL, combinedTexture.getResultTexture());
+						break;
+					case MTEX_SPEC:
+						uvCoordinatesType = VertexBuffer.Type.TexCoord3;
+						material.setTexture(MaterialHelper.TEXTURE_TYPE_SPECULAR, combinedTexture.getResultTexture());
+						break;
+					case MTEX_EMIT:
+						uvCoordinatesType = VertexBuffer.Type.TexCoord4;
+						material.setTexture(MaterialHelper.TEXTURE_TYPE_GLOW, combinedTexture.getResultTexture());
+						break;
+					case MTEX_ALPHA:
+						uvCoordinatesType = VertexBuffer.Type.TexCoord5;
+						material.setTexture(MaterialHelper.TEXTURE_TYPE_ALPHA, combinedTexture.getResultTexture());
+						break;
+					default:
+						LOGGER.severe("Unknown mapping type: " + entry.getKey().intValue());
+				}
+				
+				//applying texture coordinates
+				if(uvCoordinatesType != null) {
+					VertexBuffer uvCoordsBuffer = new VertexBuffer(uvCoordinatesType);
+		            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
+		                    BufferUtils.createFloatBuffer(combinedTexture.getResultUVS().toArray(new Vector2f[combinedTexture.getResultUVS().size()])));
+					geometry.getMesh().setBuffer(uvCoordsBuffer);
+				}
 			}
 		}
 		
@@ -254,6 +256,20 @@ public final class MaterialContext {
         geometry.setMaterial(material);
 	}
 	
+	/**
+	 * @return <b>true</b> if the material has at least one generated texture and <b>false</b> otherwise
+	 */
+	public boolean hasGeneratedTextures() {
+		if(loadedTextures != null) {
+			for(Entry<Number, CombinedTexture> entry : loadedTextures.entrySet()) {
+				if(entry.getValue().hasGeneratedTextures()) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
 	/**
 	 * This method sorts the textures by their mapping type.
 	 * In each group only textures of one type are put (either two- or three-dimensional).

+ 1 - 1
engine/src/blender/com/jme3/scene/plugins/blender/materials/MaterialHelper.java

@@ -337,7 +337,7 @@ public class MaterialHelper extends AbstractBlenderHelper {
 				materials = new MaterialContext[materialStructures.size()];
 				int i = 0;
 				for (Structure s : materialStructures) {
-					materials[i++] = materialHelper.toMaterialContext(s, blenderContext);
+					materials[i++] = s == null ? null : materialHelper.toMaterialContext(s, blenderContext);
 				}
 			}
 		}

+ 244 - 0
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshBuilder.java

@@ -0,0 +1,244 @@
+package com.jme3.scene.plugins.blender.meshes;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+
+/*package*/ class MeshBuilder {
+	/** An array of reference vertices. */
+	private Vector3f[] vertices;
+	/** A variable that indicates if the model uses generated textures. */
+	private boolean usesGeneratedTextures;
+	
+	/** This map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
+    positions (it simply tells which vertex is referenced where in the result list). */
+    private Map<Integer, Map<Integer, List<Integer>>> globalVertexReferenceMap;
+    /** A map between vertex and its normal vector. */
+    private Map<Vector3f, Vector3f> globalNormalMap = new HashMap<Vector3f, Vector3f>();
+    
+    /** A map between vertex index and its UV coordinates. */
+    private Map<Integer, Vector2f> uvsMap = new HashMap<Integer, Vector2f>();
+    /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
+    private Map<Integer, List<Vector3f>> normalMap = new HashMap<Integer, List<Vector3f>>();
+    /** The following map sorts vertices by material number (because in jme Mesh can have only one material). */
+    private Map<Integer, List<Vector3f>> vertexMap = new HashMap<Integer, List<Vector3f>>();
+    /** The following map sorts indexes by material number (because in jme Mesh can have only one material). */
+    private Map<Integer, List<Integer>> indexMap = new HashMap<Integer, List<Integer>>();
+    /** A map between material number and UV coordinates of mesh that has this material applied. */
+    private Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
+    
+    /**
+     * Constructor. Stores the given array (not copying it).
+     * The second argument describes if the model uses generated textures. If yes then no vertex amount optimisation is applied.
+     * The amount of vertices is always faceCount * 3.
+     * @param vertices the reference vertices array
+     * @param usesGeneratedTextures a variable that indicates if the model uses generated textures or not
+     */
+	public MeshBuilder(Vector3f[] vertices, boolean usesGeneratedTextures) {
+		if(vertices == null || vertices.length == 0) {
+			throw new IllegalArgumentException("No vertices loaded to build mesh.");
+		}
+		this.vertices = vertices;
+		this.usesGeneratedTextures = usesGeneratedTextures;
+		globalVertexReferenceMap = new HashMap<Integer, Map<Integer, List<Integer>>>(vertices.length);
+	}
+	
+	/**
+	 * This method adds a face to the mesh.
+	 * @param v1 index of the 1'st vertex from the reference vertex table
+	 * @param v2 index of the 2'nd vertex from the reference vertex table
+	 * @param v3 index of the 3'rd vertex from the reference vertex table
+	 * @param smooth indicates if this face should have smooth shading or flat shading
+	 * @param materialNumber the material number for this face
+	 * @param uvs a 3-element array of vertices UV coordinates
+	 */
+	public void appendFace(int v1, int v2, int v3, boolean smooth, int materialNumber, Vector2f[] uvs) {
+		if(uvs != null && uvs.length != 3) {
+			throw new IllegalArgumentException("UV coordinates must be a 3-element array!");
+		}
+		List<Integer> indexList = indexMap.get(materialNumber);
+        if (indexList == null) {
+            indexList = new ArrayList<Integer>();
+            indexMap.put(materialNumber, indexList);
+        }
+        List<Vector3f> vertexList = vertexMap.get(materialNumber);
+        if (vertexList == null) {
+            vertexList = new ArrayList<Vector3f>();
+            vertexMap.put(materialNumber, vertexList);
+        }
+        List<Vector3f> normalList = normalMap.get(materialNumber);
+        if (normalList == null) {
+        	normalList = new ArrayList<Vector3f>();
+        	normalMap.put(materialNumber, normalList);
+        }
+        Map<Integer, List<Integer>> vertexReferenceMap = globalVertexReferenceMap.get(materialNumber);
+        if(vertexReferenceMap == null) {
+        	vertexReferenceMap = new HashMap<Integer, List<Integer>>();
+        	globalVertexReferenceMap.put(materialNumber, vertexReferenceMap);
+        }
+        
+        List<Vector2f> uvCoordinatesList = null;
+        if(uvs != null) {
+	        uvCoordinatesList = uvCoordinates.get(Integer.valueOf(materialNumber));
+	        if(uvCoordinatesList == null) {
+	        	uvCoordinatesList = new ArrayList<Vector2f>();
+	        	uvCoordinates.put(Integer.valueOf(materialNumber), uvCoordinatesList);
+	        }
+        }
+        
+        Integer[] index = new Integer[] {v1, v2, v3};
+		Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
+        this.addNormal(n, globalNormalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
+        if(smooth && !usesGeneratedTextures) {
+			for (int i = 0; i < 3; ++i) {
+        		if(!vertexReferenceMap.containsKey(index[i])) {
+            		this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
+            		vertexList.add(vertices[index[i]]);
+            		normalList.add(globalNormalMap.get(vertices[index[i]]));
+            		if(uvCoordinatesList != null) {
+            			uvsMap.put(vertexList.size(), uvs[i]);
+            			uvCoordinatesList.add(uvs[i]);
+            		}
+            		index[i] = vertexList.size() - 1;
+            	} else if(uvCoordinatesList != null) {
+            		boolean vertexAlreadyUsed = false;
+            		for(Integer vertexIndex : vertexReferenceMap.get(index[i])) {
+            			if(uvs[i].equals(uvsMap.get(vertexIndex))) {
+            				vertexAlreadyUsed = true;
+            				index[i] = vertexIndex;
+            				break;
+            			}
+            		}
+            		if(!vertexAlreadyUsed) {
+            			this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
+            			uvsMap.put(vertexList.size(), uvs[i]);
+            			vertexList.add(vertices[index[i]]);
+                		normalList.add(globalNormalMap.get(vertices[index[i]]));
+            			uvCoordinatesList.add(uvs[i]);
+            			index[i] = vertexList.size() - 1;
+            		}
+            	} else {
+            		index[i] = vertexList.indexOf(vertices[index[i]]);
+            	}
+        		indexList.add(index[i]);
+        	}
+        } else {
+        	for (int i = 0; i < 3; ++i) {
+        		indexList.add(vertexList.size());
+        		this.appendVertexReference(index[i], vertexList.size(), vertexReferenceMap);
+        		if(uvCoordinatesList != null) {
+        			uvCoordinatesList.add(uvs[i]);
+        			uvsMap.put(vertexList.size(), uvs[i]);
+        		}
+        		vertexList.add(vertices[index[i]]);
+        		normalList.add(globalNormalMap.get(vertices[index[i]]));
+        	}
+        }
+	}
+	
+	/**
+	 * @return a map that maps vertex index from reference array to its indices in the result list
+	 */
+	public Map<Integer, Map<Integer, List<Integer>>> getVertexReferenceMap() {
+		return globalVertexReferenceMap;
+	}
+	
+	/**
+	 * @return result vertices array
+	 */
+	public Vector3f[] getVertices(int materialNumber) {
+		return vertexMap.get(materialNumber).toArray(new Vector3f[vertexMap.get(materialNumber).size()]);
+	}
+	
+	/**
+	 * @return the amount of result vertices
+	 */
+	public int getVerticesAmount(int materialNumber) {
+		return vertexMap.get(materialNumber).size();
+	}
+	
+	/**
+	 * @return normals result array
+	 */
+	public Vector3f[] getNormals(int materialNumber) {
+		return normalMap.get(materialNumber).toArray(new Vector3f[normalMap.get(materialNumber).size()]);
+	}
+	
+	/**
+	 * @return a map between material number and the mesh part vertices indices
+	 */
+	public Map<Integer, List<Integer>> getMeshesMap() {
+		return indexMap;
+	}
+	
+	/**
+	 * @return the amount of meshes the source mesh was split into (depends on the applied materials count)
+	 */
+	public int getMeshesPartAmount() {
+		return indexMap.size();
+	}
+	
+	/**
+	 * @param materialNumber the material number that is appied to the mesh
+	 * @return UV coordinates of vertices that belong to the required mesh part
+	 */
+	public List<Vector2f> getUVCoordinates(int materialNumber) {
+		return uvCoordinates.get(materialNumber);
+	}
+	
+	/**
+	 * @return indicates if the mesh has UV coordinates
+	 */
+	public boolean hasUVCoordinates() {
+		return uvCoordinates.size() > 0;
+	}
+	
+	/**
+     * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
+     * 
+     * @param normalToAdd
+     *            a normal to be added
+     * @param normalMap
+     *            merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
+     * @param smooth
+     *            the variable that indicates wheather to merge normals (creating the smooth mesh) or not
+     * @param vertices
+     *            a list of vertices read from the blender file
+     */
+    private void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
+        for (Vector3f v : vertices) {
+            Vector3f n = normalMap.get(v);
+            if (!smooth || n == null) {
+                normalMap.put(v, normalToAdd.clone());
+            } else {
+                n.addLocal(normalToAdd).normalizeLocal();
+            }
+        }
+    }
+    
+    /**
+     * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
+     * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key
+     * - the reference indices list.
+     * 
+     * @param basicVertexIndex
+     *            the index of the vertex from its basic table
+     * @param resultIndex
+     *            the index of the vertex in its result vertex list
+     * @param vertexReferenceMap
+     *            the reference map
+     */
+    private void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
+        List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
+        if (referenceList == null) {
+            referenceList = new ArrayList<Integer>();
+            vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
+        }
+        referenceList.add(Integer.valueOf(resultIndex));
+    }
+}

+ 33 - 40
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshContext.java

@@ -1,11 +1,12 @@
 package com.jme3.scene.plugins.blender.meshes;
 
-import com.jme3.math.Vector3f;
-import com.jme3.scene.Geometry;
-import com.jme3.scene.VertexBuffer;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+
+import com.jme3.scene.Geometry;
+import com.jme3.scene.VertexBuffer;
 
 /**
  * Class that holds information about the mesh.
@@ -13,12 +14,10 @@ import java.util.Map;
  * @author Marcin Roguski (Kaelthas)
  */
 public class MeshContext {
-	/** The mesh stored here as a list of geometries. */
-	private List<Geometry>				mesh;
-	/** Vertex list that is referenced by all the geometries. */
-	private List<Vector3f>				vertexList;
+	/** A map between material index and the geometry. */
+	private Map<Integer, Geometry> geometries = new HashMap<Integer, Geometry>();
 	/** The vertex reference map. */
-	private Map<Integer, List<Integer>>	vertexReferenceMap;
+	private Map<Integer, Map<Integer, List<Integer>>>	vertexReferenceMap;
 	/** The UV-coordinates for each of the geometries. */
 	private Map<Geometry, VertexBuffer>	uvCoordinates	= new HashMap<Geometry, VertexBuffer>();
 	/** Bind buffer for vertices is stored here and applied when required. */
@@ -27,50 +26,44 @@ public class MeshContext {
 	private VertexBuffer				bindNormalBuffer;
 
 	/**
-	 * This method returns the referenced mesh.
-	 * 
-	 * @return the referenced mesh
-	 */
-	public List<Geometry> getMesh() {
-		return mesh;
-	}
-
-	/**
-	 * This method sets the referenced mesh.
-	 * 
-	 * @param mesh
-	 *            the referenced mesh
+	 * Adds a geometry for the specified material index.
+	 * @param materialIndex the material index
+	 * @param geometry the geometry
 	 */
-	public void setMesh(List<Geometry> mesh) {
-		this.mesh = mesh;
+	public void putGeometry(Integer materialIndex, Geometry geometry) {
+		geometries.put(materialIndex, geometry);
 	}
-
+	
 	/**
-	 * This method returns the vertex list.
-	 * 
-	 * @return the vertex list
+	 * @param materialIndex the material index
+	 * @return vertices amount that is used by mesh with the specified material
 	 */
-	public List<Vector3f> getVertexList() {
-		return vertexList;
+	public int getVertexCount(int materialIndex) {
+		return geometries.get(materialIndex).getVertexCount();
 	}
-
+	
 	/**
-	 * This method sets the vertex list.
-	 * 
-	 * @param vertexList
-	 *            the vertex list
+	 * Returns material index for the geometry.
+	 * @param geometry the geometry
+	 * @return material index
+	 * @throws IllegalStateException this exception is thrown when no material is found for the specified geometry
 	 */
-	public void setVertexList(List<Vector3f> vertexList) {
-		this.vertexList = vertexList;
+	public int getMaterialIndex(Geometry geometry) {
+		for(Entry<Integer, Geometry> entry : geometries.entrySet()) {
+			if(entry.getValue().equals(geometry)) {
+				return entry.getKey();
+			}
+		}
+		throw new IllegalStateException("Cannot find material index for the given geometry: " + geometry);
 	}
-
+	
 	/**
 	 * This method returns the vertex reference map.
 	 * 
 	 * @return the vertex reference map
 	 */
-	public Map<Integer, List<Integer>> getVertexReferenceMap() {
-		return vertexReferenceMap;
+	public Map<Integer, List<Integer>> getVertexReferenceMap(int materialIndex) {
+		return vertexReferenceMap.get(materialIndex);
 	}
 
 	/**
@@ -79,7 +72,7 @@ public class MeshContext {
 	 * @param vertexReferenceMap
 	 *            the vertex reference map
 	 */
-	public void setVertexReferenceMap(Map<Integer, List<Integer>> vertexReferenceMap) {
+	public void setVertexReferenceMap(Map<Integer, Map<Integer, List<Integer>>> vertexReferenceMap) {
 		this.vertexReferenceMap = vertexReferenceMap;
 	}
 

+ 73 - 162
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java

@@ -31,8 +31,15 @@
  */
 package com.jme3.scene.plugins.blender.meshes;
 
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
-import com.jme3.math.FastMath;
 import com.jme3.math.Vector2f;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.Geometry;
@@ -54,13 +61,6 @@ import com.jme3.scene.plugins.blender.objects.Properties;
 import com.jme3.scene.plugins.blender.textures.TextureHelper;
 import com.jme3.texture.Texture;
 import com.jme3.util.BufferUtils;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
 
 /**
  * A class that is used in mesh calculations.
@@ -109,16 +109,20 @@ public class MeshHelper extends AbstractBlenderHelper {
         String name = structure.getName();
         MeshContext meshContext = new MeshContext();
 
+        // reading materials
+        MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
+        MaterialContext[] materials = null;
+        if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
+            materials = materialHelper.getMaterials(structure, blenderContext);
+        }
+        
         // reading vertices
         Vector3f[] vertices = this.getVertices(structure, blenderContext);
-        int verticesAmount = vertices.length;
-
+        MeshBuilder meshBuilder = new MeshBuilder(vertices, this.areGeneratedTexturesPresent(materials));
+        
         // vertices Colors
         List<byte[]> verticesColors = this.getVerticesColors(structure, blenderContext);
 
-        // reading faces
-        // the following map sorts faces by material number (because in jme Mesh can have only one material)
-        Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
         Pointer pMFace = (Pointer) structure.getFieldValue("mface");
         List<Structure> mFaces = null;
         if (pMFace.isNotNull()) {
@@ -131,7 +135,6 @@ public class MeshHelper extends AbstractBlenderHelper {
         }
 
         Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
-        Map<Integer, List<Vector2f>> uvCoordinates = new HashMap<Integer, List<Vector2f>>();//<material_number; list of uv coordinates for mesh's vertices>
         List<Structure> mtFaces = null;
 
         if (pMTFace.isNotNull()) {
@@ -142,17 +145,10 @@ public class MeshHelper extends AbstractBlenderHelper {
             }
         }
 
-        // normalMap merges normals of faces that will be rendered smooth
-        Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
-
-        List<Vector3f> normalList = new ArrayList<Vector3f>();
-        List<Vector3f> vertexList = new ArrayList<Vector3f>();
         // indicates if the material with the specified number should have a texture attached
         Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();
-        // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
-        // positions (it simply tells which vertex is referenced where in the result list)
-        Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);
         int vertexColorIndex = 0;
+        Vector2f[] uvCoordinatesForFace = new Vector2f[3];
         for (int i = 0; i < mFaces.size(); ++i) {
             Structure mFace = mFaces.get(i);
             int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
@@ -161,28 +157,17 @@ public class MeshHelper extends AbstractBlenderHelper {
             boolean materialWithoutTextures = false;
             Pointer pImage = null;
             
-            List<Vector2f> uvCoordinatesList = uvCoordinates.get(Integer.valueOf(matNr));
-            if(uvCoordinatesList == null) {
-            	uvCoordinatesList = new ArrayList<Vector2f>();
-            	uvCoordinates.put(Integer.valueOf(matNr), uvCoordinatesList);
-            }
-            
             if (mtFaces != null) {
                 Structure mtFace = mtFaces.get(i);
                 pImage = (Pointer) mtFace.getFieldValue("tpage");
                 materialWithoutTextures = pImage.isNull();
                 // uvs always must be added wheater we have texture or not
                 uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
-                uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
-                uvCoordinatesList.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
-                uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
+                uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
+                uvCoordinatesForFace[1] = new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue());
+                uvCoordinatesForFace[2] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
             }
             Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
-            List<Integer> indexList = meshesMap.get(materialNumber);
-            if (indexList == null) {
-                indexList = new ArrayList<Integer>();
-                meshesMap.put(materialNumber, indexList);
-            }
 
             // attaching image to texture (face can have UV's and image whlie its material may have no texture attached)
             if (pImage != null && pImage.isNotNull() && !materialNumberToTexture.containsKey(materialNumber)) {
@@ -198,46 +183,14 @@ public class MeshHelper extends AbstractBlenderHelper {
             int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
             int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
 
-            Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
-            this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
-            normalList.add(normalMap.get(vertices[v1]));
-            normalList.add(normalMap.get(vertices[v2]));
-            normalList.add(normalMap.get(vertices[v3]));
-
-            this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
-            indexList.add(vertexList.size());
-            vertexList.add(vertices[v1]);
-
-            this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);
-            indexList.add(vertexList.size());
-            vertexList.add(vertices[v2]);
-
-            this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
-            indexList.add(vertexList.size());
-            vertexList.add(vertices[v3]);
-
+            meshBuilder.appendFace(v1, v2, v3, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace);
             if (v4 > 0) {
                 if (uvs != null) {
-                	uvCoordinatesList.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
-                	uvCoordinatesList.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
-                	uvCoordinatesList.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
+                	uvCoordinatesForFace[0] = new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue());
+                	uvCoordinatesForFace[1] = new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue());
+                	uvCoordinatesForFace[2] = new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue());
                 }
-                this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
-                indexList.add(vertexList.size());
-                vertexList.add(vertices[v1]);
-
-                this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
-                indexList.add(vertexList.size());
-                vertexList.add(vertices[v3]);
-
-                this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);
-                indexList.add(vertexList.size());
-                vertexList.add(vertices[v4]);
-
-                this.addNormal(n, normalMap, smooth, vertices[v4]);
-                normalList.add(normalMap.get(vertices[v1]));
-                normalList.add(normalMap.get(vertices[v3]));
-                normalList.add(normalMap.get(vertices[v4]));
+                meshBuilder.appendFace(v1, v3, v4, smooth, materialNumber, uvs == null ? null : uvCoordinatesForFace);
 
                 if (verticesColors != null) {
                     verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));
@@ -251,10 +204,7 @@ public class MeshHelper extends AbstractBlenderHelper {
                 }
             }
         }
-        meshContext.setVertexList(vertexList);
-        meshContext.setVertexReferenceMap(vertexReferenceMap);
-
-        Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
+        meshContext.setVertexReferenceMap(meshBuilder.getVertexReferenceMap());
 
         // reading vertices groups (from the parent)
         Structure parent = blenderContext.peekParent();
@@ -266,48 +216,23 @@ public class MeshHelper extends AbstractBlenderHelper {
             verticesGroups[defIndex++] = def.getFieldValue("name").toString();
         }
 
-        // reading materials
-        MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
-        MaterialContext[] materials = null;
-        if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
-            materials = materialHelper.getMaterials(structure, blenderContext);
-        }
-
         // creating the result meshes
-        geometries = new ArrayList<Geometry>(meshesMap.size());
-
-        VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
-        verticesBuffer.setupData(Usage.Static, 3, Format.Float,
-                BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));
-
-        // initial vertex position (used with animation)
-        VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
-        verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
-
-        VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
-        normalsBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(normals));
-
-        // initial normals position (used with animation)
-        VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
-        normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
+        geometries = new ArrayList<Geometry>(meshBuilder.getMeshesPartAmount());
 
         //reading custom properties
         Properties properties = this.loadProperties(structure, blenderContext);
 
         // generating meshes
-        //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
         ByteBuffer verticesColorsBuffer = this.createByteBuffer(verticesColors);
-        verticesAmount = vertexList.size();
-        Map<Mesh, Integer> meshToMAterialMap = new HashMap<Mesh, Integer>(meshesMap.size());
-        for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
+        for (Entry<Integer, List<Integer>> meshEntry : meshBuilder.getMeshesMap().entrySet()) {
+        	int materialIndex = meshEntry.getKey();
         	//key is the material index (or -1 if the material has no texture)
         	//value is a list of vertex indices
             Mesh mesh = new Mesh();
-            meshToMAterialMap.put(mesh, meshEntry.getKey());
             
             // creating vertices indices for this mesh
             List<Integer> indexList = meshEntry.getValue();
-            if(verticesAmount <= Short.MAX_VALUE) {
+            if(meshBuilder.getVerticesAmount(materialIndex) <= Short.MAX_VALUE) {
             	short[] indices = new short[indexList.size()];
                 for (int i = 0; i < indexList.size(); ++i) {
                     indices[i] = indexList.get(i).shortValue();
@@ -321,6 +246,20 @@ public class MeshHelper extends AbstractBlenderHelper {
                 mesh.setBuffer(Type.Index, 1, indices);
             }
             
+            VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
+            verticesBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(meshBuilder.getVertices(materialIndex)));
+
+            // initial vertex position (used with animation)
+            VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
+            verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
+
+            VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
+            normalsBuffer.setupData(Usage.Static, 3, Format.Float, BufferUtils.createFloatBuffer(meshBuilder.getNormals(materialIndex)));
+
+            // initial normals position (used with animation)
+            VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
+            normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
+            
             mesh.setBuffer(verticesBuffer);
             meshContext.setBindPoseBuffer(verticesBind);//this is stored in the context and applied when needed (when animation is applied to the mesh)
 
@@ -340,6 +279,7 @@ public class MeshHelper extends AbstractBlenderHelper {
                 geometry.setUserData("properties", properties);
             }
             geometries.add(geometry);
+            meshContext.putGeometry(materialIndex, geometry);
         }
         
         //store the data in blender context before applying the material
@@ -349,20 +289,30 @@ public class MeshHelper extends AbstractBlenderHelper {
         //apply materials only when all geometries are in place
         if(materials != null) {
         	for(Geometry geometry : geometries) {
-        		int materialNumber = meshToMAterialMap.get(geometry.getMesh()).intValue();
+        		int materialNumber = meshContext.getMaterialIndex(geometry);
+        		List<Vector2f> uvCoordinates = meshBuilder.getUVCoordinates(materialNumber);
                 boolean noTextures = false;
                 if(materialNumber < 0) {
                 	materialNumber = -1 * (materialNumber + 1);
                 	noTextures = true;
                 }
-                MaterialContext materialContext = materials[materialNumber];
-                materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), noTextures, uvCoordinates.get(Integer.valueOf(materialNumber)), blenderContext);
+                if(materials[materialNumber] != null) {
+	                MaterialContext materialContext = materials[materialNumber];
+	                materialContext.applyMaterial(geometry, structure.getOldMemoryAddress(), noTextures, uvCoordinates, blenderContext);
+                } else {
+                	geometry.setMaterial(blenderContext.getDefaultMaterial());
+            		if(uvCoordinates != null) {
+            			VertexBuffer uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
+    		            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
+    					geometry.getMesh().setBuffer(uvCoordsBuffer);
+            		}
+                }
         	}
         } else {
         	//add UV coordinates if they are defined even if the material is not applied to the model
         	VertexBuffer uvCoordsBuffer = null;
-        	if(uvCoordinates.size() > 0) {
-	        	List<Vector2f> uvs = uvCoordinates.get(0);
+        	if(meshBuilder.hasUVCoordinates()) {
+	        	List<Vector2f> uvs = meshBuilder.getUVCoordinates(-1);
 	        	uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
 	            uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float, BufferUtils.createFloatBuffer(uvs.toArray(new Vector2f[uvs.size()])));
         	}
@@ -375,62 +325,23 @@ public class MeshHelper extends AbstractBlenderHelper {
         	}
         }
         
-        // if there are multiple materials used, extract the shared
-        // vertex data
-        if (geometries.size() > 1){
-            // extract from itself
-            for (Geometry geom : geometries){
-                geom.getMesh().extractVertexData(geom.getMesh());
-            }
-        }
-
         return geometries;
     }
 
     /**
-     * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
-     * 
-     * @param normalToAdd
-     *            a normal to be added
-     * @param normalMap
-     *            merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
-     * @param smooth
-     *            the variable that indicates wheather to merge normals (creating the smooth mesh) or not
-     * @param vertices
-     *            a list of vertices read from the blender file
-     */
-    public void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
-        for (Vector3f v : vertices) {
-            Vector3f n = normalMap.get(v);
-            if (!smooth || n == null) {
-                normalMap.put(v, normalToAdd.clone());
-            } else {
-                n.addLocal(normalToAdd).normalizeLocal();
-            }
-        }
-    }
-
-    /**
-     * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
-     * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key
-     * - the reference indices list.
-     * 
-     * @param basicVertexIndex
-     *            the index of the vertex from its basic table
-     * @param resultIndex
-     *            the index of the vertex in its result vertex list
-     * @param vertexReferenceMap
-     *            the reference map
-     */
-    protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
-        List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
-        if (referenceList == null) {
-            referenceList = new ArrayList<Integer>();
-            vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
-        }
-        referenceList.add(Integer.valueOf(resultIndex));
+	 * @return <b>true</b> if the material has at least one generated component and <b>false</b> otherwise
+	 */
+    private boolean areGeneratedTexturesPresent(MaterialContext[] materials) {
+    	if(materials != null) {
+    		for(MaterialContext material : materials) {
+    			if(material != null && material.hasGeneratedTextures()) {
+    				return true;
+    			}
+    		}
+    	}
+    	return false;
     }
-
+    
     /**
      * This method returns the vertices colors. Each vertex is stored in byte[4] array.
      * 

+ 80 - 89
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java

@@ -53,19 +53,14 @@ import java.util.logging.Logger;
 	// If you decide to remove this limitation, remove this code.
 	// Rémy
 
+	private Skeleton skeleton;
+	private Structure objectStructure;
+	private Structure meshStructure;
+	
 	/** Loaded animation data. */
 	private AnimData			animData;
 	/** Old memory address of the mesh that will have the skeleton applied. */
 	private Long				meshOMA;
-	/**
-	 * The maxiumum amount of bone groups applied to a single vertex (max =
-	 * MAXIMUM_WEIGHTS_PER_VERTEX).
-	 */
-	private int					boneGroups;
-	/** The weights of vertices. */
-	private VertexBuffer		verticesWeights;
-	/** The indexes of bones applied to vertices. */
-	private VertexBuffer		verticesWeightsIndices;
 
 	/**
 	 * This constructor reads animation data from the object structore. The
@@ -83,9 +78,7 @@ import java.util.logging.Logger;
 	 */
 	public ArmatureModifier(Structure objectStructure, Structure modifierStructure, BlenderContext blenderContext) throws BlenderFileException {
 		Structure meshStructure = ((Pointer) objectStructure.getFieldValue("data")).fetchData(blenderContext.getInputStream()).get(0);
-		Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert
-																		// =
-																		// DeformVERTices
+		Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
 
 		// if pDvert==null then there are not vertex groups and no need to load
 		// skeleton (untill bone envelopes are supported)
@@ -120,17 +113,17 @@ import java.util.logging.Logger;
 				}
 				bonesList.add(0, new Bone(""));
 				Bone[] bones = bonesList.toArray(new Bone[bonesList.size()]);
-				Skeleton skeleton = new Skeleton(bones);
-
+				skeleton = new Skeleton(bones);
+				this.objectStructure = objectStructure;
+				this.meshStructure = meshStructure;
+				
 				// read mesh indexes
 				this.meshOMA = meshStructure.getOldMemoryAddress();
-				this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, blenderContext);
 
 				// read animations
 				ArrayList<Animation> animations = new ArrayList<Animation>();
 				List<FileBlockHeader> actionHeaders = blenderContext.getFileBlocks(Integer.valueOf(FileBlockHeader.BLOCK_AC00));
-				if (actionHeaders != null) {// it may happen that the model has
-											// armature with no actions
+				if (actionHeaders != null) {// it may happen that the model has armature with no actions
 					for (FileBlockHeader header : actionHeaders) {
 						Structure actionStructure = header.getStructure(blenderContext);
 						String actionName = actionStructure.getName();
@@ -178,22 +171,32 @@ import java.util.logging.Logger;
 		// setting weights for bones
 		List<Geometry> geomList = (List<Geometry>) blenderContext.getLoadedFeature(meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
 		MeshContext meshContext = blenderContext.getMeshContext(meshOMA);
+		int[] bonesGroups = new int[] { 0 };
 		for (Geometry geom : geomList) {
+			int materialIndex = meshContext.getMaterialIndex(geom);
 			Mesh mesh = geom.getMesh();
-			if(meshContext.getBindNormalBuffer() != null) {
-				mesh.setBuffer(meshContext.getBindNormalBuffer());
-			}
-			if(meshContext.getBindPoseBuffer() != null) {
-				mesh.setBuffer(meshContext.getBindPoseBuffer());
-			}
-			//change the usage type of vertex and normal buffers from Static to Stream
-			mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
-			mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
 			
-			if (this.verticesWeights != null) {
-				mesh.setMaxNumWeights(this.boneGroups);
-				mesh.setBuffer(this.verticesWeights);
-				mesh.setBuffer(this.verticesWeightsIndices);
+			try {
+				VertexBuffer[] buffers = this.readVerticesWeightsData(objectStructure, meshStructure, skeleton, materialIndex, bonesGroups, blenderContext);
+				if (buffers != null) {
+					mesh.setMaxNumWeights(bonesGroups[0]);
+					mesh.setBuffer(buffers[0]);
+					mesh.setBuffer(buffers[1]);
+					
+					if(meshContext.getBindNormalBuffer() != null) {
+						mesh.setBuffer(meshContext.getBindNormalBuffer());
+					}
+					if(meshContext.getBindPoseBuffer() != null) {
+						mesh.setBuffer(meshContext.getBindPoseBuffer());
+					}
+					//change the usage type of vertex and normal buffers from Static to Stream
+					mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
+					mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
+				}
+			} catch (BlenderFileException e) {
+				LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
+				this.invalid = true;
+				return node;
 			}
 		}
 
@@ -239,18 +242,16 @@ import java.util.logging.Logger;
 	 *             this exception is thrown when the blend file structure is
 	 *             somehow invalid or corrupted
 	 */
-	private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, BlenderContext blenderContext) throws BlenderFileException {
+	private VertexBuffer[] readVerticesWeightsData(Structure objectStructure, Structure meshStructure, Skeleton skeleton, int materialIndex,
+										 int[] bonesGroups, BlenderContext blenderContext) throws BlenderFileException {
 		ArmatureHelper armatureHelper = blenderContext.getHelper(ArmatureHelper.class);
 		Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
 		Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, skeleton, blenderContext);
 
-		int[] bonesGroups = new int[] { 0 };
 		MeshContext meshContext = blenderContext.getMeshContext(meshStructure.getOldMemoryAddress());
-
-		VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexList().size(), bonesGroups, meshContext.getVertexReferenceMap(), groupToBoneIndexMap, blenderContext);
-		this.verticesWeights = boneWeightsAndIndex[0];
-		this.verticesWeightsIndices = boneWeightsAndIndex[1];
-		this.boneGroups = bonesGroups[0];
+		
+		return this.getBoneWeightAndIndexBuffer(meshStructure, meshContext.getVertexCount(materialIndex), 
+									bonesGroups, meshContext.getVertexReferenceMap(materialIndex), groupToBoneIndexMap, blenderContext);
 	}
 
 	/**
@@ -284,49 +285,52 @@ import java.util.logging.Logger;
 	 */
 	private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups, Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, BlenderContext blenderContext)
 			throws BlenderFileException {
+		bonesGroups[0] = 0;
 		Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
 		FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
 		ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
 		if (pDvert.isNotNull()) {// assigning weights and bone indices
-			List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per
-																						// vertex in blender)
+			List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
 			int vertexIndex = 0;
+			
 			for (Structure dvert : dverts) {
-				int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex
-																						// (max. 4 in JME)
-				Pointer pDW = (Pointer) dvert.getFieldValue("dw");
 				List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
-				if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
-					int weightIndex = 0;
-					List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
-					for (Structure deformWeight : dw) {
-						Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
-
-						// Remove this code if 4 weights limitation is removed
-						if (weightIndex == 4) {
-							LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex });
-							break;
-						}
-
-						// null here means that we came accross group that has no bone attached to
-						if (boneIndex != null) {
-							float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
-							if (weight == 0.0f) {
-								weight = 1;
-								boneIndex = Integer.valueOf(0);
+				if(vertexIndices != null) {
+					int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex (max. 4 in JME)
+					Pointer pDW = (Pointer) dvert.getFieldValue("dw");
+					if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
+						int weightIndex = 0;
+						List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
+						for (Structure deformWeight : dw) {
+							Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
+
+							// Remove this code if 4 weights limitation is removed
+							if (weightIndex == 4) {
+								LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex });
+								break;
 							}
-							// we apply the weight to all referenced vertices
-							for (Integer index : vertexIndices) {
-								weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
-								indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
+
+							// null here means that we came accross group that has no bone attached to
+							if (boneIndex != null) {
+								float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
+								if (weight == 0.0f) {
+									weight = 1;
+									boneIndex = Integer.valueOf(0);
+								}
+								// we apply the weight to all referenced vertices
+								for (Integer index : vertexIndices) {
+									weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
+									indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
+								}
 							}
+							++weightIndex;
+						}
+						bonesGroups[0] = Math.max(bonesGroups[0], weightIndex);
+					} else {
+						for (Integer index : vertexIndices) {
+							weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
+							indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
 						}
-						++weightIndex;
-					}
-				} else {
-					for (Integer index : vertexIndices) {
-						weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
-						indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
 					}
 				}
 				++vertexIndex;
@@ -334,10 +338,8 @@ import java.util.logging.Logger;
 		} else {
 			// always bind all vertices to 0-indexed bone
 			// this bone makes the model look normally if vertices have no bone
-			// assigned
-			// and it is used in object animation, so if we come accross object
-			// animation
-			// we can use the 0-indexed bone for this
+			// assigned and it is used in object animation, so if we come accross object
+			// animation we can use the 0-indexed bone for this
 			for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
 				// we apply the weight to all referenced vertices
 				for (Integer index : vertexIndexList) {
@@ -347,7 +349,9 @@ import java.util.logging.Logger;
 			}
 		}
 
-		bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
+		bonesGroups[0] = Math.max(bonesGroups[0], 1);
+
+		this.endBoneAssigns(vertexListSize, weightsFloatData);
 		VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
 		verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
 
@@ -365,22 +369,10 @@ import java.util.logging.Logger;
 	 * @param weightsFloatData
 	 *            weights for vertices
 	 */
-	private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
-		int maxWeightsPerVert = 0;
+	private void endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
 		weightsFloatData.rewind();
 		for (int v = 0; v < vertCount; ++v) {
 			float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();
-
-			if (w3 != 0) {
-				maxWeightsPerVert = Math.max(maxWeightsPerVert, 4);
-			} else if (w2 != 0) {
-				maxWeightsPerVert = Math.max(maxWeightsPerVert, 3);
-			} else if (w1 != 0) {
-				maxWeightsPerVert = Math.max(maxWeightsPerVert, 2);
-			} else if (w0 != 0) {
-				maxWeightsPerVert = Math.max(maxWeightsPerVert, 1);
-			}
-
 			float sum = w0 + w1 + w2 + w3;
 			if (sum != 1f && sum != 0.0f) {
 				weightsFloatData.position(weightsFloatData.position() - 4);
@@ -393,7 +385,6 @@ import java.util.logging.Logger;
 			}
 		}
 		weightsFloatData.rewind();
-		return maxWeightsPerVert;
 	}
 
 	@Override

+ 1 - 2
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ModifierHelper.java

@@ -140,8 +140,7 @@ public class ModifierHelper extends AbstractBlenderHelper {
 		if (pAction.isNotNull()) {
 			Structure action = pAction.fetchData(blenderContext.getInputStream()).get(0);
 			List<Structure> actionChannels = ((Structure) action.getFieldValue("chanbase")).evaluateListBase(blenderContext);
-			if (actionChannels.size() == 1) {// object's animtion action has
-												// only one channel
+			if (actionChannels.size() == 1) {// object's animtion action has only one channel
 				Pointer pChannelIpo = (Pointer) actionChannels.get(0).getFieldValue("ipo");
 				Structure ipoStructure = pChannelIpo.fetchData(blenderContext.getInputStream()).get(0);
 				Ipo ipo = ipoHelper.fromIpoStructure(ipoStructure, blenderContext);

+ 14 - 0
engine/src/blender/com/jme3/scene/plugins/blender/textures/CombinedTexture.java

@@ -215,6 +215,20 @@ public class CombinedTexture {
 		return textureDatas.size();
 	}
 
+	/**
+	 * @return <b>true</b> if the texture has at least one generated texture component and <b>false</b> otherwise
+	 */
+	public boolean hasGeneratedTextures() {
+		if(textureDatas != null) {
+			for(TextureData textureData : textureDatas) {
+				if(textureData.texture instanceof GeneratedTexture) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+	
 	/**
 	 * This method merges two given textures. The result is stored in the
 	 * 'target' texture.

+ 3 - 1
engine/src/blender/com/jme3/scene/plugins/blender/textures/GeneratedTexture.java

@@ -107,8 +107,10 @@ import java.util.TreeSet;
 				return o1.faceIndex - o2.faceIndex;
 			}
 		});
+		int[] indices = new int[3];
 		for (int i = 0; i < mesh.getTriangleCount(); ++i) {
-			triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, blenderContext));
+			mesh.getTriangle(i, indices);
+			triangleTextureElements.add(new TriangleTextureElement(i, boundingBox, this, uvsArray, indices, blenderContext));
 		}
 		return new TriangulatedTexture(triangleTextureElements, blenderContext);
 	}

+ 4 - 5
engine/src/blender/com/jme3/scene/plugins/blender/textures/TriangulatedTexture.java

@@ -504,7 +504,7 @@ import jme3tools.converters.ImageToAwt;
 		 * @param blenderContext
 		 *            the blender context
 		 */
-		public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, BlenderContext blenderContext) {
+		public TriangleTextureElement(int faceIndex, BoundingBox boundingBox, GeneratedTexture texture, Vector3f[] uv, int[] uvIndices, BlenderContext blenderContext) {
 			this.faceIndex = faceIndex;
 
 			// compute the face vertices from the UV coordinates
@@ -512,11 +512,10 @@ import jme3tools.converters.ImageToAwt;
 			float height = boundingBox.getYExtent() * 2;
 			float depth = boundingBox.getZExtent() * 2;
 
-			int uvIndex = faceIndex * 3;
 			Vector3f min = boundingBox.getMin(null);
-			Vector3f v1 = min.add(uv[uvIndex].x * width, uv[uvIndex].y * height, uv[uvIndex].z * depth);
-			Vector3f v2 = min.add(uv[uvIndex + 1].x * width, uv[uvIndex + 1].y * height, uv[uvIndex + 1].z * depth);
-			Vector3f v3 = min.add(uv[uvIndex + 2].x * width, uv[uvIndex + 2].y * height, uv[uvIndex + 2].z * depth);
+			Vector3f v1 = min.add(uv[uvIndices[0]].x * width, uv[uvIndices[0]].y * height, uv[uvIndices[0]].z * depth);
+			Vector3f v2 = min.add(uv[uvIndices[1]].x * width, uv[uvIndices[1]].y * height, uv[uvIndices[1]].z * depth);
+			Vector3f v3 = min.add(uv[uvIndices[2]].x * width, uv[uvIndices[2]].y * height, uv[uvIndices[2]].z * depth);
 
 			// get the rectangle envelope for the triangle
 			RectangleEnvelope envelope = this.getTriangleEnvelope(v1, v2, v3);