diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java index 8b2897e50..a3a59f3a7 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java @@ -19,7 +19,6 @@ import com.jme3.util.mikktspace.MikktspaceTangentGenerator; import javax.xml.bind.DatatypeConverter; import java.io.*; import java.nio.Buffer; -import java.sql.Time; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -714,7 +713,7 @@ public class GltfLoader implements AssetLoader { assertNotNull(samplers, "No samplers for animation " + name); //temp data storage of track data - AnimData[] animatedNodes = new AnimData[nodes.size()]; + TrackData[] animatedNodes = new TrackData[nodes.size()]; 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"); continue; } - AnimData animData = animatedNodes[targetNode]; - if (animData == null) { - animData = new AnimData(); - animatedNodes[targetNode] = animData; + TrackData trackData = animatedNodes[targetNode]; + if (trackData == null) { + trackData = new TrackData(); + animatedNodes[targetNode] = trackData; } 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"); } - animData = customContentManager.readExtensionAndExtras("animation.sampler", sampler, animData); + trackData = customContentManager.readExtensionAndExtras("animation.sampler", sampler, trackData); float[] times = fetchFromCache("accessors", timeIndex, float[].class); if (times == null) { @@ -763,17 +762,17 @@ public class GltfLoader implements AssetLoader { } 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); - animData.translations = translations; + trackData.translations = translations; } 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); - animData.scales = scales; + trackData.scales = scales; } 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); - animData.rotations = rotations; + trackData.rotations = rotations; } else { //TODO support weights logger.log(Level.WARNING, "Morph animation is not supported"); @@ -781,7 +780,7 @@ public class GltfLoader implements AssetLoader { continue; } - animatedNodes[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, animData); + animatedNodes[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, trackData); } if (name == null) { @@ -794,26 +793,26 @@ public class GltfLoader implements AssetLoader { int skinIndex = -1; for (int i = 0; i < animatedNodes.length; i++) { - AnimData animData = animatedNodes[i]; - if (animData == null) { + TrackData trackData = animatedNodes[i]; + if (trackData == null) { continue; } - animData.update(); - if (animData.length > anim.getLength()) { - anim.setLength(animData.length); + trackData.update(); + if (trackData.length > anim.getLength()) { + anim.setLength(trackData.length); } Object node = fetchFromCache("nodes", i, Object.class); if (node instanceof Spatial) { Spatial s = (Spatial) node; 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); anim.addTrack(track); } else if (node instanceof BoneWrapper) { BoneWrapper b = (BoneWrapper) node; //apply the inverseBindMatrix to animation data. - b.update(animData); - BoneTrack track = new BoneTrack(b.boneIndex, animData.times, animData.translations, animData.rotations, animData.scales); + b.update(trackData); + BoneTrack track = new BoneTrack(b.boneIndex, trackData.times, trackData.translations, trackData.rotations, trackData.scales); anim.addTrack(track); if (skinIndex == -1) { skinIndex = b.skinIndex; @@ -961,6 +960,7 @@ public class GltfLoader implements AssetLoader { bw.bone.setLocalTranslation(bw.localTransform.getTranslation()); bw.bone.setLocalRotation(bw.localTransform.getRotation()); bw.bone.setLocalScale(bw.localTransform.getScale()); + bw.bone.setUserControl(false); } 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. */ - public void update(AnimData data) { + public void update(TrackData data) { Transform bindTransforms = new Transform(bone.getBindPosition(), bone.getBindRotation(), bone.getBindScale()); SkinData skinData = fetchFromCache("skins", skinIndex, SkinData.class); - for (int i = 0; i < data.translations.length; i++) { - Transform t = new Transform(data.translations[i], data.rotations[i], data.scales[i]); + for (int i = 0; i < data.getNbKeyFrames(); 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) { //Apply the armature transforms to the root bone anim track. t.combineWithParent(skinData.armatureTransforms); @@ -1180,10 +1185,48 @@ public class GltfLoader implements AssetLoader { tmpQuat.set(bindTransforms.getRotation()).inverseLocal().multLocal(t.getRotation()); t.setRotation(tmpQuat); - data.translations[i] = t.getTranslation(); - data.rotations[i] = t.getRotation(); - data.scales[i] = t.getScale(); + if(data.translations != null) { + data.translations[i] = t.getTranslation(); + } + 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; } } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/AnimData.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TrackData.java similarity index 87% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/AnimData.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TrackData.java index 890b74239..fdbf892fa 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/AnimData.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TrackData.java @@ -4,7 +4,7 @@ import com.jme3.math.*; import java.util.*; -public class AnimData { +public class TrackData { public enum Type { Translation, @@ -27,7 +27,6 @@ public class AnimData { if (equalTimes(timeArrays)) { times = timeArrays.get(0).times; - ensureArraysInit(); } else { //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. @@ -69,7 +68,7 @@ public class AnimData { // populating transforms array from the keyframes, interpolating times = new float[keyFrames.size()]; - ensureArraysInit(); + ensureArraysLength(); TransformIndices translationIndices = new TransformIndices(); TransformIndices rotationIndices = new TransformIndices(); @@ -79,14 +78,18 @@ public class AnimData { KeyFrame kf = keyFrames.get(i); //we need Interpolate between keyframes when transforms are sparse. times[i] = kf.time; - populateTransform(Type.Translation, i, keyFrames, kf, translationIndices); - populateTransform(Type.Rotation, i, keyFrames, kf, rotationIndices); - populateTransform(Type.Scale, i, keyFrames, kf, scaleIndices); + if(translations != null) { + populateTransform(Type.Translation, i, keyFrames, kf, translationIndices); + } + 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) { //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 @@ -174,6 +177,19 @@ public class AnimData { 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) { //TODO here we should interpolate differently according to the interpolation given in the gltf file. switch (type) { @@ -217,25 +233,34 @@ public class AnimData { } } - private void ensureArraysInit() { - if (translations == null || translations.length < times.length) { + private void ensureArraysLength() { + 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]; for (int i = 0; i < translations.length; i++) { translations[i] = new Vector3f(); } } - if (rotations == null || rotations.length < times.length) { + if (rotations == null) { rotations = new Quaternion[times.length]; for (int i = 0; i < rotations.length; i++) { 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) {