Browse Source

Spatial animation support added.

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8267 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
Kae..pl 14 years ago
parent
commit
bfc57cc7db

+ 98 - 0
engine/src/core/com/jme3/animation/SpatialAnimation.java

@@ -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 - 0
engine/src/core/com/jme3/animation/SpatialTrack.java

@@ -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 - 0
engine/src/test/jme3test/model/anim/TestSpatialAnim.java

@@ -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");
+    }
+}