Răsfoiți Sursa

glTF: Fixes additional issues with bones transforms

Nehon 7 ani în urmă
părinte
comite
5dbbaf0f06

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

@@ -0,0 +1,7 @@
+package jme3test.model.shape;
+
+/**
+ * Created by Nehon on 09/12/2017.
+ */
+public class TestGltfLoading2 {
+}

+ 107 - 16
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

@@ -246,6 +246,7 @@ public class GltfLoader implements AssetLoader {
             SkinData skinData = fetchFromCache("skins", skinIndex, SkinData.class);
             List<Spatial> spatials = skinnedSpatials.get(skinData);
             spatials.add(spatial);
+            skinData.used = true;
         }
 
         spatial.setLocalTransform(readTransforms(nodeData));
@@ -732,6 +733,17 @@ public class GltfLoader implements AssetLoader {
                 logger.log(Level.WARNING, "Morph animation is not supported by JME yet, skipping animation");
                 continue;
             }
+
+            //if targetNode is a bone, check if it's in a used skin.
+            BoneWrapper bw = fetchFromCache("nodes", targetNode, BoneWrapper.class);
+            if (bw != null) {
+                SkinData skin = fetchFromCache("skins", bw.skinIndex, SkinData.class);
+                if (skin == null || !skin.used) {
+                    //this skin is not referenced by any mesh, let's not load animation for it.
+                    continue;
+                }
+            }
+
             TrackData trackData = tracks[targetNode];
             if (trackData == null) {
                 trackData = new TrackData();
@@ -790,6 +802,7 @@ public class GltfLoader implements AssetLoader {
         anim.setName(name);
         int skinIndex = -1;
 
+        List<Bone> usedBones = new ArrayList<>();
         for (int i = 0; i < tracks.length; i++) {
             TrackData trackData = tracks[i];
             if (trackData == null || trackData.timeArrays.isEmpty()) {
@@ -810,6 +823,7 @@ public class GltfLoader implements AssetLoader {
                 BoneWrapper b = (BoneWrapper) node;
                 //apply the inverseBindMatrix to animation data.
                 b.update(trackData);
+                usedBones.add(b.bone);
                 BoneTrack track = new BoneTrack(b.boneIndex, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
                 anim.addTrack(track);
                 if (skinIndex == -1) {
@@ -824,6 +838,32 @@ public class GltfLoader implements AssetLoader {
             }
         }
 
+        // Check each bone to see if their local pose is different from their bind pose.
+        // If it is, we ensure that the bone has an animation track, else JME way of applying anim transforms will apply the bind pose to those bones,
+        // 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)) {
+                    //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());
+
+                    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);
+                    anim.addTrack(track);
+                }
+            }
+        }
+
         anim = customContentManager.readExtensionAndExtras("animations", animation, anim);
 
         if (skinIndex != -1) {
@@ -935,17 +975,25 @@ public class GltfLoader implements AssetLoader {
                 computeBindTransforms(bw, skeleton);
             }
 
-            if (isKeepSkeletonPose(info)) {
-                //Set local transforms.The skeleton may come in a given pose, that is not the rest pose, so let 's apply it.
-                for (int i = 0; i < joints.size(); i++) {
-                    applyPose(joints.get(i).getAsInt());
+            // 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);
                 }
-                skeleton.updateWorldVectors();
             }
 
             skeleton = customContentManager.readExtensionAndExtras("skin", skin, skeleton);
 
             SkinData skinData = new SkinData();
+            skinData.bones = bones;
             skinData.skeletonControl = new SkeletonControl(skeleton);
             addToCache("skins", index, skinData, nodes.size());
             skinnedSpatials.put(skinData, new ArrayList<Spatial>());
@@ -1140,6 +1188,7 @@ public class GltfLoader implements AssetLoader {
         int boneIndex;
         int skinIndex;
         Transform localTransform;
+        Transform localTransformOffset;
         Matrix4f modelBindMatrix;
         boolean isRoot = false;
         boolean localUpdated = false;
@@ -1152,6 +1201,7 @@ public class GltfLoader implements AssetLoader {
             this.skinIndex = skinIndex;
             this.modelBindMatrix = modelBindMatrix;
             this.localTransform = localTransform;
+            this.localTransformOffset = localTransform.clone();
         }
 
         /**
@@ -1164,15 +1214,15 @@ public class GltfLoader implements AssetLoader {
             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(localTransform, bindTransforms);
+                reverseBlendAnimTransforms(localTransformOffset, bindTransforms);
                 localUpdated = true;
             }
 
             for (int i = 0; i < data.getNbKeyFrames(); i++) {
 
-                Vector3f translation = getTranslation(data, bindTransforms, i);
-                Quaternion rotation = getRotation(data, bindTransforms, i);
-                Vector3f scale = getScale(data, bindTransforms, 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) {
@@ -1193,7 +1243,7 @@ public class GltfLoader implements AssetLoader {
                 }
             }
 
-            data.ensureTranslationRotations(localTransform);
+            data.ensureTranslationRotations(localTransformOffset);
         }
 
         private void reverseBlendAnimTransforms(Transform t, Transform bindTransforms) {
@@ -1208,30 +1258,30 @@ public class GltfLoader implements AssetLoader {
             t.setRotation(tmpQuat);
         }
 
-        private Vector3f getTranslation(TrackData data, Transform bindTransforms, int i) {
+        private Vector3f getTranslation(TrackData data, int i) {
             Vector3f translation;
             if (data.translations == null) {
-                translation = bindTransforms.getTranslation();
+                translation = bone.getLocalPosition();
             } else {
                 translation = data.translations[i];
             }
             return translation;
         }
 
-        private Quaternion getRotation(TrackData data, Transform bindTransforms, int i) {
+        private Quaternion getRotation(TrackData data, int i) {
             Quaternion rotation;
             if (data.rotations == null) {
-                rotation = bindTransforms.getRotation();
+                rotation = bone.getLocalRotation();
             } else {
                 rotation = data.rotations[i];
             }
             return rotation;
         }
 
-        private Vector3f getScale(TrackData data, Transform bindTransforms, int i) {
+        private Vector3f getScale(TrackData data, int i) {
             Vector3f scale;
             if (data.scales == null) {
-                scale = bindTransforms.getScale();
+                scale = bone.getLocalScale();
             } else {
                 scale = data.scales[i];
             }
@@ -1243,6 +1293,47 @@ public class GltfLoader implements AssetLoader {
         SkeletonControl skeletonControl;
         AnimControl animControl;
         Transform armatureTransforms;
+        Bone[] bones;
+        boolean used = false;
+    }
+
+    private class PartialTransforms {
+        Vector3f translation;
+        Quaternion rotation;
+        Vector3f scale;
+        Transform transform;
+
+        Transform getTransforms() {
+            if (transform == null) {
+                if (translation == null) {
+                    translation = new Vector3f();
+                }
+                if (rotation == null) {
+                    rotation = new Quaternion();
+                }
+                if (scale == null) {
+                    scale = new Vector3f(1, 1, 1);
+                }
+                transform = new Transform(translation, rotation, scale);
+            }
+            return transform;
+        }
+
+        Transform getTransforms(Transform bindTransforms) {
+            if (transform == null) {
+                if (translation == null) {
+                    translation = bindTransforms.getTranslation();
+                }
+                if (rotation == null) {
+                    rotation = bindTransforms.getRotation();
+                }
+                if (scale == null) {
+                    scale = bindTransforms.getScale();
+                }
+                transform = new Transform(translation, rotation, scale);
+            }
+            return transform;
+        }
     }
 
     public static class SkinBuffers {

+ 28 - 0
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

@@ -1,6 +1,7 @@
 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.*;
@@ -685,6 +686,33 @@ 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());
+    }
+
+    private static float epsilon = 0.0001f;
+
+    public static boolean equalsEpsilon(Vector3f v1, Vector3f v2) {
+        return FastMath.abs(v1.x - v2.x) < epsilon
+                && FastMath.abs(v1.y - v2.y) < epsilon
+                && FastMath.abs(v1.z - v2.z) < epsilon;
+    }
+
+    public static boolean equalsEpsilon(Quaternion q1, Quaternion q2) {
+        return (FastMath.abs(q1.getX() - q2.getX()) < epsilon
+                && FastMath.abs(q1.getY() - q2.getY()) < epsilon
+                && FastMath.abs(q1.getZ() - q2.getZ()) < epsilon
+                && FastMath.abs(q1.getW() - q2.getW()) < epsilon)
+                ||
+                (FastMath.abs(q1.getX() + q2.getX()) < epsilon
+                        && FastMath.abs(q1.getY() + q2.getY()) < epsilon
+                        && FastMath.abs(q1.getZ() + q2.getZ()) < epsilon
+                        && FastMath.abs(q1.getW() + q2.getW()) < epsilon);
+    }
+
+
     public static void dumpArray(Object[] array) {
         if (array == null) {
             System.err.println("null");