From 05e907acca36a9c81b545c7e27a0dbab2ebcf4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81my=20Bouquet?= Date: Sat, 30 Dec 2017 17:33:35 +0100 Subject: [PATCH] Added the base of the blending system. Smooth transition between anims --- .../src/main/java/com/jme3/anim/AnimClip.java | 63 ++++------ .../main/java/com/jme3/anim/AnimComposer.java | 59 ++++++++-- .../src/main/java/com/jme3/anim/Joint.java | 3 +- .../main/java/com/jme3/anim/JointTrack.java | 108 ----------------- .../main/java/com/jme3/anim/SpatialTrack.java | 111 ------------------ .../java/com/jme3/anim/TransformTrack.java | 46 +++++--- .../com/jme3/anim/tween/AnimClipTween.java | 99 ++++++++++++++++ .../com/jme3/anim/tween/action/Action.java | 64 ++++++++++ .../anim/tween/action/SequenceAction.java | 75 ++++++++++++ .../jme3/anim/util/AnimMigrationUtils.java | 25 ++-- .../com/jme3/anim/util/HasLocalTransform.java | 10 ++ .../java/com/jme3/anim/util/Weighted.java | 11 ++ .../main/java/com/jme3/math/Transform.java | 2 +- .../src/main/java/com/jme3/scene/Spatial.java | 5 +- .../MatDefs/ShaderNodes/Misc/Dashed100.frag | 4 +- .../java/jme3test/export/TestOgreConvert.java | 2 +- .../java/jme3test/model/TestGltfLoading.java | 8 +- .../model/anim/TestAnimMigration.java | 30 +++-- .../model/anim/TestAnimSerialization.java | 6 +- .../jme3test/model/anim/TestArmature.java | 12 +- .../model/anim/TestBaseAnimSerialization.java | 12 +- .../jme3test/model/anim/TestHWSkinning.java | 8 +- .../model/anim/TestModelExportingCloning.java | 6 +- .../jme3/scene/plugins/gltf/GltfLoader.java | 17 +-- .../scene/plugins/ogre/SkeletonLoader.java | 24 ++-- 25 files changed, 452 insertions(+), 358 deletions(-) delete mode 100644 jme3-core/src/main/java/com/jme3/anim/JointTrack.java delete mode 100644 jme3-core/src/main/java/com/jme3/anim/SpatialTrack.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/tween/AnimClipTween.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/tween/action/Action.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/tween/action/SequenceAction.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/util/HasLocalTransform.java create mode 100644 jme3-core/src/main/java/com/jme3/anim/util/Weighted.java diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimClip.java b/jme3-core/src/main/java/com/jme3/anim/AnimClip.java index 6a23f4aff..96cde46a2 100644 --- a/jme3-core/src/main/java/com/jme3/anim/AnimClip.java +++ b/jme3-core/src/main/java/com/jme3/anim/AnimClip.java @@ -11,12 +11,12 @@ import java.io.IOException; /** * Created by Nehon on 20/12/2017. */ -public class AnimClip implements Tween, JmeCloneable, Savable { +public class AnimClip implements JmeCloneable, Savable { private String name; private double length; - private SafeArrayList tracks = new SafeArrayList<>(Tween.class); + private TransformTrack[] tracks; public AnimClip() { } @@ -25,26 +25,11 @@ public class AnimClip implements Tween, JmeCloneable, Savable { this.name = name; } - public void setTracks(Tween[] tracks) { - for (Tween track : tracks) { - addTrack(track); - } - } - - public void addTrack(Tween track) { - tracks.add(track); - if (track.getLength() > length) { - length = track.getLength(); - } - } - - public void removeTrack(Tween track) { - if (tracks.remove(track)) { - length = 0; - for (Tween t : tracks.getArray()) { - if (t.getLength() > length) { - length = t.getLength(); - } + public void setTracks(TransformTrack[] tracks) { + this.tracks = tracks; + for (TransformTrack track : tracks) { + if (track.getLength() > length) { + length = track.getLength(); } } } @@ -53,24 +38,14 @@ public class AnimClip implements Tween, JmeCloneable, Savable { return name; } - @Override + public double getLength() { return length; } - @Override - public boolean interpolate(double t) { - // Sanity check the inputs - if (t < 0) { - return true; - } - for (Tween track : tracks.getArray()) { - if (t <= track.getLength()) { - track.interpolate(t); - } - } - return t <= length; + public TransformTrack[] getTracks() { + return tracks; } @Override @@ -84,9 +59,9 @@ public class AnimClip implements Tween, JmeCloneable, Savable { @Override public void cloneFields(Cloner cloner, Object original) { - SafeArrayList newTracks = new SafeArrayList<>(Tween.class); - for (Tween track : tracks) { - newTracks.add(cloner.clone(track)); + TransformTrack[] newTracks = new TransformTrack[tracks.length]; + for (int i = 0; i < tracks.length; i++) { + newTracks[i] = (cloner.clone(tracks[i])); } this.tracks = newTracks; } @@ -95,7 +70,7 @@ public class AnimClip implements Tween, JmeCloneable, Savable { public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.write(name, "name", null); - oc.write(tracks.getArray(), "tracks", null); + oc.write(tracks, "tracks", null); } @@ -105,9 +80,13 @@ public class AnimClip implements Tween, JmeCloneable, Savable { name = ic.readString("name", null); Savable[] arr = ic.readSavableArray("tracks", null); if (arr != null) { - tracks = new SafeArrayList<>(Tween.class); - for (Savable savable : arr) { - addTrack((Tween) savable); + tracks = new TransformTrack[arr.length]; + for (int i = 0; i < arr.length; i++) { + TransformTrack t = (TransformTrack) arr[i]; + tracks[i] = t; + if (t.getLength() > length) { + length = t.getLength(); + } } } } diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java b/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java index 731f92a19..1515136ef 100644 --- a/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java +++ b/jme3-core/src/main/java/com/jme3/anim/AnimComposer.java @@ -1,5 +1,9 @@ package com.jme3.anim; +import com.jme3.anim.tween.AnimClipTween; +import com.jme3.anim.tween.Tween; +import com.jme3.anim.tween.action.Action; +import com.jme3.anim.tween.action.SequenceAction; import com.jme3.export.*; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; @@ -16,7 +20,8 @@ public class AnimComposer extends AbstractControl { private Map animClipMap = new HashMap<>(); - private AnimClip currentAnimClip; + private Action currentAction; + private Map actions = new HashMap<>(); private float time; /** @@ -54,16 +59,45 @@ public class AnimComposer extends AbstractControl { animClipMap.remove(anim.getName()); } - public void setCurrentAnimClip(String name) { - currentAnimClip = animClipMap.get(name); + public void setCurrentAction(String name) { + Action action = action(name); + if (currentAction != null) { + currentAction.reset(); + } + currentAction = action; time = 0; - if (currentAnimClip == null) { - throw new IllegalArgumentException("Unknown clip " + name); + } + + public Action action(String name) { + Action action = actions.get(name); + if (action == null) { + AnimClipTween tween = tweenFromClip(name); + action = new SequenceAction(tween); + actions.put(name, action); + } + return action; + } + + public AnimClipTween tweenFromClip(String clipName) { + AnimClip clip = animClipMap.get(clipName); + if (clip == null) { + throw new IllegalArgumentException("Cannot find clip named " + clipName); } + return new AnimClipTween(clip); + } + + public SequenceAction actionSequence(String name, Tween... tweens) { + SequenceAction action = new SequenceAction(tweens); + actions.put(name, action); + return action; + } + + public Action actionBlended(String name, Tween... tweens) { + return null; } public void reset() { - currentAnimClip = null; + currentAction = null; time = 0; } @@ -77,11 +111,11 @@ public class AnimComposer extends AbstractControl { @Override protected void controlUpdate(float tpf) { - if (currentAnimClip != null) { + if (currentAction != null) { time += tpf; - boolean running = currentAnimClip.interpolate(time); + boolean running = currentAction.interpolate(time); if (!running) { - time -= currentAnimClip.getLength(); + time -= currentAction.getLength(); } } } @@ -108,6 +142,11 @@ public class AnimComposer extends AbstractControl { for (String key : animClipMap.keySet()) { clips.put(key, cloner.clone(animClipMap.get(key))); } + Map act = new HashMap<>(); + for (String key : actions.keySet()) { + act.put(key, cloner.clone(actions.get(key))); + } + actions = act; animClipMap = clips; } @@ -116,6 +155,7 @@ public class AnimComposer extends AbstractControl { super.read(im); InputCapsule ic = im.getCapsule(this); animClipMap = (Map) ic.readStringSavableMap("animClipMap", new HashMap()); + actions = (Map) ic.readStringSavableMap("actions", new HashMap()); } @Override @@ -123,5 +163,6 @@ public class AnimComposer extends AbstractControl { super.write(ex); OutputCapsule oc = ex.getCapsule(this); oc.writeStringSavableMap(animClipMap, "animClipMap", new HashMap()); + oc.writeStringSavableMap(actions, "actions", new HashMap()); } } diff --git a/jme3-core/src/main/java/com/jme3/anim/Joint.java b/jme3-core/src/main/java/com/jme3/anim/Joint.java index 267a5543a..eaa7ac145 100644 --- a/jme3-core/src/main/java/com/jme3/anim/Joint.java +++ b/jme3-core/src/main/java/com/jme3/anim/Joint.java @@ -1,5 +1,6 @@ package com.jme3.anim; +import com.jme3.anim.util.HasLocalTransform; import com.jme3.anim.util.JointModelTransform; import com.jme3.export.*; import com.jme3.material.MatParamOverride; @@ -18,7 +19,7 @@ import java.util.List; * A Joint is the basic component of an armature designed to perform skeletal animation * Created by Nehon on 15/12/2017. */ -public class Joint implements Savable, JmeCloneable { +public class Joint implements Savable, JmeCloneable, HasLocalTransform { private String name; private Joint parent; diff --git a/jme3-core/src/main/java/com/jme3/anim/JointTrack.java b/jme3-core/src/main/java/com/jme3/anim/JointTrack.java deleted file mode 100644 index 2a24bcd84..000000000 --- a/jme3-core/src/main/java/com/jme3/anim/JointTrack.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2009-2012 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.anim; - -import com.jme3.export.*; -import com.jme3.math.*; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; - -import java.io.IOException; - -/** - * Contains a list of transforms and times for each keyframe. - * - * @author Rémy Bouquet - */ -public final class JointTrack extends TransformTrack implements JmeCloneable, Savable { - - private Joint target; - - /** - * Serialization-only. Do not use. - */ - public JointTrack() { - super(); - } - - /** - * Creates a joint track for the given joint index - * - * @param target The Joint target of 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 JointTrack(Joint target, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { - super(times, translations, rotations, scales); - this.target = target; - this.defaultTransform = target.getLocalTransform(); - } - - @Override - public boolean interpolate(double t) { - boolean running = super.interpolate(t); - Transform transform = getInterpolatedTransform(); - target.setLocalTransform(transform); - return running; - } - - public void setTarget(Joint target) { - this.target = target; - } - - @Override - public Object jmeClone() { - return super.clone(); - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - this.target = cloner.clone(target); - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(target, "target", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - target = (Joint) ic.readSavable("target", null); - } - -} diff --git a/jme3-core/src/main/java/com/jme3/anim/SpatialTrack.java b/jme3-core/src/main/java/com/jme3/anim/SpatialTrack.java deleted file mode 100644 index d796babfd..000000000 --- a/jme3-core/src/main/java/com/jme3/anim/SpatialTrack.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2009-2012 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.anim; - -import com.jme3.export.*; -import com.jme3.math.*; -import com.jme3.scene.Spatial; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; - -import java.io.IOException; - -/** - * Contains a list of transforms and times for each keyframe. - * - * @author Rémy Bouquet - */ -public final class SpatialTrack extends TransformTrack implements JmeCloneable, Savable { - - private Spatial target; - - /** - * Serialization-only. Do not use. - */ - public SpatialTrack() { - super(); - } - - /** - * Creates a spatial track for the given Spatial - * - * @param target The Spatial target of 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 SpatialTrack(Spatial target, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { - super(times, translations, rotations, scales); - this.target = target; - defaultTransform = target.getLocalTransform(); - } - - @Override - public boolean interpolate(double t) { - - boolean running = super.interpolate(t); - Transform transform = getInterpolatedTransform(); - target.setLocalTransform(transform); - return running; - } - - public void setTarget(Spatial target) { - this.target = target; - } - - @Override - public Object jmeClone() { - return super.clone(); - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - - this.target = cloner.clone(target); - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(target, "target", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - target = (Spatial) ic.readSavable("target", null); - } - -} diff --git a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java index bf06379ae..cc5bc317b 100644 --- a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java +++ b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java @@ -33,10 +33,13 @@ package com.jme3.anim; import com.jme3.anim.interpolator.FrameInterpolator; import com.jme3.anim.tween.Tween; +import com.jme3.anim.util.HasLocalTransform; import com.jme3.animation.CompactQuaternionArray; import com.jme3.animation.CompactVector3Array; import com.jme3.export.*; import com.jme3.math.*; +import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.JmeCloneable; import java.io.IOException; @@ -45,9 +48,10 @@ import java.io.IOException; * * @author Rémy Bouquet */ -public abstract class TransformTrack implements Tween, Cloneable, Savable { +public class TransformTrack implements JmeCloneable, Savable { private double length; + private HasLocalTransform target; /** * Transforms and times for track. @@ -55,8 +59,6 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { private CompactVector3Array translations; private CompactQuaternionArray rotations; private CompactVector3Array scales; - private Transform transform = new Transform(); - protected Transform defaultTransform = new Transform(); private FrameInterpolator interpolator = FrameInterpolator.DEFAULT; private float[] times; @@ -74,7 +76,8 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { * @param rotations the rotation of the bone for each frame * @param scales the scale of the bone for each frame */ - public TransformTrack(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { + public TransformTrack(HasLocalTransform target, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { + this.target = target; this.setKeyframes(times, translations, rotations, scales); } @@ -216,16 +219,13 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { } } - @Override public double getLength() { return length; } - @Override - public boolean interpolate(double t) { + public void getTransformAtTime(double t, Transform transform) { float time = (float) t; - transform.set(defaultTransform); int lastFrame = times.length - 1; if (time < 0 || lastFrame == 0) { if (translations != null) { @@ -237,7 +237,7 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { if (scales != null) { scales.get(0, transform.getScale()); } - return true; + return; } int startFrame = 0; @@ -272,17 +272,18 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { if (scales != null) { transform.setScale(interpolated.getScale()); } - - return time < length; } + public void setFrameInterpolator(FrameInterpolator interpolator) { + this.interpolator = interpolator; + } - public Transform getInterpolatedTransform() { - return transform; + public HasLocalTransform getTarget() { + return target; } - public void setFrameInterpolator(FrameInterpolator interpolator) { - this.interpolator = interpolator; + public void setTarget(HasLocalTransform target) { + this.target = target; } @Override @@ -292,6 +293,7 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { oc.write(rotations, "rotations", null); oc.write(times, "times", null); oc.write(scales, "scales", null); + oc.write(target, "target", null); } @Override @@ -301,16 +303,22 @@ public abstract class TransformTrack implements Tween, Cloneable, Savable { rotations = (CompactQuaternionArray) ic.readSavable("rotations", null); times = ic.readFloatArray("times", null); scales = (CompactVector3Array) ic.readSavable("scales", null); + target = (Joint) ic.readSavable("target", null); setTimes(times); } @Override - public Object clone() { + public Object jmeClone() { try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Error cloning", e); + TransformTrack clone = (TransformTrack) super.clone(); + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); } } + @Override + public void cloneFields(Cloner cloner, Object original) { + this.target = cloner.clone(target); + } } diff --git a/jme3-core/src/main/java/com/jme3/anim/tween/AnimClipTween.java b/jme3-core/src/main/java/com/jme3/anim/tween/AnimClipTween.java new file mode 100644 index 000000000..384abe31e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/tween/AnimClipTween.java @@ -0,0 +1,99 @@ +package com.jme3.anim.tween; + +import com.jme3.anim.AnimClip; +import com.jme3.anim.TransformTrack; +import com.jme3.anim.tween.action.Action; +import com.jme3.anim.util.HasLocalTransform; +import com.jme3.anim.util.Weighted; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Transform; +import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.JmeCloneable; + +import java.io.IOException; + +public class AnimClipTween implements Tween, Weighted, JmeCloneable { + + private AnimClip clip; + private Transform transform = new Transform(); + private float weight = 1f; + private Action parentAction; + + public AnimClipTween() { + } + + public AnimClipTween(AnimClip clip) { + this.clip = clip; + } + + @Override + public double getLength() { + return clip.getLength(); + } + + @Override + public boolean interpolate(double t) { + // Sanity check the inputs + if (t < 0) { + return true; + } + if (parentAction != null) { + weight = parentAction.getWeightForTween(this); + } + TransformTrack[] tracks = clip.getTracks(); + for (TransformTrack track : tracks) { + HasLocalTransform target = track.getTarget(); + transform.set(target.getLocalTransform()); + track.getTransformAtTime(t, transform); + + if (weight == 1f) { + target.setLocalTransform(transform); + } else { + Transform tr = target.getLocalTransform(); + tr.interpolateTransforms(tr, transform, weight); + target.setLocalTransform(tr); + } + } + return t < clip.getLength(); + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(clip, "clip", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + clip = (AnimClip) ic.readSavable("clip", null); + } + + @Override + public Object jmeClone() { + try { + AnimClipTween clone = (AnimClipTween) super.clone(); + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + @Override + public void cloneFields(Cloner cloner, Object original) { + clip = cloner.clone(clip); + } + +// @Override +// public void setWeight(float weight) { +// this.weight = weight; +// } + + @Override + public void setParentAction(Action action) { + this.parentAction = action; + } +} diff --git a/jme3-core/src/main/java/com/jme3/anim/tween/action/Action.java b/jme3-core/src/main/java/com/jme3/anim/tween/action/Action.java new file mode 100644 index 000000000..9c7037715 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/tween/action/Action.java @@ -0,0 +1,64 @@ +package com.jme3.anim.tween.action; + +import com.jme3.anim.tween.Tween; +import com.jme3.anim.util.Weighted; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; + +import java.io.IOException; + +public abstract class Action implements Tween, Weighted { + + protected Tween[] tweens; + protected float weight = 1; + protected double length; + protected Action parentAction; + + protected Action(Tween... tweens) { + this.tweens = tweens; + for (Tween tween : tweens) { + if (tween instanceof Weighted) { + ((Weighted) tween).setParentAction(this); + } + } + } + + @Override + public double getLength() { + return length; + } + + @Override + public boolean interpolate(double t) { + if (parentAction != null) { + weight = parentAction.getWeightForTween(this); + } + + return doInterpolate(t); + } + + public abstract float getWeightForTween(Tween tween); + + public abstract boolean doInterpolate(double t); + + public abstract void reset(); + + @Override + public void setParentAction(Action parentAction) { + this.parentAction = parentAction; + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + tweens = (Tween[]) ic.readSavableArray("tweens", null); + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(tweens, "tweens", null); + } +} diff --git a/jme3-core/src/main/java/com/jme3/anim/tween/action/SequenceAction.java b/jme3-core/src/main/java/com/jme3/anim/tween/action/SequenceAction.java new file mode 100644 index 000000000..08c59085a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/tween/action/SequenceAction.java @@ -0,0 +1,75 @@ +package com.jme3.anim.tween.action; + +import com.jme3.anim.tween.AbstractTween; +import com.jme3.anim.tween.Tween; + +public class SequenceAction extends Action { + + private int currentIndex = 0; + private double accumTime; + private double transitionTime = 0; + private float mainWeight = 1.0f; + private double transitionLength = 0.4f; + private TransitionTween transition = new TransitionTween(transitionLength); + + + public SequenceAction(Tween... tweens) { + super(tweens); + for (Tween tween : tweens) { + length += tween.getLength(); + } + } + + @Override + public float getWeightForTween(Tween tween) { + return weight * mainWeight; + } + + @Override + public boolean doInterpolate(double t) { + Tween currentTween = tweens[currentIndex]; + if (transition.getLength() > currentTween.getLength()) { + transition.setLength(currentTween.getLength()); + } + + transition.interpolate(t - transitionTime); + + boolean running = currentTween.interpolate(t - accumTime); + if (!running) { + accumTime += currentTween.getLength(); + currentIndex++; + transitionTime = accumTime; + transition.setLength(transitionLength); + } + + if (t >= length) { + reset(); + return false; + } + return true; + } + + public void reset() { + currentIndex = 0; + accumTime = 0; + transitionTime = 0; + mainWeight = 1; + } + + public void setTransitionLength(double transitionLength) { + this.transitionLength = transitionLength; + } + + private class TransitionTween extends AbstractTween { + + + public TransitionTween(double length) { + super(length); + } + + @Override + protected void doInterpolate(double t) { + mainWeight = (float) t; + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java b/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java index 799c4a7d6..c9e65b08f 100644 --- a/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java @@ -61,10 +61,14 @@ public class AnimMigrationUtils { armature.setBindPose(); skeletonArmatureMap.put(skeleton, armature); + List tracks = new ArrayList<>(); + for (String animName : control.getAnimationNames()) { + tracks.clear(); Animation anim = control.getAnim(animName); AnimClip clip = new AnimClip(animName); Joint[] staticJoints = new Joint[joints.length]; + System.arraycopy(joints, 0, staticJoints, 0, joints.length); for (Track track : anim.getTracks()) { if (track instanceof BoneTrack) { @@ -72,17 +76,20 @@ public class AnimMigrationUtils { int index = boneTrack.getTargetBoneIndex(); Bone bone = skeleton.getBone(index); Joint joint = joints[index]; - JointTrack jointTrack = fromBoneTrack(boneTrack, bone, joint); - clip.addTrack(jointTrack); + TransformTrack jointTrack = fromBoneTrack(boneTrack, bone, joint); + tracks.add(jointTrack); //this joint is animated let's remove it from the static joints staticJoints[index] = null; } + //TODO spatial tracks , Effect tracks, Audio tracks } for (int i = 0; i < staticJoints.length; i++) { - padJointTracks(clip, staticJoints[i]); + padJointTracks(tracks, staticJoints[i]); } + clip.setTracks(tracks.toArray(new TransformTrack[tracks.size()])); + composer.addAnimClip(clip); } spatial.removeControl(control); @@ -95,7 +102,7 @@ public class AnimMigrationUtils { } } - public static void padJointTracks(AnimClip clip, Joint staticJoint) { + public static void padJointTracks(List tracks, Joint staticJoint) { Joint j = staticJoint; if (j != null) { // joint has no track , we create one with the default pose @@ -103,8 +110,8 @@ public class AnimMigrationUtils { Vector3f[] translations = new Vector3f[]{j.getLocalTranslation()}; Quaternion[] rotations = new Quaternion[]{j.getLocalRotation()}; Vector3f[] scales = new Vector3f[]{j.getLocalScale()}; - JointTrack track = new JointTrack(j, times, translations, rotations, scales); - clip.addTrack(track); + TransformTrack track = new TransformTrack(j, times, translations, rotations, scales); + tracks.add(track); } } @@ -144,7 +151,7 @@ public class AnimMigrationUtils { } } - public static JointTrack fromBoneTrack(BoneTrack boneTrack, Bone bone, Joint joint) { + public static TransformTrack fromBoneTrack(BoneTrack boneTrack, Bone bone, Joint joint) { float[] times = new float[boneTrack.getTimes().length]; int length = times.length; System.arraycopy(boneTrack.getTimes(), 0, times, 0, length); @@ -178,8 +185,8 @@ public class AnimMigrationUtils { scales[i] = newScale; } } - - return new JointTrack(joint, times, translations, rotations, scales); + TransformTrack t = new TransformTrack(joint, times, translations, rotations, scales); + return t; } private static Joint fromBone(Bone b) { diff --git a/jme3-core/src/main/java/com/jme3/anim/util/HasLocalTransform.java b/jme3-core/src/main/java/com/jme3/anim/util/HasLocalTransform.java new file mode 100644 index 000000000..28f560dfc --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/util/HasLocalTransform.java @@ -0,0 +1,10 @@ +package com.jme3.anim.util; + +import com.jme3.export.Savable; +import com.jme3.math.Transform; + +public interface HasLocalTransform extends Savable { + public void setLocalTransform(Transform transform); + + public Transform getLocalTransform(); +} diff --git a/jme3-core/src/main/java/com/jme3/anim/util/Weighted.java b/jme3-core/src/main/java/com/jme3/anim/util/Weighted.java new file mode 100644 index 000000000..8fb6d3255 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/anim/util/Weighted.java @@ -0,0 +1,11 @@ +package com.jme3.anim.util; + +import com.jme3.anim.tween.action.Action; +import com.jme3.math.Transform; + +public interface Weighted { + + // public void setWeight(float weight); + + public void setParentAction(Action action); +} diff --git a/jme3-core/src/main/java/com/jme3/math/Transform.java b/jme3-core/src/main/java/com/jme3/math/Transform.java index 41e911bac..d9ac33ef0 100644 --- a/jme3-core/src/main/java/com/jme3/math/Transform.java +++ b/jme3-core/src/main/java/com/jme3/math/Transform.java @@ -176,7 +176,7 @@ public final class Transform implements Savable, Cloneable, java.io.Serializable } /** - * Sets this matrix to the interpolation between the first matrix and the second by delta amount. + * Sets this transform to the interpolation between the first transform and the second by delta amount. * @param t1 The beginning transform. * @param t2 The ending transform. * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2. diff --git a/jme3-core/src/main/java/com/jme3/scene/Spatial.java b/jme3-core/src/main/java/com/jme3/scene/Spatial.java index c36b5f347..c44db734b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Spatial.java +++ b/jme3-core/src/main/java/com/jme3/scene/Spatial.java @@ -31,6 +31,7 @@ */ package com.jme3.scene; +import com.jme3.anim.util.HasLocalTransform; import com.jme3.asset.AssetKey; import com.jme3.asset.CloneableSmartAsset; import com.jme3.bounding.BoundingVolume; @@ -67,7 +68,7 @@ import java.util.logging.Logger; * @author Joshua Slack * @version $Revision: 4075 $, $Data$ */ -public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset, JmeCloneable { +public abstract class Spatial implements Savable, Cloneable, Collidable, CloneableSmartAsset, JmeCloneable, HasLocalTransform { private static final Logger logger = Logger.getLogger(Spatial.class.getName()); @@ -1792,4 +1793,4 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab } protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue); -} \ No newline at end of file +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/Dashed100.frag b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/Dashed100.frag index 522971863..4fa7babec 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/Dashed100.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/Dashed100.frag @@ -2,8 +2,8 @@ void main(){ startPos.xy = (startPos * 0.5 + 0.5).xy * resolution; float len = distance(gl_FragCoord.xy,startPos.xy); outColor = inColor; - float factor = int(len * 0.25); - if(mod(factor, 2) > 0.0){ + float factor = float(int(len * 0.25)); + if(mod(factor, 2.0) > 0.0){ discard; } diff --git a/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java b/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java index 6495c1de1..57dba2fb5 100644 --- a/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java +++ b/jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java @@ -71,7 +71,7 @@ public class TestOgreConvert extends SimpleApplication { Node ogreModelReloaded = (Node) imp.load(bais, null, null); AnimComposer composer = ogreModelReloaded.getControl(AnimComposer.class); - composer.setCurrentAnimClip("Walk"); + composer.setCurrentAction("Walk"); rootNode.attachChild(ogreModelReloaded); } catch (IOException ex){ diff --git a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java index ad85f3efe..06ec75f5f 100644 --- a/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java +++ b/jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java @@ -116,9 +116,9 @@ public class TestGltfLoading extends SimpleApplication { //loadModel("Models/gltf/elephant/scene.gltf", new Vector3f(0, -1, 0), 0.01f); //loadModel("Models/gltf/buffalo/scene.gltf", new Vector3f(0, -1, 0), 0.1f); //loadModel("Models/gltf/war/scene.gltf", new Vector3f(0, -1, 0), 0.1f); - loadModel("Models/gltf/ganjaarl/scene.gltf", new Vector3f(0, -1, 0), 0.01f); + //loadModel("Models/gltf/ganjaarl/scene.gltf", new Vector3f(0, -1, 0), 0.01f); //loadModel("Models/gltf/hero/scene.gltf", new Vector3f(0, -1, 0), 0.1f); - //loadModel("Models/gltf/mercy/scene.gltf", new Vector3f(0, -1, 0), 0.01f); + loadModel("Models/gltf/mercy/scene.gltf", new Vector3f(0, -1, 0), 0.01f); //loadModel("Models/gltf/crab/scene.gltf", Vector3f.ZERO, 1); //loadModel("Models/gltf/manta/scene.gltf", Vector3f.ZERO, 0.2f); //loadModel("Models/gltf/bone/scene.gltf", Vector3f.ZERO, 0.1f); @@ -190,7 +190,7 @@ public class TestGltfLoading extends SimpleApplication { if (isPressed && composer != null) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); } } }, "nextAnim"); @@ -262,7 +262,7 @@ public class TestGltfLoading extends SimpleApplication { } String anim = anims.poll(); anims.add(anim); - control.setCurrentAnimClip(anim); + control.setCurrentAction(anim); composer = control; } if (s instanceof Node) { diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java index 4ed76312a..6f6f0a4c9 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java @@ -2,6 +2,7 @@ package jme3test.model.anim; import com.jme3.anim.AnimComposer; import com.jme3.anim.SkinningControl; +import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.app.ChaseCameraAppState; import com.jme3.app.SimpleApplication; import com.jme3.input.KeyInput; @@ -24,8 +25,8 @@ public class TestAnimMigration extends SimpleApplication { ArmatureDebugAppState debugAppState; AnimComposer composer; - Queue anims = new LinkedList<>(); - boolean playAnim = true; + LinkedList anims = new LinkedList<>(); + boolean playAnim = false; public static void main(String... argv) { TestAnimMigration app = new TestAnimMigration(); @@ -40,12 +41,12 @@ public class TestAnimMigration extends SimpleApplication { rootNode.addLight(new DirectionalLight(new Vector3f(-1, -1, -1).normalizeLocal())); rootNode.addLight(new AmbientLight(ColorRGBA.DarkGray)); - //Spatial model = assetManager.loadModel("Models/Jaime/Jaime.j3o"); - Spatial model = assetManager.loadModel("Models/Oto/Oto.mesh.xml").scale(0.2f).move(0, 1, 0); + Spatial model = assetManager.loadModel("Models/Jaime/Jaime.j3o"); + // Spatial model = assetManager.loadModel("Models/Oto/Oto.mesh.xml").scale(0.2f).move(0, 1, 0); //Spatial model = assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml"); //Spatial model = assetManager.loadModel("Models/Elephant/Elephant.mesh.xml").scale(0.02f); - // AnimMigrationUtils.migrate(model); + AnimMigrationUtils.migrate(model); rootNode.attachChild(model); @@ -87,7 +88,7 @@ public class TestAnimMigration extends SimpleApplication { if (playAnim) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); System.err.println(anim); } else { composer.reset(); @@ -102,7 +103,7 @@ public class TestAnimMigration extends SimpleApplication { if (isPressed && composer != null) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); System.err.println(anim); } } @@ -132,13 +133,26 @@ public class TestAnimMigration extends SimpleApplication { for (String name : composer.getAnimClipsNames()) { anims.add(name); } + composer.actionSequence("Sequence", + composer.tweenFromClip("Walk"), + composer.tweenFromClip("Run"), + composer.tweenFromClip("Jumping")); + +// composer.actionSequence("Sequence", +// composer.tweenFromClip("Walk"), +// composer.tweenFromClip("Dodge"), +// composer.tweenFromClip("push")); + + + anims.addFirst("Sequence"); + if (anims.isEmpty()) { return; } if (playAnim) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); System.err.println(anim); } diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java index 6b2196813..1d3551df2 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java @@ -102,7 +102,7 @@ public class TestAnimSerialization extends SimpleApplication { if (playAnim) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); System.err.println(anim); } else { composer.reset(); @@ -117,7 +117,7 @@ public class TestAnimSerialization extends SimpleApplication { if (isPressed && composer != null) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); System.err.println(anim); } } @@ -144,7 +144,7 @@ public class TestAnimSerialization extends SimpleApplication { if (playAnim) { String anim = anims.poll(); anims.add(anim); - composer.setCurrentAnimClip(anim); + composer.setCurrentAction(anim); System.err.println(anim); } diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java index eefb08c75..f6b85674a 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java @@ -78,10 +78,10 @@ public class TestArmature extends SimpleApplication { new Vector3f(1, 1, 1), }; - JointTrack track1 = new JointTrack(j1, times, null, rotations, scales); - JointTrack track2 = new JointTrack(j2, times, null, rotations, null); - clip.addTrack(track1); - clip.addTrack(track2); + TransformTrack track1 = new TransformTrack(j1, times, null, rotations, scales); + TransformTrack track2 = new TransformTrack(j2, times, null, rotations, null); + + clip.setTracks(new TransformTrack[]{track1, track2}); //create the animComposer control final AnimComposer composer = new AnimComposer(); @@ -103,7 +103,7 @@ public class TestArmature extends SimpleApplication { node.addControl(composer); node.addControl(ac); - composer.setCurrentAnimClip("anim"); + composer.setCurrentAction("anim"); ArmatureDebugAppState debugAppState = new ArmatureDebugAppState(); debugAppState.addArmatureFrom(ac); @@ -134,7 +134,7 @@ public class TestArmature extends SimpleApplication { armature.resetToBindPose(); } else { - composer.setCurrentAnimClip("anim"); + composer.setCurrentAction("anim"); } } }, "bind"); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java b/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java index 512a777f3..748bb43f3 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java @@ -85,10 +85,10 @@ public class TestBaseAnimSerialization extends SimpleApplication { new Vector3f(1, 1, 1), }; - JointTrack track1 = new JointTrack(j1, times, null, rotations, scales); - JointTrack track2 = new JointTrack(j2, times, null, rotations, null); - clip.addTrack(track1); - clip.addTrack(track2); + TransformTrack track1 = new TransformTrack(j1, times, null, rotations, scales); + TransformTrack track2 = new TransformTrack(j2, times, null, rotations, null); + + clip.setTracks(new TransformTrack[]{track1, track2}); //create the animComposer control composer = new AnimComposer(); @@ -125,7 +125,7 @@ public class TestBaseAnimSerialization extends SimpleApplication { ac = newNode.getControl(SkinningControl.class); ac.setHardwareSkinningPreferred(false); armature = ac.getArmature(); - composer.setCurrentAnimClip("anim"); + composer.setCurrentAction("anim"); ArmatureDebugAppState debugAppState = new ArmatureDebugAppState(); debugAppState.addArmatureFrom(ac); @@ -156,7 +156,7 @@ public class TestBaseAnimSerialization extends SimpleApplication { armature.resetToBindPose(); } else { - composer.setCurrentAnimClip("anim"); + composer.setCurrentAction("anim"); } } }, "bind"); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestHWSkinning.java b/jme3-examples/src/main/java/jme3test/model/anim/TestHWSkinning.java index 9e92cd35d..25544e8cc 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestHWSkinning.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestHWSkinning.java @@ -48,9 +48,9 @@ import java.util.List; public class TestHWSkinning extends SimpleApplication implements ActionListener{ - private AnimComposer composer; + // private AnimComposer composer; private String[] animNames = {"Dodge", "Walk", "pull", "push"}; - private final static int SIZE = 50; + private final static int SIZE = 40; private boolean hwSkinningEnable = true; private List skControls = new ArrayList(); private BitmapText hwsText; @@ -80,9 +80,9 @@ public class TestHWSkinning extends SimpleApplication implements ActionListener{ Spatial model = (Spatial) assetManager.loadModel("Models/Oto/Oto.mesh.xml"); model.setLocalScale(0.1f); model.setLocalTranslation(i - SIZE / 2, 0, j - SIZE / 2); - composer = model.getControl(AnimComposer.class); + AnimComposer composer = model.getControl(AnimComposer.class); - composer.setCurrentAnimClip(animNames[(i + j) % 4]); + composer.setCurrentAction(animNames[(i + j) % 4]); SkinningControl skinningControl = model.getControl(SkinningControl.class); skinningControl.setHardwareSkinningPreferred(hwSkinningEnable); skControls.add(skinningControl); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestModelExportingCloning.java b/jme3-examples/src/main/java/jme3test/model/anim/TestModelExportingCloning.java index 80b6feb4c..63b5023d6 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestModelExportingCloning.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestModelExportingCloning.java @@ -60,19 +60,19 @@ public class TestModelExportingCloning extends SimpleApplication { Spatial originalModel = assetManager.loadModel("Models/Oto/Oto.mesh.xml"); composer = originalModel.getControl(AnimComposer.class); - composer.setCurrentAnimClip("Walk"); + composer.setCurrentAction("Walk"); rootNode.attachChild(originalModel); Spatial clonedModel = originalModel.clone(); clonedModel.move(10, 0, 0); composer = clonedModel.getControl(AnimComposer.class); - composer.setCurrentAnimClip("push"); + composer.setCurrentAction("push"); rootNode.attachChild(clonedModel); Spatial exportedModel = BinaryExporter.saveAndLoad(assetManager, originalModel); exportedModel.move(20, 0, 0); composer = exportedModel.getControl(AnimComposer.class); - composer.setCurrentAnimClip("pull"); + composer.setCurrentAction("pull"); rootNode.attachChild(exportedModel); } } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java index a2bc44aec..d77675e27 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java @@ -793,6 +793,7 @@ public class GltfLoader implements AssetLoader { List spatials = new ArrayList<>(); AnimClip anim = new AnimClip(name); + List ttracks = new ArrayList<>(); int skinIndex = -1; List usedJoints = new ArrayList<>(); @@ -806,8 +807,8 @@ public class GltfLoader implements AssetLoader { if (node instanceof Spatial) { Spatial s = (Spatial) node; spatials.add(s); - SpatialTrack track = new SpatialTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales); - anim.addTrack(track); + TransformTrack track = new TransformTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales); + ttracks.add(track); } else if (node instanceof JointWrapper) { JointWrapper jw = (JointWrapper) node; usedJoints.add(jw.joint); @@ -822,8 +823,8 @@ public class GltfLoader implements AssetLoader { } } - JointTrack track = new JointTrack(jw.joint, trackData.times, trackData.translations, trackData.rotations, trackData.scales); - anim.addTrack(track); + TransformTrack track = new TransformTrack(jw.joint, trackData.times, trackData.translations, trackData.rotations, trackData.scales); + ttracks.add(track); } } @@ -834,19 +835,21 @@ public class GltfLoader implements AssetLoader { if (skinIndex != -1) { SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class); for (Joint joint : skin.joints) { - if (!usedJoints.contains(joint)) {// && !equalBindAndLocalTransforms(joint) + if (!usedJoints.contains(joint)) { //create a track float[] times = new float[]{0}; Vector3f[] translations = new Vector3f[]{joint.getLocalTranslation()}; Quaternion[] rotations = new Quaternion[]{joint.getLocalRotation()}; Vector3f[] scales = new Vector3f[]{joint.getLocalScale()}; - JointTrack track = new JointTrack(joint, times, translations, rotations, scales); - anim.addTrack(track); + TransformTrack track = new TransformTrack(joint, times, translations, rotations, scales); + ttracks.add(track); } } } + anim.setTracks(ttracks.toArray(new TransformTrack[ttracks.size()])); + anim = customContentManager.readExtensionAndExtras("animations", animation, anim); if (skinIndex != -1) { diff --git a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java index b1350c93c..88ab22853 100644 --- a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java +++ b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java @@ -54,16 +54,16 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader { private Stack elementStack = new Stack(); private HashMap indexToJoint = new HashMap<>(); private HashMap nameToJoint = new HashMap<>(); - private JointTrack track; - private ArrayList tracks = new ArrayList<>(); + private TransformTrack track; + private ArrayList tracks = new ArrayList<>(); private AnimClip animClip; private ArrayList animClips; private Joint joint; private Armature armature; - private ArrayList times = new ArrayList(); - private ArrayList translations = new ArrayList(); - private ArrayList rotations = new ArrayList(); - private ArrayList scales = new ArrayList(); + private ArrayList times = new ArrayList<>(); + private ArrayList translations = new ArrayList<>(); + private ArrayList rotations = new ArrayList<>(); + private ArrayList scales = new ArrayList<>(); private float time = -1; private Vector3f position; private Quaternion rotation; @@ -92,7 +92,7 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader { assert elementStack.peek().equals("tracks"); String jointName = SAXUtil.parseString(attribs.getValue("bone")); joint = nameToJoint.get(jointName); - track = new JointTrack(); + track = new TransformTrack(); track.setTarget(joint); } else if (qName.equals("boneparent")) { assert elementStack.peek().equals("bonehierarchy"); @@ -163,10 +163,6 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader { armature = new Armature(joints); armature.setBindPose(); } else if (qName.equals("animation")) { - //nameToJoint contains the joints with no track - for (Joint j : unusedJoints) { - AnimMigrationUtils.padJointTracks(animClip, j); - } animClips.add(animClip); animClip = null; } else if (qName.equals("track")) { @@ -176,7 +172,11 @@ public class SkeletonLoader extends DefaultHandler implements AssetLoader { track = null; } } else if (qName.equals("tracks")) { - JointTrack[] trackList = tracks.toArray(new JointTrack[tracks.size()]); + //nameToJoint contains the joints with no track + for (Joint j : unusedJoints) { + AnimMigrationUtils.padJointTracks(tracks, j); + } + TransformTrack[] trackList = tracks.toArray(new TransformTrack[tracks.size()]); animClip.setTracks(trackList); tracks.clear(); } else if (qName.equals("keyframe")) {