• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
BoneTrack()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      */
BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations)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      */
BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales)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      */
BoneTrack(int targetBoneIndex)96     public BoneTrack(int targetBoneIndex) {
97         this.targetBoneIndex = targetBoneIndex;
98     }
99 
100     /**
101      * @return the bone index of this bone track.
102      */
getTargetBoneIndex()103     public int getTargetBoneIndex() {
104         return targetBoneIndex;
105     }
106 
107     /**
108      * return the array of rotations of this track
109      * @return
110      */
getRotations()111     public Quaternion[] getRotations() {
112         return rotations.toObjectArray();
113     }
114 
115     /**
116      * returns the array of scales for this track
117      * @return
118      */
getScales()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      */
getTimes()127     public float[] getTimes() {
128         return times;
129     }
130 
131     /**
132      * returns the array of translations of this track
133      * @return
134      */
getTranslations()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      */
setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations)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      */
setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales)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      */
setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars)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      */
getLength()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
clone()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
write(JmeExporter ex)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
read(JmeImporter im)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 
setTime(float time, float weight, AnimControl control, AnimChannel channel)331     public void setTime(float time, float weight, AnimControl control, AnimChannel channel) {
332         throw new UnsupportedOperationException("Not supported yet.");
333     }
334 }
335