1 package com.jme3.animation; 2 3 import com.jme3.export.InputCapsule; 4 import com.jme3.export.JmeExporter; 5 import com.jme3.export.JmeImporter; 6 import com.jme3.export.OutputCapsule; 7 import com.jme3.math.Quaternion; 8 import com.jme3.math.Vector3f; 9 import com.jme3.scene.Spatial; 10 import com.jme3.util.TempVars; 11 import java.io.IOException; 12 import java.util.Arrays; 13 14 /** 15 * This class represents the track for spatial animation. 16 * 17 * @author Marcin Roguski (Kaelthas) 18 */ 19 public class SpatialTrack implements Track { 20 21 /** 22 * Translations of the track. 23 */ 24 private CompactVector3Array translations; 25 26 /** 27 * Rotations of the track. 28 */ 29 private CompactQuaternionArray rotations; 30 31 /** 32 * Scales of the track. 33 */ 34 private CompactVector3Array scales; 35 36 /** 37 * The times of the animations frames. 38 */ 39 private float[] times; 40 SpatialTrack()41 public SpatialTrack() { 42 } 43 44 /** 45 * Creates a spatial track for the given track data. 46 * 47 * @param times 48 * a float array with the time of each frame 49 * @param translations 50 * the translation of the bone for each frame 51 * @param rotations 52 * the rotation of the bone for each frame 53 * @param scales 54 * the scale of the bone for each frame 55 */ SpatialTrack(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales)56 public SpatialTrack(float[] times, Vector3f[] translations, 57 Quaternion[] rotations, Vector3f[] scales) { 58 setKeyframes(times, translations, rotations, scales); 59 } 60 61 /** 62 * 63 * Modify the spatial which this track modifies. 64 * 65 * @param time 66 * the current time of the animation 67 */ setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars)68 public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { 69 Spatial spatial = control.getSpatial(); 70 71 Vector3f tempV = vars.vect1; 72 Vector3f tempS = vars.vect2; 73 Quaternion tempQ = vars.quat1; 74 Vector3f tempV2 = vars.vect3; 75 Vector3f tempS2 = vars.vect4; 76 Quaternion tempQ2 = vars.quat2; 77 78 int lastFrame = times.length - 1; 79 if (time < 0 || lastFrame == 0) { 80 if (rotations != null) 81 rotations.get(0, tempQ); 82 if (translations != null) 83 translations.get(0, tempV); 84 if (scales != null) { 85 scales.get(0, tempS); 86 } 87 } else if (time >= times[lastFrame]) { 88 if (rotations != null) 89 rotations.get(lastFrame, tempQ); 90 if (translations != null) 91 translations.get(lastFrame, tempV); 92 if (scales != null) { 93 scales.get(lastFrame, tempS); 94 } 95 } else { 96 int startFrame = 0; 97 int endFrame = 1; 98 // use lastFrame so we never overflow the array 99 for (int i = 0; i < lastFrame && times[i] < time; ++i) { 100 startFrame = i; 101 endFrame = i + 1; 102 } 103 104 float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]); 105 106 if (rotations != null) 107 rotations.get(startFrame, tempQ); 108 if (translations != null) 109 translations.get(startFrame, tempV); 110 if (scales != null) { 111 scales.get(startFrame, tempS); 112 } 113 if (rotations != null) 114 rotations.get(endFrame, tempQ2); 115 if (translations != null) 116 translations.get(endFrame, tempV2); 117 if (scales != null) { 118 scales.get(endFrame, tempS2); 119 } 120 tempQ.nlerp(tempQ2, blend); 121 tempV.interpolate(tempV2, blend); 122 tempS.interpolate(tempS2, blend); 123 } 124 125 if (translations != null) 126 spatial.setLocalTranslation(tempV); 127 if (rotations != null) 128 spatial.setLocalRotation(tempQ); 129 if (scales != null) { 130 spatial.setLocalScale(tempS); 131 } 132 } 133 134 /** 135 * Set the translations, rotations and scales for this track. 136 * 137 * @param times 138 * a float array with the time of each frame 139 * @param translations 140 * the translation of the bone for each frame 141 * @param rotations 142 * the rotation of the bone for each frame 143 * @param scales 144 * the scale of the bone for each frame 145 */ setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales)146 public void setKeyframes(float[] times, Vector3f[] translations, 147 Quaternion[] rotations, Vector3f[] scales) { 148 if (times.length == 0) { 149 throw new RuntimeException("BoneTrack with no keyframes!"); 150 } 151 152 this.times = times; 153 if (translations != null) { 154 assert times.length == translations.length; 155 this.translations = new CompactVector3Array(); 156 this.translations.add(translations); 157 this.translations.freeze(); 158 } 159 if (rotations != null) { 160 assert times.length == rotations.length; 161 this.rotations = new CompactQuaternionArray(); 162 this.rotations.add(rotations); 163 this.rotations.freeze(); 164 } 165 if (scales != null) { 166 assert times.length == scales.length; 167 this.scales = new CompactVector3Array(); 168 this.scales.add(scales); 169 this.scales.freeze(); 170 } 171 } 172 173 /** 174 * @return the array of rotations of this track 175 */ getRotations()176 public Quaternion[] getRotations() { 177 return rotations == null ? null : rotations.toObjectArray(); 178 } 179 180 /** 181 * @return the array of scales for this track 182 */ getScales()183 public Vector3f[] getScales() { 184 return scales == null ? null : scales.toObjectArray(); 185 } 186 187 /** 188 * @return the arrays of time for this track 189 */ getTimes()190 public float[] getTimes() { 191 return times; 192 } 193 194 /** 195 * @return the array of translations of this track 196 */ getTranslations()197 public Vector3f[] getTranslations() { 198 return translations == null ? null : translations.toObjectArray(); 199 } 200 201 /** 202 * @return the length of the track 203 */ getLength()204 public float getLength() { 205 return times == null ? 0 : times[times.length - 1] - times[0]; 206 } 207 208 /** 209 * This method creates a clone of the current object. 210 * @return a clone of the current object 211 */ 212 @Override clone()213 public SpatialTrack clone() { 214 int tablesLength = times.length; 215 216 float[] timesCopy = this.times.clone(); 217 Vector3f[] translationsCopy = this.getTranslations() == null ? null : Arrays.copyOf(this.getTranslations(), tablesLength); 218 Quaternion[] rotationsCopy = this.getRotations() == null ? null : Arrays.copyOf(this.getRotations(), tablesLength); 219 Vector3f[] scalesCopy = this.getScales() == null ? null : Arrays.copyOf(this.getScales(), tablesLength); 220 221 //need to use the constructor here because of the final fields used in this class 222 return new SpatialTrack(timesCopy, translationsCopy, rotationsCopy, scalesCopy); 223 } 224 225 @Override write(JmeExporter ex)226 public void write(JmeExporter ex) throws IOException { 227 OutputCapsule oc = ex.getCapsule(this); 228 oc.write(translations, "translations", null); 229 oc.write(rotations, "rotations", null); 230 oc.write(times, "times", null); 231 oc.write(scales, "scales", null); 232 } 233 234 @Override read(JmeImporter im)235 public void read(JmeImporter im) throws IOException { 236 InputCapsule ic = im.getCapsule(this); 237 translations = (CompactVector3Array) ic.readSavable("translations", null); 238 rotations = (CompactQuaternionArray) ic.readSavable("rotations", null); 239 times = ic.readFloatArray("times", null); 240 scales = (CompactVector3Array) ic.readSavable("scales", null); 241 } 242 } 243