1 /* 2 * Copyright (c) 2009-2012 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.animation; 33 34 import com.jme3.export.*; 35 import com.jme3.math.Quaternion; 36 import com.jme3.math.Vector3f; 37 import com.jme3.util.TempVars; 38 import java.io.IOException; 39 import java.util.BitSet; 40 41 /** 42 * Contains a list of transforms and times for each keyframe. 43 * 44 * @author Kirill Vainer 45 */ 46 public final class BoneTrack implements Track { 47 48 /** 49 * Bone index in the skeleton which this track effects. 50 */ 51 private int targetBoneIndex; 52 53 /** 54 * Transforms and times for track. 55 */ 56 private CompactVector3Array translations; 57 private CompactQuaternionArray rotations; 58 private CompactVector3Array scales; 59 private float[] times; 60 61 /** 62 * Serialization-only. Do not use. 63 */ 64 public BoneTrack() { 65 } 66 67 /** 68 * Creates a bone track for the given bone index 69 * @param targetBoneIndex the bone index 70 * @param times a float array with the time of each frame 71 * @param translations the translation of the bone for each frame 72 * @param rotations the rotation of the bone for each frame 73 */ 74 public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations) { 75 this.targetBoneIndex = targetBoneIndex; 76 this.setKeyframes(times, translations, rotations); 77 } 78 79 /** 80 * Creates a bone track for the given bone index 81 * @param targetBoneIndex the bone index 82 * @param times a float array with the time of each frame 83 * @param translations the translation of the bone for each frame 84 * @param rotations the rotation of the bone for each frame 85 * @param scales the scale of the bone for each frame 86 */ 87 public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { 88 this.targetBoneIndex = targetBoneIndex; 89 this.setKeyframes(times, translations, rotations, scales); 90 } 91 92 /** 93 * Creates a bone track for the given bone index 94 * @param targetBoneIndex the bone's index 95 */ 96 public BoneTrack(int targetBoneIndex) { 97 this.targetBoneIndex = targetBoneIndex; 98 } 99 100 /** 101 * @return the bone index of this bone track. 102 */ 103 public int getTargetBoneIndex() { 104 return targetBoneIndex; 105 } 106 107 /** 108 * return the array of rotations of this track 109 * @return 110 */ 111 public Quaternion[] getRotations() { 112 return rotations.toObjectArray(); 113 } 114 115 /** 116 * returns the array of scales for this track 117 * @return 118 */ 119 public Vector3f[] getScales() { 120 return scales == null ? null : scales.toObjectArray(); 121 } 122 123 /** 124 * returns the arrays of time for this track 125 * @return 126 */ 127 public float[] getTimes() { 128 return times; 129 } 130 131 /** 132 * returns the array of translations of this track 133 * @return 134 */ 135 public Vector3f[] getTranslations() { 136 return translations.toObjectArray(); 137 } 138 139 /** 140 * Set the translations and rotations for this bone track 141 * @param times a float array with the time of each frame 142 * @param translations the translation of the bone for each frame 143 * @param rotations the rotation of the bone for each frame 144 */ 145 public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations) { 146 if (times.length == 0) { 147 throw new RuntimeException("BoneTrack with no keyframes!"); 148 } 149 150 assert times.length == translations.length && times.length == rotations.length; 151 152 this.times = times; 153 this.translations = new CompactVector3Array(); 154 this.translations.add(translations); 155 this.translations.freeze(); 156 this.rotations = new CompactQuaternionArray(); 157 this.rotations.add(rotations); 158 this.rotations.freeze(); 159 } 160 161 /** 162 * Set the translations, rotations and scales for this bone track 163 * @param times a float array with the time of each frame 164 * @param translations the translation of the bone for each frame 165 * @param rotations the rotation of the bone for each frame 166 * @param scales the scale of the bone for each frame 167 */ 168 public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { 169 this.setKeyframes(times, translations, rotations); 170 assert times.length == scales.length; 171 if (scales != null) { 172 this.scales = new CompactVector3Array(); 173 this.scales.add(scales); 174 this.scales.freeze(); 175 } 176 } 177 178 /** 179 * 180 * Modify the bone which this track modifies in the skeleton to contain 181 * the correct animation transforms for a given time. 182 * The transforms can be interpolated in some method from the keyframes. 183 * 184 * @param time the current time of the animation 185 * @param weight the weight of the animation 186 * @param control 187 * @param channel 188 * @param vars 189 */ 190 public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { 191 BitSet affectedBones = channel.getAffectedBones(); 192 if (affectedBones != null && !affectedBones.get(targetBoneIndex)) { 193 return; 194 } 195 196 Bone target = control.getSkeleton().getBone(targetBoneIndex); 197 198 Vector3f tempV = vars.vect1; 199 Vector3f tempS = vars.vect2; 200 Quaternion tempQ = vars.quat1; 201 Vector3f tempV2 = vars.vect3; 202 Vector3f tempS2 = vars.vect4; 203 Quaternion tempQ2 = vars.quat2; 204 205 int lastFrame = times.length - 1; 206 if (time < 0 || lastFrame == 0) { 207 rotations.get(0, tempQ); 208 translations.get(0, tempV); 209 if (scales != null) { 210 scales.get(0, tempS); 211 } 212 } else if (time >= times[lastFrame]) { 213 rotations.get(lastFrame, tempQ); 214 translations.get(lastFrame, tempV); 215 if (scales != null) { 216 scales.get(lastFrame, tempS); 217 } 218 } else { 219 int startFrame = 0; 220 int endFrame = 1; 221 // use lastFrame so we never overflow the array 222 int i; 223 for (i = 0; i < lastFrame && times[i] < time; i++) { 224 startFrame = i; 225 endFrame = i + 1; 226 } 227 228 float blend = (time - times[startFrame]) 229 / (times[endFrame] - times[startFrame]); 230 231 rotations.get(startFrame, tempQ); 232 translations.get(startFrame, tempV); 233 if (scales != null) { 234 scales.get(startFrame, tempS); 235 } 236 rotations.get(endFrame, tempQ2); 237 translations.get(endFrame, tempV2); 238 if (scales != null) { 239 scales.get(endFrame, tempS2); 240 } 241 tempQ.nlerp(tempQ2, blend); 242 tempV.interpolate(tempV2, blend); 243 tempS.interpolate(tempS2, blend); 244 } 245 246 if (weight != 1f) { 247 target.blendAnimTransforms(tempV, tempQ, scales != null ? tempS : null, weight); 248 } else { 249 target.setAnimTransforms(tempV, tempQ, scales != null ? tempS : null); 250 } 251 } 252 253 /** 254 * @return the length of the track 255 */ 256 public float getLength() { 257 return times == null ? 0 : times[times.length - 1] - times[0]; 258 } 259 260 /** 261 * This method creates a clone of the current object. 262 * @return a clone of the current object 263 */ 264 @Override 265 public BoneTrack clone() { 266 int tablesLength = times.length; 267 268 float[] times = this.times.clone(); 269 Vector3f[] sourceTranslations = this.getTranslations(); 270 Quaternion[] sourceRotations = this.getRotations(); 271 Vector3f[] sourceScales = this.getScales(); 272 273 Vector3f[] translations = new Vector3f[tablesLength]; 274 Quaternion[] rotations = new Quaternion[tablesLength]; 275 Vector3f[] scales = new Vector3f[tablesLength]; 276 for (int i = 0; i < tablesLength; ++i) { 277 translations[i] = sourceTranslations[i].clone(); 278 rotations[i] = sourceRotations[i].clone(); 279 scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f); 280 } 281 282 // Need to use the constructor here because of the final fields used in this class 283 return new BoneTrack(targetBoneIndex, times, translations, rotations, scales); 284 } 285 286 @Override 287 public void write(JmeExporter ex) throws IOException { 288 OutputCapsule oc = ex.getCapsule(this); 289 oc.write(targetBoneIndex, "boneIndex", 0); 290 oc.write(translations, "translations", null); 291 oc.write(rotations, "rotations", null); 292 oc.write(times, "times", null); 293 oc.write(scales, "scales", null); 294 } 295 296 @Override 297 public void read(JmeImporter im) throws IOException { 298 InputCapsule ic = im.getCapsule(this); 299 targetBoneIndex = ic.readInt("boneIndex", 0); 300 301 translations = (CompactVector3Array) ic.readSavable("translations", null); 302 rotations = (CompactQuaternionArray) ic.readSavable("rotations", null); 303 times = ic.readFloatArray("times", null); 304 scales = (CompactVector3Array) ic.readSavable("scales", null); 305 306 //Backward compatibility for old j3o files generated before revision 6807 307 if (im.getFormatVersion() == 0){ 308 if (translations == null) { 309 Savable[] sav = ic.readSavableArray("translations", null); 310 if (sav != null) { 311 translations = new CompactVector3Array(); 312 Vector3f[] transCopy = new Vector3f[sav.length]; 313 System.arraycopy(sav, 0, transCopy, 0, sav.length); 314 translations.add(transCopy); 315 translations.freeze(); 316 } 317 } 318 if (rotations == null) { 319 Savable[] sav = ic.readSavableArray("rotations", null); 320 if (sav != null) { 321 rotations = new CompactQuaternionArray(); 322 Quaternion[] rotCopy = new Quaternion[sav.length]; 323 System.arraycopy(sav, 0, rotCopy, 0, sav.length); 324 rotations.add(rotCopy); 325 rotations.freeze(); 326 } 327 } 328 } 329 } 330 331 public void setTime(float time, float weight, AnimControl control, AnimChannel channel) { 332 throw new UnsupportedOperationException("Not supported yet."); 333 } 334 } 335