|
@@ -0,0 +1,296 @@
|
|
|
|
+package com.jme3.scene.plugins.gltf;
|
|
|
|
+
|
|
|
|
+import com.jme3.math.*;
|
|
|
|
+
|
|
|
|
+import java.util.*;
|
|
|
|
+
|
|
|
|
+public class AnimData {
|
|
|
|
+
|
|
|
|
+ public enum Type {
|
|
|
|
+ Translation,
|
|
|
|
+ Rotation,
|
|
|
|
+ Scale
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Float length;
|
|
|
|
+ float[] times;
|
|
|
|
+ List<TimeData> timeArrays = new ArrayList<>();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ Vector3f[] translations;
|
|
|
|
+ Quaternion[] rotations;
|
|
|
|
+ Vector3f[] scales;
|
|
|
|
+ //not used for now
|
|
|
|
+ float[] weights;
|
|
|
|
+
|
|
|
|
+ public void update() {
|
|
|
|
+
|
|
|
|
+ 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.
|
|
|
|
+
|
|
|
|
+ //extracting keyframes information
|
|
|
|
+ List<KeyFrame> keyFrames = new ArrayList<>();
|
|
|
|
+ TimeData timeData = timeArrays.get(0);
|
|
|
|
+ Type type = timeData.type;
|
|
|
|
+ for (int i = 0; i < timeData.times.length; i++) {
|
|
|
|
+ float time = timeData.times[i];
|
|
|
|
+ KeyFrame keyFrame = new KeyFrame();
|
|
|
|
+ keyFrame.time = time;
|
|
|
|
+ setKeyFrameTransforms(type, keyFrame, timeData.times);
|
|
|
|
+ keyFrames.add(keyFrame);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for (int i = 1; i < timeArrays.size(); i++) {
|
|
|
|
+ timeData = timeArrays.get(i);
|
|
|
|
+ type = timeData.type;
|
|
|
|
+ for (float time : timeData.times) {
|
|
|
|
+ for (int j = 0; j < keyFrames.size(); j++) {
|
|
|
|
+ KeyFrame kf = keyFrames.get(j);
|
|
|
|
+ if (Float.floatToIntBits(kf.time) != Float.floatToIntBits(time)) {
|
|
|
|
+ if (time > kf.time) {
|
|
|
|
+ continue;
|
|
|
|
+ } else {
|
|
|
|
+ kf = new KeyFrame();
|
|
|
|
+ kf.time = time;
|
|
|
|
+ keyFrames.add(j, kf);
|
|
|
|
+ //we inserted a keyframe let's shift the counter.
|
|
|
|
+ j++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ setKeyFrameTransforms(type, kf, timeData.times);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // populating transforms array from the keyframes, interpolating
|
|
|
|
+ times = new float[keyFrames.size()];
|
|
|
|
+
|
|
|
|
+ ensureArraysInit();
|
|
|
|
+
|
|
|
|
+ TransformIndices translationIndices = new TransformIndices();
|
|
|
|
+ TransformIndices rotationIndices = new TransformIndices();
|
|
|
|
+ TransformIndices scaleIndices = new TransformIndices();
|
|
|
|
+
|
|
|
|
+ for (int i = 0; i < keyFrames.size(); i++) {
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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
|
|
|
|
+
|
|
|
|
+ float[] newTimes = new float[times.length + 1];
|
|
|
|
+ newTimes[0] = 0f;
|
|
|
|
+ System.arraycopy(times, 0, newTimes, 1, times.length);
|
|
|
|
+ times = newTimes;
|
|
|
|
+
|
|
|
|
+ if (translations != null) {
|
|
|
|
+ Vector3f[] newTranslations = new Vector3f[translations.length + 1];
|
|
|
|
+ newTranslations[0] = translations[0];
|
|
|
|
+ System.arraycopy(translations, 0, newTranslations, 1, translations.length);
|
|
|
|
+ translations = newTranslations;
|
|
|
|
+ }
|
|
|
|
+ if (rotations != null) {
|
|
|
|
+ Quaternion[] newRotations = new Quaternion[rotations.length + 1];
|
|
|
|
+ newRotations[0] = rotations[0];
|
|
|
|
+ System.arraycopy(rotations, 0, newRotations, 1, rotations.length);
|
|
|
|
+ rotations = newRotations;
|
|
|
|
+ }
|
|
|
|
+ if (scales != null) {
|
|
|
|
+ Vector3f[] newScales = new Vector3f[scales.length + 1];
|
|
|
|
+ newScales[0] = scales[0];
|
|
|
|
+ System.arraycopy(scales, 0, newScales, 1, scales.length);
|
|
|
|
+ scales = newScales;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ length = times[times.length - 1];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void populateTransform(Type type, int index, List<KeyFrame> keyFrames, KeyFrame currentKeyFrame, TransformIndices transformIndices) {
|
|
|
|
+ Object transform = getTransform(type, currentKeyFrame);
|
|
|
|
+ if (transform != null) {
|
|
|
|
+ getArray(type)[index] = transform;
|
|
|
|
+ transformIndices.last = index;
|
|
|
|
+ } else {
|
|
|
|
+ transformIndices.next = findNext(keyFrames, type, index);
|
|
|
|
+ if (transformIndices.next == -1) {
|
|
|
|
+ //no next let's use prev value.
|
|
|
|
+ if (transformIndices.last == -1) {
|
|
|
|
+ //last Transform Index = -1 it means there are no transforms. nothing more to do
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ KeyFrame lastKeyFrame = keyFrames.get(transformIndices.last);
|
|
|
|
+ getArray(type)[index] = getTransform(type, lastKeyFrame);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ KeyFrame nextKeyFrame = keyFrames.get(transformIndices.next);
|
|
|
|
+ if (transformIndices.last == -1) {
|
|
|
|
+ //no previous transforms let's use the new one.
|
|
|
|
+ translations[index] = nextKeyFrame.translation;
|
|
|
|
+ } else {
|
|
|
|
+ //interpolation between the previous transform and the next one.
|
|
|
|
+ KeyFrame lastKeyFrame = keyFrames.get(transformIndices.last);
|
|
|
|
+ float ratio = currentKeyFrame.time / (nextKeyFrame.time - lastKeyFrame.time);
|
|
|
|
+ interpolate(type, ratio, lastKeyFrame, nextKeyFrame, index);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private int findNext(List<KeyFrame> keyFrames, Type type, int fromIndex) {
|
|
|
|
+ for (int i = fromIndex + 1; i < keyFrames.size(); i++) {
|
|
|
|
+ KeyFrame kf = keyFrames.get(i);
|
|
|
|
+ switch (type) {
|
|
|
|
+ case Translation:
|
|
|
|
+ if (kf.translation != null) {
|
|
|
|
+ return i;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case Rotation:
|
|
|
|
+ if (kf.rotation != null) {
|
|
|
|
+ return i;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case Scale:
|
|
|
|
+ if (kf.scale != null) {
|
|
|
|
+ return i;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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) {
|
|
|
|
+ case Translation:
|
|
|
|
+ translations[currentIndex] = FastMath.interpolateLinear(ratio, lastKeyFrame.translation, nextKeyFrame.translation);
|
|
|
|
+ break;
|
|
|
|
+ case Rotation:
|
|
|
|
+ Quaternion rot = new Quaternion().set(lastKeyFrame.rotation);
|
|
|
|
+ rot.nlerp(nextKeyFrame.rotation, ratio);
|
|
|
|
+ rotations[currentIndex] = rot;
|
|
|
|
+ break;
|
|
|
|
+ case Scale:
|
|
|
|
+ scales[currentIndex] = FastMath.interpolateLinear(ratio, lastKeyFrame.scale, nextKeyFrame.scale);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Object[] getArray(Type type) {
|
|
|
|
+ switch (type) {
|
|
|
|
+ case Translation:
|
|
|
|
+ return translations;
|
|
|
|
+ case Rotation:
|
|
|
|
+ return rotations;
|
|
|
|
+ case Scale:
|
|
|
|
+ return scales;
|
|
|
|
+ default:
|
|
|
|
+ return translations;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Object getTransform(Type type, KeyFrame kf) {
|
|
|
|
+ switch (type) {
|
|
|
|
+ case Translation:
|
|
|
|
+ return kf.translation;
|
|
|
|
+ case Rotation:
|
|
|
|
+ return kf.rotation;
|
|
|
|
+ case Scale:
|
|
|
|
+ return kf.scale;
|
|
|
|
+ default:
|
|
|
|
+ return kf.translation;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void ensureArraysInit() {
|
|
|
|
+ if (translations == null || translations.length < times.length) {
|
|
|
|
+ translations = new Vector3f[times.length];
|
|
|
|
+ for (int i = 0; i < translations.length; i++) {
|
|
|
|
+ translations[i] = new Vector3f();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (rotations == null || rotations.length < times.length) {
|
|
|
|
+ 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) {
|
|
|
|
+ int index = 0;
|
|
|
|
+ while (Float.floatToIntBits(transformTimes[index]) != Float.floatToIntBits(keyFrame.time)) {
|
|
|
|
+ index++;
|
|
|
|
+ }
|
|
|
|
+ switch (type) {
|
|
|
|
+ case Translation:
|
|
|
|
+ keyFrame.translation = translations[index];
|
|
|
|
+ break;
|
|
|
|
+ case Rotation:
|
|
|
|
+ keyFrame.rotation = rotations[index];
|
|
|
|
+ break;
|
|
|
|
+ case Scale:
|
|
|
|
+ keyFrame.scale = scales[index];
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private boolean equalTimes(List<TimeData> timeData) {
|
|
|
|
+ if (timeData.size() == 1) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ float[] times0 = timeData.get(0).times;
|
|
|
|
+ for (int i = 1; i < timeData.size(); i++) {
|
|
|
|
+ float[] timesI = timeData.get(i).times;
|
|
|
|
+ if (!Arrays.equals(times0, timesI)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static class TimeData {
|
|
|
|
+
|
|
|
|
+ float[] times;
|
|
|
|
+ Type type;
|
|
|
|
+
|
|
|
|
+ public TimeData(float[] times, Type type) {
|
|
|
|
+ this.times = times;
|
|
|
|
+ this.type = type;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private class TransformIndices {
|
|
|
|
+ int last = -1;
|
|
|
|
+ int next = -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private class KeyFrame {
|
|
|
|
+ float time;
|
|
|
|
+ Vector3f translation;
|
|
|
|
+ Quaternion rotation;
|
|
|
|
+ Vector3f scale;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|