1package com.jme3.animation;
2
3import com.jme3.export.InputCapsule;
4import com.jme3.export.JmeExporter;
5import com.jme3.export.JmeImporter;
6import com.jme3.export.OutputCapsule;
7import com.jme3.math.Quaternion;
8import com.jme3.math.Vector3f;
9import com.jme3.scene.Spatial;
10import com.jme3.util.TempVars;
11import java.io.IOException;
12import java.util.Arrays;
13
14/**
15 * This class represents the track for spatial animation.
16 *
17 * @author Marcin Roguski (Kaelthas)
18 */
19public 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
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     */
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     */
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     */
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     */
176    public Quaternion[] getRotations() {
177            return rotations == null ? null : rotations.toObjectArray();
178    }
179
180    /**
181     * @return the array of scales for this track
182     */
183    public Vector3f[] getScales() {
184            return scales == null ? null : scales.toObjectArray();
185    }
186
187    /**
188     * @return the arrays of time for this track
189     */
190    public float[] getTimes() {
191            return times;
192    }
193
194    /**
195     * @return the array of translations of this track
196     */
197    public Vector3f[] getTranslations() {
198            return translations == null ? null : translations.toObjectArray();
199    }
200
201    /**
202     * @return the length of the track
203     */
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
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
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
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