glTF: Fixes additional issues with bones transforms
This commit is contained in:
parent
09f4ae4832
commit
5dbbaf0f06
@ -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.
|
||||||
|
// We will need it later for animation
|
||||||
for (int i = 0; i < joints.size(); i++) {
|
for (int i = 0; i < joints.size(); i++) {
|
||||||
applyPose(joints.get(i).getAsInt());
|
applyPose(joints.get(i).getAsInt());
|
||||||
}
|
}
|
||||||
skeleton.updateWorldVectors();
|
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 = 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…
x
Reference in New Issue
Block a user