Bugfix: fix that allows to use either quaternion or euler rotation in

bone animation.
experimental
jmekaelthas 11 years ago
parent 539c2e65fc
commit 3b187e9342
  1. 503
      jme3-blender/src/main/java/com/jme3/scene/plugins/blender/animations/Ipo.java

@ -1,243 +1,260 @@
package com.jme3.scene.plugins.blender.animations; package com.jme3.scene.plugins.blender.animations;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import com.jme3.animation.BoneTrack; import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack; import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track; import com.jme3.animation.Track;
import com.jme3.math.FastMath; import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.curves.BezierCurve; import com.jme3.scene.plugins.blender.curves.BezierCurve;
/** /**
* This class is used to calculate bezier curves value for the given frames. The * This class is used to calculate bezier curves value for the given frames. The
* Ipo (interpolation object) consists of several b-spline curves (connected 3rd * Ipo (interpolation object) consists of several b-spline curves (connected 3rd
* degree bezier curves) of a different type. * degree bezier curves) of a different type.
* *
* @author Marcin Roguski * @author Marcin Roguski
*/ */
public class Ipo { public class Ipo {
private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName()); private static final Logger LOGGER = Logger.getLogger(Ipo.class.getName());
public static final int AC_LOC_X = 1; public static final int AC_LOC_X = 1;
public static final int AC_LOC_Y = 2; public static final int AC_LOC_Y = 2;
public static final int AC_LOC_Z = 3; public static final int AC_LOC_Z = 3;
public static final int OB_ROT_X = 7; public static final int OB_ROT_X = 7;
public static final int OB_ROT_Y = 8; public static final int OB_ROT_Y = 8;
public static final int OB_ROT_Z = 9; public static final int OB_ROT_Z = 9;
public static final int AC_SIZE_X = 13; public static final int AC_SIZE_X = 13;
public static final int AC_SIZE_Y = 14; public static final int AC_SIZE_Y = 14;
public static final int AC_SIZE_Z = 15; public static final int AC_SIZE_Z = 15;
public static final int AC_QUAT_W = 25; public static final int AC_QUAT_W = 25;
public static final int AC_QUAT_X = 26; public static final int AC_QUAT_X = 26;
public static final int AC_QUAT_Y = 27; public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28; public static final int AC_QUAT_Z = 28;
/** A list of bezier curves for this interpolation object. */ /** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves; private BezierCurve[] bezierCurves;
/** Each ipo contains one bone track. */ /** Each ipo contains one bone track. */
private Track calculatedTrack; private Track calculatedTrack;
/** This variable indicates if the Y asxis is the UP axis or not. */ /** This variable indicates if the Y asxis is the UP axis or not. */
protected boolean fixUpAxis; protected boolean fixUpAxis;
/** /**
* Depending on the blender version rotations are stored in degrees or * Depending on the blender version rotations are stored in degrees or
* radians so we need to know the version that is used. * radians so we need to know the version that is used.
*/ */
protected final int blenderVersion; protected final int blenderVersion;
/** /**
* Constructor. Stores the bezier curves. * Constructor. Stores the bezier curves.
* *
* @param bezierCurves * @param bezierCurves
* a table of bezier curves * a table of bezier curves
* @param fixUpAxis * @param fixUpAxis
* indicates if the Y is the up axis or not * indicates if the Y is the up axis or not
* @param blenderVersion * @param blenderVersion
* the blender version that is currently used * the blender version that is currently used
*/ */
public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) { public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis, int blenderVersion) {
this.bezierCurves = bezierCurves; this.bezierCurves = bezierCurves;
this.fixUpAxis = fixUpAxis; this.fixUpAxis = fixUpAxis;
this.blenderVersion = blenderVersion; this.blenderVersion = blenderVersion;
} }
/** /**
* This method calculates the ipo value for the first curve. * This method calculates the ipo value for the first curve.
* *
* @param frame * @param frame
* the frame for which the value is calculated * the frame for which the value is calculated
* @return calculated ipo value * @return calculated ipo value
*/ */
public float calculateValue(int frame) { public float calculateValue(int frame) {
return this.calculateValue(frame, 0); return this.calculateValue(frame, 0);
} }
/** /**
* This method calculates the ipo value for the curve of the specified * This method calculates the ipo value for the curve of the specified
* index. Make sure you do not exceed the curves amount. Alway chech the * index. Make sure you do not exceed the curves amount. Alway chech the
* amount of curves before calling this method. * amount of curves before calling this method.
* *
* @param frame * @param frame
* the frame for which the value is calculated * the frame for which the value is calculated
* @param curveIndex * @param curveIndex
* the index of the curve * the index of the curve
* @return calculated ipo value * @return calculated ipo value
*/ */
public float calculateValue(int frame, int curveIndex) { public float calculateValue(int frame, int curveIndex) {
return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE); return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
} }
/** /**
* This method returns the frame where last bezier triple center point of * This method returns the frame where last bezier triple center point of
* the specified bezier curve is located. * the specified bezier curve is located.
* *
* @return the frame number of the last defined bezier triple point for the * @return the frame number of the last defined bezier triple point for the
* specified ipo * specified ipo
*/ */
public int getLastFrame() { public int getLastFrame() {
int result = 1; int result = 1;
for (int i = 0; i < bezierCurves.length; ++i) { for (int i = 0; i < bezierCurves.length; ++i) {
int tempResult = bezierCurves[i].getLastFrame(); int tempResult = bezierCurves[i].getLastFrame();
if (tempResult > result) { if (tempResult > result) {
result = tempResult; result = tempResult;
} }
} }
return result; return result;
} }
/** /**
* This method calculates the value of the curves as a bone track between * This method calculates the value of the curves as a bone track between
* the specified frames. * the specified frames.
* *
* @param targetIndex * @param targetIndex
* the index of the target for which the method calculates the * the index of the target for which the method calculates the
* tracks IMPORTANT! Aet to -1 (or any negative number) if you * tracks IMPORTANT! Aet to -1 (or any negative number) if you
* want to load spatial animation. * want to load spatial animation.
* @param localTranslation * @param localTranslation
* the local translation of the object/bone that will be animated by * the local translation of the object/bone that will be animated by
* the track * the track
* @param localRotation * @param localRotation
* the local rotation of the object/bone that will be animated by * the local rotation of the object/bone that will be animated by
* the track * the track
* @param localScale * @param localScale
* the local scale of the object/bone that will be animated by * the local scale of the object/bone that will be animated by
* the track * the track
* @param startFrame * @param startFrame
* the first frame of tracks (inclusive) * the first frame of tracks (inclusive)
* @param stopFrame * @param stopFrame
* the last frame of the tracks (inclusive) * the last frame of the tracks (inclusive)
* @param fps * @param fps
* frame rate (frames per second) * frame rate (frames per second)
* @param spatialTrack * @param spatialTrack
* this flag indicates if the track belongs to a spatial or to a * this flag indicates if the track belongs to a spatial or to a
* bone; the difference is important because it appears that bones * bone; the difference is important because it appears that bones
* in blender have the same type of coordinate system (Y as UP) * in blender have the same type of coordinate system (Y as UP)
* as jme while other features have different one (Z is UP) * as jme while other features have different one (Z is UP)
* @return bone track for the specified bone * @return bone track for the specified bone
*/ */
public Track calculateTrack(int targetIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) { public Track calculateTrack(int targetIndex, Vector3f localTranslation, Quaternion localRotation, Vector3f localScale, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
if (calculatedTrack == null) { if (calculatedTrack == null) {
// preparing data for track // preparing data for track
int framesAmount = stopFrame - startFrame; int framesAmount = stopFrame - startFrame;
float timeBetweenFrames = 1.0f / fps; float timeBetweenFrames = 1.0f / fps;
float[] times = new float[framesAmount + 1]; float[] times = new float[framesAmount + 1];
Vector3f[] translations = new Vector3f[framesAmount + 1]; Vector3f[] translations = new Vector3f[framesAmount + 1];
float[] translation = new float[] { localTranslation.x, localTranslation.y, localTranslation.z }; float[] translation = new float[] { localTranslation.x, localTranslation.y, localTranslation.z };
Quaternion[] rotations = new Quaternion[framesAmount + 1]; Quaternion[] rotations = new Quaternion[framesAmount + 1];
float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), }; float[] quaternionRotation = new float[] { localRotation.getX(), localRotation.getY(), localRotation.getZ(), localRotation.getW(), };
float[] objectRotation = localRotation.toAngles(null); float[] eulerRotation = localRotation.toAngles(null);
Vector3f[] scales = new Vector3f[framesAmount + 1]; Vector3f[] scales = new Vector3f[framesAmount + 1];
float[] scale = new float[] { localScale.x, localScale.y, localScale.z }; float[] scale = new float[] { localScale.x, localScale.y, localScale.z };
float degreeToRadiansFactor = 1; float degreeToRadiansFactor = 1;
if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees if (blenderVersion < 250) {// in blender earlier than 2.50 the values are stored in degrees
degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here degreeToRadiansFactor *= FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
} }
int yIndex = 1, zIndex = 2; int yIndex = 1, zIndex = 2;
boolean swapAxes = spatialTrack && fixUpAxis; boolean swapAxes = spatialTrack && fixUpAxis;
if (swapAxes) { if (swapAxes) {
yIndex = 2; yIndex = 2;
zIndex = 1; zIndex = 1;
} }
boolean eulerRotationUsed = false, queternionRotationUsed = false;
// calculating track data
for (int frame = startFrame; frame <= stopFrame; ++frame) { // calculating track data
int index = frame - startFrame; for (int frame = startFrame; frame <= stopFrame; ++frame) {
times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames; int index = frame - startFrame;
for (int j = 0; j < bezierCurves.length; ++j) { times[index] = index * timeBetweenFrames;// start + (frame - 1) * timeBetweenFrames;
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); for (int j = 0; j < bezierCurves.length; ++j) {
switch (bezierCurves[j].getType()) { double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
// LOCATION switch (bezierCurves[j].getType()) {
case AC_LOC_X: // LOCATION
translation[0] = (float) value; case AC_LOC_X:
break; translation[0] = (float) value;
case AC_LOC_Y: break;
if (swapAxes && value != 0) { case AC_LOC_Y:
value = -value; if (swapAxes && value != 0) {
} value = -value;
translation[yIndex] = (float) value; }
break; translation[yIndex] = (float) value;
case AC_LOC_Z: break;
translation[zIndex] = (float) value; case AC_LOC_Z:
break; translation[zIndex] = (float) value;
break;
// ROTATION (used with object animation)
case OB_ROT_X: // EULER ROTATION
objectRotation[0] = (float) value * degreeToRadiansFactor; case OB_ROT_X:
break; eulerRotationUsed = true;
case OB_ROT_Y: eulerRotation[0] = (float) value * degreeToRadiansFactor;
if (swapAxes && value != 0) { break;
value = -value; case OB_ROT_Y:
} eulerRotationUsed = true;
objectRotation[yIndex] = (float) value * degreeToRadiansFactor; if (swapAxes && value != 0) {
break; value = -value;
case OB_ROT_Z: }
objectRotation[zIndex] = (float) value * degreeToRadiansFactor; eulerRotation[yIndex] = (float) value * degreeToRadiansFactor;
break; break;
case OB_ROT_Z:
// SIZE eulerRotationUsed = true;
case AC_SIZE_X: eulerRotation[zIndex] = (float) value * degreeToRadiansFactor;
scale[0] = (float) value; break;
break;
case AC_SIZE_Y: // SIZE
scale[yIndex] = (float) value; case AC_SIZE_X:
break; scale[0] = (float) value;
case AC_SIZE_Z: break;
scale[zIndex] = (float) value; case AC_SIZE_Y:
break; scale[yIndex] = (float) value;
break;
// QUATERNION ROTATION (used with bone animation) case AC_SIZE_Z:
case AC_QUAT_W: scale[zIndex] = (float) value;
quaternionRotation[3] = (float) value; break;
break;
case AC_QUAT_X: // QUATERNION ROTATION (used with bone animation)
quaternionRotation[0] = (float) value; case AC_QUAT_W:
break; queternionRotationUsed = true;
case AC_QUAT_Y: quaternionRotation[3] = (float) value;
if (swapAxes && value != 0) { break;
value = -value; case AC_QUAT_X:
} queternionRotationUsed = true;
quaternionRotation[yIndex] = (float) value; quaternionRotation[0] = (float) value;
break; break;
case AC_QUAT_Z: case AC_QUAT_Y:
quaternionRotation[zIndex] = (float) value; queternionRotationUsed = true;
break; if (swapAxes && value != 0) {
default: value = -value;
LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType()); }
} quaternionRotation[yIndex] = (float) value;
} break;
translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2])); case AC_QUAT_Z:
rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); quaternionRotation[zIndex] = (float) value;
scales[index] = new Vector3f(scale[0], scale[1], scale[2]); break;
} default:
if (spatialTrack) { LOGGER.log(Level.WARNING, "Unknown ipo curve type: {0}.", bezierCurves[j].getType());
calculatedTrack = new SpatialTrack(times, translations, rotations, scales); }
} else { }
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales); translations[index] = localRotation.multLocal(new Vector3f(translation[0], translation[1], translation[2]));
} if(queternionRotationUsed) {
} rotations[index] = new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
return calculatedTrack; } else {
} rotations[index] = new Quaternion().fromAngles(eulerRotation);
} }
scales[index] = new Vector3f(scale[0], scale[1], scale[2]);
}
if (spatialTrack) {
calculatedTrack = new SpatialTrack(times, translations, rotations, scales);
} else {
calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales);
}
if(queternionRotationUsed && eulerRotationUsed) {
LOGGER.warning("Animation uses both euler and quaternion tracks for rotations. Quaternion rotation is applied. Make sure that this is what you wanted!");
}
}
return calculatedTrack;
}
}

Loading…
Cancel
Save