瀏覽代碼

* Fix bug where animation loop mode "Cycle" would start at the end of the animation at each cycle causing discontinuities in the animation
* Fix bug where blending between the animations of a bone that did not have keyframes on the 2nd animation would fail and cause snapping / incorrect blending

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

Sha..rd 13 年之前
父節點
當前提交
a99d94c263

+ 15 - 7
engine/src/core/com/jme3/animation/AnimChannel.java

@@ -66,7 +66,7 @@ public final class AnimChannel {
     
     private float blendAmount = 1f;
     private float blendRate   = 0;
-
+    
     private static float clampWrapTime(float t, float max, LoopMode loopMode){
         if (t == 0) {
             return 0; // prevent division by 0 errors
@@ -76,11 +76,19 @@ public final class AnimChannel {
             case Cycle:
                 boolean sign = ((int) (t / max) % 2) != 0;
                 float result;
-                if (t < 0){
-                    result = sign ? t % max : -(max + (t % max));
-                } else {
+                
+//                if (t < 0){
+//                    result = sign ? t % max : -(max + (t % max));
+//                } else {
+                    // NOTE: This algorithm seems stable for both high and low
+                    // tpf so for now its a keeper.
                     result = sign ? -(max - (t % max)) : t % max;
-                }
+//                }
+                    
+//                if (result <= 0 || result >= max) {
+//                    System.out.println("SIGN: " + sign + ", RESULT: " + result + ", T: " + t + ", M: " + max);
+//                }
+                
                 return result;
             case DontLoop:
                 return t > max ? max : (t < 0 ? 0 : t);
@@ -362,8 +370,8 @@ public final class AnimChannel {
         if (blendFrom != null && blendAmount != 1.0f){
             // The blendFrom anim is set, the actual animation
             // playing will be set 
-            blendFrom.setTime(timeBlendFrom, 1f, control, this, vars);
-//            blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
+//            blendFrom.setTime(timeBlendFrom, 1f, control, this, vars);
+            blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
             
             timeBlendFrom += tpf * speedBlendFrom;
             timeBlendFrom = clampWrapTime(timeBlendFrom,

+ 86 - 23
engine/src/core/com/jme3/animation/Bone.java

@@ -85,8 +85,16 @@ public final class Bone implements Savable {
     private Vector3f worldPos = new Vector3f();
     private Quaternion worldRot = new Quaternion();
     private Vector3f worldScale = new Vector3f();
-    //used for getCombinedTransform 
+    
+    // Used for getCombinedTransform
     private Transform tmpTransform = new Transform();
+    
+    /**
+     * Used to handle blending from one animation to another.
+     * See {@link #blendAnimTransforms(com.jme3.math.Vector3f, com.jme3.math.Quaternion, com.jme3.math.Vector3f, float)}
+     * on how this variable is used.
+     */
+    private transient float currentWeightSum = -1;
 
     /**
      * Creates a new bone with the given name.
@@ -323,6 +331,25 @@ public final class Bone implements Savable {
      * world transform with this bones' local transform.
      */
     public final void updateWorldVectors() {
+        if (currentWeightSum == 1f) {
+            currentWeightSum = -1;
+        } else if (currentWeightSum != -1f) {
+            // Apply the weight to the local transform
+            if (currentWeightSum == 0) {
+                localRot.set(initialRot);
+                localPos.set(initialPos);
+                localScale.set(initialScale);
+            } else {
+                float invWeightSum = 1f - currentWeightSum;
+                localRot.nlerp(initialRot, invWeightSum);
+                localPos.interpolate(initialPos, invWeightSum);
+                localScale.interpolate(initialScale, invWeightSum);
+            }
+            
+            // Future invocations of transform blend will start over.
+            currentWeightSum = -1;
+        }
+        
         if (parent != null) {
             //rotation
             parent.worldRot.mult(localRot, worldRot);
@@ -522,34 +549,70 @@ public final class Bone implements Savable {
         }
     }
 
+    /**
+     * Blends the given animation transform onto the bone's local transform.
+     * <p>
+     * Subsequent calls of this method stack up, with the final transformation
+     * of the bone computed at {@link #updateWorldVectors() } which resets
+     * the stack.
+     * <p>
+     * E.g. a single transform blend with weight = 0.5 followed by an
+     * updateWorldVectors() call will result in final transform = transform * 0.5.
+     * Two transform blends with weight = 0.5 each will result in the two
+     * transforms blended together (nlerp) with blend = 0.5.
+     * 
+     * @param translation The translation to blend in
+     * @param rotation The rotation to blend in
+     * @param scale The scale to blend in
+     * @param weight The weight of the transform to apply. Set to 1.0 to prevent
+     * any other transform from being applied until updateWorldVectors().
+     */
     void blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight) {
         if (userControl) {
             return;
         }
-
-        TempVars vars = TempVars.get();
-//        assert vars.lock();
-
-        Vector3f tmpV = vars.vect1;
-        Vector3f tmpV2 = vars.vect2;
-        Quaternion tmpQ = vars.quat1;
-
-        //location
-        tmpV.set(initialPos).addLocal(translation);
-        localPos.interpolate(tmpV, weight);
-
-        //rotation
-        tmpQ.set(initialRot).multLocal(rotation);
-        localRot.nlerp(tmpQ, weight);
-
-        //scale
-        if (scale != null) {
-            tmpV2.set(initialScale).multLocal(scale);
-            localScale.interpolate(tmpV2, weight);
+        
+        if (weight == 0) {
+            // Do not apply this transform at all.
+            return;
         }
 
-
-        vars.release();
+        if (currentWeightSum == 1){
+            return; // More than 2 transforms are being blended
+        } else if (currentWeightSum == -1 || currentWeightSum == 0) {
+            // Set the transform fully
+            localPos.set(initialPos).addLocal(translation);
+            localRot.set(initialRot).multLocal(rotation);
+            if (scale != null) {
+                localScale.set(initialScale).multLocal(scale);
+            }
+            // Set the weight. It will be applied in updateWorldVectors().
+            currentWeightSum = weight;
+        } else {
+            // The weight is already set. 
+            // Blend in the new transform.
+            TempVars vars = TempVars.get();
+
+            Vector3f tmpV = vars.vect1;
+            Vector3f tmpV2 = vars.vect2;
+            Quaternion tmpQ = vars.quat1;
+            
+            tmpV.set(initialPos).addLocal(translation);
+            localPos.interpolate(tmpV, weight);
+
+            tmpQ.set(initialRot).multLocal(rotation);
+            localRot.nlerp(tmpQ, weight);
+
+            if (scale != null) {
+                tmpV2.set(initialScale).multLocal(scale);
+                localScale.interpolate(tmpV2, weight);
+            }
+        
+            // Ensures no new weights will be blended in the future.
+            currentWeightSum = 1;
+            
+            vars.release();
+        }
     }
 
     /**

+ 4 - 4
engine/src/core/com/jme3/animation/BoneTrack.java

@@ -243,11 +243,11 @@ public final class BoneTrack implements Track {
             tempS.interpolate(tempS2, blend);
         }
 
-        if (weight != 1f) {
+//        if (weight != 1f) {
             target.blendAnimTransforms(tempV, tempQ, scales != null ? tempS : null, weight);
-        } else {
-            target.setAnimTransforms(tempV, tempQ, scales != null ? tempS : null);
-        }
+//        } else {
+//            target.setAnimTransforms(tempV, tempQ, scales != null ? tempS : null);
+//        }
     }
     
     /**