1 package com.jme3.scene.plugins.blender.animations; 2 3 import com.jme3.animation.BoneTrack; 4 import com.jme3.animation.SpatialTrack; 5 import com.jme3.animation.Track; 6 import com.jme3.math.FastMath; 7 import com.jme3.math.Quaternion; 8 import com.jme3.math.Vector3f; 9 import com.jme3.scene.plugins.blender.curves.BezierCurve; 10 11 /** 12 * This class is used to calculate bezier curves value for the given frames. The 13 * Ipo (interpolation object) consists of several b-spline curves (connected 3rd 14 * degree bezier curves) of a different type. 15 * 16 * @author Marcin Roguski 17 */ 18 public class Ipo { 19 20 public static final int AC_LOC_X = 1; 21 public static final int AC_LOC_Y = 2; 22 public static final int AC_LOC_Z = 3; 23 public static final int OB_ROT_X = 7; 24 public static final int OB_ROT_Y = 8; 25 public static final int OB_ROT_Z = 9; 26 public static final int AC_SIZE_X = 13; 27 public static final int AC_SIZE_Y = 14; 28 public static final int AC_SIZE_Z = 15; 29 public static final int AC_QUAT_W = 25; 30 public static final int AC_QUAT_X = 26; 31 public static final int AC_QUAT_Y = 27; 32 public static final int AC_QUAT_Z = 28; 33 34 /** A list of bezier curves for this interpolation object. */ 35 private BezierCurve[] bezierCurves; 36 /** Each ipo contains one bone track. */ 37 private Track calculatedTrack; 38 /** This variable indicates if the Y asxis is the UP axis or not. */ 39 protected boolean fixUpAxis; 40 41 /** 42 * Constructor. Stores the bezier curves. 43 * 44 * @param bezierCurves 45 * a table of bezier curves 46 */ 47 public Ipo(BezierCurve[] bezierCurves, boolean fixUpAxis) { 48 this.bezierCurves = bezierCurves; 49 this.fixUpAxis = fixUpAxis; 50 } 51 52 /** 53 * This method calculates the ipo value for the first curve. 54 * 55 * @param frame 56 * the frame for which the value is calculated 57 * @return calculated ipo value 58 */ 59 public float calculateValue(int frame) { 60 return this.calculateValue(frame, 0); 61 } 62 63 /** 64 * This method calculates the ipo value for the curve of the specified 65 * index. Make sure you do not exceed the curves amount. Alway chech the 66 * amount of curves before calling this method. 67 * 68 * @param frame 69 * the frame for which the value is calculated 70 * @param curveIndex 71 * the index of the curve 72 * @return calculated ipo value 73 */ 74 public float calculateValue(int frame, int curveIndex) { 75 return bezierCurves[curveIndex].evaluate(frame, BezierCurve.Y_VALUE); 76 } 77 78 /** 79 * This method returns the curves amount. 80 * 81 * @return the curves amount 82 */ 83 public int getCurvesAmount() { 84 return bezierCurves.length; 85 } 86 87 /** 88 * This method returns the frame where last bezier triple center point of 89 * the specified bezier curve is located. 90 * 91 * @return the frame number of the last defined bezier triple point for the 92 * specified ipo 93 */ 94 public int getLastFrame() { 95 int result = 1; 96 for (int i = 0; i < bezierCurves.length; ++i) { 97 int tempResult = bezierCurves[i].getLastFrame(); 98 if (tempResult > result) { 99 result = tempResult; 100 } 101 } 102 return result; 103 } 104 105 /** 106 * This method calculates the value of the curves as a bone track between 107 * the specified frames. 108 * 109 * @param targetIndex 110 * the index of the target for which the method calculates the 111 * tracks IMPORTANT! Aet to -1 (or any negative number) if you 112 * want to load spatial animation. 113 * @param startFrame 114 * the firs frame of tracks (inclusive) 115 * @param stopFrame 116 * the last frame of the tracks (inclusive) 117 * @param fps 118 * frame rate (frames per second) 119 * @param spatialTrack 120 * this flag indicates if the track belongs to a spatial or to a 121 * bone; the diference is important because it appears that bones 122 * in blender have the same type of coordinate system (Y as UP) 123 * as jme while other features have different one (Z is UP) 124 * @return bone track for the specified bone 125 */ 126 public Track calculateTrack(int targetIndex, int startFrame, int stopFrame, int fps, boolean spatialTrack) { 127 if (calculatedTrack == null) { 128 // preparing data for track 129 int framesAmount = stopFrame - startFrame; 130 float start = (startFrame - 1.0f) / fps; 131 float timeBetweenFrames = 1.0f / fps; 132 133 float[] times = new float[framesAmount + 1]; 134 Vector3f[] translations = new Vector3f[framesAmount + 1]; 135 float[] translation = new float[3]; 136 Quaternion[] rotations = new Quaternion[framesAmount + 1]; 137 float[] quaternionRotation = new float[4]; 138 float[] objectRotation = new float[3]; 139 Vector3f[] scales = new Vector3f[framesAmount + 1]; 140 float[] scale = new float[] { 1.0f, 1.0f, 1.0f }; 141 float degreeToRadiansFactor = FastMath.DEG_TO_RAD * 10;// the values in blender are divided by 10, so we need to mult it here 142 143 // calculating track data 144 for (int frame = startFrame; frame <= stopFrame; ++frame) { 145 int index = frame - startFrame; 146 times[index] = start + (frame - 1) * timeBetweenFrames; 147 for (int j = 0; j < bezierCurves.length; ++j) { 148 double value = bezierCurves[j].evaluate(frame, BezierCurve.Y_VALUE); 149 switch (bezierCurves[j].getType()) { 150 // LOCATION 151 case AC_LOC_X: 152 translation[0] = (float) value; 153 break; 154 case AC_LOC_Y: 155 if (fixUpAxis && spatialTrack) { 156 translation[2] = (float) -value; 157 } else { 158 translation[1] = (float) value; 159 } 160 break; 161 case AC_LOC_Z: 162 translation[fixUpAxis && spatialTrack ? 1 : 2] = (float) value; 163 break; 164 165 // ROTATION (used with object animation) 166 // the value here is in degrees divided by 10 (so in 167 // example: 9 = PI/2) 168 case OB_ROT_X: 169 objectRotation[0] = (float) value * degreeToRadiansFactor; 170 break; 171 case OB_ROT_Y: 172 if (fixUpAxis) { 173 objectRotation[2] = (float) -value * degreeToRadiansFactor; 174 } else { 175 objectRotation[1] = (float) value * degreeToRadiansFactor; 176 } 177 break; 178 case OB_ROT_Z: 179 objectRotation[fixUpAxis ? 1 : 2] = (float) value * degreeToRadiansFactor; 180 break; 181 182 // SIZE 183 case AC_SIZE_X: 184 scale[0] = (float) value; 185 break; 186 case AC_SIZE_Y: 187 if (fixUpAxis && spatialTrack) { 188 scale[2] = (float) value; 189 } else { 190 scale[1] = (float) value; 191 } 192 break; 193 case AC_SIZE_Z: 194 scale[fixUpAxis && spatialTrack ? 1 : 2] = (float) value; 195 break; 196 197 // QUATERNION ROTATION (used with bone animation), dunno 198 // why but here we shouldn't check the 199 // spatialTrack flag value 200 case AC_QUAT_W: 201 quaternionRotation[3] = (float) value; 202 break; 203 case AC_QUAT_X: 204 quaternionRotation[0] = (float) value; 205 break; 206 case AC_QUAT_Y: 207 if (fixUpAxis) { 208 quaternionRotation[2] = -(float) value; 209 } else { 210 quaternionRotation[1] = (float) value; 211 } 212 break; 213 case AC_QUAT_Z: 214 if (fixUpAxis) { 215 quaternionRotation[1] = (float) value; 216 } else { 217 quaternionRotation[2] = (float) value; 218 } 219 break; 220 default: 221 throw new IllegalStateException("Unknown ipo curve type: " + bezierCurves[j].getType()); 222 } 223 } 224 translations[index] = new Vector3f(translation[0], translation[1], translation[2]); 225 rotations[index] = spatialTrack ? new Quaternion().fromAngles(objectRotation) : new Quaternion(quaternionRotation[0], quaternionRotation[1], quaternionRotation[2], quaternionRotation[3]); 226 scales[index] = new Vector3f(scale[0], scale[1], scale[2]); 227 } 228 if (spatialTrack) { 229 calculatedTrack = new SpatialTrack(times, translations, rotations, scales); 230 } else { 231 calculatedTrack = new BoneTrack(targetIndex, times, translations, rotations, scales); 232 } 233 } 234 return calculatedTrack; 235 } 236 }