diff --git a/engine/src/core/com/jme3/animation/AnimChannel.java b/engine/src/core/com/jme3/animation/AnimChannel.java index f22631499..44503981f 100644 --- a/engine/src/core/com/jme3/animation/AnimChannel.java +++ b/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, diff --git a/engine/src/core/com/jme3/animation/Bone.java b/engine/src/core/com/jme3/animation/Bone.java index e7e9e2b15..b34614d12 100644 --- a/engine/src/core/com/jme3/animation/Bone.java +++ b/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. + *

+ * Subsequent calls of this method stack up, with the final transformation + * of the bone computed at {@link #updateWorldVectors() } which resets + * the stack. + *

+ * 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(); + } } /** diff --git a/engine/src/core/com/jme3/animation/BoneTrack.java b/engine/src/core/com/jme3/animation/BoneTrack.java index 0d28ddc73..dda20d733 100644 --- a/engine/src/core/com/jme3/animation/BoneTrack.java +++ b/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); +// } } /**