|
|
@ -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 { |
|
|
|