glTF: Fixed animation data interpolation, also renamed AnimData to TrackData to match JME paradigm.

empirephoenix-patch-1
Nehon 7 years ago
parent 132b0abc50
commit 54061124a9
  1. 99
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  2. 59
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TrackData.java

@ -19,7 +19,6 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
import javax.xml.bind.DatatypeConverter; import javax.xml.bind.DatatypeConverter;
import java.io.*; import java.io.*;
import java.nio.Buffer; import java.nio.Buffer;
import java.sql.Time;
import java.util.*; import java.util.*;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -714,7 +713,7 @@ public class GltfLoader implements AssetLoader {
assertNotNull(samplers, "No samplers for animation " + name); assertNotNull(samplers, "No samplers for animation " + name);
//temp data storage of track data //temp data storage of track data
AnimData[] animatedNodes = new AnimData[nodes.size()]; TrackData[] animatedNodes = new TrackData[nodes.size()];
for (JsonElement channel : channels) { for (JsonElement channel : channels) {
@ -733,10 +732,10 @@ 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;
} }
AnimData animData = animatedNodes[targetNode]; TrackData trackData = animatedNodes[targetNode];
if (animData == null) { if (trackData == null) {
animData = new AnimData(); trackData = new TrackData();
animatedNodes[targetNode] = animData; animatedNodes[targetNode] = trackData;
} }
Integer samplerIndex = getAsInteger(channel.getAsJsonObject(), "sampler"); Integer samplerIndex = getAsInteger(channel.getAsJsonObject(), "sampler");
@ -754,7 +753,7 @@ public class GltfLoader implements AssetLoader {
logger.log(Level.WARNING, "JME only supports linear interpolation for animations"); logger.log(Level.WARNING, "JME only supports linear interpolation for animations");
} }
animData = customContentManager.readExtensionAndExtras("animation.sampler", sampler, animData); trackData = customContentManager.readExtensionAndExtras("animation.sampler", sampler, trackData);
float[] times = fetchFromCache("accessors", timeIndex, float[].class); float[] times = fetchFromCache("accessors", timeIndex, float[].class);
if (times == null) { if (times == null) {
@ -763,17 +762,17 @@ public class GltfLoader implements AssetLoader {
} }
if (targetPath.equals("translation")) { if (targetPath.equals("translation")) {
animData.timeArrays.add(new AnimData.TimeData(times, AnimData.Type.Translation)); trackData.timeArrays.add(new TrackData.TimeData(times, TrackData.Type.Translation));
Vector3f[] translations = readAccessorData(dataIndex, vector3fArrayPopulator); Vector3f[] translations = readAccessorData(dataIndex, vector3fArrayPopulator);
animData.translations = translations; trackData.translations = translations;
} else if (targetPath.equals("scale")) { } else if (targetPath.equals("scale")) {
animData.timeArrays.add(new AnimData.TimeData(times, AnimData.Type.Scale)); trackData.timeArrays.add(new TrackData.TimeData(times, TrackData.Type.Scale));
Vector3f[] scales = readAccessorData(dataIndex, vector3fArrayPopulator); Vector3f[] scales = readAccessorData(dataIndex, vector3fArrayPopulator);
animData.scales = scales; trackData.scales = scales;
} else if (targetPath.equals("rotation")) { } else if (targetPath.equals("rotation")) {
animData.timeArrays.add(new AnimData.TimeData(times, AnimData.Type.Rotation)); trackData.timeArrays.add(new TrackData.TimeData(times, TrackData.Type.Rotation));
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator); Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
animData.rotations = rotations; trackData.rotations = rotations;
} else { } else {
//TODO support weights //TODO support weights
logger.log(Level.WARNING, "Morph animation is not supported"); logger.log(Level.WARNING, "Morph animation is not supported");
@ -781,7 +780,7 @@ public class GltfLoader implements AssetLoader {
continue; continue;
} }
animatedNodes[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, animData); animatedNodes[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, trackData);
} }
if (name == null) { if (name == null) {
@ -794,26 +793,26 @@ public class GltfLoader implements AssetLoader {
int skinIndex = -1; int skinIndex = -1;
for (int i = 0; i < animatedNodes.length; i++) { for (int i = 0; i < animatedNodes.length; i++) {
AnimData animData = animatedNodes[i]; TrackData trackData = animatedNodes[i];
if (animData == null) { if (trackData == null) {
continue; continue;
} }
animData.update(); trackData.update();
if (animData.length > anim.getLength()) { if (trackData.length > anim.getLength()) {
anim.setLength(animData.length); anim.setLength(trackData.length);
} }
Object node = fetchFromCache("nodes", i, Object.class); Object node = fetchFromCache("nodes", i, Object.class);
if (node instanceof Spatial) { if (node instanceof Spatial) {
Spatial s = (Spatial) node; Spatial s = (Spatial) node;
spatials.add(s); spatials.add(s);
SpatialTrack track = new SpatialTrack(animData.times, animData.translations, animData.rotations, animData.scales); SpatialTrack track = new SpatialTrack(trackData.times, trackData.translations, trackData.rotations, trackData.scales);
track.setTrackSpatial(s); track.setTrackSpatial(s);
anim.addTrack(track); anim.addTrack(track);
} else if (node instanceof BoneWrapper) { } else if (node instanceof BoneWrapper) {
BoneWrapper b = (BoneWrapper) node; BoneWrapper b = (BoneWrapper) node;
//apply the inverseBindMatrix to animation data. //apply the inverseBindMatrix to animation data.
b.update(animData); b.update(trackData);
BoneTrack track = new BoneTrack(b.boneIndex, animData.times, animData.translations, animData.rotations, animData.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) {
skinIndex = b.skinIndex; skinIndex = b.skinIndex;
@ -961,6 +960,7 @@ public class GltfLoader implements AssetLoader {
bw.bone.setLocalTranslation(bw.localTransform.getTranslation()); bw.bone.setLocalTranslation(bw.localTransform.getTranslation());
bw.bone.setLocalRotation(bw.localTransform.getRotation()); bw.bone.setLocalRotation(bw.localTransform.getRotation());
bw.bone.setLocalScale(bw.localTransform.getScale()); bw.bone.setLocalScale(bw.localTransform.getScale());
bw.bone.setUserControl(false);
} }
private void computeBindTransforms(BoneWrapper boneWrapper, Skeleton skeleton) { private void computeBindTransforms(BoneWrapper boneWrapper, Skeleton skeleton) {
@ -1159,12 +1159,17 @@ public class GltfLoader implements AssetLoader {
/** /**
* Applies the inverse Bind transforms to anim data. and the armature transforms if relevant. * Applies the inverse Bind transforms to anim data. and the armature transforms if relevant.
*/ */
public void update(AnimData data) { public void update(TrackData data) {
Transform bindTransforms = new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale()); Transform bindTransforms = new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale());
SkinData skinData = fetchFromCache("skins", skinIndex, SkinData.class); SkinData skinData = fetchFromCache("skins", skinIndex, SkinData.class);
for (int i = 0; i < data.translations.length; i++) { for (int i = 0; i < data.getNbKeyFrames(); i++) {
Transform t = new Transform(data.translations[i], data.rotations[i], data.scales[i]);
Vector3f translation = getTranslation(data, bindTransforms, i);
Quaternion rotation = getRotation(data, bindTransforms, i);
Vector3f scale = getScale(data, bindTransforms, i);
Transform t = new Transform(translation, rotation, scale);
if (isRoot) { if (isRoot) {
//Apply the armature transforms to the root bone anim track. //Apply the armature transforms to the root bone anim track.
t.combineWithParent(skinData.armatureTransforms); t.combineWithParent(skinData.armatureTransforms);
@ -1180,10 +1185,48 @@ public class GltfLoader implements AssetLoader {
tmpQuat.set(bindTransforms.getRotation()).inverseLocal().multLocal(t.getRotation()); tmpQuat.set(bindTransforms.getRotation()).inverseLocal().multLocal(t.getRotation());
t.setRotation(tmpQuat); t.setRotation(tmpQuat);
data.translations[i] = t.getTranslation(); if(data.translations != null) {
data.rotations[i] = t.getRotation(); data.translations[i] = t.getTranslation();
data.scales[i] = t.getScale(); }
if(data.rotations != null) {
data.rotations[i] = t.getRotation();
}
if(data.scales != null) {
data.scales[i] = t.getScale();
}
}
data.ensureTranslationRotations();
}
private Vector3f getTranslation(TrackData data, Transform bindTransforms, int i) {
Vector3f translation;
if(data.translations == null){
translation = bindTransforms.getTranslation();
} else {
translation = data.translations[i];
}
return translation;
}
private Quaternion getRotation(TrackData data, Transform bindTransforms, int i) {
Quaternion rotation;
if(data.rotations == null){
rotation = bindTransforms.getRotation();
} else {
rotation = data.rotations[i];
}
return rotation;
}
private Vector3f getScale(TrackData data, Transform bindTransforms, int i) {
Vector3f scale;
if(data.scales == null){
scale = bindTransforms.getScale();
} else {
scale = data.scales[i];
} }
return scale;
} }
} }

@ -4,7 +4,7 @@ import com.jme3.math.*;
import java.util.*; import java.util.*;
public class AnimData { public class TrackData {
public enum Type { public enum Type {
Translation, Translation,
@ -27,7 +27,6 @@ public class AnimData {
if (equalTimes(timeArrays)) { if (equalTimes(timeArrays)) {
times = timeArrays.get(0).times; times = timeArrays.get(0).times;
ensureArraysInit();
} else { } else {
//Times array are different and contains different sampling times. //Times array are different and contains different sampling times.
//We have to merge them because JME needs the 3 types of transforms for each keyFrame. //We have to merge them because JME needs the 3 types of transforms for each keyFrame.
@ -69,7 +68,7 @@ public class AnimData {
// populating transforms array from the keyframes, interpolating // populating transforms array from the keyframes, interpolating
times = new float[keyFrames.size()]; times = new float[keyFrames.size()];
ensureArraysInit(); ensureArraysLength();
TransformIndices translationIndices = new TransformIndices(); TransformIndices translationIndices = new TransformIndices();
TransformIndices rotationIndices = new TransformIndices(); TransformIndices rotationIndices = new TransformIndices();
@ -79,14 +78,18 @@ public class AnimData {
KeyFrame kf = keyFrames.get(i); KeyFrame kf = keyFrames.get(i);
//we need Interpolate between keyframes when transforms are sparse. //we need Interpolate between keyframes when transforms are sparse.
times[i] = kf.time; times[i] = kf.time;
populateTransform(Type.Translation, i, keyFrames, kf, translationIndices); if(translations != null) {
populateTransform(Type.Rotation, i, keyFrames, kf, rotationIndices); populateTransform(Type.Translation, i, keyFrames, kf, translationIndices);
populateTransform(Type.Scale, i, keyFrames, kf, scaleIndices); }
if(rotations != null) {
populateTransform(Type.Rotation, i, keyFrames, kf, rotationIndices);
}
if(scales != null) {
populateTransform(Type.Scale, i, keyFrames, kf, scaleIndices);
}
} }
} }
ensureArraysInit();
if (times[0] > 0) { if (times[0] > 0) {
//Anim doesn't start at 0, JME can't handle that and will interpolate transforms linearly from 0 to the first frame of the anim. //Anim doesn't start at 0, JME can't handle that and will interpolate transforms linearly from 0 to the first frame of the anim.
//we need to add a frame at 0 that copies the first real frame //we need to add a frame at 0 that copies the first real frame
@ -174,6 +177,19 @@ public class AnimData {
return -1; return -1;
} }
public int getNbKeyFrames(){
if(translations != null){
return translations.length;
}
if(rotations != null){
return rotations.length;
}
if(scales != null){
return scales.length;
}
return 0;
}
private void interpolate(Type type, float ratio, KeyFrame lastKeyFrame, KeyFrame nextKeyFrame, int currentIndex) { private void interpolate(Type type, float ratio, KeyFrame lastKeyFrame, KeyFrame nextKeyFrame, int currentIndex) {
//TODO here we should interpolate differently according to the interpolation given in the gltf file. //TODO here we should interpolate differently according to the interpolation given in the gltf file.
switch (type) { switch (type) {
@ -217,25 +233,34 @@ public class AnimData {
} }
} }
private void ensureArraysInit() { private void ensureArraysLength() {
if (translations == null || translations.length < times.length) { if (translations != null && translations.length < times.length) {
translations = new Vector3f[times.length];
}
if (rotations != null && rotations.length < times.length) {
rotations = new Quaternion[times.length];
}
if (scales != null && scales.length < times.length) {
scales = new Vector3f[times.length];
}
}
//JME assumes there are translation and rotation track every time, so we create them with identity transforms if they don't exist
//TODO change this behavior in BoneTrack.
public void ensureTranslationRotations() {
if (translations == null) {
translations = new Vector3f[times.length]; translations = new Vector3f[times.length];
for (int i = 0; i < translations.length; i++) { for (int i = 0; i < translations.length; i++) {
translations[i] = new Vector3f(); translations[i] = new Vector3f();
} }
} }
if (rotations == null || rotations.length < times.length) { if (rotations == null) {
rotations = new Quaternion[times.length]; rotations = new Quaternion[times.length];
for (int i = 0; i < rotations.length; i++) { for (int i = 0; i < rotations.length; i++) {
rotations[i] = new Quaternion(); rotations[i] = new Quaternion();
} }
} }
if (scales == null || scales.length < times.length) {
scales = new Vector3f[times.length];
for (int i = 0; i < scales.length; i++) {
scales[i] = new Vector3f().set(Vector3f.UNIT_XYZ);
}
}
} }
private void setKeyFrameTransforms(Type type, KeyFrame keyFrame, float[] transformTimes) { private void setKeyFrameTransforms(Type type, KeyFrame keyFrame, float[] transformTimes) {
Loading…
Cancel
Save