commit
5108f52ebf
@ -0,0 +1,41 @@ |
|||||||
|
package com.jme3.export.binary; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetInfo; |
||||||
|
import com.jme3.asset.AssetLoader; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.ArrayDeque; |
||||||
|
import java.util.Deque; |
||||||
|
|
||||||
|
/** |
||||||
|
* The default loader to load binaries files. |
||||||
|
* |
||||||
|
* @author JavaSaBr |
||||||
|
*/ |
||||||
|
public class BinaryLoader implements AssetLoader { |
||||||
|
|
||||||
|
/** |
||||||
|
* The importers queue. |
||||||
|
*/ |
||||||
|
private final Deque<BinaryImporter> importers; |
||||||
|
|
||||||
|
public BinaryLoader() { |
||||||
|
importers = new ArrayDeque<>(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object load(final AssetInfo assetInfo) throws IOException { |
||||||
|
|
||||||
|
BinaryImporter importer = importers.pollLast(); |
||||||
|
|
||||||
|
if (importer == null) { |
||||||
|
importer = new BinaryImporter(); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
return importer.load(assetInfo); |
||||||
|
} finally { |
||||||
|
importers.addLast(importer); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue