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