* 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
3.0
Sha..rd 13 years ago
parent 4dd46f4c5b
commit a99d94c263
  1. 22
      engine/src/core/com/jme3/animation/AnimChannel.java
  2. 109
      engine/src/core/com/jme3/animation/Bone.java
  3. 8
      engine/src/core/com/jme3/animation/BoneTrack.java

@ -66,7 +66,7 @@ public final class AnimChannel {
private float blendAmount = 1f; private float blendAmount = 1f;
private float blendRate = 0; private float blendRate = 0;
private static float clampWrapTime(float t, float max, LoopMode loopMode){ private static float clampWrapTime(float t, float max, LoopMode loopMode){
if (t == 0) { if (t == 0) {
return 0; // prevent division by 0 errors return 0; // prevent division by 0 errors
@ -76,11 +76,19 @@ public final class AnimChannel {
case Cycle: case Cycle:
boolean sign = ((int) (t / max) % 2) != 0; boolean sign = ((int) (t / max) % 2) != 0;
float result; float result;
if (t < 0){
result = sign ? t % max : -(max + (t % max)); // if (t < 0){
} else { // 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; 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; return result;
case DontLoop: case DontLoop:
return t > max ? max : (t < 0 ? 0 : t); return t > max ? max : (t < 0 ? 0 : t);
@ -362,8 +370,8 @@ public final class AnimChannel {
if (blendFrom != null && blendAmount != 1.0f){ if (blendFrom != null && blendAmount != 1.0f){
// The blendFrom anim is set, the actual animation // The blendFrom anim is set, the actual animation
// playing will be set // playing will be set
blendFrom.setTime(timeBlendFrom, 1f, control, this, vars); // blendFrom.setTime(timeBlendFrom, 1f, control, this, vars);
// blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars); blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
timeBlendFrom += tpf * speedBlendFrom; timeBlendFrom += tpf * speedBlendFrom;
timeBlendFrom = clampWrapTime(timeBlendFrom, timeBlendFrom = clampWrapTime(timeBlendFrom,

@ -85,8 +85,16 @@ public final class Bone implements Savable {
private Vector3f worldPos = new Vector3f(); private Vector3f worldPos = new Vector3f();
private Quaternion worldRot = new Quaternion(); private Quaternion worldRot = new Quaternion();
private Vector3f worldScale = new Vector3f(); private Vector3f worldScale = new Vector3f();
//used for getCombinedTransform
// Used for getCombinedTransform
private Transform tmpTransform = new Transform(); 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. * 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. * world transform with this bones' local transform.
*/ */
public final void updateWorldVectors() { 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) { if (parent != null) {
//rotation //rotation
parent.worldRot.mult(localRot, worldRot); 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) { void blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight) {
if (userControl) { if (userControl) {
return; return;
} }
TempVars vars = TempVars.get(); if (weight == 0) {
// assert vars.lock(); // Do not apply this transform at all.
return;
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 (currentWeightSum == 1){
vars.release(); 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();
}
} }
/** /**

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

Loading…
Cancel
Save