Prechádzať zdrojové kódy

Bugfixes:
- fixed issues with bones transformation applying and reading in world space
- fixed issue with lack of animation track for some bones whose constraints affected the animation

Refactoring:
- improved (and simplified) bone loading

Features:
- added basic implementation of the IK constraint (still a lot to be done but works for simplest cases)

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

Kae..pl 12 rokov pred
rodič
commit
0921540124
18 zmenil súbory, kde vykonal 328 pridanie a 123 odobranie
  1. 15 0
      engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java
  2. 2 25
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java
  3. 27 10
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java
  4. 16 13
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java
  5. 60 40
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/SimulationNode.java
  6. 0 9
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java
  7. 24 7
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java
  8. 9 3
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java
  9. 1 1
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java
  10. 115 0
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java
  11. 10 2
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java
  12. 10 3
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java
  13. 2 1
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java
  14. 8 1
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java
  15. 9 2
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java
  16. 9 2
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java
  17. 9 3
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java
  18. 2 1
      engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java

+ 15 - 0
engine/src/blender/com/jme3/scene/plugins/blender/animations/BoneContext.java

@@ -34,6 +34,8 @@ public class BoneContext {
     private BlenderContext        blenderContext;
     /** The OMA of the bone's armature object. */
     private Long                  armatureObjectOMA;
+    /** The OMA of the model that owns the bone's skeleton. */
+    private Long skeletonOwnerOma;
     /** The structure of the bone. */
     private Structure             boneStructure;
     /** Bone's name. */
@@ -128,6 +130,7 @@ public class BoneContext {
      * @return newly created bone
      */
     public Bone buildBone(List<Bone> bones, Long skeletonOwnerOma, BlenderContext blenderContext) {
+        this.skeletonOwnerOma = skeletonOwnerOma;
         Long boneOMA = boneStructure.getOldMemoryAddress();
         bone = new Bone(boneName);
         bones.add(bone);
@@ -183,6 +186,13 @@ public class BoneContext {
     public Long getArmatureObjectOMA() {
         return armatureObjectOMA;
     }
+    
+    /**
+     * @return the OMA of the model that owns the bone's skeleton
+     */
+    public Long getSkeletonOwnerOma() {
+        return skeletonOwnerOma;
+    }
 
     /**
      * @return the skeleton the bone of this context belongs to
@@ -200,4 +210,9 @@ public class BoneContext {
     private boolean is(int flagMask) {
         return (flag & flagMask) != 0;
     }
+    
+    @Override
+    public String toString() {
+        return "BoneContext: " + bone.getName();
+    }
 }

+ 2 - 25
engine/src/blender/com/jme3/scene/plugins/blender/constraints/BoneConstraint.java

@@ -3,7 +3,6 @@ package com.jme3.scene.plugins.blender.constraints;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import com.jme3.math.Transform;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
@@ -21,8 +20,6 @@ import com.jme3.scene.plugins.blender.file.Structure;
 /* package */class BoneConstraint extends Constraint {
     private static final Logger LOGGER = Logger.getLogger(BoneConstraint.class.getName());
 
-    protected boolean           isNodeTarget;
-
     /**
      * The bone constraint constructor.
      * 
@@ -45,14 +42,14 @@ import com.jme3.scene.plugins.blender.file.Structure;
     public boolean validate() {
         if (targetOMA != null) {
             Spatial nodeTarget = (Spatial) blenderContext.getLoadedFeature(targetOMA, LoadedFeatureDataType.LOADED_FEATURE);
-            if(nodeTarget == null) {
+            if (nodeTarget == null) {
                 LOGGER.log(Level.WARNING, "Cannot find target for constraint: {0}.", name);
                 return false;
             }
             // the second part of the if expression verifies if the found node
             // (if any) is an armature node
             if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, nodeTarget) != null) {
-                if(subtargetName.trim().isEmpty()) {
+                if (subtargetName.trim().isEmpty()) {
                     LOGGER.log(Level.WARNING, "No bone target specified for constraint: {0}.", name);
                     return false;
                 }
@@ -63,28 +60,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
                     LOGGER.log(Level.WARNING, "Bone constraint {0} must target bone in the its own skeleton! Targeting bone in another skeleton is not supported!", name);
                     return false;
                 }
-            } else {
-                isNodeTarget = true;
             }
         }
         return true;
     }
-
-    @Override
-    public void apply(int frame) {
-        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
-        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
-        if (targetOMA != null) {
-            if (isNodeTarget) {
-                Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
-                constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
-            } else {
-                Transform targetTransform = constraintHelper.getTransform(targetOMA, subtargetName, targetSpace);
-                constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
-            }
-        } else {
-            constraintDefinition.bake(ownerTransform, null, this.ipo.calculateValue(frame));
-        }
-        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
-    }
 }

+ 27 - 10
engine/src/blender/com/jme3/scene/plugins/blender/constraints/Constraint.java

@@ -1,8 +1,10 @@
 package com.jme3.scene.plugins.blender.constraints;
 
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.jme3.math.Transform;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.animations.Ipo;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
@@ -58,15 +60,15 @@ public abstract class Constraint {
      */
     public Constraint(Structure constraintStructure, Long ownerOMA, Ipo influenceIpo, BlenderContext blenderContext) throws BlenderFileException {
         this.blenderContext = blenderContext;
-        this.name = constraintStructure.getFieldValue("name").toString();
+        name = constraintStructure.getFieldValue("name").toString();
         Pointer pData = (Pointer) constraintStructure.getFieldValue("data");
         if (pData.isNotNull()) {
             Structure data = pData.fetchData(blenderContext.getInputStream()).get(0);
             constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(data, ownerOMA, blenderContext);
             Pointer pTar = (Pointer) data.getFieldValue("tar");
             if (pTar != null && pTar.isNotNull()) {
-                this.targetOMA = pTar.getOldMemoryAddress();
-                this.targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
+                targetOMA = pTar.getOldMemoryAddress();
+                targetSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("tarspace")).byteValue());
                 Object subtargetValue = data.getFieldValue("subtarget");
                 if (subtargetValue != null) {// not all constraint data have the
                                              // subtarget field
@@ -77,10 +79,10 @@ public abstract class Constraint {
             // Null constraint has no data, so create it here
             constraintDefinition = ConstraintDefinitionFactory.createConstraintDefinition(null, null, blenderContext);
         }
-        this.ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
-        this.ipo = influenceIpo;
+        ownerSpace = Space.valueOf(((Number) constraintStructure.getFieldValue("ownspace")).byteValue());
+        ipo = influenceIpo;
         this.ownerOMA = ownerOMA;
-        this.constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+        constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
         LOGGER.log(Level.INFO, "Created constraint: {0} with definition: {1}", new Object[] { name, constraintDefinition });
     }
 
@@ -100,6 +102,13 @@ public abstract class Constraint {
         return constraintDefinition.getConstraintTypeName();
     }
 
+    /**
+     * @return the OMAs of the features whose transform had been altered beside the constraint owner
+     */
+    public Set<Long> getAlteredOmas() {
+        return constraintDefinition.getAlteredOmas();
+    }
+
     /**
      * Performs validation before baking. Checks factors that can prevent
      * constraint from baking that could not be checked during constraint
@@ -107,14 +116,22 @@ public abstract class Constraint {
      */
     public abstract boolean validate();
 
-    public abstract void apply(int frame);
+    /**
+     * Applies the constraint to owner (and in some cases can alter other bones of the skeleton).
+     * @param frame
+     *            the frame of the animation
+     */
+    public void apply(int frame) {
+        Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
+        constraintDefinition.bake(ownerSpace, targetSpace, targetTransform, ipo.calculateValue(frame));
+    }
 
     @Override
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + ((ownerOMA == null) ? 0 : ownerOMA.hashCode());
+        result = prime * result + (name == null ? 0 : name.hashCode());
+        result = prime * result + (ownerOMA == null ? 0 : ownerOMA.hashCode());
         return result;
     }
 
@@ -126,7 +143,7 @@ public abstract class Constraint {
         if (obj == null) {
             return false;
         }
-        if (getClass() != obj.getClass()) {
+        if (this.getClass() != obj.getClass()) {
             return false;
         }
         Constraint other = (Constraint) obj;

+ 16 - 13
engine/src/blender/com/jme3/scene/plugins/blender/constraints/ConstraintHelper.java

@@ -228,11 +228,14 @@ public class ConstraintHelper extends AbstractBlenderHelper {
 
             switch (space) {
                 case CONSTRAINT_SPACE_WORLD:
-                    return new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
+                    Spatial model = (Spatial) blenderContext.getLoadedFeature(targetBoneContext.getSkeletonOwnerOma(), LoadedFeatureDataType.LOADED_FEATURE);
+                    Transform worldTransform = new Transform(bone.getModelSpacePosition(), bone.getModelSpaceRotation(), bone.getModelSpaceScale());
+                    worldTransform.getTranslation().addLocal(model.getWorldTranslation());
+                    worldTransform.getRotation().multLocal(model.getWorldRotation());
+                    worldTransform.getScale().multLocal(model.getWorldScale());
+                    return worldTransform;
                 case CONSTRAINT_SPACE_LOCAL:
-                    Transform localTransform = new Transform(bone.getLocalPosition(), bone.getLocalRotation());
-                    localTransform.setScale(bone.getLocalScale());
-                    return localTransform;
+                    return new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale());
                 case CONSTRAINT_SPACE_POSE:
                     Node nodeWithAnimationControl = blenderContext.getControlledNode(targetBoneContext.getSkeleton());
                     Matrix4f m = this.toMatrix(nodeWithAnimationControl.getWorldTransform());
@@ -313,15 +316,16 @@ public class ConstraintHelper extends AbstractBlenderHelper {
                     bone.setBindTransforms(transform.getTranslation(), transform.getRotation(), transform.getScale());
                     break;
                 case CONSTRAINT_SPACE_WORLD:
-                    Matrix4f boneMatrix = this.toMatrix(transform);
+                    Matrix4f boneMatrixInWorldSpace = this.toMatrix(transform);
+                    Matrix4f invertedModelMatrix = this.toMatrix(this.getTransform(targetBoneContext.getSkeletonOwnerOma(), null, Space.CONSTRAINT_SPACE_WORLD)).invertLocal();
+                    Matrix4f boneMatrixInModelSpace = invertedModelMatrix.mult(boneMatrixInWorldSpace);
                     Bone parent = bone.getParent();
                     if (parent != null) {
-                        Matrix4f invertedParentWorldMatrix = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
-                        boneMatrix = invertedParentWorldMatrix.multLocal(boneMatrix);
-                    }
+                        Matrix4f invertedParentMatrixInModelSpace = this.toMatrix(parent.getModelSpacePosition(), parent.getModelSpaceRotation(), parent.getModelSpaceScale()).invertLocal();
 
-                    boneMatrix = invertedNodeMatrix.multLocal(boneMatrix);
-                    bone.setBindTransforms(boneMatrix.toTranslationVector(), boneMatrix.toRotationQuat(), boneMatrix.toScaleVector());
+                        boneMatrixInModelSpace = invertedParentMatrixInModelSpace.mult(boneMatrixInModelSpace);
+                    }
+                    bone.setBindTransforms(boneMatrixInModelSpace.toTranslationVector(), boneMatrixInModelSpace.toRotationQuat(), boneMatrixInModelSpace.toScaleVector());
                     break;
                 case CONSTRAINT_SPACE_POSE:
                     Matrix4f armatureWorldMatrix = this.toMatrix(feature.getWorldTransform());
@@ -391,11 +395,10 @@ public class ConstraintHelper extends AbstractBlenderHelper {
      * @return 4x4 matrix that represents the given transform
      */
     public Matrix4f toMatrix(Transform transform) {
-        Matrix4f result = Matrix4f.IDENTITY;
         if (transform != null) {
-            result = this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
+            return this.toMatrix(transform.getTranslation(), transform.getRotation(), transform.getScale());
         }
-        return result;
+        return Matrix4f.IDENTITY.clone();
     }
 
     /**

+ 60 - 40
engine/src/blender/com/jme3/scene/plugins/blender/constraints/SimulationNode.java

@@ -2,9 +2,11 @@ package com.jme3.scene.plugins.blender.constraints;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -38,31 +40,33 @@ import com.jme3.util.TempVars;
  * @author Marcin Roguski (Kaelthas)
  */
 public class SimulationNode {
-    private static final Logger       LOGGER       = Logger.getLogger(SimulationNode.class.getName());
+    private static final Logger  LOGGER   = Logger.getLogger(SimulationNode.class.getName());
 
+    /** The blender context. */
+    private BlenderContext       blenderContext;
     /** The name of the node (for debugging purposes). */
-    private String                    name;
+    private String               name;
     /** A list of children for the node (either bones or child spatials). */
-    private List<SimulationNode>      children     = new ArrayList<SimulationNode>();
+    private List<SimulationNode> children = new ArrayList<SimulationNode>();
     /** A list of constraints that the current node has. */
-    private List<Constraint>          constraints;
+    private List<Constraint>     constraints;
     /** A list of node's animations. */
-    private List<Animation>           animations;
+    private List<Animation>      animations;
 
     /** The nodes spatial (if null then the boneContext should be set). */
-    private Spatial                   spatial;
+    private Spatial              spatial;
     /** The skeleton of the bone (not null if the node simulated the bone). */
-    private Skeleton                  skeleton;
+    private Skeleton             skeleton;
     /** Animation controller for the node's feature. */
-    private AnimControl               animControl;
+    private AnimControl          animControl;
 
     /**
      * The star transform of a spatial. Needed to properly reset the spatial to
      * its start position.
      */
-    private Transform                 spatialStartTransform;
+    private Transform            spatialStartTransform;
     /** Star transformations for bones. Needed to properly reset the bones. */
-    private Map<Bone, Transform>      boneStartTransforms;
+    private Map<Bone, Transform> boneStartTransforms;
 
     /**
      * Builds the nodes tree for the given feature. The feature (bone or
@@ -89,12 +93,13 @@ public class SimulationNode {
      *            indicates if the feature is a root bone or root spatial or not
      */
     private SimulationNode(Long featureOMA, BlenderContext blenderContext, boolean rootNode) {
+        this.blenderContext = blenderContext;
         Node spatial = (Node) blenderContext.getLoadedFeature(featureOMA, LoadedFeatureDataType.LOADED_FEATURE);
         if (blenderContext.getMarkerValue(ArmatureHelper.ARMATURE_NODE_MARKER, spatial) != null) {
-            this.skeleton = blenderContext.getSkeleton(featureOMA);
+            skeleton = blenderContext.getSkeleton(featureOMA);
 
             Node nodeWithAnimationControl = blenderContext.getControlledNode(skeleton);
-            this.animControl = nodeWithAnimationControl.getControl(AnimControl.class);
+            animControl = nodeWithAnimationControl.getControl(AnimControl.class);
 
             boneStartTransforms = new HashMap<Bone, Transform>();
             for (int i = 0; i < skeleton.getBoneCount(); ++i) {
@@ -106,10 +111,10 @@ public class SimulationNode {
                 throw new IllegalStateException("Given spatial must be a root node!");
             }
             this.spatial = spatial;
-            this.spatialStartTransform = spatial.getLocalTransform().clone();
+            spatialStartTransform = spatial.getLocalTransform().clone();
         }
 
-        this.name = '>' + spatial.getName() + '<';
+        name = '>' + spatial.getName() + '<';
 
         constraints = this.findConstraints(featureOMA, blenderContext);
         if (constraints == null) {
@@ -143,14 +148,14 @@ public class SimulationNode {
 
         LOGGER.info("Removing invalid constraints.");
         List<Constraint> validConstraints = new ArrayList<Constraint>(constraints.size());
-        for (Constraint constraint : this.constraints) {
+        for (Constraint constraint : constraints) {
             if (constraint.validate()) {
                 validConstraints.add(constraint);
             } else {
                 LOGGER.log(Level.WARNING, "Constraint {0} is invalid and will not be applied.", constraint.name);
             }
         }
-        this.constraints = validConstraints;
+        constraints = validConstraints;
     }
 
     /**
@@ -246,6 +251,7 @@ public class SimulationNode {
     private void simulateSkeleton() {
         if (constraints != null && constraints.size() > 0) {
             boolean applyStaticConstraints = true;
+            Set<Long> alteredOmas = new HashSet<Long>();
 
             if (animations != null) {
                 TempVars vars = TempVars.get();
@@ -256,49 +262,45 @@ public class SimulationNode {
                     float maxTime = animationTimeBoundaries[1];
 
                     Map<Integer, VirtualTrack> tracks = new HashMap<Integer, VirtualTrack>();
-                    Map<Integer, Transform> previousTransforms = new HashMap<Integer, Transform>();
+                    Map<Integer, Transform> previousTransforms = this.getInitialTransforms();
                     for (int frame = 0; frame < maxFrame; ++frame) {
                         // this MUST be done here, otherwise setting next frame of animation will
                         // lead to possible errors
                         this.reset();
-                        
+
                         // first set proper time for all bones in all the tracks ...
                         for (Track track : animation.getTracks()) {
                             float time = ((BoneTrack) track).getTimes()[frame];
-                            Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
-
                             track.setTime(time, 1, animControl, animChannel, vars);
                             skeleton.updateWorldVectors();
-
-                            Transform previousTransform = previousTransforms.get(boneIndex);
-                            if (previousTransform == null) {
-                                Bone bone = skeleton.getBone(boneIndex);
-                                previousTransform = new Transform();
-                                previousTransform.setTranslation(bone.getLocalPosition());
-                                previousTransform.setRotation(bone.getLocalRotation());
-                                previousTransform.setScale(bone.getLocalScale());
-                                previousTransforms.put(boneIndex, previousTransform);
-                            }
                         }
 
                         // ... and then apply constraints ...
                         for (Constraint constraint : constraints) {
                             constraint.apply(frame);
+                            if (constraint.getAlteredOmas() != null) {
+                                alteredOmas.addAll(constraint.getAlteredOmas());
+                            }
                         }
 
+                        // ... add virtual tracks if neccessary, for bones that were altered but had no tracks before ...
+                        for (Long boneOMA : alteredOmas) {
+                            BoneContext boneContext = blenderContext.getBoneContext(boneOMA);
+                            int boneIndex = skeleton.getBoneIndex(boneContext.getBone());
+                            if (!tracks.containsKey(boneIndex)) {
+                                tracks.put(boneIndex, new VirtualTrack(maxFrame, maxTime));
+                            }
+                        }
+                        alteredOmas.clear();
+
                         // ... and fill in another frame in the result track
-                        for (Track track : animation.getTracks()) {
-                            Integer boneIndex = ((BoneTrack) track).getTargetBoneIndex();
+                        for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
+                            Integer boneIndex = trackEntry.getKey();
                             Bone bone = skeleton.getBone(boneIndex);
 
-                            // take the initial transform of a bone
+                            // take the initial transform of a bone and its virtual track
                             Transform previousTransform = previousTransforms.get(boneIndex);
-
-                            VirtualTrack vTrack = tracks.get(boneIndex);
-                            if (vTrack == null) {
-                                vTrack = new VirtualTrack(maxFrame, maxTime);
-                                tracks.put(boneIndex, vTrack);
-                            }
+                            VirtualTrack vTrack = trackEntry.getValue();
 
                             Vector3f bonePositionDifference = bone.getLocalPosition().subtract(previousTransform.getTranslation());
                             Quaternion boneRotationDifference = bone.getLocalRotation().mult(previousTransform.getRotation().inverse()).normalizeLocal();
@@ -319,13 +321,18 @@ public class SimulationNode {
                     for (Entry<Integer, VirtualTrack> trackEntry : tracks.entrySet()) {
                         Track newTrack = trackEntry.getValue().getAsBoneTrack(trackEntry.getKey());
                         if (newTrack != null) {
+                            boolean trackReplaced = false;
                             for (Track track : animation.getTracks()) {
                                 if (((BoneTrack) track).getTargetBoneIndex() == trackEntry.getKey().intValue()) {
                                     animation.removeTrack(track);
                                     animation.addTrack(newTrack);
+                                    trackReplaced = true;
                                     break;
                                 }
                             }
+                            if (!trackReplaced) {
+                                animation.addTrack(newTrack);
+                            }
                         }
                         applyStaticConstraints = false;
                     }
@@ -341,6 +348,7 @@ public class SimulationNode {
                 for (Constraint constraint : constraints) {
                     constraint.apply(0);
                 }
+                skeleton.updateWorldVectors();
             }
         }
     }
@@ -355,7 +363,6 @@ public class SimulationNode {
         } else {
             this.simulateSkeleton();
         }
-        this.reset();
     }
 
     /**
@@ -406,6 +413,19 @@ public class SimulationNode {
         return result.size() > 0 ? result : null;
     }
 
+    /**
+     * Creates the initial transforms for all bones in the skelketon.
+     * @return the map where the key is the bone index and the value us the bone's initial transformation
+     */
+    private Map<Integer, Transform> getInitialTransforms() {
+        Map<Integer, Transform> result = new HashMap<Integer, Transform>();
+        for (int i = 0; i < skeleton.getBoneCount(); ++i) {
+            Bone bone = skeleton.getBone(i);
+            result.put(i, new Transform(bone.getLocalPosition(), bone.getLocalRotation(), bone.getLocalScale()));
+        }
+        return result;
+    }
+
     @Override
     public String toString() {
         return name;

+ 0 - 9
engine/src/blender/com/jme3/scene/plugins/blender/constraints/SpatialConstraint.java

@@ -1,6 +1,5 @@
 package com.jme3.scene.plugins.blender.constraints;
 
-import com.jme3.math.Transform;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
 import com.jme3.scene.plugins.blender.animations.Ipo;
@@ -25,12 +24,4 @@ import com.jme3.scene.plugins.blender.file.Structure;
         }
         return true;
     }
-
-    @Override
-    public void apply(int frame) {
-        Transform ownerTransform = constraintHelper.getTransform(ownerOMA, null, ownerSpace);
-        Transform targetTransform = targetOMA != null ? constraintHelper.getTransform(targetOMA, subtargetName, targetSpace) : null;
-        constraintDefinition.bake(ownerTransform, targetTransform, this.ipo.calculateValue(frame));
-        constraintHelper.applyTransform(ownerOMA, subtargetName, ownerSpace, ownerTransform);
-    }
 }

+ 24 - 7
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinition.java

@@ -1,8 +1,12 @@
 package com.jme3.scene.plugins.blender.constraints.definitions;
 
+import java.util.Set;
+
 import com.jme3.math.Transform;
 import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -11,14 +15,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
  * @author Marcin Roguski (Kaelthas)
  */
 public abstract class ConstraintDefinition {
+    protected ConstraintHelper constraintHelper;
     /** Constraints flag. Used to load user's options applied to the constraint. */
-    protected int          flag;
+    protected int              flag;
     /** The constraint's owner. Loaded during runtime. */
-    private Object         owner;
+    private Object             owner;
     /** The blender context. */
-    private BlenderContext blenderContext;
+    protected BlenderContext   blenderContext;
     /** The constraint's owner OMA. */
-    private Long           ownerOMA;
+    protected Long             ownerOMA;
+    /** Stores the OMA addresses of all features whose transform had been altered beside the constraint owner. */
+    protected Set<Long>        alteredOmas;
 
     /**
      * Loads a constraint definition based on the constraint definition
@@ -39,6 +46,7 @@ public abstract class ConstraintDefinition {
             }
         }
         this.blenderContext = blenderContext;
+        constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
         this.ownerOMA = ownerOMA;
     }
 
@@ -67,6 +75,13 @@ public abstract class ConstraintDefinition {
         return true;
     }
 
+    /**
+     * @return a list of all OMAs of the features that the constraint had altered beside its owner
+     */
+    public Set<Long> getAlteredOmas() {
+        return alteredOmas;
+    }
+
     /**
      * @return the type name of the constraint
      */
@@ -75,12 +90,14 @@ public abstract class ConstraintDefinition {
     /**
      * Bakes the constraint for the current feature (bone or spatial) position.
      * 
-     * @param ownerTransform
-     *            the input transform (here the result is stored)
+     * @param ownerSpace
+     *            the space where owner transform will be evaluated in
+     * @param targetSpace
+     *            the space where target transform will be evaluated in
      * @param targetTransform
      *            the target transform used by some of the constraints
      * @param influence
      *            the influence of the constraint (from range <0; 1>)
      */
-    public abstract void bake(Transform ownerTransform, Transform targetTransform, float influence);
+    public abstract void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence);
 }

+ 9 - 3
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionDistLimit.java

@@ -4,6 +4,8 @@ import com.jme3.animation.Bone;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -24,17 +26,19 @@ import com.jme3.scene.plugins.blender.file.Structure;
         mode = ((Number) constraintData.getFieldValue("mode")).intValue();
         dist = ((Number) constraintData.getFieldValue("dist")).floatValue();
     }
-
+    
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
         if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
             // distance limit does not work on bones who have parent
             return;
         }
+        
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
 
         Vector3f v = ownerTransform.getTranslation().subtract(targetTransform.getTranslation());
         float currentDistance = v.length();
-
         switch (mode) {
             case LIMITDIST_INSIDE:
                 if (currentDistance >= dist) {
@@ -62,6 +66,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
             default:
                 throw new IllegalStateException("Unknown distance limit constraint mode: " + mode);
         }
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 1 - 1
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionFactory.java

@@ -50,6 +50,7 @@ public class ConstraintDefinitionFactory {
         CONSTRAINT_CLASSES.put("bRotLimitConstraint", ConstraintDefinitionRotLimit.class);
         CONSTRAINT_CLASSES.put("bSizeLikeConstraint", ConstraintDefinitionSizeLike.class);
         CONSTRAINT_CLASSES.put("bSizeLimitConstraint", ConstraintDefinitionSizeLimit.class);
+        CONSTRAINT_CLASSES.put("bKinematicConstraint", ConstraintDefinitionIK.class);
     }
 
     private static final Map<String, String>                                UNSUPPORTED_CONSTRAINTS = new HashMap<String, String>();
@@ -58,7 +59,6 @@ public class ConstraintDefinitionFactory {
         UNSUPPORTED_CONSTRAINTS.put("bChildOfConstraint", "Child of");
         UNSUPPORTED_CONSTRAINTS.put("bClampToConstraint", "Clamp to");
         UNSUPPORTED_CONSTRAINTS.put("bFollowPathConstraint", "Follow path");
-        UNSUPPORTED_CONSTRAINTS.put("bKinematicConstraint", "Inverse kinematic");
         UNSUPPORTED_CONSTRAINTS.put("bLockTrackConstraint", "Lock track");
         UNSUPPORTED_CONSTRAINTS.put("bMinMaxConstraint", "Min max");
         UNSUPPORTED_CONSTRAINTS.put("bPythonConstraint", "Python/Script");

+ 115 - 0
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java

@@ -0,0 +1,115 @@
+package com.jme3.scene.plugins.blender.constraints.definitions;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import com.jme3.animation.Bone;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
+import com.jme3.scene.plugins.blender.file.Structure;
+
+public class ConstraintDefinitionIK extends ConstraintDefinition {
+
+    private static final int FLAG_POSITION = 0x20;
+
+    /** The number of affected bones. Zero means that all parent bones of the current bone should take part in baking. */
+    private int              bonesAffected;
+    private float            chainLength;
+    private BoneContext[]    bones;
+    private boolean          needToCompute = true;
+
+    public ConstraintDefinitionIK(Structure constraintData, Long ownerOMA, BlenderContext blenderContext) {
+        super(constraintData, ownerOMA, blenderContext);
+        bonesAffected = ((Number) constraintData.getFieldValue("rootbone")).intValue();
+
+        if ((flag & FLAG_POSITION) == 0) {
+            needToCompute = false;
+        }
+
+        if (needToCompute) {
+            alteredOmas = new HashSet<Long>();
+        }
+    }
+    
+    @Override
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        if (needToCompute) {
+            ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
+            BoneContext[] boneContexts = this.getBones();
+            float b = chainLength;
+            Quaternion boneWorldRotation = new Quaternion();
+
+            for (int i = 0; i < boneContexts.length; ++i) {
+                Bone bone = boneContexts[i].getBone();
+
+                bone.updateWorldVectors();
+                Transform boneWorldTransform = constraintHelper.getTransform(boneContexts[i].getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
+
+                Vector3f head = boneWorldTransform.getTranslation();
+                Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneContexts[i].getLength())));
+
+                Vector3f vectorA = tail.subtract(head);
+                float a = vectorA.length();
+                vectorA.normalizeLocal();
+
+                Vector3f vectorC = targetTransform.getTranslation().subtract(head);
+                float c = vectorC.length();
+                vectorC.normalizeLocal();
+
+                b -= a;
+                float theta = 0;
+
+                if (c >= a + b) {
+                    theta = vectorA.angleBetween(vectorC);
+                } else if (c <= FastMath.abs(a - b) && i < boneContexts.length - 1) {
+                    theta = vectorA.angleBetween(vectorC) - FastMath.HALF_PI;
+                } else {
+                    theta = vectorA.angleBetween(vectorC) - FastMath.acos(-(b * b - a * a - c * c) / (2 * a * c));
+                }
+
+                if (theta != 0) {
+                    Vector3f vectorR = vectorA.cross(vectorC);
+                    boneWorldRotation.fromAngleAxis(theta, vectorR);
+                    boneWorldTransform.getRotation().multLocal(boneWorldRotation);
+                    constraintHelper.applyTransform(boneContexts[i].getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform);
+                }
+
+                bone.updateWorldVectors();
+                alteredOmas.add(boneContexts[i].getBoneOma());
+            }
+        }
+    }
+
+    @Override
+    public String getConstraintTypeName() {
+        return "Inverse kinematics";
+    }
+
+    /**
+     * @return the bone contexts of all bones that will be used in this constraint computations
+     */
+    private BoneContext[] getBones() {
+        if (bones == null) {
+            List<BoneContext> bones = new ArrayList<BoneContext>();
+            Bone bone = (Bone) this.getOwner();
+            while (bone != null) {
+                BoneContext boneContext = blenderContext.getBoneContext(bone);
+                bones.add(0, boneContext);
+                chainLength += boneContext.getLength();
+                if (bonesAffected != 0 && bones.size() >= bonesAffected) {
+                    break;
+                }
+                bone = bone.getParent();
+            }
+            this.bones = bones.toArray(new BoneContext[bones.size()]);
+        }
+        return bones;
+    }
+}

+ 10 - 2
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLike.java

@@ -4,6 +4,8 @@ import com.jme3.animation.Bone;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -40,14 +42,18 @@ import com.jme3.scene.plugins.blender.file.Structure;
             flag |= invZ >> 1;
         }
     }
-
+    
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
         if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
             // cannot copy the location of a bone attached to its parent,
             // Blender forbids that
             return;
         }
+        
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
+        
         Vector3f ownerLocation = ownerTransform.getTranslation();
         Vector3f targetLocation = targetTransform.getTranslation();
 
@@ -82,6 +88,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
             startLocation.subtractLocal(ownerLocation).normalizeLocal().mult(influence);
             ownerLocation.addLocal(startLocation);
         }
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 10 - 3
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionLocLimit.java

@@ -4,6 +4,8 @@ import com.jme3.animation.Bone;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -51,14 +53,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
             limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
         }
     }
-
+    
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
         if (this.getOwner() instanceof Bone && ((Bone) this.getOwner()).getParent() != null) {
             // location limit does not work on bones who have parent
             return;
         }
-
+        
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
+        
         Vector3f translation = ownerTransform.getTranslation();
 
         if ((flag & LIMIT_XMIN) != 0 && translation.x < limits[0][0]) {
@@ -79,6 +84,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
         if ((flag & LIMIT_ZMAX) != 0 && translation.z > limits[2][1]) {
             translation.z -= (translation.z - limits[2][1]) * influence;
         }
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 2 - 1
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionNull.java

@@ -2,6 +2,7 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
 
 import com.jme3.math.Transform;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -16,7 +17,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
     }
 
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
         // null constraint does nothing so no need to implement this one
     }
 

+ 8 - 1
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLike.java

@@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Transform;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -27,7 +29,10 @@ import com.jme3.scene.plugins.blender.file.Structure;
     }
 
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
+        
         Quaternion ownerRotation = ownerTransform.getRotation();
         ownerAngles = ownerRotation.toAngles(ownerAngles);
         targetAngles = targetTransform.getRotation().toAngles(targetAngles);
@@ -64,6 +69,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
             // ownerLocation.addLocal(startLocation);
             // TODO
         }
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 9 - 2
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionRotLimit.java

@@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
 import com.jme3.math.FastMath;
 import com.jme3.math.Transform;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -65,9 +67,12 @@ import com.jme3.scene.plugins.blender.file.Structure;
          * limits[i][0] = limits[i][1]; limits[i][1] = temp; } }
          */
     }
-
+    
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
+        
         ownerTransform.getRotation().toAngles(angles);
         // make sure that the rotations are always in range [0, 2PI)
         // TODO: same comment as in constructor
@@ -105,6 +110,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
             angles[2] -= difference;
         }
         ownerTransform.getRotation().fromAngles(angles);
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 9 - 2
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLike.java

@@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -28,9 +30,12 @@ import com.jme3.scene.plugins.blender.file.Structure;
             flag |= z >> 1;
         }
     }
-
+    
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
+        
         Vector3f ownerScale = ownerTransform.getScale();
         Vector3f targetScale = targetTransform.getScale();
 
@@ -50,6 +55,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
             ownerScale.z = targetScale.z * influence + (1.0f - influence) * ownerScale.z;
         }
         ownerScale.addLocal(offset);
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 9 - 3
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionSizeLimit.java

@@ -3,6 +3,8 @@ package com.jme3.scene.plugins.blender.constraints.definitions;
 import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.scene.plugins.blender.BlenderContext;
+import com.jme3.scene.plugins.blender.animations.BoneContext;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 import com.jme3.scene.plugins.blender.file.Structure;
 
 /**
@@ -50,11 +52,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
             limits[2][1] = ((Number) constraintData.getFieldValue("zmax")).floatValue();
         }
     }
-
+    
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
+        BoneContext boneContext = blenderContext.getBoneContext(ownerOMA);
+        Transform ownerTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace);
+        
         Vector3f scale = ownerTransform.getScale();
-
         if ((flag & LIMIT_XMIN) != 0 && scale.x < limits[0][0]) {
             scale.x -= (scale.x - limits[0][0]) * influence;
         }
@@ -73,6 +77,8 @@ import com.jme3.scene.plugins.blender.file.Structure;
         if ((flag & LIMIT_ZMAX) != 0 && scale.z > limits[2][1]) {
             scale.z -= (scale.z - limits[2][1]) * influence;
         }
+        
+        constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), boneContext.getBone().getName(), ownerSpace, ownerTransform);
     }
 
     @Override

+ 2 - 1
engine/src/blender/com/jme3/scene/plugins/blender/constraints/definitions/UnsupportedConstraintDefinition.java

@@ -1,6 +1,7 @@
 package com.jme3.scene.plugins.blender.constraints.definitions;
 
 import com.jme3.math.Transform;
+import com.jme3.scene.plugins.blender.constraints.ConstraintHelper.Space;
 
 /**
  * This class represents a constraint that is defined by blender but not
@@ -18,7 +19,7 @@ import com.jme3.math.Transform;
     }
 
     @Override
-    public void bake(Transform ownerTransform, Transform targetTransform, float influence) {
+    public void bake(Space ownerSpace, Space targetSpace, Transform targetTransform, float influence) {
     }
 
     @Override