diff --git a/engine/src/core/com/jme3/animation/Animation.java b/engine/src/core/com/jme3/animation/Animation.java index 456bf2fb9..ab41d3804 100644 --- a/engine/src/core/com/jme3/animation/Animation.java +++ b/engine/src/core/com/jme3/animation/Animation.java @@ -31,15 +31,174 @@ */ package com.jme3.animation; +import java.io.IOException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.List; + +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.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; -public interface Animation extends Savable, Cloneable { +/** + * The animation class updates the animation target with the tracks of a given type. + * @author Kirill Vainer, Marcin Roguski (Kaelthas) + */ +public class Animation implements Savable, Cloneable { + /** The name of the animation. */ + private String name; + /** The length of the animation. */ + private float length; + /** The tracks of the animation. */ + private Track[] tracks; + + /** + * Serialization-only. Do not use. + */ + public Animation() {} + + /** + * Creates a new BoneAnimation with the given name and length. + * + * @param name The name of the bone animation. + * @param length Length in seconds of the bone animation. + */ + public Animation(String name, float length) { + this.name = name; + this.length = length; + } + + /** + * @return the name of the animation + */ + public String getName() { + return name; + } - public String getName(); + /** + * @return the length of the animation + */ + public float getLength() { + return length; + } - public float getLength(); + /** + * This method sets the current time of the animation. + * This method behaves differently for every known track type. + * Override this method if you have your own type of track. + * @param time the time of the animation + * @param blendAmount the blend amount factor + * @param control the nimation control + * @param channel the animation channel + */ + void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel) { + if(tracks != null && tracks.length > 0) { + Track trackInstance = tracks[0]; + if(trackInstance instanceof SpatialTrack) { + Spatial spatial = control.getSpatial(); + if (spatial != null) { + ((SpatialTrack)tracks[0]).setTime(time, spatial, blendAmount); + } + } else if(trackInstance instanceof BoneTrack) { + BitSet affectedBones = channel.getAffectedBones(); + Skeleton skeleton = control.getSkeleton(); + for (int i = 0; i < tracks.length; ++i) { + if (affectedBones == null || affectedBones.get(((BoneTrack)tracks[i]).getTargetIndex())) { + ((BoneTrack)tracks[i]).setTime(time, skeleton, blendAmount); + } + } + } else if(trackInstance instanceof PoseTrack) { + Spatial spatial = control.getSpatial(); + List meshes = new ArrayList(); + this.getMeshes(spatial, meshes); + if(meshes.size() > 0) { + Mesh[] targets = meshes.toArray(new Mesh[meshes.size()]); + for (int i = 0; i < tracks.length; ++i){ + ((PoseTrack)tracks[i]).setTime(time, targets, blendAmount); + } + } + } + } + } - public void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel); + /** + * This method returns the meshes within the given spatial. + * @param spatial the spatial to search the meshes from + * @param meshes the collection that will have the found meshes + */ + private void getMeshes(Spatial spatial, Collection meshes) { + if(spatial instanceof Geometry) { + meshes.add(((Geometry) spatial).getMesh()); + } else if(spatial instanceof Node) { + for(Spatial child : ((Node) spatial).getChildren()) { + this.getMeshes(child, meshes); + } + } + } - public Animation clone(); + /** + * Set the {@link Track}s to be used by this animation. + *

+ * The array should be organized so that the appropriate BoneTrack can + * be retrieved based on a bone index. + * + * @param tracks the tracks to set + */ + public void setTracks(Track[] tracks){ + this.tracks = tracks; + } + + /** + * @return the tracks of the animation + */ + public Track[] getTracks() { + return tracks; + } + + /** + * This method creates a clone of the current object. + * @return a clone of the current object + */ + public Animation clone() { + try { + Animation result = (Animation) super.clone(); + if (tracks != null) { + result.tracks = tracks.clone(); + for (int i = 0; i < tracks.length; ++i) { + result.tracks[i] = this.tracks[i].clone(); + } + } + return result; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + + @Override + public String toString() { + return "Animation[name=" + name + ", length=" + length + ']'; + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule out = ex.getCapsule(this); + out.write(name, "name", null); + out.write(length, "length", 0f); + out.write(tracks, "tracks", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule in = im.getCapsule(this); + name = in.readString("name", null); + length = in.readFloat("length", 0f); + tracks = (Track[]) in.readSavableArray("tracks", null); + } } diff --git a/engine/src/core/com/jme3/animation/BoneAnimation.java b/engine/src/core/com/jme3/animation/BoneAnimation.java index 2d08c687c..9494d4087 100644 --- a/engine/src/core/com/jme3/animation/BoneAnimation.java +++ b/engine/src/core/com/jme3/animation/BoneAnimation.java @@ -47,8 +47,10 @@ import java.util.BitSet; * to apply the animation. * * @author Kirill Vainer + * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack) */ -public final class BoneAnimation implements Animation, Savable, Cloneable { +@Deprecated +public final class BoneAnimation extends Animation { private String name; private float length; @@ -118,7 +120,7 @@ public final class BoneAnimation implements Animation, Savable, Cloneable { for (int i = 0; i < tracks.length; i++) { if (affectedBones == null - || affectedBones.get(tracks[i].getTargetBoneIndex())) { + || affectedBones.get(tracks[i].getTargetIndex())) { tracks[i].setTime(time, skeleton, blendAmount); } } @@ -131,12 +133,7 @@ public final class BoneAnimation implements Animation, Savable, Cloneable { @Override public BoneAnimation clone() { - BoneAnimation result; - try { - result = (BoneAnimation) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } + BoneAnimation result = (BoneAnimation) super.clone(); if (result.tracks == null) { result.tracks = new BoneTrack[tracks.length]; } @@ -157,7 +154,7 @@ public final class BoneAnimation implements Animation, Savable, Cloneable { scales[j] = sourceScales != null ? sourceScales[j].clone() : new Vector3f(1.0f, 1.0f, 1.0f); } // times do not change, no need to clone them - result.tracks[i] = new BoneTrack(tracks[i].getTargetBoneIndex(), times, + result.tracks[i] = new BoneTrack(tracks[i].getTargetIndex(), times, translations, rotations, scales); } return result; diff --git a/engine/src/core/com/jme3/animation/BoneTrack.java b/engine/src/core/com/jme3/animation/BoneTrack.java index 017de7039..98dbc18ad 100644 --- a/engine/src/core/com/jme3/animation/BoneTrack.java +++ b/engine/src/core/com/jme3/animation/BoneTrack.java @@ -45,7 +45,7 @@ import java.io.IOException; * * @author Kirill Vainer */ -public final class BoneTrack implements Savable { +public final class BoneTrack implements Track { /** * Bone index in the skeleton which this track effects. @@ -105,14 +105,22 @@ public final class BoneTrack implements Savable { this.targetBoneIndex = targetBoneIndex; } - /** - * returns the bone index of this bone track - * @return + /** + * @return the bone index of this bone track + * @deprecated use getTargetIndex() instead */ + @Deprecated public int getTargetBoneIndex() { return targetBoneIndex; } + /** + * @return the bone index of this bone track + */ + public int getTargetIndex() { + return targetBoneIndex; + } + /** * return the array of rotations of this track * @return @@ -245,6 +253,30 @@ public final class BoneTrack implements Savable { } } + /** + * This method creates a clone of the current object. + * @return a clone of the current object + */ + public BoneTrack clone() { + int tablesLength = times.length; + + float[] times = this.times.clone(); + Vector3f[] sourceTranslations = this.getTranslations(); + Quaternion[] sourceRotations = this.getRotations(); + Vector3f[] sourceScales = this.getScales(); + + Vector3f[] translations = new Vector3f[tablesLength]; + Quaternion[] rotations = new Quaternion[tablesLength]; + Vector3f[] scales = new Vector3f[tablesLength]; + for (int i = 0; i < tablesLength; ++i) { + translations[i] = sourceTranslations[i].clone(); + rotations[i] = sourceRotations[i].clone(); + scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f); + } + //need to use the constructor here because of the final fields used in this class + return new BoneTrack(targetBoneIndex, times, translations, rotations, scales); + } + @Override public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); diff --git a/engine/src/core/com/jme3/animation/MeshAnimation.java b/engine/src/core/com/jme3/animation/MeshAnimation.java index 20113612c..e36f57390 100644 --- a/engine/src/core/com/jme3/animation/MeshAnimation.java +++ b/engine/src/core/com/jme3/animation/MeshAnimation.java @@ -32,21 +32,25 @@ 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.InputCapsule; import com.jme3.export.OutputCapsule; -import com.jme3.export.Savable; import com.jme3.scene.Mesh; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; -public class MeshAnimation implements Animation, Savable { +/** + * + * @author Kirill Vainer + * @deprecated use Animation instead with tracks of selected type (ie. BoneTrack, SpatialTrack, MeshTrack) + */ +@Deprecated +public class MeshAnimation extends Animation { private String name; private float length; - private Track[] tracks; + private PoseTrack[] tracks; public MeshAnimation(String name, float length){ this.name = name; @@ -61,11 +65,11 @@ public class MeshAnimation implements Animation, Savable { return length; } - public void setTracks(Track[] tracks){ + public void setTracks(PoseTrack[] tracks){ this.tracks = tracks; } - public Track[] getTracks(){ + public PoseTrack[] getTracks(){ return tracks; } @@ -92,18 +96,6 @@ public class MeshAnimation implements Animation, Savable { InputCapsule in = i.getCapsule(this); name = in.readString("name", ""); length = in.readFloat("length", -1f); - tracks = (Track[]) in.readSavableArray("tracks", null); + tracks = (PoseTrack[]) in.readSavableArray("tracks", null); } - - @Override - public Animation clone() { - try { - return (Animation) super.clone(); - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - - } diff --git a/engine/src/core/com/jme3/animation/Pose.java b/engine/src/core/com/jme3/animation/Pose.java index 05a221459..d1b447c93 100644 --- a/engine/src/core/com/jme3/animation/Pose.java +++ b/engine/src/core/com/jme3/animation/Pose.java @@ -32,20 +32,21 @@ package com.jme3.animation; +import java.io.IOException; +import java.nio.FloatBuffer; + +import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; -import com.jme3.export.InputCapsule; import com.jme3.export.OutputCapsule; import com.jme3.export.Savable; import com.jme3.math.Vector3f; import com.jme3.util.BufferUtils; -import java.io.IOException; -import java.nio.FloatBuffer; /** * A pose is a list of offsets that say where a mesh vertices should be for this pose. */ -public final class Pose implements Savable { +public final class Pose implements Savable, Cloneable { private String name; private int targetMeshIndex; @@ -91,6 +92,26 @@ public final class Pose implements Savable { BufferUtils.setInBuffer(tempVec2, vertbuf, vertIndex); } } + + /** + * This method creates a clone of the current object. + * @return a clone of the current object + */ + public Pose clone() { + try { + Pose result = (Pose) super.clone(); + result.indices = this.indices.clone(); + if(this.offsets!=null) { + result.offsets = new Vector3f[this.offsets.length]; + for(int i=0;i { + protected int targetMeshIndex; private PoseFrame[] frames; private float[] times; - public static class PoseFrame implements Savable { + public static class PoseFrame implements Savable, Cloneable { Pose[] poses; float[] weights; @@ -60,6 +63,26 @@ public final class PoseTrack extends Track { this.poses = poses; this.weights = weights; } + + /** + * This method creates a clone of the current object. + * @return a clone of the current object + */ + public PoseFrame clone() { + try { + PoseFrame result = (PoseFrame) super.clone(); + result.weights = this.weights.clone(); + if(this.poses != null) { + result.poses = new Pose[this.poses.length]; + for(int i=0;i { /** Translations of the track. */ private CompactVector3Array translations; /** Rotations of the track. */ @@ -63,7 +62,7 @@ public class SpatialTrack implements Savable { * @param spatial * the spatial that should be animated with this track */ - public void setTime(float time, Spatial spatial) { + public void setTime(float time, Spatial spatial, float weight) { int lastFrame = times.length - 1; if (time < 0 || lastFrame == 0) { rotations.get(0, tempQ); @@ -147,6 +146,13 @@ public class SpatialTrack implements Savable { } } + /** + * @return the index of the target object for this track + */ + public int getTargetIndex() { + return 0; + } + /** * @return the array of rotations of this track */ @@ -175,6 +181,30 @@ public class SpatialTrack implements Savable { return translations.toObjectArray(); } + /** + * This method creates a clone of the current object. + * @return a clone of the current object + */ + public SpatialTrack clone() { + int tablesLength = times.length; + + float[] times = this.times.clone(); + Vector3f[] sourceTranslations = this.getTranslations(); + Quaternion[] sourceRotations = this.getRotations(); + Vector3f[] sourceScales = this.getScales(); + + Vector3f[] translations = new Vector3f[tablesLength]; + Quaternion[] rotations = new Quaternion[tablesLength]; + Vector3f[] scales = new Vector3f[tablesLength]; + for (int i = 0; i < tablesLength; ++i) { + translations[i] = sourceTranslations[i].clone(); + rotations[i] = sourceRotations[i].clone(); + scales[i] = sourceScales != null ? sourceScales[i].clone() : new Vector3f(1.0f, 1.0f, 1.0f); + } + //need to use the constructor here because of the final fields used in this class + return new SpatialTrack(times, translations, rotations, scales); + } + @Override public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); diff --git a/engine/src/core/com/jme3/animation/Track.java b/engine/src/core/com/jme3/animation/Track.java index 77b497e04..1d8691b84 100644 --- a/engine/src/core/com/jme3/animation/Track.java +++ b/engine/src/core/com/jme3/animation/Track.java @@ -31,49 +31,66 @@ */ package com.jme3.animation; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; import com.jme3.export.Savable; -import com.jme3.scene.Mesh; -import java.io.IOException; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; /** * A single track of mesh animation (either morph or pose based). * Currently morph animations are not supported (only pose). */ -public abstract class Track implements Savable { - - protected int targetMeshIndex; - - /** - * build a track for a an index - * @param targetMeshIndex - */ - public Track(int targetMeshIndex) { - this.targetMeshIndex = targetMeshIndex; - } - - /** - * return the mesh index - * @return - */ - public int getTargetMeshIndex() { - return targetMeshIndex; - } - +public interface Track extends Savable, Cloneable { /** * sets time for this track * @param time - * @param targets + * @param target * @param weight */ - public abstract void setTime(float time, Mesh[] targets, float weight); + void setTime(float time, T target, float weight); + + /** + * 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 + */ + void setKeyframes(float[] times, Vector3f[] translations, + Quaternion[] rotations, Vector3f[] scales); + + /** + * @return the index of the target object for this track + */ + int getTargetIndex(); + + /** + * @return the array of rotations of this track + */ + Quaternion[] getRotations(); + + /** + * @return the array of scales for this track + */ + Vector3f[] getScales(); - public void write(JmeExporter ex) throws IOException { - ex.getCapsule(this).write(targetMeshIndex, "meshIndex", 0); - } + /** + * @return the arrays of time for this track + */ + float[] getTimes(); - public void read(JmeImporter im) throws IOException { - targetMeshIndex = im.getCapsule(this).readInt("meshIndex", 0); - } + /** + * @return the array of translations of this track + */ + Vector3f[] getTranslations(); + + /** + * This method creates a clone of the current object. + * @return a clone of the current object + */ + Track clone(); } diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java index cc3cf374a..69c62e004 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SkeletonLoader.java @@ -31,30 +31,16 @@ */ package com.jme3.scene.plugins.ogre; -import com.jme3.animation.Animation; -import com.jme3.animation.Bone; -import com.jme3.animation.BoneAnimation; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetLoader; -import com.jme3.asset.AssetManager; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.system.JmeSystem; -import com.jme3.util.xml.SAXUtil; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.Stack; import java.util.logging.Logger; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; @@ -62,7 +48,17 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; -import org.xml.sax.helpers.XMLReaderFactory; + +import com.jme3.animation.Animation; +import com.jme3.animation.Bone; +import com.jme3.animation.BoneTrack; +import com.jme3.animation.Skeleton; +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetLoader; +import com.jme3.asset.AssetManager; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.util.xml.SAXUtil; public class SkeletonLoader extends DefaultHandler implements AssetLoader { @@ -73,7 +69,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader { private HashMap nameToBone = new HashMap(); private BoneTrack track; private ArrayList tracks = new ArrayList(); - private BoneAnimation animation; + private Animation animation; private ArrayList animations; private Bone bone; private Skeleton skeleton; @@ -132,7 +128,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader { assert elementStack.peek().equals("animations"); String name = SAXUtil.parseString(attribs.getValue("name")); float length = SAXUtil.parseFloat(attribs.getValue("length")); - animation = new BoneAnimation(name, length); + animation = new Animation(name, length); } else if (qName.equals("bonehierarchy")) { assert elementStack.peek().equals("skeleton"); } else if (qName.equals("animations")) { diff --git a/engine/src/test/jme3test/model/anim/TestSpatialAnim.java b/engine/src/test/jme3test/model/anim/TestSpatialAnim.java index 04c7a55ad..e8bee5dd9 100644 --- a/engine/src/test/jme3test/model/anim/TestSpatialAnim.java +++ b/engine/src/test/jme3test/model/anim/TestSpatialAnim.java @@ -4,7 +4,6 @@ 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; @@ -71,8 +70,8 @@ public class TestSpatialAnim extends SimpleApplication { SpatialTrack spatialTrack = new SpatialTrack(times, translations, rotations, scales); //creating the animation - SpatialAnimation spatialAnimation = new SpatialAnimation("anim", animTime); - spatialAnimation.setTrack(spatialTrack); + Animation spatialAnimation = new Animation("anim", animTime); + spatialAnimation.setTracks(new SpatialTrack[] { spatialTrack }); //create spatial animation control AnimControl control = new AnimControl();