فهرست منبع

Armature loading refactoring and bugfixing.
- animations should now load properly
- armature loading moved to ArmatureModifier (no bones are touched inside MeshHelper so the code should be a little more clear now)

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

Kae..pl 14 سال پیش
والد
کامیت
3402f51824

+ 26 - 0
engine/src/blender/com/jme3/scene/plugins/blender/DataRepository.java

@@ -49,6 +49,7 @@ import com.jme3.scene.plugins.blender.file.DnaBlockData;
 import com.jme3.scene.plugins.blender.file.FileBlockHeader;
 import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.materials.MaterialContext;
+import com.jme3.scene.plugins.blender.meshes.MeshHelper.VertexData;
 import com.jme3.scene.plugins.blender.modifiers.Modifier;
 
 /**
@@ -89,6 +90,8 @@ public class DataRepository {
     protected Map<Long, List<Modifier>> modifiers = new HashMap<Long, List<Modifier>>();
     /** A list of constraints for the specified object. */
     protected Map<Long, List<Constraint>> constraints = new HashMap<Long, List<Constraint>>();
+    /** Vertex data for a mesh specified by OMA. */
+    protected Map<Long, VertexData> vertexData = new HashMap<Long, VertexData>();
     /** A map of material contexts. */
     protected Map<Material, MaterialContext> materialContexts = new HashMap<Material, MaterialContext>();
     /** A map og helpers that perform loading. */
@@ -398,6 +401,29 @@ public class DataRepository {
     	return constraints.get(objectOMA);
     }
     
+	/**
+	 * This method sets the vertex data for the specified mesh. Attention!!! If
+	 * vertex data is already set it will be overwritten.
+	 * @param meshOMA
+	 *        old memeory address of mesh
+	 * @param vertexData
+	 *        mesh's vertex data
+	 */
+    public void setVertexData(Long meshOMA, VertexData vertexData) {
+    	this.vertexData.put(meshOMA, vertexData);
+    }
+    
+	/**
+	 * This method returns vertex data for a mesh with the specified old memory
+	 * address. If no data is registered then null is returned.
+	 * @param meshOMA
+	 *        old memeory address of mesh
+	 * @return mesh's vertex data
+	 */
+    public VertexData getVertexData(Long meshOMA) {
+    	return vertexData.get(meshOMA);
+    }
+    
 	/**
 	 * This method sets the material context for the given material.
 	 * If the context is already set it will be replaced.

+ 8 - 49
engine/src/blender/com/jme3/scene/plugins/blender/animations/ArmatureHelper.java

@@ -31,7 +31,6 @@
  */
 package com.jme3.scene.plugins.blender.animations;
 
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -41,12 +40,8 @@ import java.util.logging.Logger;
 
 import com.jme3.animation.Bone;
 import com.jme3.animation.BoneTrack;
-import com.jme3.animation.Skeleton;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Vector3f;
-import com.jme3.scene.Mesh;
-import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.DataRepository;
 import com.jme3.scene.plugins.blender.curves.BezierCurve;
@@ -100,27 +95,6 @@ public class ArmatureHelper extends AbstractBlenderHelper {
         return result;
     }
 
-    /**
-     * This method reads the bones and returns an empty skeleton. Bones should be assigned later.
-     * @param structure
-     *        armature structure
-     * @param dataRepository
-     *        the data repository
-     * @return an empty skeleton, bones are stored within the helper object
-     * @throws BlenderFileException
-     *         this exception is thrown when the blender file is somehow corrupted
-     */
-    public Skeleton toArmature(Structure structure, DataRepository dataRepository) throws BlenderFileException {
-        LOGGER.log(Level.INFO, "Converting structure to Armature!");
-        Structure bonebase = (Structure) structure.getFieldValue("bonebase");
-        List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);
-        for (Structure boneStructure : bonesStructures) {
-            BoneTransformationData rootBoneTransformationData = this.readBoneAndItsChildren(boneStructure, null, dataRepository);
-            boneDataRoots.add(rootBoneTransformationData);
-        }
-        return new Skeleton();//bones are assigned later
-    }
-
     /**
      * This method returns a map where the key is the object's group index that is used by a bone and the key is the
      * bone index in the armature.
@@ -135,7 +109,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
         if (bonesMap != null && bonesMap.size() != 0) {
             result = new HashMap<Integer, Integer>();
             List<Structure> deformGroups = defBaseStructure.evaluateListBase(dataRepository);//bDeformGroup
-            int groupIndex = 0;//TODO: consider many armatures attached to one object in the future !!!
+            int groupIndex = 0;
             for (Structure deformGroup : deformGroups) {
                 String deformGroupName = deformGroup.getFieldValue("name").toString();
                 Integer boneIndex = bonesMap.get(deformGroupName);
@@ -179,7 +153,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
      *         this exception is thrown when the blender file is somehow corrupted
      */
     @SuppressWarnings("unchecked")
-    protected BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {
+    public BoneTransformationData readBoneAndItsChildren(Structure boneStructure, BoneTransformationData parent, DataRepository dataRepository) throws BlenderFileException {
         String name = boneStructure.getFieldValue("name").toString();
         Bone bone = new Bone(name);
         int bonesAmount = bonesOMAs.size();
@@ -230,6 +204,10 @@ public class ArmatureHelper extends AbstractBlenderHelper {
         }
     }
 
+    public void addBoneDataRoot(BoneTransformationData dataRoot) {
+    	this.boneDataRoots.add(dataRoot);
+    }
+    
     /**
      * This method returns bone transformation data for the bone of a given index.
      * @param index
@@ -252,7 +230,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
      * This class holds the data needed later for bone transformation calculation and to bind parent with children.
      * @author Marcin Roguski
      */
-    private static class BoneTransformationData {
+    public static class BoneTransformationData {
 
         /** Inverse matrix of bone's parent bone. */
         private Matrix4f totalInverseBoneParentMatrix;
@@ -299,7 +277,7 @@ public class ArmatureHelper extends AbstractBlenderHelper {
      *        additional bone transformation which indicates it's mesh parent and armature object transformations
      * @return
      */
-    public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {//TODO: consider many skeletons ???
+    public Bone[] buildBonesStructure(Long armatureOMA, Matrix4f additionalRootBoneTransformation) {
         List<Bone> bones = new ArrayList<Bone>(boneDataRoots.size() + 1);
         bones.add(new Bone(""));
         for (BoneTransformationData btd : boneDataRoots) {
@@ -308,25 +286,6 @@ public class ArmatureHelper extends AbstractBlenderHelper {
         return bones.toArray(new Bone[bones.size()]);
     }
 
-    /**
-     * This method assigns an immovable bone to vertices that have no bone assigned. They have the bone index with the
-     * value -1.
-     * @param immovableBoneIndex
-     *        the ondex of immovable bone
-     * @param meshes
-     *        a list of meshes whose vertices will be assigned to immovable bone
-     */
-    public void assignBoneToOrphanedVertices(byte immovableBoneIndex, Mesh[] meshes) {
-        //bone indices are common for all the object's meshes (vertex indices specify which are to be used)
-        VertexBuffer boneIndices = meshes[0].getBuffer(Type.BoneIndex);//common buffer to all the meshes
-        ByteBuffer data = (ByteBuffer) boneIndices.getData();
-        for (int i = 0; i < boneIndices.getNumElements(); ++i) {
-            if (data.get(i) == -1) {
-                data.put(i, immovableBoneIndex);
-            }
-        }
-    }
-
     @Override
     public void clearState() {
         bonesMap.clear();

+ 20 - 170
engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java

@@ -31,7 +31,6 @@
  */
 package com.jme3.scene.plugins.blender.meshes;
 
-import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -41,9 +40,6 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import com.jme3.asset.BlenderKey.FeaturesToLoad;
-import com.jme3.bounding.BoundingBox;
-import com.jme3.bounding.BoundingSphere;
-import com.jme3.bounding.BoundingVolume;
 import com.jme3.material.Material;
 import com.jme3.math.FastMath;
 import com.jme3.math.Vector2f;
@@ -58,7 +54,6 @@ import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.DataRepository;
 import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 import com.jme3.scene.plugins.blender.file.DynamicArray;
 import com.jme3.scene.plugins.blender.file.Pointer;
@@ -77,8 +72,6 @@ import com.jme3.util.BufferUtils;
  * @author Marcin Roguski (Kaelthas)
  */
 public class MeshHelper extends AbstractBlenderHelper {
-	protected static final int	MAXIMUM_WEIGHTS_PER_VERTEX	= 4;	// have no idea why 4, could someone please explain ?
-
 	/**
 	 * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
 	 * versions.
@@ -250,6 +243,8 @@ public class MeshHelper extends AbstractBlenderHelper {
 				}
 			}
 		}
+		dataRepository.setVertexData(structure.getOldMemoryAddress(), new VertexData(vertexList, vertexReferenceMap));
+		
 		Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
 
 		// reading vertices groups (from the parent)
@@ -262,18 +257,6 @@ public class MeshHelper extends AbstractBlenderHelper {
 			verticesGroups[defIndex++] = def.getFieldValue("name").toString();
 		}
 
-		// vertices bone weights and indices
-		ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
-		Structure defBase = (Structure) parent.getFieldValue("defbase");
-		Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository);
-
-		VertexBuffer verticesWeights = null, verticesWeightsIndices = null;
-		int[] bonesGroups = new int[] { 0 };
-		VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(structure, vertexList.size(), bonesGroups,
-				vertexReferenceMap, groupToBoneIndexMap, dataRepository);
-		verticesWeights = boneWeightsAndIndex[0];
-		verticesWeightsIndices = boneWeightsAndIndex[1];
-
 		// reading materials
 		MaterialHelper materialHelper = dataRepository.getHelper(MaterialHelper.class);
 		Material[] materials = null;
@@ -333,13 +316,6 @@ public class MeshHelper extends AbstractBlenderHelper {
 				mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);
 			}
 
-			// setting weights for bones
-			if (verticesWeights != null) {
-				mesh.setMaxNumWeights(bonesGroups[0]);
-				mesh.setBuffer(verticesWeights);
-				mesh.setBuffer(verticesWeightsIndices);
-			}
-
 			// setting faces' normals
 			mesh.setBuffer(normalsBuffer);
 			mesh.setBuffer(normalsBind);
@@ -418,22 +394,6 @@ public class MeshHelper extends AbstractBlenderHelper {
 		return geometries;
 	}
 	
-	/**
-	 * This method returns bounding shpere of a given mesh.
-	 * @param mesh the mesh to read the bounding sphere from
-	 * @return the bounding sphere of the given mesh
-	 */
-	protected BoundingSphere getBoundingSphere(Mesh mesh) {
-		BoundingVolume bv = mesh.getBound();
-		if(bv instanceof BoundingSphere) {
-			return (BoundingSphere)bv;
-		} else if(bv instanceof BoundingBox) {
-			BoundingBox bb = (BoundingBox)bv;
-			return new BoundingSphere(bb.getCenter().subtract(bb.getMin(null)).length(), bb.getCenter());
-		}
-		throw new IllegalStateException("Unknown bounding volume type: " + bv.getClass().getName());
-	}
-
 	/**
 	 * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
 	 * 
@@ -535,136 +495,26 @@ public class MeshHelper extends AbstractBlenderHelper {
 		return vertices;
 	}
 
-	/**
-	 * This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The
-	 * second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).
-	 * 
-	 * @param meshStructure
-	 *            the mesh structure object
-	 * @param vertexListSize
-	 *            a number of vertices in the model
-	 * @param bonesGroups
-	 *            this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to
-	 *            MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
-	 * @param vertexReferenceMap
-	 *            this reference map allows to map the original vertices read from blender to vertices that are really in the model; one
-	 *            vertex may appear several times in the result model
-	 * @param groupToBoneIndexMap
-	 *            this object maps the group index (to which a vertices in blender belong) to bone index of the model
-	 * @param dataRepository
-	 *            the data repository
-	 * @return arrays of vertices weights and their bone indices and (as an outpot parameter) the maximum amount of weights for a vertex
-	 * @throws BlenderFileException
-	 *             this exception is thrown when the blend file structure is somehow invalid or corrupted
-	 */
-	public VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,
-			Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, DataRepository dataRepository)
-			throws BlenderFileException {
-		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(dataRepository.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(dataRepository.getInputStream());
-					for (Structure deformWeight : dw) {
-						Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
-						if (boneIndex != null) {// null here means that we came accross group that has no bone attached to
-							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) {
-								// all indices are always assigned to 0-indexed bone
-								// weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, 1.0f);
-								// indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, (byte)0);
-								// if(weight != 0.0f) {
-								weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
-								indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
-								// }
-							}
-						}
-						++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;
-			}
-		} 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
-			for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
-				// we apply the weight to all referenced vertices
-				for (Integer index : vertexIndexList) {
-					weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
-					indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
-				}
-			}
-		}
-
-		bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
-		VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
-		verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
-
-		VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);
-		verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);
-		return new VertexBuffer[] { verticesWeights, verticesWeightsIndices };
-	}
-
-	/**
-	 * Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.
-	 */
-	protected int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
-		int maxWeightsPerVert = 0;
-		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);
-				// compute new vals based on sum
-				float sumToB = 1f / sum;
-				weightsFloatData.put(w0 * sumToB);
-				weightsFloatData.put(w1 * sumToB);
-				weightsFloatData.put(w2 * sumToB);
-				weightsFloatData.put(w3 * sumToB);
-			}
-		}
-		weightsFloatData.rewind();
-
-		// mesh.setMaxNumWeights(maxWeightsPerVert);
-		return maxWeightsPerVert;
-	}
-	
 	@Override
 	public boolean shouldBeLoaded(Structure structure, DataRepository dataRepository) {
 		return true;
 	}
+	
+	public static class VertexData {
+		private List<Vector3f> vertexList;
+		private Map<Integer, List<Integer>> vertexReferenceMap;
+		
+		public VertexData(List<Vector3f> vertexList, Map<Integer, List<Integer>> vertexReferenceMap) {
+			this.vertexList = vertexList;
+			this.vertexReferenceMap = vertexReferenceMap;
+		}
+		
+		public List<Vector3f> getVertexList() {
+			return vertexList;
+		}
+		
+		public Map<Integer, List<Integer>> getVertexReferenceMap() {
+			return vertexReferenceMap;
+		}
+	}
 }

+ 203 - 75
engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java

@@ -1,16 +1,17 @@
 package com.jme3.scene.plugins.blender.modifiers;
 
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map.Entry;
+import java.util.Map;
 import java.util.Set;
 
 import com.jme3.animation.AnimControl;
 import com.jme3.animation.Animation;
 import com.jme3.animation.Bone;
 import com.jme3.animation.BoneAnimation;
-import com.jme3.animation.BoneTrack;
 import com.jme3.animation.Skeleton;
 import com.jme3.animation.SkeletonControl;
 import com.jme3.math.Matrix4f;
@@ -18,16 +19,23 @@ import com.jme3.scene.Geometry;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.VertexBuffer.Type;
+import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.scene.plugins.blender.DataRepository;
 import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
 import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
+import com.jme3.scene.plugins.blender.animations.ArmatureHelper.BoneTransformationData;
 import com.jme3.scene.plugins.blender.constraints.Constraint;
 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
 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.meshes.MeshHelper.VertexData;
 import com.jme3.scene.plugins.blender.objects.ObjectHelper;
 import com.jme3.scene.plugins.ogre.AnimData;
+import com.jme3.util.BufferUtils;
 
 /**
  * This modifier allows to add bone animation to the object.
@@ -35,7 +43,19 @@ import com.jme3.scene.plugins.ogre.AnimData;
  * @author Marcin Roguski (Kaelthas)
  */
 /* package */class ArmatureModifier extends Modifier {
-
+	private static final int	MAXIMUM_WEIGHTS_PER_VERTEX	= 4;	// have no idea why 4, could someone please explain ?
+	
+	/** Old memory address of the armature's object. */
+	private Long armatureObjectOMA;
+	/** 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 is only temporary. It will be removed when object
 	 * animation is implemented in jme. TODO!!!!!!!
@@ -61,29 +81,31 @@ import com.jme3.scene.plugins.ogre.AnimData;
 		Pointer pArmatureObject = (Pointer) modifierStructure.getFieldValue("object");
 		if (pArmatureObject.isNotNull()) {
 			ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
-			Structure armatureObject = (Structure) dataRepository.getLoadedFeature(pArmatureObject.getOldMemoryAddress(),
-							LoadedFeatureDataType.LOADED_STRUCTURE);
-			if (armatureObject == null) {// we check this first not to fetch the
-											// structure unnecessary
-				armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);
-				objectHelper.toObject(armatureObject, dataRepository);
-			}
-			additionalData = armatureObject.getOldMemoryAddress();
-			ArmatureHelper armatureHelper = dataRepository
-					.getHelper(ArmatureHelper.class);
+			ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
+			
+			Structure armatureObject = pArmatureObject.fetchData(dataRepository.getInputStream()).get(0);
+			this.armatureObjectOMA = armatureObject.getOldMemoryAddress();
 
-			// changing bones matrices so that they fit the current object (that
-			// is why we need a copy of a skeleton)
+			//read skeleton
+			// changing bones matrices so that they fit the current object
+			Structure armatureStructure = ((Pointer)armatureObject.getFieldValue("data")).fetchData(dataRepository.getInputStream()).get(0);
+			Structure bonebase = (Structure) armatureStructure.getFieldValue("bonebase");
+	        List<Structure> bonesStructures = bonebase.evaluateListBase(dataRepository);
+	        for (Structure boneStructure : bonesStructures) {
+	            BoneTransformationData rootBoneTransformationData = armatureHelper.readBoneAndItsChildren(boneStructure, null, dataRepository);
+	            armatureHelper.addBoneDataRoot(rootBoneTransformationData);
+	        }
 			Matrix4f armatureObjectMatrix = objectHelper.getTransformationMatrix(armatureObject);
 			Matrix4f inverseMeshObjectMatrix = objectHelper.getTransformationMatrix(objectStructure).invert();
 			Matrix4f additionalRootBoneTransformation = inverseMeshObjectMatrix.multLocal(armatureObjectMatrix);
 			Bone[] bones = armatureHelper.buildBonesStructure(Long.valueOf(0L), additionalRootBoneTransformation);
 
-			// setting the bones structure inside the skeleton (thus completing
-			// its loading)
-			Skeleton skeleton = new Skeleton(bones);
-			dataRepository.addLoadedFeatures(armatureObject.getOldMemoryAddress(), armatureObject.getName(), armatureObject, skeleton);
-
+			//read mesh indexes
+			Structure meshStructure = ((Pointer)objectStructure.getFieldValue("data")).fetchData(dataRepository.getInputStream()).get(0);
+			this.meshOMA = meshStructure.getOldMemoryAddress();
+			this.readVerticesWeightsData(objectStructure, meshStructure, dataRepository);
+			
+			//read animations
 			String objectName = objectStructure.getName();
 			Set<String> animationNames = dataRepository.getBlenderKey().getAnimationNames(objectName);
 			if (animationNames != null && animationNames.size() > 0) {
@@ -106,17 +128,29 @@ import com.jme3.scene.plugins.ogre.AnimData;
 			}
 		}
 	}
-
+	
 	@Override
+	@SuppressWarnings("unchecked")
 	public Node apply(Node node, DataRepository dataRepository) {
 		if(jmeModifierRepresentation == null) {
 			return node;
 		}
+		
+		// setting weights for bones
+		List<Geometry> geomList = (List<Geometry>) dataRepository.getLoadedFeature(this.meshOMA, LoadedFeatureDataType.LOADED_FEATURE);
+		for(Geometry geom : geomList) {
+			Mesh mesh = geom.getMesh();
+			if (this.verticesWeights != null) {
+				mesh.setMaxNumWeights(this.boneGroups);
+				mesh.setBuffer(this.verticesWeights);
+				mesh.setBuffer(this.verticesWeightsIndices);
+			}
+		}
+		
 		AnimData ad = (AnimData) jmeModifierRepresentation;
 		ArrayList<Animation> animList = ad.anims;
-		Long modifierArmatureObject = (Long) additionalData;
 		if (animList != null && animList.size() > 0) {
-			List<Constraint> constraints = dataRepository.getConstraints(modifierArmatureObject);
+			List<Constraint> constraints = dataRepository.getConstraints(this.armatureObjectOMA);
 			HashMap<String, Animation> anims = new HashMap<String, Animation>();
 			for (int i = 0; i < animList.size(); ++i) {
 				BoneAnimation boneAnimation = (BoneAnimation) animList.get(i).clone();
@@ -146,72 +180,166 @@ import com.jme3.scene.plugins.ogre.AnimData;
 
 			// applying the control to the node
 			SkeletonControl skeletonControl = new SkeletonControl(meshes, ad.skeleton);
-			AnimControl control = node.getControl(AnimControl.class);
-
-			if (control == null) {
-				control = new AnimControl(ad.skeleton);
-			} else {
-				// merging skeletons
-				Skeleton controlSkeleton = control.getSkeleton();
-				int boneIndexIncrease = controlSkeleton.getBoneCount();
-				Skeleton skeleton = this.merge(controlSkeleton, ad.skeleton);
-
-				// merging animations
-				HashMap<String, Animation> animations = new HashMap<String, Animation>();
-				for (String animationName : control.getAnimationNames()) {
-					animations.put(animationName,
-							control.getAnim(animationName));
-				}
-				for (Entry<String, Animation> animEntry : anims.entrySet()) {
-					BoneAnimation ba = (BoneAnimation) animEntry.getValue();
-					for (int i = 0; i < ba.getTracks().length; ++i) {
-						BoneTrack bt = ba.getTracks()[i];
-						int newBoneIndex = bt.getTargetBoneIndex()
-								+ boneIndexIncrease;
-						ba.getTracks()[i] = new BoneTrack(newBoneIndex,
-								bt.getTimes(), bt.getTranslations(),
-								bt.getRotations(), bt.getScales());
-					}
-					animations.put(animEntry.getKey(), animEntry.getValue());
-				}
+			AnimControl control = new AnimControl(ad.skeleton);
 
-				// replacing the control
-				node.removeControl(control);
-				control = new AnimControl(skeleton);
-			}
 			control.setAnimations(anims);
 			node.addControl(control);
 			node.addControl(skeletonControl);
 		}
 		return node;
 	}
+	
+	/**
+	 * This method reads mesh indexes
+	 * @param objectStructure structure of the object that has the armature modifier applied
+	 * @param meshStructure the structure of the object's mesh
+	 * @param dataRepository the data repository
+	 * @throws BlenderFileException
+	 * 		   this exception is thrown when the blend file structure is somehow invalid or corrupted
+	 */
+	private void readVerticesWeightsData(Structure objectStructure, Structure meshStructure, DataRepository dataRepository) throws BlenderFileException {
+		ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
+		Structure defBase = (Structure) objectStructure.getFieldValue("defbase");
+		Map<Integer, Integer> groupToBoneIndexMap = armatureHelper.getGroupToBoneIndexMap(defBase, dataRepository);
 
-	@Override
-	public String getType() {
-		return Modifier.ARMATURE_MODIFIER_DATA;
+		int[] bonesGroups = new int[] { 0 };
+		
+		VertexData vertexData = dataRepository.getVertexData(meshStructure.getOldMemoryAddress());
+		
+		VertexBuffer[] boneWeightsAndIndex = this.getBoneWeightAndIndexBuffer(meshStructure, vertexData.getVertexList().size(), bonesGroups,
+				vertexData.getVertexReferenceMap(), groupToBoneIndexMap, dataRepository);
+		this.verticesWeights = boneWeightsAndIndex[0];
+		this.verticesWeightsIndices = boneWeightsAndIndex[1];
+		this.boneGroups = bonesGroups[0];
 	}
 
 	/**
-	 * This method merges two skeletons into one. I assume that each skeleton's
-	 * 0-indexed bone is objectAnimationBone so only one such bone should be
-	 * placed in the result
+	 * This method returns an array of size 2. The first element is a vertex buffer holding bone weights for every vertex in the model. The
+	 * second element is a vertex buffer holding bone indices for vertices (the indices of bones the vertices are assigned to).
 	 * 
-	 * @param s1
-	 *            first skeleton
-	 * @param s2
-	 *            second skeleton
-	 * @return merged skeleton
+	 * @param meshStructure
+	 *            the mesh structure object
+	 * @param vertexListSize
+	 *            a number of vertices in the model
+	 * @param bonesGroups
+	 *            this is an output parameter, it should be a one-sized array; the maximum amount of weights per vertex (up to
+	 *            MAXIMUM_WEIGHTS_PER_VERTEX) is stored there
+	 * @param vertexReferenceMap
+	 *            this reference map allows to map the original vertices read from blender to vertices that are really in the model; one
+	 *            vertex may appear several times in the result model
+	 * @param groupToBoneIndexMap
+	 *            this object maps the group index (to which a vertices in blender belong) to bone index of the model
+	 * @param dataRepository
+	 *            the data repository
+	 * @return arrays of vertices weights and their bone indices and (as an output parameter) the maximum amount of weights for a vertex
+	 * @throws BlenderFileException
+	 *             this exception is thrown when the blend file structure is somehow invalid or corrupted
 	 */
-	protected Skeleton merge(Skeleton s1, Skeleton s2) {
-		List<Bone> bones = new ArrayList<Bone>(s1.getBoneCount()
-				+ s2.getBoneCount());
-		for (int i = 0; i < s1.getBoneCount(); ++i) {
-			bones.add(s1.getBone(i));
+	private VertexBuffer[] getBoneWeightAndIndexBuffer(Structure meshStructure, int vertexListSize, int[] bonesGroups,
+			Map<Integer, List<Integer>> vertexReferenceMap, Map<Integer, Integer> groupToBoneIndexMap, DataRepository dataRepository)
+			throws BlenderFileException {
+		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(dataRepository.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(dataRepository.getInputStream());
+					for (Structure deformWeight : dw) {
+						Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
+						if (boneIndex != null) {// null here means that we came accross group that has no bone attached to
+							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;
+					}
+				} else {
+					for (Integer index : vertexIndices) {
+						weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
+						indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
+					}
+				}
+				++vertexIndex;
+			}
+		} 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
+			for (List<Integer> vertexIndexList : vertexReferenceMap.values()) {
+				// we apply the weight to all referenced vertices
+				for (Integer index : vertexIndexList) {
+					weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, 1.0f);
+					indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX, (byte) 0);
+				}
+			}
 		}
-		for (int i = 1; i < s2.getBoneCount(); ++i) {// ommit
-														// objectAnimationBone
-			bones.add(s2.getBone(i));
+
+		bonesGroups[0] = this.endBoneAssigns(vertexListSize, weightsFloatData);
+		VertexBuffer verticesWeights = new VertexBuffer(Type.BoneWeight);
+		verticesWeights.setupData(Usage.CpuOnly, bonesGroups[0], Format.Float, weightsFloatData);
+
+		VertexBuffer verticesWeightsIndices = new VertexBuffer(Type.BoneIndex);
+		verticesWeightsIndices.setupData(Usage.CpuOnly, bonesGroups[0], Format.UnsignedByte, indicesData);
+		return new VertexBuffer[] { verticesWeights, verticesWeightsIndices };
+	}
+	
+	/**
+	 * Normalizes weights if needed and finds largest amount of weights used for all vertices in the buffer.
+	 * @param vertCount amount of vertices
+	 * @param weightsFloatData weights for vertices
+	 */
+	private int endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
+		int maxWeightsPerVert = 0;
+		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);
+				// compute new vals based on sum
+				float sumToB = 1f / sum;
+				weightsFloatData.put(w0 * sumToB);
+				weightsFloatData.put(w1 * sumToB);
+				weightsFloatData.put(w2 * sumToB);
+				weightsFloatData.put(w3 * sumToB);
+			}
 		}
-		return new Skeleton(bones.toArray(new Bone[bones.size()]));
+		weightsFloatData.rewind();
+
+		// mesh.setMaxNumWeights(maxWeightsPerVert);
+		return maxWeightsPerVert;
+	}
+	
+	@Override
+	public String getType() {
+		return Modifier.ARMATURE_MODIFIER_DATA;
 	}
 }

+ 8 - 14
engine/src/blender/com/jme3/scene/plugins/blender/objects/ObjectHelper.java

@@ -55,7 +55,6 @@ import com.jme3.scene.Spatial.CullHint;
 import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
 import com.jme3.scene.plugins.blender.DataRepository;
 import com.jme3.scene.plugins.blender.DataRepository.LoadedFeatureDataType;
-import com.jme3.scene.plugins.blender.animations.ArmatureHelper;
 import com.jme3.scene.plugins.blender.cameras.CameraHelper;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
 import com.jme3.scene.plugins.blender.curves.CurvesHelper;
@@ -141,9 +140,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
 		}
 
 		dataRepository.pushParent(objectStructure);
-
 		ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);
-		ArmatureHelper armatureHelper = dataRepository.getHelper(ArmatureHelper.class);
 
 		//get object data
 		int type = ((Number)objectStructure.getFieldValue("type")).intValue();
@@ -161,7 +158,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
 		Pointer pParent = (Pointer)objectStructure.getFieldValue("parent");
 		Object parent = dataRepository.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedFeatureDataType.LOADED_FEATURE);
 		if(parent == null && pParent.isNotNull()) {
-			Structure parentStructure = pParent.fetchData(dataRepository.getInputStream()).get(0);//TODO: moze byc wiecej rodzicow
+			Structure parentStructure = pParent.fetchData(dataRepository.getInputStream()).get(0);//TODO: what if there are more parents ??
 			parent = this.toObject(parentStructure, dataRepository);
 		}
 
@@ -263,10 +260,7 @@ public class ObjectHelper extends AbstractBlenderHelper {
 					}
 					break;
 				case OBJECT_TYPE_ARMATURE:
-					LOGGER.log(Level.INFO, "Importing armature.");
-					Pointer pArmature = (Pointer)objectStructure.getFieldValue("data");
-					List<Structure> armaturesArray = pArmature.fetchData(dataRepository.getInputStream());//TODO: moze byc wiecej???
-					result = armatureHelper.toArmature(armaturesArray.get(0), dataRepository);
+					//Do not do anything, the object with all needed data is loaded when armature modifier loads
 					break;
 				default:
 					LOGGER.log(Level.WARNING, "Unknown object type: {0}", type);
@@ -275,13 +269,13 @@ public class ObjectHelper extends AbstractBlenderHelper {
 			dataRepository.popParent();
 		}
 		
-		//reading custom properties
-		Properties properties = this.loadProperties(objectStructure, dataRepository);
-		if(result instanceof Spatial && properties != null && properties.getValue() != null) {
-			((Spatial)result).setUserData("properties", properties);
-		}
-		
 		if(result != null) {
+			//reading custom properties
+			Properties properties = this.loadProperties(objectStructure, dataRepository);
+			if(result instanceof Spatial && properties != null && properties.getValue() != null) {
+				((Spatial)result).setUserData("properties", properties);
+			}
+			
 			dataRepository.addLoadedFeatures(objectStructure.getOldMemoryAddress(), name, objectStructure, result);
 		}
 		return result;