Created an AnimationHelper to ease the creation process of a spatial animation, based on key frames interpolation.
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8955 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
1b1bad7da5
commit
7de07d056c
@ -70,21 +70,17 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
* Skeleton object must contain corresponding data for the targets' weight buffers.
|
||||
*/
|
||||
Skeleton skeleton;
|
||||
|
||||
/** only used for backward compatibility */
|
||||
@Deprecated
|
||||
private SkeletonControl skeletonControl;
|
||||
|
||||
/**
|
||||
* List of animations
|
||||
*/
|
||||
HashMap<String, Animation> animationMap;
|
||||
|
||||
/**
|
||||
* Animation channels
|
||||
*/
|
||||
private transient ArrayList<AnimChannel> channels = new ArrayList<AnimChannel>();
|
||||
|
||||
/**
|
||||
* Animation event listeners
|
||||
*/
|
||||
@ -117,14 +113,14 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
clone.spatial = spatial;
|
||||
clone.channels = new ArrayList<AnimChannel>();
|
||||
clone.listeners = new ArrayList<AnimEventListener>();
|
||||
|
||||
if (skeleton != null){
|
||||
|
||||
if (skeleton != null) {
|
||||
clone.skeleton = new Skeleton(skeleton);
|
||||
}
|
||||
|
||||
|
||||
// animationMap is reference-copied, animation data should be shared
|
||||
// to reduce memory usage.
|
||||
|
||||
|
||||
return clone;
|
||||
} catch (CloneNotSupportedException ex) {
|
||||
throw new AssertionError();
|
||||
@ -147,6 +143,9 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
* such named animation exists.
|
||||
*/
|
||||
public Animation getAnim(String name) {
|
||||
if (animationMap == null) {
|
||||
animationMap = new HashMap<String, Animation>();
|
||||
}
|
||||
return animationMap.get(name);
|
||||
}
|
||||
|
||||
@ -156,6 +155,9 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
* @param anim The animation to add.
|
||||
*/
|
||||
public void addAnim(Animation anim) {
|
||||
if (animationMap == null) {
|
||||
animationMap = new HashMap<String, Animation>();
|
||||
}
|
||||
animationMap.put(anim.getName(), anim);
|
||||
}
|
||||
|
||||
@ -273,7 +275,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
*/
|
||||
@Override
|
||||
public void setSpatial(Spatial spatial) {
|
||||
if (spatial == null && skeletonControl != null){
|
||||
if (spatial == null && skeletonControl != null) {
|
||||
this.spatial.removeControl(skeletonControl);
|
||||
}
|
||||
|
||||
@ -313,13 +315,13 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
|
||||
return a.getLength();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@Override
|
||||
protected void controlUpdate(float tpf) {
|
||||
if (skeleton != null){
|
||||
if (skeleton != null) {
|
||||
skeleton.reset(); // reset skeleton to bind pose
|
||||
}
|
||||
|
||||
@ -329,7 +331,7 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
}
|
||||
vars.release();
|
||||
|
||||
if (skeleton != null){
|
||||
if (skeleton != null) {
|
||||
skeleton.updateWorldVectors();
|
||||
}
|
||||
}
|
||||
@ -356,10 +358,10 @@ public final class AnimControl extends AbstractControl implements Cloneable {
|
||||
skeleton = (Skeleton) in.readSavable("skeleton", null);
|
||||
animationMap = (HashMap<String, Animation>) in.readStringSavableMap("animations", null);
|
||||
|
||||
if (im.getFormatVersion() == 0){
|
||||
if (im.getFormatVersion() == 0) {
|
||||
// Changed for backward compatibility with j3o files generated
|
||||
// before the AnimControl/SkeletonControl split.
|
||||
|
||||
|
||||
// If we find a target mesh array the AnimControl creates the
|
||||
// SkeletonControl for old files and add it to the spatial.
|
||||
// When backward compatibility won't be needed anymore this can deleted
|
||||
|
494
engine/src/core/com/jme3/animation/AnimationHelper.java
Normal file
494
engine/src/core/com/jme3/animation/AnimationHelper.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
85
engine/src/test/jme3test/model/anim/TestAnimationHelper.java
Normal file
85
engine/src/test/jme3test/model/anim/TestAnimationHelper.java
Normal file
@ -0,0 +1,85 @@
|
||||
package jme3test.model.anim;
|
||||
|
||||
import com.jme3.animation.AnimControl;
|
||||
import com.jme3.animation.AnimationHelper;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.shape.Box;
|
||||
import com.jme3.util.TangentBinormalGenerator;
|
||||
|
||||
public class TestAnimationHelper extends SimpleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestSpatialAnim app = new TestSpatialAnim();
|
||||
app.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
|
||||
AmbientLight al = new AmbientLight();
|
||||
rootNode.addLight(al);
|
||||
|
||||
DirectionalLight dl = new DirectionalLight();
|
||||
dl.setDirection(Vector3f.UNIT_XYZ.negate());
|
||||
rootNode.addLight(dl);
|
||||
|
||||
// Create model
|
||||
Box box = new Box(1, 1, 1);
|
||||
Geometry geom = new Geometry("box", box);
|
||||
geom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
|
||||
Node model = new Node("model");
|
||||
model.attachChild(geom);
|
||||
|
||||
Box child = new Box(0.5f, 0.5f, 0.5f);
|
||||
Geometry childGeom = new Geometry("box", child);
|
||||
childGeom.setMaterial(assetManager.loadMaterial("Textures/Terrain/BrickWall/BrickWall.j3m"));
|
||||
Node childModel = new Node("childmodel");
|
||||
childModel.setLocalTranslation(2, 2, 2);
|
||||
childModel.attachChild(childGeom);
|
||||
model.attachChild(childModel);
|
||||
TangentBinormalGenerator.generate(model);
|
||||
|
||||
//creating quite complex animation witht the AnimationHelper
|
||||
// animation of 6 seconds named "anim" and with 25 frames per second
|
||||
AnimationHelper animationHelper = new AnimationHelper(6, "anim", 25);
|
||||
|
||||
//creating a translation keyFrame at time = 3 with a translation on the x axis of 5 WU
|
||||
animationHelper.addTimeTranslation(3, new Vector3f(5, 0, 0));
|
||||
//reseting the translation to the start position at time = 6
|
||||
animationHelper.addTimeTranslation(6, new Vector3f(0, 0, 0));
|
||||
|
||||
//Creating a scale keyFrame at time = 2 with the unit scale.
|
||||
animationHelper.addTimeScale(2, new Vector3f(1, 1, 1));
|
||||
//Creating a scale keyFrame at time = 4 scaling to 1.5
|
||||
animationHelper.addTimeScale(4, new Vector3f(1.5f, 1.5f, 1.5f));
|
||||
//reseting the scale to the start value at time = 5
|
||||
animationHelper.addTimeScale(5, new Vector3f(1, 1, 1));
|
||||
|
||||
|
||||
//Creating a rotation keyFrame at time = 0.5 of quarter PI around the Z axis
|
||||
animationHelper.addTimeRotation(0.5f,new Quaternion().fromAngleAxis(FastMath.QUARTER_PI, Vector3f.UNIT_Z));
|
||||
//rotating back to initial rotation value at time = 1
|
||||
animationHelper.addTimeRotation(1,Quaternion.IDENTITY);
|
||||
//Creating a rotation keyFrame at time = 2. Note that i used the Euler angle version because the angle is higher than PI
|
||||
//this should result in a complete revolution of the spatial around the x axis in 1 second (from 1 to 2)
|
||||
animationHelper.addTimeRotationAngles(2, FastMath.TWO_PI,0, 0);
|
||||
|
||||
|
||||
AnimControl control = new AnimControl();
|
||||
control.addAnim(animationHelper.buildAnimation());
|
||||
|
||||
model.addControl(control);
|
||||
|
||||
rootNode.attachChild(model);
|
||||
|
||||
//run animation
|
||||
control.createChannel().setAnim("anim");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user