瀏覽代碼

BlendAction: resolve slow-motion side effect caused by stretching actions (#1848)

* BlendAction: resolve slow motion side effect caused by stretching any action that doesn't have the same length. It generates speed factor for each child animation that are dynamically interpolated and applied to base speed based on the blend weight taken from blend space.

* Add missing javadoc.

* Add Copyright.

* Renamed calculateSpeedFactors() to applyDefaultSpeedFactors() and made it non static.
Ali-RS 3 年之前
父節點
當前提交
7fa8f7d3fc

+ 90 - 0
jme3-core/src/main/java/com/jme3/anim/tween/action/BlendAction.java

@@ -1,8 +1,41 @@
+/*
+ * Copyright (c) 2009-2022 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 package com.jme3.anim.tween.action;
 
 import com.jme3.anim.util.HasLocalTransform;
+import com.jme3.math.FastMath;
 import com.jme3.math.Transform;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -14,6 +47,7 @@ public class BlendAction extends BlendableAction {
     final private BlendSpace blendSpace;
     private float blendWeight;
     final private double[] timeFactor;
+    private double[] speedFactors;
     final private Map<HasLocalTransform, Transform> targetMap = new HashMap<>();
 
     public BlendAction(BlendSpace blendSpace, BlendableAction... actions) {
@@ -47,6 +81,10 @@ public class BlendAction extends BlendableAction {
                 }
             }
         }
+
+        // Calculate default factors that dynamically adjust speed to resolve
+        // slow motion effect on stretched actions.
+        applyDefaultSpeedFactors();
     }
 
     @Override
@@ -85,6 +123,58 @@ public class BlendAction extends BlendableAction {
         return blendSpace;
     }
 
+    @Override
+    public double getSpeed() {
+        if (speedFactors != null) {
+            return super.getSpeed() * FastMath.interpolateLinear(blendWeight,
+                    (float) speedFactors[firstActiveIndex],
+                    (float) speedFactors[secondActiveIndex]);
+        }
+
+        return super.getSpeed();
+    }
+
+    /**
+     * @return The speed factor or null if there is none
+     */
+    public double[] getSpeedFactors() {
+        return speedFactors;
+    }
+
+    /**
+     * Used to resolve the slow motion side effect caused by stretching actions that
+     * doesn't have the same length.
+     *
+     * @param speedFactors The speed factors for each child action. BlendAction will
+     *                     interpolate factor for current frame based on blend weight
+     *                     and will multiply it to speed.
+     */
+    public void setSpeedFactors(double... speedFactors) {
+        if (speedFactors.length != actions.length) {
+            throw new IllegalArgumentException("Array length must be " + actions.length);
+        }
+
+        this.speedFactors = speedFactors;
+    }
+
+    public void clearSpeedFactors() {
+        this.speedFactors = null;
+    }
+
+    /**
+     * BlendAction will stretch it's child actions if they don't have the same length.
+     * This might cause stretched animations to run slowly. This method generates factors
+     * based on how much actions are stretched. Multiplying this factor to base speed will
+     * resolve the slow-motion side effect caused by stretching. BlendAction will use the
+     * blend weight taken from BlendSpace to interpolate the speed factor for current frame.
+     */
+    public void applyDefaultSpeedFactors() {
+        double[] factors = Arrays.stream(getActions())
+                .mapToDouble(action -> getLength() / action.getLength())
+                .toArray();
+        setSpeedFactors(factors);
+    }
+
     protected void setFirstActiveIndex(int index) {
         this.firstActiveIndex = index;
     }

+ 2 - 2
jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java

@@ -174,13 +174,13 @@ public class TestAnimMigration extends SimpleApplication {
                     blendValue += value;
                     blendValue = FastMath.clamp(blendValue, 1, 4);
                     action.getBlendSpace().setValue(blendValue);
-                    action.setSpeed(blendValue);
+                    //action.setSpeed(blendValue);
                 }
                 if (name.equals("blendDown")) {
                     blendValue -= value;
                     blendValue = FastMath.clamp(blendValue, 1, 4);
                     action.getBlendSpace().setValue(blendValue);
-                    action.setSpeed(blendValue);
+                    //action.setSpeed(blendValue);
                 }
                 //System.err.println(blendValue);
             }