|
@@ -0,0 +1,494 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (c) 2009-2011 jMonkeyEngine
|
|
|
|
+ * All rights reserved.
|
|
|
|
+ *
|
|
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
|
|
+ * modification, are permitted provided that the following conditions are
|
|
|
|
+ * met:
|
|
|
|
+ *
|
|
|
|
+ * * Redistributions of source code must retain the above copyright
|
|
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
|
|
+ *
|
|
|
|
+ * * Redistributions in binary form must reproduce the above copyright
|
|
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
|
|
+ *
|
|
|
|
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
|
|
+ * may be used to endorse or promote products derived from this software
|
|
|
|
+ * without specific prior written permission.
|
|
|
|
+ *
|
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
|
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
+ */
|
|
|
|
+package com.jme3.animation;
|
|
|
|
+
|
|
|
|
+import com.jme3.math.FastMath;
|
|
|
|
+import com.jme3.math.Quaternion;
|
|
|
|
+import com.jme3.math.Transform;
|
|
|
|
+import com.jme3.math.Vector3f;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * A convenience class to easily setup a spatial keyframed animation
|
|
|
|
+ * you can add some keyFrames for a given time or a given keyFrameIndex, for translation rotation and scale.
|
|
|
|
+ * The animationHelper will then generate an appropriate SpatialAnimation by interpolating values between the keyFrames.
|
|
|
|
+ * <br><br>
|
|
|
|
+ * Usage is : <br>
|
|
|
|
+ * - Create the AnimationHelper<br>
|
|
|
|
+ * - add some keyFrames<br>
|
|
|
|
+ * - call the buildAnimation() method that will retruna new Animation<br>
|
|
|
|
+ * - add the generated Animation to any existing AnimationControl<br>
|
|
|
|
+ * <br><br>
|
|
|
|
+ * Note that the first keyFrame (index 0) is defaulted with the identy transforms.
|
|
|
|
+ * If you want to change that you have to replace this keyFrame with any transform you want.
|
|
|
|
+ *
|
|
|
|
+ * @author Nehon
|
|
|
|
+ */
|
|
|
|
+public class AnimationHelper {
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * step for splitting rotation that have a n ange above PI/2
|
|
|
|
+ */
|
|
|
|
+ private final static float EULER_STEP = FastMath.QUARTER_PI * 3;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * enum to determine the type of interpolation
|
|
|
|
+ */
|
|
|
|
+ private enum Type {
|
|
|
|
+
|
|
|
|
+ Translation, Rotation, Scale;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Inner Rotation type class to kep track on a rotation Euler angle
|
|
|
|
+ */
|
|
|
|
+ protected class Rotation {
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * The rotation Quaternion
|
|
|
|
+ */
|
|
|
|
+ Quaternion rotation = new Quaternion();
|
|
|
|
+ /**
|
|
|
|
+ * This rotation expressed in Euler angles
|
|
|
|
+ */
|
|
|
|
+ Vector3f eulerAngles = new Vector3f();
|
|
|
|
+ /**
|
|
|
|
+ * the index of the parent key frame is this keyFrame is a splitted rotation
|
|
|
|
+ */
|
|
|
|
+ int masterKeyFrame = -1;
|
|
|
|
+
|
|
|
|
+ public Rotation() {
|
|
|
|
+ rotation.loadIdentity();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void set(Quaternion rot) {
|
|
|
|
+ rotation.set(rot);
|
|
|
|
+ float[] a = new float[3];
|
|
|
|
+ rotation.toAngles(a);
|
|
|
|
+ eulerAngles.set(a[0], a[1], a[2]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ void set(float x, float y, float z) {
|
|
|
|
+ float[] a = {x, y, z};
|
|
|
|
+ rotation.fromAngles(a);
|
|
|
|
+ eulerAngles.set(x, y, z);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * Name of the animation
|
|
|
|
+ */
|
|
|
|
+ protected String name;
|
|
|
|
+ /**
|
|
|
|
+ * frames per seconds
|
|
|
|
+ */
|
|
|
|
+ protected int fps;
|
|
|
|
+ /**
|
|
|
|
+ * Animation duration in seconds
|
|
|
|
+ */
|
|
|
|
+ protected float duration;
|
|
|
|
+ /**
|
|
|
|
+ * total number of frames
|
|
|
|
+ */
|
|
|
|
+ protected int totalFrames;
|
|
|
|
+ /**
|
|
|
|
+ * time per frame
|
|
|
|
+ */
|
|
|
|
+ protected float tpf;
|
|
|
|
+ /**
|
|
|
|
+ * Time array for this animation
|
|
|
|
+ */
|
|
|
|
+ protected float[] times;
|
|
|
|
+ /**
|
|
|
|
+ * Translation array for this animation
|
|
|
|
+ */
|
|
|
|
+ protected Vector3f[] translations;
|
|
|
|
+ /**
|
|
|
|
+ * rotation array for this animation
|
|
|
|
+ */
|
|
|
|
+ protected Quaternion[] rotations;
|
|
|
|
+ /**
|
|
|
|
+ * scales array for this animation
|
|
|
|
+ */
|
|
|
|
+ protected Vector3f[] scales;
|
|
|
|
+ /**
|
|
|
|
+ * The map of keyFrames to compute the animation. The key is the index of the frame
|
|
|
|
+ */
|
|
|
|
+ protected Vector3f[] keyFramesTranslation;
|
|
|
|
+ protected Vector3f[] keyFramesScale;
|
|
|
|
+ protected Rotation[] keyFramesRotation;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Creates and AnimationHelper
|
|
|
|
+ * @param duration the desired duration for the resulting animation
|
|
|
|
+ * @param name the name of the resulting animation
|
|
|
|
+ */
|
|
|
|
+ public AnimationHelper(float duration, String name) {
|
|
|
|
+ this(duration, name, 30);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Creates and AnimationHelper
|
|
|
|
+ * @param duration the desired duration for the resulting animation
|
|
|
|
+ * @param name the name of the resulting animation
|
|
|
|
+ * @param fps the number of frames per second for this animation (default is 30)
|
|
|
|
+ */
|
|
|
|
+ public AnimationHelper(float duration, String name, int fps) {
|
|
|
|
+ this.name = name;
|
|
|
|
+ this.duration = duration;
|
|
|
|
+ this.fps = fps;
|
|
|
|
+ totalFrames = (int) (fps * duration) + 1;
|
|
|
|
+ tpf = 1 / (float) fps;
|
|
|
|
+ times = new float[totalFrames];
|
|
|
|
+ translations = new Vector3f[totalFrames];
|
|
|
|
+ rotations = new Quaternion[totalFrames];
|
|
|
|
+ scales = new Vector3f[totalFrames];
|
|
|
|
+ keyFramesTranslation = new Vector3f[totalFrames];
|
|
|
|
+ keyFramesTranslation[0] = new Vector3f();
|
|
|
|
+ keyFramesScale = new Vector3f[totalFrames];
|
|
|
|
+ keyFramesScale[0] = new Vector3f(1, 1, 1);
|
|
|
|
+ keyFramesRotation = new Rotation[totalFrames];
|
|
|
|
+ keyFramesRotation[0] = new Rotation();
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given Transform at the given time
|
|
|
|
+ * @param time the time at which the keyFrame must be inserted
|
|
|
|
+ * @param transform the transforms to use for this keyFrame
|
|
|
|
+ */
|
|
|
|
+ public void addTimeTransform(float time, Transform transform) {
|
|
|
|
+ addKeyFrameTransform((int) (time / tpf), transform);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given Transform at the given keyFrame index
|
|
|
|
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
|
|
|
|
+ * @param transform the transforms to use for this keyFrame
|
|
|
|
+ */
|
|
|
|
+ public void addKeyFrameTransform(int keyFrameIndex, Transform transform) {
|
|
|
|
+ addKeyFrameTranslation(keyFrameIndex, transform.getTranslation());
|
|
|
|
+ addKeyFrameScale(keyFrameIndex, transform.getScale());
|
|
|
|
+ addKeyFrameRotation(keyFrameIndex, transform.getRotation());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given translation at the given time
|
|
|
|
+ * @param time the time at which the keyFrame must be inserted
|
|
|
|
+ * @param translation the translation to use for this keyFrame
|
|
|
|
+ */
|
|
|
|
+ public void addTimeTranslation(float time, Vector3f translation) {
|
|
|
|
+ addKeyFrameTranslation((int) (time / tpf), translation);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given translation at the given keyFrame index
|
|
|
|
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
|
|
|
|
+ * @param translation the translation to use for this keyFrame
|
|
|
|
+ */
|
|
|
|
+ public void addKeyFrameTranslation(int keyFrameIndex, Vector3f translation) {
|
|
|
|
+ Vector3f t = getTranslationForFrame(keyFrameIndex);
|
|
|
|
+ t.set(translation);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given rotation at the given time<br>
|
|
|
|
+ * This can't be used if the interpolated angle is higher than PI (180°)<br>
|
|
|
|
+ * Use {@link addTimeRotationAngles(float time, float x, float y, float z)} instead that uses Euler angles rotations.<br> *
|
|
|
|
+ * @param time the time at which the keyFrame must be inserted
|
|
|
|
+ * @param rotation the rotation Quaternion to use for this keyFrame
|
|
|
|
+ * @see #addTimeRotationAngles(float time, float x, float y, float z)
|
|
|
|
+ */
|
|
|
|
+ public void addTimeRotation(float time, Quaternion rotation) {
|
|
|
|
+ addKeyFrameRotation((int) (time / tpf), rotation);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given rotation at the given keyFrame index<br>
|
|
|
|
+ * This can't be used if the interpolated angle is higher than PI (180°)<br>
|
|
|
|
+ * Use {@link addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z)} instead that uses Euler angles rotations.
|
|
|
|
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
|
|
|
|
+ * @param rotation the rotation Quaternion to use for this keyFrame
|
|
|
|
+ * @see #addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z)
|
|
|
|
+ */
|
|
|
|
+ public void addKeyFrameRotation(int keyFrameIndex, Quaternion rotation) {
|
|
|
|
+ Rotation r = getRotationForFrame(keyFrameIndex);
|
|
|
|
+ r.set(rotation);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given rotation at the given time.<br>
|
|
|
|
+ * Rotation is expressed by Euler angles values in radians.<br>
|
|
|
|
+ * Note that the generated rotation will be stored as a quaternion and interpolated using a spherical linear interpolation (slerp)<br>
|
|
|
|
+ * Hence, this method may create intermediate keyFrames if the interpolation angle is higher than PI to ensure continuity in animation<br>
|
|
|
|
+ *
|
|
|
|
+ * @param time the time at which the keyFrame must be inserted
|
|
|
|
+ * @param x the rotation around the x axis (aka yaw) in radians
|
|
|
|
+ * @param y the rotation around the y axis (aka roll) in radians
|
|
|
|
+ * @param z the rotation around the z axis (aka pitch) in radians
|
|
|
|
+ */
|
|
|
|
+ public void addTimeRotationAngles(float time, float x, float y, float z) {
|
|
|
|
+ addKeyFrameRotationAngles((int) (time / tpf), x, y, z);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given rotation at the given key frame index.<br>
|
|
|
|
+ * Rotation is expressed by Euler angles values in radians.<br>
|
|
|
|
+ * Note that the generated rotation will be stored as a quaternion and interpolated using a spherical linear interpolation (slerp)<br>
|
|
|
|
+ * Hence, this method may create intermediate keyFrames if the interpolation angle is higher than PI to ensure continuity in animation<br>
|
|
|
|
+ *
|
|
|
|
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
|
|
|
|
+ * @param x the rotation around the x axis (aka yaw) in radians
|
|
|
|
+ * @param y the rotation around the y axis (aka roll) in radians
|
|
|
|
+ * @param z the rotation around the z axis (aka pitch) in radians
|
|
|
|
+ */
|
|
|
|
+ public void addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z) {
|
|
|
|
+ Rotation r = getRotationForFrame(keyFrameIndex);
|
|
|
|
+ r.set(x, y, z);
|
|
|
|
+
|
|
|
|
+ // if the delta of euler angles is higher than PI, we create intermediate keyframes
|
|
|
|
+ // since we are using quaternions and slerp for rotation interpolation, we cannot interpolate over an angle higher than PI
|
|
|
|
+ int prev = getPreviousKeyFrame(keyFrameIndex, keyFramesRotation);
|
|
|
|
+ //previous rotation keyframe
|
|
|
|
+ Rotation prevRot = keyFramesRotation[prev];
|
|
|
|
+ //the maximum delta angle (x,y or z)
|
|
|
|
+ float delta = Math.max(Math.abs(x - prevRot.eulerAngles.x), Math.abs(y - prevRot.eulerAngles.y));
|
|
|
|
+ delta = Math.max(delta, Math.abs(z - prevRot.eulerAngles.z));
|
|
|
|
+ //if delta > PI we have to create intermediates key frames
|
|
|
|
+ if (delta >= FastMath.PI) {
|
|
|
|
+ //frames delta
|
|
|
|
+ int dF = keyFrameIndex - prev;
|
|
|
|
+ //angle per frame for x,y ,z
|
|
|
|
+ float dXAngle = (x - prevRot.eulerAngles.x) / (float) dF;
|
|
|
|
+ float dYAngle = (y - prevRot.eulerAngles.y) / (float) dF;
|
|
|
|
+ float dZAngle = (z - prevRot.eulerAngles.z) / (float) dF;
|
|
|
|
+
|
|
|
|
+ // the keyFrame step
|
|
|
|
+ int keyStep = (int) (((float) (dF)) / delta * (float) EULER_STEP);
|
|
|
|
+ // the current keyFrame
|
|
|
|
+ int cursor = prev + keyStep;
|
|
|
|
+ while (cursor < keyFrameIndex) {
|
|
|
|
+ //for each step we create a new rotation by interpolating the angles
|
|
|
|
+ Rotation dr = getRotationForFrame(cursor);
|
|
|
|
+ dr.masterKeyFrame = keyFrameIndex;
|
|
|
|
+ dr.set(prevRot.eulerAngles.x + cursor * dXAngle, prevRot.eulerAngles.y + cursor * dYAngle, prevRot.eulerAngles.z + cursor * dZAngle);
|
|
|
|
+ cursor += keyStep;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given scale at the given time
|
|
|
|
+ * @param time the time at which the keyFrame must be inserted
|
|
|
|
+ * @param scale the scale to use for this keyFrame
|
|
|
|
+ */
|
|
|
|
+ public void addTimeScale(float time, Vector3f scale) {
|
|
|
|
+ addKeyFrameScale((int) (time / tpf), scale);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Adds a key frame for the given scale at the given keyFrame index
|
|
|
|
+ * @param keyFrameIndex the index at which the keyFrame must be inserted
|
|
|
|
+ * @param scale the scale to use for this keyFrame
|
|
|
|
+ */
|
|
|
|
+ public void addKeyFrameScale(int keyFrameIndex, Vector3f scale) {
|
|
|
|
+ Vector3f s = getScaleForFrame(keyFrameIndex);
|
|
|
|
+ s.set(scale);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * returns the translation for a given frame index
|
|
|
|
+ * creates the translation if it doesn't exists
|
|
|
|
+ * @param keyFrameIndex index
|
|
|
|
+ * @return the translation
|
|
|
|
+ */
|
|
|
|
+ private Vector3f getTranslationForFrame(int keyFrameIndex) {
|
|
|
|
+ if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) {
|
|
|
|
+ throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")");
|
|
|
|
+ }
|
|
|
|
+ Vector3f v = keyFramesTranslation[keyFrameIndex];
|
|
|
|
+ if (v == null) {
|
|
|
|
+ v = new Vector3f();
|
|
|
|
+ keyFramesTranslation[keyFrameIndex] = v;
|
|
|
|
+ }
|
|
|
|
+ return v;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * returns the scale for a given frame index
|
|
|
|
+ * creates the scale if it doesn't exists
|
|
|
|
+ * @param keyFrameIndex index
|
|
|
|
+ * @return the scale
|
|
|
|
+ */
|
|
|
|
+ private Vector3f getScaleForFrame(int keyFrameIndex) {
|
|
|
|
+ if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) {
|
|
|
|
+ throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")");
|
|
|
|
+ }
|
|
|
|
+ Vector3f v = keyFramesScale[keyFrameIndex];
|
|
|
|
+ if (v == null) {
|
|
|
|
+ v = new Vector3f();
|
|
|
|
+ keyFramesScale[keyFrameIndex] = v;
|
|
|
|
+ }
|
|
|
|
+ return v;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * returns the rotation for a given frame index
|
|
|
|
+ * creates the rotation if it doesn't exists
|
|
|
|
+ * @param keyFrameIndex index
|
|
|
|
+ * @return the rotation
|
|
|
|
+ */
|
|
|
|
+ private Rotation getRotationForFrame(int keyFrameIndex) {
|
|
|
|
+ if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) {
|
|
|
|
+ throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")");
|
|
|
|
+ }
|
|
|
|
+ Rotation v = keyFramesRotation[keyFrameIndex];
|
|
|
|
+ if (v == null) {
|
|
|
|
+ v = new Rotation();
|
|
|
|
+ keyFramesRotation[keyFrameIndex] = v;
|
|
|
|
+ }
|
|
|
|
+ return v;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Creates an Animation based on the keyFrames previously added to the helper.
|
|
|
|
+ * @return the generated animation
|
|
|
|
+ */
|
|
|
|
+ public Animation buildAnimation() {
|
|
|
|
+ interpolateTime();
|
|
|
|
+ interpolate(keyFramesTranslation, Type.Translation);
|
|
|
|
+ interpolate(keyFramesRotation, Type.Rotation);
|
|
|
|
+ interpolate(keyFramesScale, Type.Scale);
|
|
|
|
+
|
|
|
|
+ SpatialTrack spatialTrack = new SpatialTrack(times, translations, rotations, scales);
|
|
|
|
+
|
|
|
|
+ //creating the animation
|
|
|
|
+ Animation spatialAnimation = new Animation(name, duration);
|
|
|
|
+ spatialAnimation.setTracks(new SpatialTrack[]{spatialTrack});
|
|
|
|
+
|
|
|
|
+ return spatialAnimation;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * interpolates time values
|
|
|
|
+ */
|
|
|
|
+ private void interpolateTime() {
|
|
|
|
+ for (int i = 0; i < totalFrames; i++) {
|
|
|
|
+ times[i] = i * tpf;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Interpolates over the key frames for the given keyFrame array and the given type of transform
|
|
|
|
+ * @param keyFrames the keyFrames array
|
|
|
|
+ * @param type the type of transforms
|
|
|
|
+ */
|
|
|
|
+ private void interpolate(Object[] keyFrames, Type type) {
|
|
|
|
+ int i = 0;
|
|
|
|
+ while (i < totalFrames) {
|
|
|
|
+ //fetching the next keyFrame index transform in the array
|
|
|
|
+ int key = getNextKeyFrame(i, keyFrames);
|
|
|
|
+ if (key != -1) {
|
|
|
|
+ //computing the frame span to interpolate over
|
|
|
|
+ int span = key - i;
|
|
|
|
+ //interating over the frames
|
|
|
|
+ for (int j = i; j <= key; j++) {
|
|
|
|
+ // computing interpolation value
|
|
|
|
+ float val = (float) (j - i) / (float) span;
|
|
|
|
+ //interpolationg depending on the transform type
|
|
|
|
+ switch (type) {
|
|
|
|
+ case Translation:
|
|
|
|
+ translations[j] = FastMath.interpolateLinear(val, (Vector3f) keyFrames[i], (Vector3f) keyFrames[key]);
|
|
|
|
+ break;
|
|
|
|
+ case Rotation:
|
|
|
|
+ Quaternion rot = new Quaternion();
|
|
|
|
+ rotations[j] = rot.slerp(((Rotation) keyFrames[i]).rotation, ((Rotation) keyFrames[key]).rotation, val);
|
|
|
|
+ break;
|
|
|
|
+ case Scale:
|
|
|
|
+ scales[j] = FastMath.interpolateLinear(val, (Vector3f) keyFrames[i], (Vector3f) keyFrames[key]);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ //jumping to the next keyFrame
|
|
|
|
+ i = key;
|
|
|
|
+ } else {
|
|
|
|
+ //No more key frame, filling the array witht he last transform computed.
|
|
|
|
+ for (int j = i; j < totalFrames; j++) {
|
|
|
|
+
|
|
|
|
+ switch (type) {
|
|
|
|
+ case Translation:
|
|
|
|
+ translations[j] = ((Vector3f) keyFrames[i]).clone();
|
|
|
|
+ break;
|
|
|
|
+ case Rotation:
|
|
|
|
+ rotations[j] = ((Quaternion) ((Rotation) keyFrames[i]).rotation).clone();
|
|
|
|
+ break;
|
|
|
|
+ case Scale:
|
|
|
|
+ scales[j] = ((Vector3f) keyFrames[i]).clone();
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ //we're done
|
|
|
|
+ i = totalFrames;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the index of the next keyFrame that as a transform
|
|
|
|
+ * @param index the start index
|
|
|
|
+ * @param keyFrames the keyFrames array
|
|
|
|
+ * @return the index of the next keyFrame
|
|
|
|
+ */
|
|
|
|
+ private int getNextKeyFrame(int index, Object[] keyFrames) {
|
|
|
|
+ for (int i = index + 1; i < totalFrames; i++) {
|
|
|
|
+ if (keyFrames[i] != null) {
|
|
|
|
+ return i;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get the index of the previous keyFrame that as a transform
|
|
|
|
+ * @param index the start index
|
|
|
|
+ * @param keyFrames the keyFrames array
|
|
|
|
+ * @return the index of the previous keyFrame
|
|
|
|
+ */
|
|
|
|
+ private int getPreviousKeyFrame(int index, Object[] keyFrames) {
|
|
|
|
+ for (int i = index - 1; i >= 0; i--) {
|
|
|
|
+ if (keyFrames[i] != null) {
|
|
|
|
+ return i;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+}
|