Skip to content

Commit 5dbbaf0

Browse files
committed
glTF: Fixes additional issues with bones transforms
1 parent 09f4ae4 commit 5dbbaf0

File tree

3 files changed

+142
-16
lines changed

3 files changed

+142
-16
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package jme3test.model.shape;
2+
3+
/**
4+
* Created by Nehon on 09/12/2017.
5+
*/
6+
public class TestGltfLoading2 {
7+
}

jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

Lines changed: 107 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ public Object readNode(int nodeIndex) throws IOException {
246246
SkinData skinData = fetchFromCache("skins", skinIndex, SkinData.class);
247247
List<Spatial> spatials = skinnedSpatials.get(skinData);
248248
spatials.add(spatial);
249+
skinData.used = true;
249250
}
250251

251252
spatial.setLocalTransform(readTransforms(nodeData));
@@ -732,6 +733,17 @@ public void readAnimation(int animationIndex) throws IOException {
732733
logger.log(Level.WARNING, "Morph animation is not supported by JME yet, skipping animation");
733734
continue;
734735
}
736+
737+
//if targetNode is a bone, check if it's in a used skin.
738+
BoneWrapper bw = fetchFromCache("nodes", targetNode, BoneWrapper.class);
739+
if (bw != null) {
740+
SkinData skin = fetchFromCache("skins", bw.skinIndex, SkinData.class);
741+
if (skin == null || !skin.used) {
742+
//this skin is not referenced by any mesh, let's not load animation for it.
743+
continue;
744+
}
745+
}
746+
735747
TrackData trackData = tracks[targetNode];
736748
if (trackData == null) {
737749
trackData = new TrackData();
@@ -790,6 +802,7 @@ public void readAnimation(int animationIndex) throws IOException {
790802
anim.setName(name);
791803
int skinIndex = -1;
792804

805+
List<Bone> usedBones = new ArrayList<>();
793806
for (int i = 0; i < tracks.length; i++) {
794807
TrackData trackData = tracks[i];
795808
if (trackData == null || trackData.timeArrays.isEmpty()) {
@@ -810,6 +823,7 @@ public void readAnimation(int animationIndex) throws IOException {
810823
BoneWrapper b = (BoneWrapper) node;
811824
//apply the inverseBindMatrix to animation data.
812825
b.update(trackData);
826+
usedBones.add(b.bone);
813827
BoneTrack track = new BoneTrack(b.boneIndex, trackData.times, trackData.translations, trackData.rotations, trackData.scales);
814828
anim.addTrack(track);
815829
if (skinIndex == -1) {
@@ -824,6 +838,32 @@ public void readAnimation(int animationIndex) throws IOException {
824838
}
825839
}
826840

841+
// Check each bone to see if their local pose is different from their bind pose.
842+
// 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,
843+
// instead of the local pose that is supposed to be the default
844+
if (skinIndex != -1) {
845+
SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class);
846+
Skeleton skeleton = skin.skeletonControl.getSkeleton();
847+
for (Bone bone : skin.bones) {
848+
if (!usedBones.contains(bone) && !equalBindAndLocalTransforms(bone)) {
849+
//create a track
850+
float[] times = new float[]{0, anim.getLength()};
851+
852+
Vector3f t = bone.getLocalPosition().subtract(bone.getBindPosition());
853+
Quaternion r = tmpQuat.set(bone.getBindRotation()).inverse().multLocal(bone.getLocalRotation());
854+
Vector3f s = bone.getLocalScale().divide(bone.getBindScale());
855+
856+
Vector3f[] translations = new Vector3f[]{t, t};
857+
Quaternion[] rotations = new Quaternion[]{r, r};
858+
Vector3f[] scales = new Vector3f[]{s, s};
859+
860+
int boneIndex = skeleton.getBoneIndex(bone);
861+
BoneTrack track = new BoneTrack(boneIndex, times, translations, rotations, scales);
862+
anim.addTrack(track);
863+
}
864+
}
865+
}
866+
827867
anim = customContentManager.readExtensionAndExtras("animations", animation, anim);
828868

829869
if (skinIndex != -1) {
@@ -935,17 +975,25 @@ public void readSkins() throws IOException {
935975
computeBindTransforms(bw, skeleton);
936976
}
937977

938-
if (isKeepSkeletonPose(info)) {
939-
//Set local transforms.The skeleton may come in a given pose, that is not the rest pose, so let 's apply it.
940-
for (int i = 0; i < joints.size(); i++) {
941-
applyPose(joints.get(i).getAsInt());
978+
// Set local transforms.
979+
// The skeleton may come in a given pose, that is not the rest pose, so let 's apply it.
980+
// We will need it later for animation
981+
for (int i = 0; i < joints.size(); i++) {
982+
applyPose(joints.get(i).getAsInt());
983+
}
984+
skeleton.updateWorldVectors();
985+
986+
//If the user didn't ask to keep the pose we reset the skeleton user control
987+
if (!isKeepSkeletonPose(info)) {
988+
for (Bone bone : bones) {
989+
bone.setUserControl(false);
942990
}
943-
skeleton.updateWorldVectors();
944991
}
945992

946993
skeleton = customContentManager.readExtensionAndExtras("skin", skin, skeleton);
947994

948995
SkinData skinData = new SkinData();
996+
skinData.bones = bones;
949997
skinData.skeletonControl = new SkeletonControl(skeleton);
950998
addToCache("skins", index, skinData, nodes.size());
951999
skinnedSpatials.put(skinData, new ArrayList<Spatial>());
@@ -1140,6 +1188,7 @@ private class BoneWrapper {
11401188
int boneIndex;
11411189
int skinIndex;
11421190
Transform localTransform;
1191+
Transform localTransformOffset;
11431192
Matrix4f modelBindMatrix;
11441193
boolean isRoot = false;
11451194
boolean localUpdated = false;
@@ -1152,6 +1201,7 @@ public BoneWrapper(Bone bone, int boneIndex, int skinIndex, Matrix4f modelBindMa
11521201
this.skinIndex = skinIndex;
11531202
this.modelBindMatrix = modelBindMatrix;
11541203
this.localTransform = localTransform;
1204+
this.localTransformOffset = localTransform.clone();
11551205
}
11561206

11571207
/**
@@ -1164,15 +1214,15 @@ public void update(TrackData data) {
11641214
if (!localUpdated) {
11651215
//LocalTransform of the bone are default position to use for animations when there is no track.
11661216
//We need to transform them so that JME can us them in blendAnimTransform.
1167-
reverseBlendAnimTransforms(localTransform, bindTransforms);
1217+
reverseBlendAnimTransforms(localTransformOffset, bindTransforms);
11681218
localUpdated = true;
11691219
}
11701220

11711221
for (int i = 0; i < data.getNbKeyFrames(); i++) {
11721222

1173-
Vector3f translation = getTranslation(data, bindTransforms, i);
1174-
Quaternion rotation = getRotation(data, bindTransforms, i);
1175-
Vector3f scale = getScale(data, bindTransforms, i);
1223+
Vector3f translation = getTranslation(data, i);
1224+
Quaternion rotation = getRotation(data, i);
1225+
Vector3f scale = getScale(data, i);
11761226

11771227
Transform t = new Transform(translation, rotation, scale);
11781228
if (isRoot) {
@@ -1193,7 +1243,7 @@ public void update(TrackData data) {
11931243
}
11941244
}
11951245

1196-
data.ensureTranslationRotations(localTransform);
1246+
data.ensureTranslationRotations(localTransformOffset);
11971247
}
11981248

11991249
private void reverseBlendAnimTransforms(Transform t, Transform bindTransforms) {
@@ -1208,30 +1258,30 @@ private void reverseBlendAnimTransforms(Transform t, Transform bindTransforms) {
12081258
t.setRotation(tmpQuat);
12091259
}
12101260

1211-
private Vector3f getTranslation(TrackData data, Transform bindTransforms, int i) {
1261+
private Vector3f getTranslation(TrackData data, int i) {
12121262
Vector3f translation;
12131263
if (data.translations == null) {
1214-
translation = bindTransforms.getTranslation();
1264+
translation = bone.getLocalPosition();
12151265
} else {
12161266
translation = data.translations[i];
12171267
}
12181268
return translation;
12191269
}
12201270

1221-
private Quaternion getRotation(TrackData data, Transform bindTransforms, int i) {
1271+
private Quaternion getRotation(TrackData data, int i) {
12221272
Quaternion rotation;
12231273
if (data.rotations == null) {
1224-
rotation = bindTransforms.getRotation();
1274+
rotation = bone.getLocalRotation();
12251275
} else {
12261276
rotation = data.rotations[i];
12271277
}
12281278
return rotation;
12291279
}
12301280

1231-
private Vector3f getScale(TrackData data, Transform bindTransforms, int i) {
1281+
private Vector3f getScale(TrackData data, int i) {
12321282
Vector3f scale;
12331283
if (data.scales == null) {
1234-
scale = bindTransforms.getScale();
1284+
scale = bone.getLocalScale();
12351285
} else {
12361286
scale = data.scales[i];
12371287
}
@@ -1243,6 +1293,47 @@ private class SkinData {
12431293
SkeletonControl skeletonControl;
12441294
AnimControl animControl;
12451295
Transform armatureTransforms;
1296+
Bone[] bones;
1297+
boolean used = false;
1298+
}
1299+
1300+
private class PartialTransforms {
1301+
Vector3f translation;
1302+
Quaternion rotation;
1303+
Vector3f scale;
1304+
Transform transform;
1305+
1306+
Transform getTransforms() {
1307+
if (transform == null) {
1308+
if (translation == null) {
1309+
translation = new Vector3f();
1310+
}
1311+
if (rotation == null) {
1312+
rotation = new Quaternion();
1313+
}
1314+
if (scale == null) {
1315+
scale = new Vector3f(1, 1, 1);
1316+
}
1317+
transform = new Transform(translation, rotation, scale);
1318+
}
1319+
return transform;
1320+
}
1321+
1322+
Transform getTransforms(Transform bindTransforms) {
1323+
if (transform == null) {
1324+
if (translation == null) {
1325+
translation = bindTransforms.getTranslation();
1326+
}
1327+
if (rotation == null) {
1328+
rotation = bindTransforms.getRotation();
1329+
}
1330+
if (scale == null) {
1331+
scale = bindTransforms.getScale();
1332+
}
1333+
transform = new Transform(translation, rotation, scale);
1334+
}
1335+
return transform;
1336+
}
12461337
}
12471338

12481339
public static class SkinBuffers {

jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.jme3.scene.plugins.gltf;
22

33
import com.google.gson.*;
4+
import com.jme3.animation.Bone;
45
import com.jme3.asset.AssetInfo;
56
import com.jme3.asset.AssetLoadException;
67
import com.jme3.math.*;
@@ -685,6 +686,33 @@ public static void assertNotNull(Object o, String errorMessage) {
685686
}
686687
}
687688

689+
public static boolean equalBindAndLocalTransforms(Bone b) {
690+
return equalsEpsilon(b.getBindPosition(), b.getLocalPosition())
691+
&& equalsEpsilon(b.getBindRotation(), b.getLocalRotation())
692+
&& equalsEpsilon(b.getBindScale(), b.getLocalScale());
693+
}
694+
695+
private static float epsilon = 0.0001f;
696+
697+
public static boolean equalsEpsilon(Vector3f v1, Vector3f v2) {
698+
return FastMath.abs(v1.x - v2.x) < epsilon
699+
&& FastMath.abs(v1.y - v2.y) < epsilon
700+
&& FastMath.abs(v1.z - v2.z) < epsilon;
701+
}
702+
703+
public static boolean equalsEpsilon(Quaternion q1, Quaternion q2) {
704+
return (FastMath.abs(q1.getX() - q2.getX()) < epsilon
705+
&& FastMath.abs(q1.getY() - q2.getY()) < epsilon
706+
&& FastMath.abs(q1.getZ() - q2.getZ()) < epsilon
707+
&& FastMath.abs(q1.getW() - q2.getW()) < epsilon)
708+
||
709+
(FastMath.abs(q1.getX() + q2.getX()) < epsilon
710+
&& FastMath.abs(q1.getY() + q2.getY()) < epsilon
711+
&& FastMath.abs(q1.getZ() + q2.getZ()) < epsilon
712+
&& FastMath.abs(q1.getW() + q2.getW()) < epsilon);
713+
}
714+
715+
688716
public static void dumpArray(Object[] array) {
689717
if (array == null) {
690718
System.err.println("null");

0 commit comments

Comments
 (0)