Pārlūkot izejas kodu

Gltf loader now supports the new animation system

Nehon 7 gadi atpakaļ
vecāks
revīzija
1f3c0e4c84

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

@@ -55,7 +55,7 @@ public final class JointTrack extends TransformTrack implements JmeCloneable, Sa
     }
 
     /**
-     * Creates a bone track for the given bone index
+     * 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

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

@@ -0,0 +1,114 @@
+/*
+ * 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;
+    }
+
+    @Override
+    public boolean interpolate(double t) {
+        setDefaultTransform(target.getLocalTransform());
+        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() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException("Error cloning", e);
+        }
+    }
+
+    @Override
+    public void cloneFields(Cloner cloner, Object original) {
+        super.cloneFields(cloner, 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);
+    }
+
+}

+ 64 - 38
jme3-examples/src/main/java/jme3test/model/TestGltfLoading.java

@@ -31,7 +31,8 @@
  */
 package jme3test.model;
 
-import com.jme3.animation.*;
+import com.jme3.anim.AnimComposer;
+import com.jme3.anim.SkinningControl;
 import com.jme3.app.ChaseCameraAppState;
 import com.jme3.app.SimpleApplication;
 import com.jme3.asset.plugins.FileLocator;
@@ -43,11 +44,10 @@ import com.jme3.renderer.Limits;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.control.Control;
+import com.jme3.scene.debug.custom.ArmatureDebugAppState;
+import com.jme3.scene.plugins.gltf.GltfModelKey;
 
-import java.util.ArrayList;
-import java.util.List;
-
-//import com.jme3.scene.debug.custom.SkeletonDebugAppState;
+import java.util.*;
 
 public class TestGltfLoading extends SimpleApplication {
 
@@ -75,8 +75,8 @@ public class TestGltfLoading extends SimpleApplication {
      */
     public void simpleInitApp() {
 
-//        SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
-//        getStateManager().attach(skeletonDebugAppState);
+        ArmatureDebugAppState armatureDebugappState = new ArmatureDebugAppState();
+        getStateManager().attach(armatureDebugappState);
 
         String folder = System.getProperty("user.home");
         assetManager.registerLocator(folder, FileLocator.class);
@@ -109,28 +109,40 @@ public class TestGltfLoading extends SimpleApplication {
 //        rootNode.addLight(pl);
 //        PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
 //        rootNode.addLight(pl1);
-
+        //loadModel("Models/gltf/nier/scene.gltf", new Vector3f(0, -1.5f, 0), 0.01f);
+        //loadModel("Models/gltf/izzy/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
+        //loadModel("Models/gltf/darth/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
+        //loadModel("Models/gltf/mech/scene.gltf", new Vector3f(0, -1, 0), 0.01f);
+        //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/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/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);
 //        loadModel("Models/gltf/box/box.gltf", Vector3f.ZERO, 1);
 //        loadModel("Models/gltf/duck/Duck.gltf", new Vector3f(0, -1, 0), 1);
 //        loadModel("Models/gltf/damagedHelmet/damagedHelmet.gltf", Vector3f.ZERO, 1);
-        loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
+//        loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
 ////        loadModel("Models/gltf/adamHead/adamHead.gltf", Vector3f.ZERO, 0.6f);
-        //      loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
+        //loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
 //        loadModel("Models/gltf/animatedCube/AnimatedCube.gltf", Vector3f.ZERO, 0.5f);
 //
-//        //loadModel("Models/gltf/BoxAnimated/BoxAnimated.gltf", new Vector3f(0, 0f, 0), 0.8f);
+        //loadModel("Models/gltf/BoxAnimated/BoxAnimated.gltf", new Vector3f(0, 0f, 0), 0.8f);
 //
-//        loadModel("Models/gltf/RiggedFigure/RiggedSimple.gltf", new Vector3f(0, -0.3f, 0), 0.2f);
+        //loadModel("Models/gltf/RiggedFigure/RiggedSimple.gltf", new Vector3f(0, -0.3f, 0), 0.2f);
         //loadModel("Models/gltf/RiggedFigure/RiggedFigure.gltf", new Vector3f(0, -1f, 0), 1f);
         //loadModel("Models/gltf/CesiumMan/CesiumMan.gltf", new Vector3f(0, -1, 0), 1f);
         //loadModel("Models/gltf/BrainStem/BrainStem.gltf", new Vector3f(0, -1, 0), 1f);
         //loadModel("Models/gltf/Jaime/Jaime.gltf", new Vector3f(0, -1, 0), 1f);
-//        loadModel("Models/gltf/GiantWorm/GiantWorm.gltf", new Vector3f(0, -1, 0), 1f);
-//        //loadModel("Models/gltf/RiggedFigure/WalkingLady.gltf", new Vector3f(0, -0.f, 0), 1f);
-//        loadModel("Models/gltf/Monster/Monster.gltf", Vector3f.ZERO, 0.03f);
+        // loadModel("Models/gltf/GiantWorm/GiantWorm.gltf", new Vector3f(0, -1, 0), 1f);
+        //loadModel("Models/gltf/RiggedFigure/WalkingLady.gltf", new Vector3f(0, -0.f, 0), 1f);
+        //loadModel("Models/gltf/Monster/Monster.gltf", Vector3f.ZERO, 0.03f);
 
 //        loadModel("Models/gltf/corset/Corset.gltf", new Vector3f(0, -1, 0), 20f);
-        loadModel("Models/gltf/boxInter/BoxInterleaved.gltf", new Vector3f(0, 0, 0), 1f);
+        //    loadModel("Models/gltf/boxInter/BoxInterleaved.gltf", new Vector3f(0, 0, 0), 1f);
 
 
         probeNode.attachChild(assets.get(0));
@@ -157,6 +169,7 @@ public class TestGltfLoading extends SimpleApplication {
         }, "autorotate");
 
         inputManager.addMapping("toggleAnim", new KeyTrigger(KeyInput.KEY_RETURN));
+
         inputManager.addListener(new ActionListener() {
             @Override
             public void onAction(String name, boolean isPressed, float tpf) {
@@ -170,6 +183,17 @@ public class TestGltfLoading extends SimpleApplication {
                 }
             }
         }, "toggleAnim");
+        inputManager.addMapping("nextAnim", new KeyTrigger(KeyInput.KEY_RIGHT));
+        inputManager.addListener(new ActionListener() {
+            @Override
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (isPressed && composer != null) {
+                    String anim = anims.poll();
+                    anims.add(anim);
+                    composer.setCurrentAnimClip(anim);
+                }
+            }
+        }, "nextAnim");
 
         dumpScene(rootNode, 0);
     }
@@ -192,7 +216,9 @@ public class TestGltfLoading extends SimpleApplication {
     }
 
     private void loadModel(String path, Vector3f offset, float scale) {
-        Spatial s = assetManager.loadModel(path);
+        GltfModelKey k = new GltfModelKey(path);
+        //k.setKeepSkeletonPose(true);
+        Spatial s = assetManager.loadModel(k);
         s.scale(scale);
         s.move(offset);
         assets.add(s);
@@ -200,14 +226,15 @@ public class TestGltfLoading extends SimpleApplication {
             playFirstAnim(s);
         }
 
-        SkeletonControl ctrl = findControl(s, SkeletonControl.class);
+        SkinningControl ctrl = findControl(s, SkinningControl.class);
 
-//        //ctrl.getSpatial().removeControl(ctrl);
+        //  ctrl.getSpatial().removeControl(ctrl);
         if (ctrl == null) {
             return;
         }
-        ctrl.setHardwareSkinningPreferred(false);
-        //getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
+        //System.err.println(ctrl.getArmature().toString());
+        //ctrl.setHardwareSkinningPreferred(false);
+        getStateManager().getState(ArmatureDebugAppState.class).addArmatureFrom(ctrl);
 //        AnimControl aCtrl = findControl(s, AnimControl.class);
 //        //ctrl.getSpatial().removeControl(ctrl);
 //        if (aCtrl == null) {
@@ -219,17 +246,24 @@ public class TestGltfLoading extends SimpleApplication {
 
     }
 
+    Queue<String> anims = new LinkedList<>();
+    AnimComposer composer;
+
     private void playFirstAnim(Spatial s) {
 
-        AnimControl control = s.getControl(AnimControl.class);
+        AnimComposer control = s.getControl(AnimComposer.class);
         if (control != null) {
-//            if (control.getAnimationNames().size() > 0) {
-//                control.createChannel().setAnim(control.getAnimationNames().iterator().next());
-//            }
-            for (String name : control.getAnimationNames()) {
-                control.createChannel().setAnim(name);
+            anims.clear();
+            for (String name : control.getAnimClipsNames()) {
+                anims.add(name);
             }
-
+            if (anims.isEmpty()) {
+                return;
+            }
+            String anim = anims.poll();
+            anims.add(anim);
+            control.setCurrentAnimClip(anim);
+            composer = control;
         }
         if (s instanceof Node) {
             Node n = (Node) s;
@@ -241,17 +275,9 @@ public class TestGltfLoading extends SimpleApplication {
 
     private void stopAnim(Spatial s) {
 
-        AnimControl control = s.getControl(AnimControl.class);
+        AnimComposer control = s.getControl(AnimComposer.class);
         if (control != null) {
-            for (int i = 0; i < control.getNumChannels(); i++) {
-                AnimChannel ch = control.getChannel(i);
-                ch.reset(true);
-            }
-            control.clearChannels();
-            if (control.getSkeleton() != null) {
-                control.getSkeleton().reset();
-            }
-
+            control.reset();
         }
         if (s instanceof Node) {
             Node n = (Node) s;

+ 0 - 320
jme3-examples/src/main/java/jme3test/model/TestGltfLoading2.java

@@ -1,320 +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 jme3test.model;
-
-import com.jme3.animation.*;
-import com.jme3.app.ChaseCameraAppState;
-import com.jme3.app.SimpleApplication;
-import com.jme3.input.KeyInput;
-import com.jme3.input.controls.ActionListener;
-import com.jme3.input.controls.KeyTrigger;
-import com.jme3.math.*;
-import com.jme3.renderer.Limits;
-import com.jme3.scene.Node;
-import com.jme3.scene.Spatial;
-import com.jme3.scene.control.Control;
-import com.jme3.scene.plugins.gltf.GltfModelKey;
-
-import java.util.*;
-
-//import com.jme3.scene.debug.custom.SkeletonDebugAppState;
-
-public class TestGltfLoading2 extends SimpleApplication {
-
-    Node autoRotate = new Node("autoRotate");
-    List<Spatial> assets = new ArrayList<>();
-    Node probeNode;
-    float time = 0;
-    int assetIndex = 0;
-    boolean useAutoRotate = false;
-    private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
-    int duration = 2;
-    boolean playAnim = true;
-
-    public static void main(String[] args) {
-        TestGltfLoading2 app = new TestGltfLoading2();
-        app.start();
-    }
-
-    /*
-    WARNING this test case can't wok without the assets, and considering their size, they are not pushed into the repo
-    you can find them here :
-    https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0
-    https://sketchfab.com/features/gltf
-    You have to copy them in Model/gltf folder in the test-data project.
-     */
-    public void simpleInitApp() {
-
-//        SkeletonDebugAppState skeletonDebugAppState = new SkeletonDebugAppState();
-//        getStateManager().attach(skeletonDebugAppState);
-
-        // cam.setLocation(new Vector3f(4.0339394f, 2.645184f, 6.4627485f));
-        // cam.setRotation(new Quaternion(-0.013950467f, 0.98604023f, -0.119502485f, -0.11510504f));
-        cam.setFrustumPerspective(45f, (float) cam.getWidth() / cam.getHeight(), 0.1f, 100f);
-        renderer.setDefaultAnisotropicFilter(Math.min(renderer.getLimits().get(Limits.TextureAnisotropy), 8));
-        setPauseOnLostFocus(false);
-
-        flyCam.setMoveSpeed(5);
-        flyCam.setDragToRotate(true);
-        flyCam.setEnabled(false);
-        viewPort.setBackgroundColor(new ColorRGBA().setAsSrgb(0.2f, 0.2f, 0.2f, 1.0f));
-        rootNode.attachChild(autoRotate);
-        probeNode = (Node) assetManager.loadModel("Scenes/defaultProbe.j3o");
-        autoRotate.attachChild(probeNode);
-
-//        DirectionalLight dl = new DirectionalLight();
-//        dl.setDirection(new Vector3f(-1f, -1.0f, -1f).normalizeLocal());
-//        dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
-//        rootNode.addLight(dl);
-
-//        DirectionalLight dl2 = new DirectionalLight();
-//        dl2.setDirection(new Vector3f(1f, 1.0f, 1f).normalizeLocal());
-//        dl2.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
-//        rootNode.addLight(dl2);
-
-//        PointLight pl = new PointLight(new Vector3f(5.0f, 5.0f, 5.0f), ColorRGBA.White, 30);
-//        rootNode.addLight(pl);
-//        PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50);
-//        rootNode.addLight(pl1);
-        //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/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/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);
-//        loadModel("Models/gltf/box/box.gltf", Vector3f.ZERO, 1);
-//        loadModel("Models/gltf/duck/Duck.gltf", new Vector3f(0, -1, 0), 1);
-//        loadModel("Models/gltf/damagedHelmet/damagedHelmet.gltf", Vector3f.ZERO, 1);
-//        loadModel("Models/gltf/hornet/scene.gltf", new Vector3f(0, -0.5f, 0), 0.4f);
-////        loadModel("Models/gltf/adamHead/adamHead.gltf", Vector3f.ZERO, 0.6f);
-        //loadModel("Models/gltf/busterDrone/busterDrone.gltf", new Vector3f(0, 0f, 0), 0.8f);
-//        loadModel("Models/gltf/animatedCube/AnimatedCube.gltf", Vector3f.ZERO, 0.5f);
-//
-//        //loadModel("Models/gltf/BoxAnimated/BoxAnimated.gltf", new Vector3f(0, 0f, 0), 0.8f);
-//
-        //loadModel("Models/gltf/RiggedFigure/RiggedSimple.gltf", new Vector3f(0, -0.3f, 0), 0.2f);
-        //loadModel("Models/gltf/RiggedFigure/RiggedFigure.gltf", new Vector3f(0, -1f, 0), 1f);
-        //loadModel("Models/gltf/CesiumMan/CesiumMan.gltf", new Vector3f(0, -1, 0), 1f);
-        //loadModel("Models/gltf/BrainStem/BrainStem.gltf", new Vector3f(0, -1, 0), 1f);
-        //loadModel("Models/gltf/Jaime/Jaime.gltf", new Vector3f(0, -1, 0), 1f);
-        //loadModel("Models/gltf/GiantWorm/GiantWorm.gltf", new Vector3f(0, -1, 0), 1f);
-        //loadModel("Models/gltf/RiggedFigure/WalkingLady.gltf", new Vector3f(0, -0.f, 0), 1f);
-        //loadModel("Models/gltf/Monster/Monster.gltf", Vector3f.ZERO, 0.03f);
-
-//        loadModel("Models/gltf/corset/Corset.gltf", new Vector3f(0, -1, 0), 20f);
-        //    loadModel("Models/gltf/boxInter/BoxInterleaved.gltf", new Vector3f(0, 0, 0), 1f);
-
-
-        probeNode.attachChild(assets.get(0));
-
-        ChaseCameraAppState chaseCam = new ChaseCameraAppState();
-        chaseCam.setTarget(probeNode);
-        getStateManager().attach(chaseCam);
-        chaseCam.setInvertHorizontalAxis(true);
-        chaseCam.setInvertVerticalAxis(true);
-        chaseCam.setZoomSpeed(0.5f);
-        chaseCam.setMinVerticalRotation(-FastMath.HALF_PI);
-        chaseCam.setRotationSpeed(3);
-        chaseCam.setDefaultDistance(3);
-        chaseCam.setDefaultVerticalRotation(0.3f);
-
-        inputManager.addMapping("autorotate", new KeyTrigger(KeyInput.KEY_SPACE));
-        inputManager.addListener(new ActionListener() {
-            @Override
-            public void onAction(String name, boolean isPressed, float tpf) {
-                if (isPressed) {
-                    useAutoRotate = !useAutoRotate;
-                }
-            }
-        }, "autorotate");
-
-        inputManager.addMapping("toggleAnim", new KeyTrigger(KeyInput.KEY_RETURN));
-
-        inputManager.addListener(new ActionListener() {
-            @Override
-            public void onAction(String name, boolean isPressed, float tpf) {
-                if (isPressed) {
-                    playAnim = !playAnim;
-                    if (playAnim) {
-                        playFirstAnim(rootNode);
-                    } else {
-                        stopAnim(rootNode);
-                    }
-                }
-            }
-        }, "toggleAnim");
-        inputManager.addMapping("nextAnim", new KeyTrigger(KeyInput.KEY_RIGHT));
-        inputManager.addListener(new ActionListener() {
-            @Override
-            public void onAction(String name, boolean isPressed, float tpf) {
-                if (isPressed && animControl != null) {
-                    AnimChannel c = animControl.getChannel(0);
-                    if (c == null) {
-                        c = animControl.createChannel();
-                    }
-                    String anim = anims.poll();
-                    anims.add(anim);
-                    c.setAnim(anim);
-                }
-            }
-        }, "nextAnim");
-
-        dumpScene(rootNode, 0);
-    }
-
-    private <T extends Control> T findControl(Spatial s, Class<T> controlClass) {
-        T ctrl = s.getControl(controlClass);
-        if (ctrl != null) {
-            return ctrl;
-        }
-        if (s instanceof Node) {
-            Node n = (Node) s;
-            for (Spatial spatial : n.getChildren()) {
-                ctrl = findControl(spatial, controlClass);
-                if (ctrl != null) {
-                    return ctrl;
-                }
-            }
-        }
-        return null;
-    }
-
-    private void loadModel(String path, Vector3f offset, float scale) {
-        GltfModelKey k = new GltfModelKey(path);
-        //k.setKeepSkeletonPose(true);
-        Spatial s = assetManager.loadModel(k);
-        s.scale(scale);
-        s.move(offset);
-        assets.add(s);
-        if (playAnim) {
-            playFirstAnim(s);
-        }
-
-        SkeletonControl ctrl = findControl(s, SkeletonControl.class);
-
-        //  ctrl.getSpatial().removeControl(ctrl);
-        if (ctrl == null) {
-            return;
-        }
-        //System.err.println(ctrl.getArmature().toString());
-        //ctrl.setHardwareSkinningPreferred(false);
-        // getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(ctrl, true);
-//        AnimControl aCtrl = findControl(s, AnimControl.class);
-//        //ctrl.getSpatial().removeControl(ctrl);
-//        if (aCtrl == null) {
-//            return;
-//        }
-//        if (aCtrl.getArmature() != null) {
-//            getStateManager().getState(SkeletonDebugAppState.class).addSkeleton(aCtrl.getArmature(), aCtrl.getSpatial(), true);
-//        }
-
-    }
-
-    Queue<String> anims = new LinkedList<>();
-    AnimControl animControl;
-
-    private void playFirstAnim(Spatial s) {
-
-        AnimControl control = s.getControl(AnimControl.class);
-        if (control != null) {
-            anims.clear();
-            for (String name : control.getAnimationNames()) {
-                anims.add(name);
-            }
-            if (anims.isEmpty()) {
-                return;
-            }
-            String anim = anims.poll();
-            anims.add(anim);
-            control.createChannel().setAnim(anim);
-            animControl = control;
-        }
-        if (s instanceof Node) {
-            Node n = (Node) s;
-            for (Spatial spatial : n.getChildren()) {
-                playFirstAnim(spatial);
-            }
-        }
-    }
-
-    private void stopAnim(Spatial s) {
-
-        AnimControl control = s.getControl(AnimControl.class);
-        if (control != null) {
-            for (int i = 0; i < control.getNumChannels(); i++) {
-                AnimChannel ch = control.getChannel(i);
-                ch.reset(true);
-            }
-            control.clearChannels();
-        }
-        if (s instanceof Node) {
-            Node n = (Node) s;
-            for (Spatial spatial : n.getChildren()) {
-                stopAnim(spatial);
-            }
-        }
-    }
-
-    @Override
-    public void simpleUpdate(float tpf) {
-
-        if (!useAutoRotate) {
-            return;
-        }
-        time += tpf;
-        autoRotate.rotate(0, tpf * 0.5f, 0);
-        if (time > duration) {
-            assets.get(assetIndex).removeFromParent();
-            assetIndex = (assetIndex + 1) % assets.size();
-            if (assetIndex == 0) {
-                duration = 10;
-            }
-            probeNode.attachChild(assets.get(assetIndex));
-            time = 0;
-        }
-    }
-
-    private void dumpScene(Spatial s, int indent) {
-        System.err.println(indentString.substring(0, indent) + s.getName() + " (" + s.getClass().getSimpleName() + ") / " +
-                s.getLocalTransform().getTranslation().toString() + ", " +
-                s.getLocalTransform().getRotation().toString() + ", " +
-                s.getLocalTransform().getScale().toString());
-        if (s instanceof Node) {
-            Node n = (Node) s;
-            for (Spatial spatial : n.getChildren()) {
-                dumpScene(spatial, indent + 1);
-            }
-        }
-    }
-}

+ 78 - 244
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

@@ -2,7 +2,7 @@ package com.jme3.scene.plugins.gltf;
 
 import com.google.gson.*;
 import com.google.gson.stream.JsonReader;
-import com.jme3.animation.*;
+import com.jme3.anim.*;
 import com.jme3.asset.*;
 import com.jme3.material.Material;
 import com.jme3.material.RenderState;
@@ -196,7 +196,7 @@ public class GltfLoader implements AssetLoader {
     public Object readNode(int nodeIndex) throws IOException {
         Object obj = fetchFromCache("nodes", nodeIndex, Object.class);
         if (obj != null) {
-            if (obj instanceof BoneWrapper) {
+            if (obj instanceof JointWrapper) {
                 //the node can be a previously loaded bone let's return it
                 return obj;
             } else {
@@ -274,9 +274,9 @@ public class GltfLoader implements AssetLoader {
                     readChild(spatial, child);
                 }
             }
-        } else if (loaded instanceof BoneWrapper) {
+        } else if (loaded instanceof JointWrapper) {
             //parent is the Armature Node, we have to apply its transforms to the root bone's animation data
-            BoneWrapper bw = (BoneWrapper) loaded;
+            JointWrapper bw = (JointWrapper) loaded;
             bw.isRoot = true;
             SkinData skinData = fetchFromCache("skins", bw.skinIndex, SkinData.class);
             if (skinData == null) {
@@ -792,44 +792,37 @@ public class GltfLoader implements AssetLoader {
         }
 
         List<Spatial> spatials = new ArrayList<>();
-        Animation anim = new Animation();
-        anim.setName(name);
+        AnimClip anim = new AnimClip(name);
         int skinIndex = -1;
 
-        List<Bone> usedBones = new ArrayList<>();
+        List<Joint> usedJoints = new ArrayList<>();
         for (int i = 0; i < tracks.length; i++) {
             TrackData trackData = tracks[i];
             if (trackData == null || trackData.timeArrays.isEmpty()) {
                 continue;
             }
             trackData.update();
-            if (trackData.length > anim.getLength()) {
-                anim.setLength(trackData.length);
-            }
             Object node = fetchFromCache("nodes", i, Object.class);
             if (node instanceof Spatial) {
                 Spatial s = (Spatial) node;
                 spatials.add(s);
-                SpatialTrack track = new SpatialTrack(trackData.times, trackData.translations, trackData.rotations, trackData.scales);
-                track.setTrackSpatial(s);
+                SpatialTrack track = new SpatialTrack(s, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
                 anim.addTrack(track);
-            } else if (node instanceof BoneWrapper) {
-                BoneWrapper b = (BoneWrapper) node;
-                //apply the inverseBindMatrix to animation data.
-                b.update(trackData);
-                usedBones.add(b.bone);
+            } else if (node instanceof JointWrapper) {
+                JointWrapper jw = (JointWrapper) node;
+                usedJoints.add(jw.joint);
 
                 if (skinIndex == -1) {
-                    skinIndex = b.skinIndex;
+                    skinIndex = jw.skinIndex;
                 } else {
-                    //Check if all bones affected by this animation are from the same skin, the track will be skipped.
-                    if (skinIndex != b.skinIndex) {
-                        logger.log(Level.WARNING, "Animation " + animationIndex + " (" + name + ") applies to bones that are not from the same skin: skin " + skinIndex + ", bone " + b.bone.getName() + " from skin " + b.skinIndex);
+                    //Check if all joints affected by this animation are from the same skin, the track will be skipped.
+                    if (skinIndex != jw.skinIndex) {
+                        logger.log(Level.WARNING, "Animation " + animationIndex + " (" + name + ") applies to joints that are not from the same skin: skin " + skinIndex + ", joint " + jw.joint.getName() + " from skin " + jw.skinIndex);
                         continue;
                     }
                 }
 
-                BoneTrack track = new BoneTrack(b.boneIndex, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
+                JointTrack track = new JointTrack(jw.joint, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
                 anim.addTrack(track);
 
             }
@@ -840,22 +833,15 @@ public class GltfLoader implements AssetLoader {
         // instead of the local pose that is supposed to be the default
         if (skinIndex != -1) {
             SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class);
-            Skeleton skeleton = skin.skeletonControl.getSkeleton();
-            for (Bone bone : skin.bones) {
-                if (!usedBones.contains(bone) && !equalBindAndLocalTransforms(bone)) {
+            for (Joint joint : skin.joints) {
+                if (!usedJoints.contains(joint)) {// && !equalBindAndLocalTransforms(joint)
                     //create a track
-                    float[] times = new float[]{0, anim.getLength()};
-
-                    Vector3f t = bone.getLocalPosition().subtract(bone.getBindPosition());
-                    Quaternion r = tmpQuat.set(bone.getBindRotation()).inverse().multLocal(bone.getLocalRotation());
-                    Vector3f s = bone.getLocalScale().divide(bone.getBindScale());
+                    float[] times = new float[]{0};
 
-                    Vector3f[] translations = new Vector3f[]{t, t};
-                    Quaternion[] rotations = new Quaternion[]{r, r};
-                    Vector3f[] scales = new Vector3f[]{s, s};
-
-                    int boneIndex = skeleton.getBoneIndex(bone);
-                    BoneTrack track = new BoneTrack(boneIndex, times, translations, rotations, scales);
+                    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);
                 }
             }
@@ -864,9 +850,9 @@ public class GltfLoader implements AssetLoader {
         anim = customContentManager.readExtensionAndExtras("animations", animation, anim);
 
         if (skinIndex != -1) {
-            //we have a bone animation.
+            //we have a armature animation.
             SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class);
-            skin.animControl.addAnim(anim);
+            skin.animComposer.addAnimClip(anim);
         }
 
         if (!spatials.isEmpty()) {
@@ -885,12 +871,12 @@ public class GltfLoader implements AssetLoader {
                     spatial = findCommonAncestor(spatials);
                 }
 
-                AnimControl control = spatial.getControl(AnimControl.class);
-                if (control == null) {
-                    control = new AnimControl();
-                    spatial.addControl(control);
+                AnimComposer composer = spatial.getControl(AnimComposer.class);
+                if (composer == null) {
+                    composer = new AnimComposer();
+                    spatial.addControl(composer);
                 }
-                control.addAnim(anim);
+                composer.addAnimClip(anim);
             }
         }
     }
@@ -932,16 +918,16 @@ public class GltfLoader implements AssetLoader {
             //It's not mandatory and exporters tends to mix up how it should be used because the specs are not clear.
             //Anyway we have other means to detect both armature structures and root bones.
 
-            JsonArray joints = skin.getAsJsonArray("joints");
-            assertNotNull(joints, "No joints defined for skin");
-            int idx = allJoints.indexOf(joints);
+            JsonArray jsonJoints = skin.getAsJsonArray("joints");
+            assertNotNull(jsonJoints, "No joints defined for skin");
+            int idx = allJoints.indexOf(jsonJoints);
             if (idx >= 0) {
                 //skin already exists let's just set it in the cache
                 SkinData sd = fetchFromCache("skins", idx, SkinData.class);
                 addToCache("skins", index, sd, nodes.size());
                 continue;
             } else {
-                allJoints.add(joints);
+                allJoints.add(jsonJoints);
             }
 
             //These inverse bind matrices, once inverted again, will give us the real bind pose of the bones (in model space),
@@ -951,136 +937,75 @@ public class GltfLoader implements AssetLoader {
             if (matricesIndex != null) {
                 inverseBindMatrices = readAccessorData(matricesIndex, matrix4fArrayPopulator);
             } else {
-                inverseBindMatrices = new Matrix4f[joints.size()];
+                inverseBindMatrices = new Matrix4f[jsonJoints.size()];
                 for (int i = 0; i < inverseBindMatrices.length; i++) {
                     inverseBindMatrices[i] = new Matrix4f();
                 }
             }
 
-            Bone[] bones = new Bone[joints.size()];
-            for (int i = 0; i < joints.size(); i++) {
-                int boneIndex = joints.get(i).getAsInt();
-                //we don't need the inverse bind matrix, we need the bind matrix so let's invert it.
-                Matrix4f modelBindMatrix = inverseBindMatrices[i].invertLocal();
-                bones[i] = readNodeAsBone(boneIndex, i, index, modelBindMatrix);
+            Joint[] joints = new Joint[jsonJoints.size()];
+            for (int i = 0; i < jsonJoints.size(); i++) {
+                int boneIndex = jsonJoints.get(i).getAsInt();
+                Matrix4f inverseModelBindMatrix = inverseBindMatrices[i];
+                joints[i] = readNodeAsBone(boneIndex, i, index, inverseModelBindMatrix);
             }
 
-            for (int i = 0; i < joints.size(); i++) {
-                findChildren(joints.get(i).getAsInt());
+            for (int i = 0; i < jsonJoints.size(); i++) {
+                findChildren(jsonJoints.get(i).getAsInt());
             }
 
-            Skeleton skeleton = new Skeleton(bones);
+            Armature armature = new Armature(joints);
 
-            //Compute bind transforms. We need to do it from root bone to leaves bone.
-            for (Bone bone : skeleton.getRoots()) {
-                BoneWrapper bw = findBoneWrapper(bone);
-                computeBindTransforms(bw, skeleton);
-            }
-
-            skeleton = customContentManager.readExtensionAndExtras("skin", skin, skeleton);
+            armature = customContentManager.readExtensionAndExtras("skin", skin, armature);
             SkinData skinData = new SkinData();
-            skinData.bones = bones;
-            skinData.skeletonControl = new SkeletonControl(skeleton);
-            skinData.animControl = new AnimControl(skinData.skeletonControl.getSkeleton());
+            skinData.joints = joints;
+            skinData.skinningControl = new SkinningControl(armature);
+            skinData.animComposer = new AnimComposer();
             addToCache("skins", index, skinData, nodes.size());
             skinnedSpatials.put(skinData, new ArrayList<Spatial>());
 
-            // Set local transforms.
-            // The skeleton may come in a given pose, that is not the rest pose, so let 's apply it.
-            // We will need it later for animation
-            for (int i = 0; i < joints.size(); i++) {
-                applyPose(joints.get(i).getAsInt());
-            }
-            skeleton.updateWorldVectors();
-
-            //If the user didn't ask to keep the pose we reset the skeleton user control
-            if (!isKeepSkeletonPose(info)) {
-                for (Bone bone : bones) {
-                    bone.setUserControl(false);
-                }
-            }
+            armature.update();
         }
     }
 
-    private void applyPose(int index) {
-        BoneWrapper bw = fetchFromCache("nodes", index, BoneWrapper.class);
-        bw.bone.setUserControl(true);
-        bw.bone.setLocalTranslation(bw.localTransform.getTranslation());
-        bw.bone.setLocalRotation(bw.localTransform.getRotation());
-        bw.bone.setLocalScale(bw.localTransform.getScale());
-    }
+    public Joint readNodeAsBone(int nodeIndex, int jointIndex, int skinIndex, Matrix4f inverseModelBindMatrix) throws IOException {
 
-    private void computeBindTransforms(BoneWrapper boneWrapper, Skeleton skeleton) {
-        Bone bone = boneWrapper.bone;
-        tmpTransforms.fromTransformMatrix(boneWrapper.modelBindMatrix);
-        if (bone.getParent() != null) {
-            //root bone, model transforms are the same as the local transforms
-            //but for child bones we need to combine it with the parents inverse model transforms.
-            tmpMat.setTranslation(bone.getParent().getModelSpacePosition());
-            tmpMat.setRotationQuaternion(bone.getParent().getModelSpaceRotation());
-            tmpMat.setScale(bone.getParent().getModelSpaceScale());
-            tmpMat.invertLocal();
-            tmpTransforms2.fromTransformMatrix(tmpMat);
-            tmpTransforms.combineWithParent(tmpTransforms2);
-        }
-        bone.setBindTransforms(tmpTransforms.getTranslation(), tmpTransforms.getRotation(), tmpTransforms.getScale());
-
-        //resets the local transforms to bind transforms for all bones.
-        //then computes the model transforms from local transforms for each bone.
-        skeleton.resetAndUpdate();
-        skeleton.setBindingPose();
-        for (Integer childIndex : boneWrapper.children) {
-            BoneWrapper child = fetchFromCache("nodes", childIndex, BoneWrapper.class);
-            computeBindTransforms(child, skeleton);
-        }
-    }
-
-    private BoneWrapper findBoneWrapper(Bone bone) {
-        for (int i = 0; i < nodes.size(); i++) {
-            BoneWrapper bw = fetchFromCache("nodes", i, BoneWrapper.class);
-            if (bw != null && bw.bone == bone) {
-                return bw;
-            }
-        }
-        return null;
-    }
-
-    public Bone readNodeAsBone(int nodeIndex, int boneIndex, int skinIndex, Matrix4f modelBindMatrix) throws IOException {
-
-        BoneWrapper boneWrapper = fetchFromCache("nodes", nodeIndex, BoneWrapper.class);
-        if (boneWrapper != null) {
-            return boneWrapper.bone;
+        JointWrapper jointWrapper = fetchFromCache("nodes", nodeIndex, JointWrapper.class);
+        if (jointWrapper != null) {
+            return jointWrapper.joint;
         }
         JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
         String name = getAsString(nodeData, "name");
         if (name == null) {
-            name = "Bone_" + nodeIndex;
+            name = "Joint_" + nodeIndex;
         }
-        Bone bone = new Bone(name);
+        Joint joint = new Joint(name);
         Transform boneTransforms = null;
         boneTransforms = readTransforms(nodeData);
+        joint.setLocalTransform(boneTransforms);
+        joint.setInverseModelBindMatrix(inverseModelBindMatrix);
 
-        addToCache("nodes", nodeIndex, new BoneWrapper(bone, boneIndex, skinIndex, modelBindMatrix, boneTransforms), nodes.size());
+        addToCache("nodes", nodeIndex, new JointWrapper(joint, jointIndex, skinIndex), nodes.size());
 
-        return bone;
+        return joint;
     }
 
     private void findChildren(int nodeIndex) throws IOException {
-        BoneWrapper bw = fetchFromCache("nodes", nodeIndex, BoneWrapper.class);
+        JointWrapper jw = fetchFromCache("nodes", nodeIndex, JointWrapper.class);
         JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
         JsonArray children = nodeData.getAsJsonArray("children");
 
         if (children != null) {
             for (JsonElement child : children) {
                 int childIndex = child.getAsInt();
-                if (bw.children.contains(childIndex)) {
+                if (jw.children.contains(childIndex)) {
                     //bone already has the child in its children
                     continue;
                 }
-                BoneWrapper cbw = fetchFromCache("nodes", childIndex, BoneWrapper.class);
-                if (cbw != null) {
-                    bw.bone.addChild(cbw.bone);
-                    bw.children.add(childIndex);
+                JointWrapper cjw = fetchFromCache("nodes", childIndex, JointWrapper.class);
+                if (cjw != null) {
+                    jw.joint.addChild(cjw.joint);
+                    jw.children.add(childIndex);
                 } else {
                     //The child might be a Node
                     //Creating a dummy node to read the subgraph
@@ -1089,7 +1014,7 @@ public class GltfLoader implements AssetLoader {
                     Spatial s = n.getChild(0);
                     //removing the spatial from the dummy node, it will be attached to the attachment node of the bone
                     s.removeFromParent();
-                    bw.attachedSpatial = s;
+                    jw.attachedSpatial = s;
                 }
             }
 
@@ -1113,19 +1038,19 @@ public class GltfLoader implements AssetLoader {
                 skinData.rootBoneTransformOffset.combineWithParent(skinData.parent.getWorldTransform());
             }
 
-            if (skinData.animControl != null && skinData.animControl.getSpatial() == null) {
-                spatial.addControl(skinData.animControl);
+            if (skinData.animComposer != null && skinData.animComposer.getSpatial() == null) {
+                spatial.addControl(skinData.animComposer);
             }
-            spatial.addControl(skinData.skeletonControl);
+            spatial.addControl(skinData.skinningControl);
         }
 
         for (int i = 0; i < nodes.size(); i++) {
-            BoneWrapper bw = fetchFromCache("nodes", i, BoneWrapper.class);
+            JointWrapper bw = fetchFromCache("nodes", i, JointWrapper.class);
             if (bw == null || bw.attachedSpatial == null) {
                 continue;
             }
             SkinData skinData = fetchFromCache("skins", bw.skinIndex, SkinData.class);
-            skinData.skeletonControl.getAttachmentsNode(bw.bone.getName()).attachChild(bw.attachedSpatial);
+            skinData.skinningControl.getAttachmentsNode(bw.joint.getName()).attachChild(bw.attachedSpatial);
         }
     }
 
@@ -1182,118 +1107,27 @@ public class GltfLoader implements AssetLoader {
     }
 
 
-    private class BoneWrapper {
-        Bone bone;
-        int boneIndex;
+    private class JointWrapper {
+        Joint joint;
+        int jointIndex;
         int skinIndex;
-        Transform localTransform;
-        Transform localTransformOffset;
-        Matrix4f modelBindMatrix;
         boolean isRoot = false;
-        boolean localUpdated = false;
         Spatial attachedSpatial;
         List<Integer> children = new ArrayList<>();
 
-        public BoneWrapper(Bone bone, int boneIndex, int skinIndex, Matrix4f modelBindMatrix, Transform localTransform) {
-            this.bone = bone;
-            this.boneIndex = boneIndex;
+        public JointWrapper(Joint joint, int jointIndex, int skinIndex) {
+            this.joint = joint;
+            this.jointIndex = jointIndex;
             this.skinIndex = skinIndex;
-            this.modelBindMatrix = modelBindMatrix;
-            this.localTransform = localTransform;
-            this.localTransformOffset = localTransform.clone();
-        }
-
-        /**
-         * Applies the inverse Bind transforms to anim data. and the armature transforms if relevant.
-         */
-        public void update(TrackData data) {
-            Transform bindTransforms = new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale());
-            SkinData skinData = fetchFromCache("skins", skinIndex, SkinData.class);
-
-            if (!localUpdated) {
-                //LocalTransform of the bone are default position to use for animations when there is no track.
-                //We need to transform them so that JME can us them in blendAnimTransform.
-                reverseBlendAnimTransforms(localTransformOffset, bindTransforms);
-                localUpdated = true;
-            }
-
-            for (int i = 0; i < data.getNbKeyFrames(); i++) {
-
-                Vector3f translation = getTranslation(data, i);
-                Quaternion rotation = getRotation(data, i);
-                Vector3f scale = getScale(data, i);
-
-                Transform t = new Transform(translation, rotation, scale);
-                if (isRoot && skinData.rootBoneTransformOffset != null) {
-                    //Apply the armature transforms to the root bone anim track.
-                    t.combineWithParent(skinData.rootBoneTransformOffset);
-                }
-
-                reverseBlendAnimTransforms(t, bindTransforms);
-
-                if (data.translations != null) {
-                    data.translations[i] = t.getTranslation();
-                }
-                if (data.rotations != null) {
-                    data.rotations[i] = t.getRotation();
-                }
-                if (data.scales != null) {
-                    data.scales[i] = t.getScale();
-                }
-            }
-
-            data.ensureTranslationRotations(localTransformOffset);
-        }
-
-        private void reverseBlendAnimTransforms(Transform t, Transform bindTransforms) {
-            //This is wrong
-            //You'd normally combine those transforms with transform.combineWithParent()
-            //Here we actually do in reverse what JME does to combine anim transforms with bind transfoms (add trans/mult rot/ mult scale)
-            //The code to fix is in Bone.blendAnimTransforms
-            //TODO fix blendAnimTransforms
-            t.getTranslation().subtractLocal(bindTransforms.getTranslation());
-            t.getScale().divideLocal(bindTransforms.getScale());
-            tmpQuat.set(bindTransforms.getRotation()).inverseLocal().multLocal(t.getRotation());
-            t.setRotation(tmpQuat);
-        }
-
-        private Vector3f getTranslation(TrackData data, int i) {
-            Vector3f translation;
-            if (data.translations == null) {
-                translation = bone.getLocalPosition();
-            } else {
-                translation = data.translations[i];
-            }
-            return translation;
-        }
-
-        private Quaternion getRotation(TrackData data, int i) {
-            Quaternion rotation;
-            if (data.rotations == null) {
-                rotation = bone.getLocalRotation();
-            } else {
-                rotation = data.rotations[i];
-            }
-            return rotation;
-        }
-
-        private Vector3f getScale(TrackData data, int i) {
-            Vector3f scale;
-            if (data.scales == null) {
-                scale = bone.getLocalScale();
-            } else {
-                scale = data.scales[i];
-            }
-            return scale;
         }
     }
 
     private class SkinData {
-        SkeletonControl skeletonControl;
-        AnimControl animControl;
+        SkinningControl skinningControl;
+        AnimComposer animComposer;
         Spatial parent;
         Transform rootBoneTransformOffset;
-        Bone[] bones;
+        Joint[] joints;
         boolean used = false;
     }
 

+ 5 - 6
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

@@ -1,7 +1,6 @@
 package com.jme3.scene.plugins.gltf;
 
 import com.google.gson.*;
-import com.jme3.animation.Bone;
 import com.jme3.asset.AssetInfo;
 import com.jme3.asset.AssetLoadException;
 import com.jme3.math.*;
@@ -686,11 +685,11 @@ public class GltfUtils {
         }
     }
 
-    public static boolean equalBindAndLocalTransforms(Bone b) {
-        return equalsEpsilon(b.getBindPosition(), b.getLocalPosition())
-                && equalsEpsilon(b.getBindRotation(), b.getLocalRotation())
-                && equalsEpsilon(b.getBindScale(), b.getLocalScale());
-    }
+//    public static boolean equalBindAndLocalTransforms(Joint b) {
+//        return equalsEpsilon(b.getBindPosition(), b.getLocalPosition())
+//                && equalsEpsilon(b.getBindRotation(), b.getLocalRotation())
+//                && equalsEpsilon(b.getBindScale(), b.getLocalScale());
+//    }
 
     private static float epsilon = 0.0001f;