Spatial animation support added.
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8267 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
6600473a82
commit
bfc57cc7db
98
engine/src/core/com/jme3/animation/SpatialAnimation.java
Normal file
98
engine/src/core/com/jme3/animation/SpatialAnimation.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package com.jme3.animation;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.jme3.export.InputCapsule;
|
||||||
|
import com.jme3.export.JmeExporter;
|
||||||
|
import com.jme3.export.JmeImporter;
|
||||||
|
import com.jme3.export.OutputCapsule;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class animates the whole spatials. All spatial's children are also
|
||||||
|
* affected by their parent's movement.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class SpatialAnimation implements Animation {
|
||||||
|
/** The name of the animation. */
|
||||||
|
private String name;
|
||||||
|
/** The length of the animation. */
|
||||||
|
private float length;
|
||||||
|
/** The track of the animation. */
|
||||||
|
private SpatialTrack track;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Stores the name and length of the animation.
|
||||||
|
* @param name the name of the animation
|
||||||
|
* @param length the length of the animation
|
||||||
|
*/
|
||||||
|
public SpatialAnimation(String name, float length) {
|
||||||
|
this.name = name;
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTime(float time, float blendAmount, AnimControl control,
|
||||||
|
AnimChannel channel) {
|
||||||
|
Spatial spatial = control.getSpatial();
|
||||||
|
if (spatial != null) {
|
||||||
|
track.setTime(time, spatial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method sets the animation track.
|
||||||
|
* @param track the animation track
|
||||||
|
*/
|
||||||
|
public void setTrack(SpatialTrack track) {
|
||||||
|
this.track = track;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the animation track
|
||||||
|
*/
|
||||||
|
public SpatialTrack getTracks() {
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SpatialAnim[name=" + name + ", length=" + length + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Animation clone() {
|
||||||
|
try {
|
||||||
|
return (Animation) super.clone();
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
oc.write(name, "name", null);
|
||||||
|
oc.write(length, "length", 0);
|
||||||
|
oc.write(track, "track", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
InputCapsule in = im.getCapsule(this);
|
||||||
|
name = in.readString("name", null);
|
||||||
|
length = in.readFloat("length", 0);
|
||||||
|
track = (SpatialTrack) in.readSavable("track", null);
|
||||||
|
}
|
||||||
|
}
|
195
engine/src/core/com/jme3/animation/SpatialTrack.java
Normal file
195
engine/src/core/com/jme3/animation/SpatialTrack.java
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package com.jme3.animation;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.jme3.export.InputCapsule;
|
||||||
|
import com.jme3.export.JmeExporter;
|
||||||
|
import com.jme3.export.JmeImporter;
|
||||||
|
import com.jme3.export.OutputCapsule;
|
||||||
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the track for spatial animation.
|
||||||
|
*
|
||||||
|
* @author Marcin Roguski (Kaelthas)
|
||||||
|
*/
|
||||||
|
public class SpatialTrack implements Savable {
|
||||||
|
/** Translations of the track. */
|
||||||
|
private CompactVector3Array translations;
|
||||||
|
/** Rotations of the track. */
|
||||||
|
private CompactQuaternionArray rotations;
|
||||||
|
/** Scales of the track. */
|
||||||
|
private CompactVector3Array scales;
|
||||||
|
/** The times of the animations frames. */
|
||||||
|
private float[] times;
|
||||||
|
|
||||||
|
// temp vectors for interpolation
|
||||||
|
private transient final Vector3f tempV = new Vector3f();
|
||||||
|
private transient final Quaternion tempQ = new Quaternion();
|
||||||
|
private transient final Vector3f tempS = new Vector3f();
|
||||||
|
private transient final Vector3f tempV2 = new Vector3f();
|
||||||
|
private transient final Quaternion tempQ2 = new Quaternion();
|
||||||
|
private transient final Vector3f tempS2 = new Vector3f();
|
||||||
|
|
||||||
|
public SpatialTrack() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a spatial track for the given track data.
|
||||||
|
*
|
||||||
|
* @param times
|
||||||
|
* a float array with the time of each frame
|
||||||
|
* @param translations
|
||||||
|
* the translation of the bone for each frame
|
||||||
|
* @param rotations
|
||||||
|
* the rotation of the bone for each frame
|
||||||
|
* @param scales
|
||||||
|
* the scale of the bone for each frame
|
||||||
|
*/
|
||||||
|
public SpatialTrack(float[] times, Vector3f[] translations,
|
||||||
|
Quaternion[] rotations, Vector3f[] scales) {
|
||||||
|
this.setKeyframes(times, translations, rotations, scales);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Modify the spatial which this track modifies.
|
||||||
|
*
|
||||||
|
* @param time
|
||||||
|
* the current time of the animation
|
||||||
|
* @param spatial
|
||||||
|
* the spatial that should be animated with this track
|
||||||
|
*/
|
||||||
|
public void setTime(float time, Spatial spatial) {
|
||||||
|
int lastFrame = times.length - 1;
|
||||||
|
if (time < 0 || lastFrame == 0) {
|
||||||
|
rotations.get(0, tempQ);
|
||||||
|
translations.get(0, tempV);
|
||||||
|
if (scales != null) {
|
||||||
|
scales.get(0, tempS);
|
||||||
|
}
|
||||||
|
} else if (time >= times[lastFrame]) {
|
||||||
|
rotations.get(lastFrame, tempQ);
|
||||||
|
translations.get(lastFrame, tempV);
|
||||||
|
if (scales != null) {
|
||||||
|
scales.get(lastFrame, tempS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int startFrame = 0;
|
||||||
|
int endFrame = 1;
|
||||||
|
// use lastFrame so we never overflow the array
|
||||||
|
for (int i = 0; i < lastFrame && times[i] < time; ++i) {
|
||||||
|
startFrame = i;
|
||||||
|
endFrame = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]);
|
||||||
|
|
||||||
|
rotations.get(startFrame, tempQ);
|
||||||
|
translations.get(startFrame, tempV);
|
||||||
|
if (scales != null) {
|
||||||
|
scales.get(startFrame, tempS);
|
||||||
|
}
|
||||||
|
rotations.get(endFrame, tempQ2);
|
||||||
|
translations.get(endFrame, tempV2);
|
||||||
|
if (scales != null) {
|
||||||
|
scales.get(endFrame, tempS2);
|
||||||
|
}
|
||||||
|
tempQ.nlerp(tempQ2, blend);
|
||||||
|
tempV.interpolate(tempV2, blend);
|
||||||
|
tempS.interpolate(tempS2, blend);
|
||||||
|
}
|
||||||
|
spatial.setLocalTranslation(tempV);
|
||||||
|
spatial.setLocalRotation(tempQ);
|
||||||
|
if (scales != null) {
|
||||||
|
spatial.setLocalScale(tempS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the translations, rotations and scales for this track.
|
||||||
|
*
|
||||||
|
* @param times
|
||||||
|
* a float array with the time of each frame
|
||||||
|
* @param translations
|
||||||
|
* the translation of the bone for each frame
|
||||||
|
* @param rotations
|
||||||
|
* the rotation of the bone for each frame
|
||||||
|
* @param scales
|
||||||
|
* the scale of the bone for each frame
|
||||||
|
*/
|
||||||
|
public void setKeyframes(float[] times, Vector3f[] translations,
|
||||||
|
Quaternion[] rotations, Vector3f[] scales) {
|
||||||
|
if (times.length == 0) {
|
||||||
|
throw new RuntimeException("BoneTrack with no keyframes!");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert times.length == translations.length
|
||||||
|
&& times.length == rotations.length;
|
||||||
|
|
||||||
|
this.times = times;
|
||||||
|
this.translations = new CompactVector3Array();
|
||||||
|
this.translations.add(translations);
|
||||||
|
this.translations.freeze();
|
||||||
|
this.rotations = new CompactQuaternionArray();
|
||||||
|
this.rotations.add(rotations);
|
||||||
|
this.rotations.freeze();
|
||||||
|
|
||||||
|
assert times.length == scales.length;
|
||||||
|
|
||||||
|
if (scales != null) {
|
||||||
|
this.scales = new CompactVector3Array();
|
||||||
|
this.scales.add(scales);
|
||||||
|
this.scales.freeze();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the array of rotations of this track
|
||||||
|
*/
|
||||||
|
public Quaternion[] getRotations() {
|
||||||
|
return rotations.toObjectArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the array of scales for this track
|
||||||
|
*/
|
||||||
|
public Vector3f[] getScales() {
|
||||||
|
return scales == null ? null : scales.toObjectArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the arrays of time for this track
|
||||||
|
*/
|
||||||
|
public float[] getTimes() {
|
||||||
|
return times;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the array of translations of this track
|
||||||
|
*/
|
||||||
|
public Vector3f[] getTranslations() {
|
||||||
|
return translations.toObjectArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JmeExporter ex) throws IOException {
|
||||||
|
OutputCapsule oc = ex.getCapsule(this);
|
||||||
|
oc.write(translations, "translations", null);
|
||||||
|
oc.write(rotations, "rotations", null);
|
||||||
|
oc.write(times, "times", null);
|
||||||
|
oc.write(scales, "scales", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(JmeImporter im) throws IOException {
|
||||||
|
InputCapsule ic = im.getCapsule(this);
|
||||||
|
translations = (CompactVector3Array) ic.readSavable("translations", null);
|
||||||
|
rotations = (CompactQuaternionArray) ic.readSavable("rotations", null);
|
||||||
|
times = ic.readFloatArray("times", null);
|
||||||
|
scales = (CompactVector3Array) ic.readSavable("scales", null);
|
||||||
|
}
|
||||||
|
}
|
89
engine/src/test/jme3test/model/anim/TestSpatialAnim.java
Normal file
89
engine/src/test/jme3test/model/anim/TestSpatialAnim.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package jme3test.model.anim;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.jme3.animation.AnimControl;
|
||||||
|
import com.jme3.animation.Animation;
|
||||||
|
import com.jme3.animation.SpatialAnimation;
|
||||||
|
import com.jme3.animation.SpatialTrack;
|
||||||
|
import com.jme3.app.SimpleApplication;
|
||||||
|
import com.jme3.light.AmbientLight;
|
||||||
|
import com.jme3.light.DirectionalLight;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class TestSpatialAnim 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);
|
||||||
|
|
||||||
|
//animation parameters
|
||||||
|
float animTime = 5;
|
||||||
|
int fps = 25;
|
||||||
|
float totalXLength = 10;
|
||||||
|
|
||||||
|
//calculating frames
|
||||||
|
int totalFrames = (int) (fps * animTime);
|
||||||
|
float dT = animTime / totalFrames, t = 0;
|
||||||
|
float dX = totalXLength / totalFrames, x = 0;
|
||||||
|
float[] times = new float[totalFrames];
|
||||||
|
Vector3f[] translations = new Vector3f[totalFrames];
|
||||||
|
Quaternion[] rotations = new Quaternion[totalFrames];
|
||||||
|
Vector3f[] scales = new Vector3f[totalFrames];
|
||||||
|
for (int i = 0; i < totalFrames; ++i) {
|
||||||
|
times[i] = t;
|
||||||
|
t += dT;
|
||||||
|
translations[i] = new Vector3f(x, 0, 0);
|
||||||
|
x += dX;
|
||||||
|
rotations[i] = Quaternion.IDENTITY;
|
||||||
|
scales[i] = Vector3f.UNIT_XYZ;
|
||||||
|
}
|
||||||
|
SpatialTrack spatialTrack = new SpatialTrack(times, translations, rotations, scales);
|
||||||
|
|
||||||
|
//creating the animation
|
||||||
|
SpatialAnimation spatialAnimation = new SpatialAnimation("anim", animTime);
|
||||||
|
spatialAnimation.setTrack(spatialTrack);
|
||||||
|
|
||||||
|
//create spatial animation control
|
||||||
|
AnimControl control = new AnimControl();
|
||||||
|
HashMap<String, Animation> animations = new HashMap<String, Animation>();
|
||||||
|
animations.put("anim", spatialAnimation);
|
||||||
|
control.setAnimations(animations);
|
||||||
|
model.addControl(control);
|
||||||
|
|
||||||
|
rootNode.attachChild(model);
|
||||||
|
|
||||||
|
//run animation
|
||||||
|
control.createChannel().setAnim("anim");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user