glTF: Fixes additional issues with bones transforms

empirephoenix-patch-1
Nehon 7 years ago
parent 09f4ae4832
commit 5dbbaf0f06
  1. 7
      jme3-examples/src/main/java/jme3test/model/shape/TestGltfLoading2.java
  2. 123
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  3. 28
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

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

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

@ -1,6 +1,7 @@
package com.jme3.scene.plugins.gltf; package com.jme3.scene.plugins.gltf;
import com.google.gson.*; import com.google.gson.*;
import com.jme3.animation.Bone;
import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetLoadException;
import com.jme3.math.*; 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) { public static void dumpArray(Object[] array) {
if (array == null) { if (array == null) {
System.err.println("null"); System.err.println("null");

Loading…
Cancel
Save