blob: 58ce48cf9fbc54e2609b15363b9e659b0e768a43 [file] [log] [blame]
package com.jme3.scene.plugins.blender.animations;
import com.jme3.animation.BoneTrack;
import com.jme3.animation.SpatialTrack;
import com.jme3.animation.Track;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.curves.BezierCurve;
/**
* 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
* degree bezier curves) of a different type.
*
* @author Marcin Roguski
*/
public class Ipo {
public static final int AC_LOC_X = 1;
public static final int AC_LOC_Y = 2;
public static final int AC_LOC_Z = 3;
public static final int OB_ROT_X = 7;
public static final int OB_ROT_Y = 8;
public static final int OB_ROT_Z = 9;
public static final int AC_SIZE_X = 13;
public static final int AC_SIZE_Y = 14;
public static final int AC_SIZE_Z = 15;
public static final int AC_QUAT_W = 25;
public static final int AC_QUAT_X = 26;
public static final int AC_QUAT_Y = 27;
public static final int AC_QUAT_Z = 28;
/** A list of bezier curves for this interpolation object. */
private BezierCurve[] bezierCurves;
/** Each ipo contains one bone track. */
private Track calculatedTrack;
/** This variable indicates if the Y asxis is the UP axis or not. */
protected boolean fixUpAxis;
/**
* Constructor. Stores the bezier curves.
*
* @param bezierCurves
* a table of bezier curves
*/
public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis) {
this.bezierCurves = bezierCurves;
this.fixUpAxis = fixUpAxis;
}
/**
* This method calculates the ipo value for the first curve.
*
* @param frame
* the frame for which the value is calculated
* @return calculated ipo value
*/
public float calculateValue(int frame) {
return this.calculateValue(frame, 0);
}
/**
* 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
* amount of curves before calling this method.
*
* @param frame
* the frame for which the value is calculated
* @param curveIndex
* the index of the curve
* @return calculated ipo value
*/
public float calculateValue(int frame, int curveIndex) {
return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE);
}
/**
* This method returns the curves amount.
*
* @return the curves amount
*/
public int getCurvesAmount() {
return bezierCurves.length;
}
/**
* This method returns the frame where last bezier triple center point of
* the specified bezier curve is located.
*
* @return the frame number of the last defined bezier triple point for the
* specified ipo
*/
public int getLastFrame() {
int result = 1;
for (int i = 0; i < bezierCurves.length; ++i) {
int tempResult = bezierCurves[i].getLastFrame();
if (tempResult > result) {
result = tempResult;
}
}
return result;
}
/**
* This method calculates the value of the curves as a bone track between
* the specified frames.
*
* @param targetIndex
* the index of the target for which the method calculates the
* tracks IMPORTANT! Aet to -1 (or any negative number) if you
* want to load spatial animation.
* @param startFrame
* the firs frame of tracks (inclusive)
* @param stopFrame
* the last frame of the tracks (inclusive)
* @param fps
* frame rate (frames per second)
* @param spatialTrack
* this flag indicates if the track belongs to a spatial or to a
* bone; the diference is important because it appears that bones
* in blender have the same type of coordinate system (Y as UP)
* as jme while other features have different one (Z is UP)
* @return bone track for the specified bone
*/
public Track calculateTrack(int targetIndex, int startFrame, int stopFrame, int fps, boolean spatialTrack) {
if (calculatedTrack == null) {
// preparing data for track
int framesAmount = stopFrame - startFrame;
float start = (startFrame - 1.0f) / fps;
float timeBetweenFrames = 1.0f / fps;
float[] times = new float[framesAmount + 1];
Vector3f[] translations = new Vector3f[framesAmount + 1];
float[] translation = new float[3];
Quaternion[] rotations = new Quaternion[framesAmount + 1];
float[] quaternionRotation = new float[4];
float[] objectRotation = new float[3];
Vector3f[] scales = new Vector3f[framesAmount + 1];
float[] scale = new float[] { 1.0f, 1.0f, 1.0f };
float degreeToRadiansFactor = FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here
// calculating track data
for (int frame = startFrame; frame <= stopFrame; ++frame) {
int index = frame - startFrame;
times[index] = start + (frame - 1) * timeBetweenFrames;
for (int j = 0; j < bezierCurves.length; ++j) {
double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE);
switch (bezierCurves[j].getType()) {
// LOCATION
case AC_LOC_X:
translation[0] = (float) value;
break;
case AC_LOC_Y:
if (fixUpAxis && spatialTrack) {
translation[2] = (float) -value;
} else {
translation[1] = (float) value;
}
break;
case AC_LOC_Z:
translation[fixUpAxis && spatialTrack ? 1 : 2] = (float) value;
break;
// ROTATION (used with object animation)
// the value here is in degrees divided by 10 (so in
// example: 9 = PI/2)
case OB_ROT_X:
objectRotation[0] = (float) value * degreeToRadiansFactor;
break;
case OB_ROT_Y:
if (fixUpAxis) {
objectRotation[2] = (float) -value * degreeToRadiansFactor;
} else {
objectRotation[1] = (float) value * degreeToRadiansFactor;
}
break;
case OB_ROT_Z:
objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor;
break;
// SIZE
case AC_SIZE_X:
scale[0] = (float) value;
break;
case AC_SIZE_Y:
if (fixUpAxis && spatialTrack) {
scale[2] = (float) value;
} else {
scale[1] = (float) value;
}
break;
case AC_SIZE_Z:
scale[fixUpAxis && spatialTrack ? 1 : 2] = (float) value;
break;
// QUATERNION ROTATION (used with bone animation), dunno
// why but here we shouldn't check the
// spatialTrack flag value
case AC_QUAT_W:
quaternionRotation[3] = (float) value;
break;
case AC_QUAT_X:
quaternionRotation[0] = (float) value;
break;
case AC_QUAT_Y:
if (fixUpAxis) {
quaternionRotation[2] = -(float) value;
} else {
quaternionRotation[1] = (float) value;
}
break;
case AC_QUAT_Z:
if (fixUpAxis) {
quaternionRotation[1] = (float) value;
} else {
quaternionRotation[2] = (float) value;
}
break;
default:
throw new IllegalStateException("Unknown ipo curve type: " + bezierCurves[j].getType());
}
}
translations[index] = new Vector3f(translation[0], translation[1], translation[2]);
rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]);
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);
}
}
return calculatedTrack;
}
}