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