Explorar o código

Feature: improved IK algorithm by taking axis locks for bones into
consideration. Also rotation limits, stretch factor and stiffness are
also loaded from the blend file to be used later.

jmekaelthas %!s(int64=11) %!d(string=hai) anos
pai
achega
34cdd21488

+ 123 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/BoneContext.java

@@ -13,6 +13,8 @@ import com.jme3.scene.plugins.blender.BlenderContext;
 import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
 import com.jme3.scene.plugins.blender.constraints.ConstraintHelper;
 import com.jme3.scene.plugins.blender.file.BlenderFileException;
+import com.jme3.scene.plugins.blender.file.DynamicArray;
+import com.jme3.scene.plugins.blender.file.Pointer;
 import com.jme3.scene.plugins.blender.file.Structure;
 import com.jme3.scene.plugins.blender.objects.ObjectHelper;
 
@@ -33,6 +35,13 @@ public class BoneContext {
      */
     public static final Matrix4f BONE_ARMATURE_TRANSFORMATION_MATRIX = new Matrix4f(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
 
+    private static final int     IKFLAG_LOCK_X                       = 0x01;
+    private static final int     IKFLAG_LOCK_Y                       = 0x02;
+    private static final int     IKFLAG_LOCK_Z                       = 0x04;
+    private static final int     IKFLAG_LIMIT_X                      = 0x08;
+    private static final int     IKFLAG_LIMIT_Y                      = 0x10;
+    private static final int     IKFLAG_LIMIT_Z                      = 0x20;
+
     private BlenderContext       blenderContext;
     /** The OMA of the bone's armature object. */
     private Long                 armatureObjectOMA;
@@ -58,6 +67,21 @@ public class BoneContext {
     private float                length;
     /** The bone's deform envelope. */
     private BoneEnvelope         boneEnvelope;
+    
+    // The below data is used only for IK constraint computations.
+    
+    /** The bone's stretch value. */
+    private float                ikStretch;
+    /** Bone's rotation minimum values. */
+    private Vector3f             limitMin;
+    /** Bone's rotation maximum values. */
+    private Vector3f             limitMax;
+    /** The bone's stiffness values (how much it rotates during IK computations. */
+    private Vector3f             stiffness;
+    /** Values that indicate if any axis' rotation should be limited by some angle. */
+    private boolean[]            limits;
+    /** Values that indicate if any axis' rotation should be disabled during IK computations. */
+    private boolean[]            locks;
 
     /**
      * Constructor. Creates the basic set of bone's data.
@@ -91,6 +115,7 @@ public class BoneContext {
      *             an exception is thrown when problem with blender data reading
      *             occurs
      */
+    @SuppressWarnings("unchecked")
     private BoneContext(Structure boneStructure, Long armatureObjectOMA, BoneContext parent, BlenderContext blenderContext) throws BlenderFileException {
         this.parent = parent;
         this.blenderContext = blenderContext;
@@ -108,7 +133,8 @@ public class BoneContext {
             globalBoneMatrix.multLocal(BONE_ARMATURE_TRANSFORMATION_MATRIX);
         }
 
-        Spatial armature = (Spatial) objectHelper.toObject(blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext), blenderContext);
+        Structure armatureStructure = blenderContext.getFileBlock(armatureObjectOMA).getStructure(blenderContext);
+        Spatial armature = (Spatial) objectHelper.toObject(armatureStructure, blenderContext);
         ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
         Matrix4f armatureWorldMatrix = constraintHelper.toMatrix(armature.getWorldTransform(), new Matrix4f());
 
@@ -120,6 +146,32 @@ public class BoneContext {
             boneEnvelope = new BoneEnvelope(boneStructure, armatureWorldMatrix, blenderContext.getBlenderKey().isFixUpAxis());
         }
 
+        // load bone's pose channel data
+        Pointer pPose = (Pointer) armatureStructure.getFieldValue("pose");
+        if (pPose != null && pPose.isNotNull()) {
+            List<Structure> poseChannels = ((Structure) pPose.fetchData().get(0).getFieldValue("chanbase")).evaluateListBase();
+            for (Structure poseChannel : poseChannels) {
+                Long boneOMA = ((Pointer) poseChannel.getFieldValue("bone")).getOldMemoryAddress();
+                if (boneOMA.equals(this.boneStructure.getOldMemoryAddress())) {
+                    ikStretch = ((Number) poseChannel.getFieldValue("ikstretch")).floatValue();
+                    DynamicArray<Number> limitMin = (DynamicArray<Number>) poseChannel.getFieldValue("limitmin");
+                    this.limitMin = new Vector3f(limitMin.get(0).floatValue(), limitMin.get(1).floatValue(), limitMin.get(2).floatValue());
+
+                    DynamicArray<Number> limitMax = (DynamicArray<Number>) poseChannel.getFieldValue("limitmax");
+                    this.limitMax = new Vector3f(limitMax.get(0).floatValue(), limitMax.get(1).floatValue(), limitMax.get(2).floatValue());
+
+                    DynamicArray<Number> stiffness = (DynamicArray<Number>) poseChannel.getFieldValue("stiffness");
+                    this.stiffness = new Vector3f(stiffness.get(0).floatValue(), stiffness.get(1).floatValue(), stiffness.get(2).floatValue());
+
+                    int ikFlag = ((Number) poseChannel.getFieldValue("ikflag")).intValue();
+                    locks = new boolean[] { (ikFlag & IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LOCK_Z) != 0 };
+                    // limits are enabled when locks are disabled, so we ween to take that into account here
+                    limits = new boolean[] { (ikFlag & IKFLAG_LIMIT_X & ~IKFLAG_LOCK_X) != 0, (ikFlag & IKFLAG_LIMIT_Y & ~IKFLAG_LOCK_Y) != 0, (ikFlag & IKFLAG_LIMIT_Z & ~IKFLAG_LOCK_Z) != 0 };
+                    break;// we have found what we need, no need to search further
+                }
+            }
+        }
+
         // create the children
         List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase();
         for (Structure child : childbase) {
@@ -234,6 +286,76 @@ public class BoneContext {
         return boneEnvelope;
     }
 
+    /**
+     * @return bone's stretch factor
+     */
+    public float getIkStretch() {
+        return ikStretch;
+    }
+
+    /**
+     * @return indicates if the X rotation should be limited
+     */
+    public boolean isLimitX() {
+        return limits != null ? limits[0] : false;
+    }
+
+    /**
+     * @return indicates if the Y rotation should be limited
+     */
+    public boolean isLimitY() {
+        return limits != null ? limits[1] : false;
+    }
+
+    /**
+     * @return indicates if the Z rotation should be limited
+     */
+    public boolean isLimitZ() {
+        return limits != null ? limits[2] : false;
+    }
+
+    /**
+     * @return indicates if the X rotation should be disabled
+     */
+    public boolean isLockX() {
+        return locks != null ? locks[0] : false;
+    }
+
+    /**
+     * @return indicates if the Y rotation should be disabled
+     */
+    public boolean isLockY() {
+        return locks != null ? locks[1] : false;
+    }
+
+    /**
+     * @return indicates if the Z rotation should be disabled
+     */
+    public boolean isLockZ() {
+        return locks != null ? locks[2] : false;
+    }
+
+    /**
+     * @return the minimum values in rotation limitation (if limitation is enabled for specific axis).
+     */
+    public Vector3f getLimitMin() {
+        return limitMin;
+    }
+
+    /**
+     * @return the maximum values in rotation limitation (if limitation is enabled for specific axis).
+     */
+    public Vector3f getLimitMax() {
+        return limitMax;
+    }
+
+    /**
+     * @return the stiffness of the bone
+     */
+    public Vector3f getStiffness() {
+        return stiffness;
+    }
+
     /**
      * Tells if the bone is of specified property defined by its flag.
      * @param flagMask

+ 21 - 1
jme3-blender/src/main/java/com/jme3/scene/plugins/blender/constraints/definitions/ConstraintDefinitionIK.java

@@ -81,6 +81,15 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
                     if (angle != 0) {
                         Vector3f cross = currentDir.crossLocal(target).normalizeLocal();
                         q.fromAngleAxis(angle, cross);
+                        if(boneContext.isLockX()) {
+                            q.set(0, q.getY(), q.getZ(), q.getW());
+                        }
+                        if(boneContext.isLockY()) {
+                            q.set(q.getX(), 0, q.getZ(), q.getW());
+                        }
+                        if(boneContext.isLockZ()) {
+                            q.set(q.getX(), q.getY(), 0, q.getW());
+                        }
 
                         boneTransform.getRotation().set(q.multLocal(boneTransform.getRotation()));
                         constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneTransform);
@@ -97,7 +106,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
                 Bone bone = boneContext.getBone();
                 Transform topBoneTransform = constraintHelper.getTransform(topBone.getArmatureObjectOMA(), topBone.getBone().getName(), Space.CONSTRAINT_SPACE_WORLD);
                 Transform boneWorldTransform = constraintHelper.getTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD);
-
+                
                 Vector3f e = topBoneTransform.getTranslation().addLocal(topBoneTransform.getRotation().mult(Vector3f.UNIT_Y).multLocal(topBone.getLength()));// effector
                 Vector3f j = boneWorldTransform.getTranslation(); // current join position
 
@@ -108,6 +117,16 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
                     Vector3f cross = currentDir.crossLocal(target).normalizeLocal();
                     q.fromAngleAxis(angle, cross);
 
+                    if(boneContext.isLockX()) {
+                        q.set(0, q.getY(), q.getZ(), q.getW());
+                    }
+                    if(boneContext.isLockY()) {
+                        q.set(q.getX(), 0, q.getZ(), q.getW());
+                    }
+                    if(boneContext.isLockZ()) {
+                        q.set(q.getX(), q.getY(), 0, q.getW());
+                    }
+                    
                     boneWorldTransform.getRotation().set(q.multLocal(boneWorldTransform.getRotation()));
                     constraintHelper.applyTransform(boneContext.getArmatureObjectOMA(), bone.getName(), Space.CONSTRAINT_SPACE_WORLD, boneWorldTransform);
                 }
@@ -130,6 +149,7 @@ public class ConstraintDefinitionIK extends ConstraintDefinition {
     private List<BoneContext> loadBones() {
         List<BoneContext> bones = new ArrayList<BoneContext>();
         Bone bone = (Bone) this.getOwner();
+        chainLength = 0;
         while (bone != null) {
             BoneContext boneContext = blenderContext.getBoneContext(bone);
             chainLength += boneContext.getLength();