Prechádzať zdrojové kódy

Added the base of the blending system. Smooth transition between anims

Rémy Bouquet 7 rokov pred
rodič
commit
05e907acca
25 zmenil súbory, kde vykonal 452 pridanie a 358 odobranie
  1. 21 42
      jme3-core/src/main/java/com/jme3/anim/AnimClip.java
  2. 50 9
      jme3-core/src/main/java/com/jme3/anim/AnimComposer.java
  3. 2 1
      jme3-core/src/main/java/com/jme3/anim/Joint.java
  4. 0 108
      jme3-core/src/main/java/com/jme3/anim/JointTrack.java
  5. 0 111
      jme3-core/src/main/java/com/jme3/anim/SpatialTrack.java
  6. 27 19
      jme3-core/src/main/java/com/jme3/anim/TransformTrack.java
  7. 99 0
      jme3-core/src/main/java/com/jme3/anim/tween/AnimClipTween.java
  8. 64 0
      jme3-core/src/main/java/com/jme3/anim/tween/action/Action.java
  9. 75 0
      jme3-core/src/main/java/com/jme3/anim/tween/action/SequenceAction.java
  10. 16 9
      jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java
  11. 10 0
      jme3-core/src/main/java/com/jme3/anim/util/HasLocalTransform.java
  12. 11 0
      jme3-core/src/main/java/com/jme3/anim/util/Weighted.java
  13. 1 1
      jme3-core/src/main/java/com/jme3/math/Transform.java
  14. 3 2
      jme3-core/src/main/java/com/jme3/scene/Spatial.java
  15. 2 2
      jme3-core/src/main/resources/Common/MatDefs/ShaderNodes/Misc/Dashed100.frag
  16. 1 1
      jme3-examples/src/main/java/jme3test/export/TestOgreConvert.java
  17. 4 4
      jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java
  18. 22 8
      jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java
  19. 3 3
      jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java
  20. 6 6
      jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java
  21. 6 6
      jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java
  22. 4 4
      jme3-examples/src/main/java/jme3test/model/anim/TestHWSkinning.java
  23. 3 3
      jme3-examples/src/main/java/jme3test/model/anim/TestModelExportingCloning.java
  24. 10 7
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  25. 12 12
      jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java

+ 21 - 42
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<Tween> 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<Tween> 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();
+                }
             }
         }
     }

+ 50 - 9
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<String, AnimClip> animClipMap = new HashMap<>();
 
-    private AnimClip currentAnimClip;
+    private Action currentAction;
+    private Map<String, Action> 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<String, Action> 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<String, AnimClip>) ic.readStringSavableMap("animClipMap", new HashMap<String, AnimClip>());
+        actions = (Map<String, Action>) ic.readStringSavableMap("actions", new HashMap<String, Action>());
     }
 
     @Override
@@ -123,5 +163,6 @@ public class AnimComposer extends AbstractControl {
         super.write(ex);
         OutputCapsule oc = ex.getCapsule(this);
         oc.writeStringSavableMap(animClipMap, "animClipMap", new HashMap<String, AnimClip>());
+        oc.writeStringSavableMap(actions, "actions", new HashMap<String, Action>());
     }
 }

+ 2 - 1
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;

+ 0 - 108
jme3-core/src/main/java/com/jme3/anim/JointTrack.java

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

+ 0 - 111
jme3-core/src/main/java/com/jme3/anim/SpatialTrack.java

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

+ 27 - 19
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);
+    }
 }

+ 99 - 0
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;
+    }
+}

+ 64 - 0
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);
+    }
+}

+ 75 - 0
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;
+        }
+    }
+}

+ 16 - 9
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<TransformTrack> 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<TransformTrack> 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) {

+ 10 - 0
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();
+}

+ 11 - 0
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);
+}

+ 1 - 1
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.

+ 3 - 2
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<Spatial> queue);
-}
+}

+ 2 - 2
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;
     }
 

+ 1 - 1
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){

+ 4 - 4
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) {

+ 22 - 8
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<String> anims = new LinkedList<>();
-    boolean playAnim = true;
+    LinkedList<String> 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);
             }
 

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

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

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

+ 4 - 4
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<SkinningControl> skControls = new ArrayList<SkinningControl>();
     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);

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

+ 10 - 7
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

@@ -793,6 +793,7 @@ public class GltfLoader implements AssetLoader {
 
         List<Spatial> spatials = new ArrayList<>();
         AnimClip anim = new AnimClip(name);
+        List<TransformTrack> ttracks = new ArrayList<>();
         int skinIndex = -1;
 
         List<Joint> 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) {

+ 12 - 12
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<String> elementStack = new Stack<String>();
     private HashMap<Integer, Joint> indexToJoint = new HashMap<>();
     private HashMap<String, Joint> nameToJoint = new HashMap<>();
-    private JointTrack track;
-    private ArrayList<JointTrack> tracks = new ArrayList<>();
+    private TransformTrack track;
+    private ArrayList<TransformTrack> tracks = new ArrayList<>();
     private AnimClip animClip;
     private ArrayList<AnimClip> animClips;
     private Joint joint;
     private Armature armature;
-    private ArrayList<Float> times = new ArrayList<Float>();
-    private ArrayList<Vector3f> translations = new ArrayList<Vector3f>();
-    private ArrayList<Quaternion> rotations = new ArrayList<Quaternion>();
-    private ArrayList<Vector3f> scales = new ArrayList<Vector3f>();
+    private ArrayList<Float> times = new ArrayList<>();
+    private ArrayList<Vector3f> translations = new ArrayList<>();
+    private ArrayList<Quaternion> rotations = new ArrayList<>();
+    private ArrayList<Vector3f> 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")) {