Gltf Spatial animation loading

fix-456
Nehon 8 years ago committed by Rémy Bouquet
parent 4daa0b5d9b
commit 42aec10020
  1. 296
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  2. 150
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

@ -2,6 +2,9 @@ package com.jme3.scene.plugins.gltf;
import com.google.gson.*; import com.google.gson.*;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Animation;
import com.jme3.animation.SpatialTrack;
import com.jme3.asset.*; import com.jme3.asset.*;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.material.RenderState; import com.jme3.material.RenderState;
@ -41,9 +44,14 @@ public class GltfLoader implements AssetLoader {
private JsonArray textures; private JsonArray textures;
private JsonArray images; private JsonArray images;
private JsonArray samplers; private JsonArray samplers;
private JsonArray animations;
private Material defaultMat; private Material defaultMat;
private AssetInfo info; private AssetInfo info;
private FloatArrayPopulator floatArrayPopulator = new FloatArrayPopulator();
private Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator();
private QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator();
private static Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>(); private static Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>();
private boolean useNormalsFlag = false; private boolean useNormalsFlag = false;
@ -85,6 +93,7 @@ public class GltfLoader implements AssetLoader {
textures = root.getAsJsonArray("textures"); textures = root.getAsJsonArray("textures");
images = root.getAsJsonArray("images"); images = root.getAsJsonArray("images");
samplers = root.getAsJsonArray("samplers"); samplers = root.getAsJsonArray("samplers");
animations = root.getAsJsonArray("animations");
JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene"); JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene");
@ -127,6 +136,13 @@ public class GltfLoader implements AssetLoader {
root.attachChild(sceneNode); root.attachChild(sceneNode);
} }
//Loading animations
if (animations != null) {
for (JsonElement animation : animations) {
loadAnimation(animation.getAsJsonObject());
}
}
//Setting the default scene cul hint to inherit. //Setting the default scene cul hint to inherit.
int activeChild = 0; int activeChild = 0;
if (defaultScene != null) { if (defaultScene != null) {
@ -147,9 +163,7 @@ public class GltfLoader implements AssetLoader {
JsonArray children = nodeData.getAsJsonArray("children"); JsonArray children = nodeData.getAsJsonArray("children");
Integer meshIndex = getAsInteger(nodeData, "mesh"); Integer meshIndex = getAsInteger(nodeData, "mesh");
if (meshIndex != null) { if (meshIndex != null) {
if (meshes == null) { assertNotNull(meshes, "Can't find any mesh data, yet a node references a mesh");
throw new AssetLoadException("Can't find any mesh data, yet a node references a mesh");
}
//there is a mesh in this node, however gltf can split meshes in primitives (some kind of sub meshes), //there is a mesh in this node, however gltf can split meshes in primitives (some kind of sub meshes),
//We don't have this in JME so we have to make one mesh and one Geometry for each primitive. //We don't have this in JME so we have to make one mesh and one Geometry for each primitive.
@ -242,9 +256,8 @@ public class GltfLoader implements AssetLoader {
} }
JsonObject meshData = meshes.get(meshIndex).getAsJsonObject(); JsonObject meshData = meshes.get(meshIndex).getAsJsonObject();
JsonArray primitives = meshData.getAsJsonArray("primitives"); JsonArray primitives = meshData.getAsJsonArray("primitives");
if (primitives == null) { assertNotNull(primitives, "Can't find any primitives in mesh " + meshIndex);
throw new AssetLoadException("Can't find any primitives in mesh " + meshIndex);
}
String name = getAsString(meshData, "name"); String name = getAsString(meshData, "name");
geomArray = new Geometry[primitives.size()]; geomArray = new Geometry[primitives.size()];
@ -256,13 +269,12 @@ public class GltfLoader implements AssetLoader {
mesh.setMode(getMeshMode(mode)); mesh.setMode(getMeshMode(mode));
Integer indices = getAsInteger(meshObject, "indices"); Integer indices = getAsInteger(meshObject, "indices");
if (indices != null) { if (indices != null) {
mesh.setBuffer(loadVertexBuffer(indices, VertexBuffer.Type.Index)); mesh.setBuffer(loadAccessorData(indices, new VertexBufferPopulator(VertexBuffer.Type.Index)));
} }
JsonObject attributes = meshObject.getAsJsonObject("attributes"); JsonObject attributes = meshObject.getAsJsonObject("attributes");
assertNotNull(attributes, "No attributes defined for mesh " + mesh); assertNotNull(attributes, "No attributes defined for mesh " + mesh);
for (Map.Entry<String, JsonElement> entry : attributes.entrySet()) { for (Map.Entry<String, JsonElement> entry : attributes.entrySet()) {
mesh.setBuffer(loadVertexBuffer(entry.getValue().getAsInt(), getVertexBufferType(entry.getKey()))); mesh.setBuffer(loadAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(getVertexBufferType(entry.getKey()))));
} }
Geometry geom = new Geometry(null, mesh); Geometry geom = new Geometry(null, mesh);
@ -297,11 +309,10 @@ public class GltfLoader implements AssetLoader {
return geomArray; return geomArray;
} }
private VertexBuffer loadVertexBuffer(int accessorIndex, VertexBuffer.Type bufferType) throws IOException { private <R> R loadAccessorData(int accessorIndex, Populator<R> populator) throws IOException {
assertNotNull(accessors, "No accessor attribute in the gltf file");
if (accessors == null) {
throw new AssetLoadException("No accessor attribute in the gltf file");
}
JsonObject accessor = accessors.get(accessorIndex).getAsJsonObject(); JsonObject accessor = accessors.get(accessorIndex).getAsJsonObject();
Integer bufferViewIndex = getAsInteger(accessor, "bufferView"); Integer bufferViewIndex = getAsInteger(accessor, "bufferView");
int byteOffset = getAsInteger(accessor, "byteOffset", 0); int byteOffset = getAsInteger(accessor, "byteOffset", 0);
@ -313,30 +324,16 @@ public class GltfLoader implements AssetLoader {
String type = getAsString(accessor, "type"); String type = getAsString(accessor, "type");
assertNotNull(type, "No type attribute defined for accessor " + accessorIndex); assertNotNull(type, "No type attribute defined for accessor " + accessorIndex);
VertexBuffer vb = new VertexBuffer(bufferType);
VertexBuffer.Format format = getVertexBufferFormat(componentType);
int numComponents = getNumberOfComponents(type);
Buffer buff = VertexBuffer.createBuffer(format, numComponents, count);
readBuffer(bufferViewIndex, byteOffset, numComponents * count, buff, numComponents);
if (bufferType == VertexBuffer.Type.Index) {
numComponents = 3;
}
vb.setupData(VertexBuffer.Usage.Dynamic, numComponents, format, buff);
//TODO min / max //TODO min / max
//TODO sparse //TODO sparse
//TODO extensions? //TODO extensions?
//TODO extras? //TODO extras?
return vb;
return populator.populate(bufferViewIndex, componentType, type, count, byteOffset);
} }
private void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Buffer buff, int numComponents) throws IOException { private void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Object store, int numComponents) throws IOException {
if (bufferViewIndex == null) {
//no referenced buffer, specs says to pad the buffer with zeros.
padBuffer(buff, bufferSize);
return;
}
JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject();
Integer bufferIndex = getAsInteger(bufferView, "buffer"); Integer bufferIndex = getAsInteger(bufferView, "buffer");
@ -351,7 +348,7 @@ public class GltfLoader implements AssetLoader {
//int target = getAsInteger(bufferView, "target", 0); //int target = getAsInteger(bufferView, "target", 0);
byte[] data = readData(bufferIndex); byte[] data = readData(bufferIndex);
populateBuffer(buff, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents); populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents);
//TODO extensions? //TODO extensions?
//TODO extras? //TODO extras?
@ -360,9 +357,8 @@ public class GltfLoader implements AssetLoader {
private byte[] readData(int bufferIndex) throws IOException { private byte[] readData(int bufferIndex) throws IOException {
if (buffers == null) { assertNotNull(buffers, "No buffer defined");
throw new AssetLoadException("No buffer defined");
}
JsonObject buffer = buffers.get(bufferIndex).getAsJsonObject(); JsonObject buffer = buffers.get(bufferIndex).getAsJsonObject();
String uri = getAsString(buffer, "uri"); String uri = getAsString(buffer, "uri");
Integer bufferLength = getAsInteger(buffer, "byteLength"); Integer bufferLength = getAsInteger(buffer, "byteLength");
@ -398,9 +394,8 @@ public class GltfLoader implements AssetLoader {
} }
private Material loadMaterial(int materialIndex) { private Material loadMaterial(int materialIndex) {
if (materials == null) { assertNotNull(materials, "There is no material defined yet a mesh references one");
throw new AssetLoadException("There is no material defined yet a mesh references one");
}
JsonObject matData = materials.get(materialIndex).getAsJsonObject(); JsonObject matData = materials.get(materialIndex).getAsJsonObject();
JsonObject pbrMat = matData.getAsJsonObject("pbrMetallicRoughness"); JsonObject pbrMat = matData.getAsJsonObject("pbrMetallicRoughness");
@ -448,12 +443,9 @@ public class GltfLoader implements AssetLoader {
return null; return null;
} }
Integer textureIndex = getAsInteger(texture, "index"); Integer textureIndex = getAsInteger(texture, "index");
if (textureIndex == null) { assertNotNull(textureIndex, "Texture as no index");
throw new AssetLoadException("Texture as no index"); assertNotNull(textures, "There are no textures, yet one is referenced by a material");
}
if (textures == null) {
throw new AssetLoadException("There are no textures, yet one is referenced by a material");
}
JsonObject textureData = textures.get(textureIndex).getAsJsonObject(); JsonObject textureData = textures.get(textureIndex).getAsJsonObject();
Integer sourceIndex = getAsInteger(textureData, "source"); Integer sourceIndex = getAsInteger(textureData, "source");
Integer samplerIndex = getAsInteger(textureData, "sampler"); Integer samplerIndex = getAsInteger(textureData, "sampler");
@ -487,6 +479,115 @@ public class GltfLoader implements AssetLoader {
} }
private void loadAnimation(JsonObject animation) throws IOException {
JsonArray channels = animation.getAsJsonArray("channels");
JsonArray samplers = animation.getAsJsonArray("samplers");
String name = getAsString(animation, "name");
assertNotNull(channels, "No channels for animation " + name);
assertNotNull(samplers, "No samplers for animation " + name);
//temp data storage of track data
AnimData[] animatedNodes = new AnimData[nodes.size()];
for (JsonElement channel : channels) {
JsonObject target = channel.getAsJsonObject().getAsJsonObject("target");
Integer targetNode = getAsInteger(target, "node");
String targetPath = getAsString(target, "path");
if (targetNode == null) {
//no target node for the channel, specs say to ignore the channel.
continue;
}
assertNotNull(targetPath, "No target path for channel");
if (targetPath.equals("weight")) {
//Morph animation, not implemented in JME, let's warn the user and skip the channel
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;
}
Integer samplerIndex = getAsInteger(channel.getAsJsonObject(), "sampler");
assertNotNull(samplerIndex, "No animation sampler provided for channel");
JsonObject sampler = samplers.get(samplerIndex).getAsJsonObject();
Integer timeIndex = getAsInteger(sampler, "input");
assertNotNull(timeIndex, "No input accessor Provided for animation sampler");
Integer dataIndex = getAsInteger(sampler, "output");
assertNotNull(dataIndex, "No output accessor Provided for animation sampler");
String interpolation = getAsString(sampler, "interpolation");
if (interpolation == null || !interpolation.equals("LINEAR")) {
//JME anim system only supports Linear interpolation (will be possible with monkanim though)
//TODO rework this once monkanim is core, or allow a hook for animation loading to fit custom animation systems
logger.log(Level.WARNING, "JME only supports linear interpolation for animations");
}
float[] times = fetchFromCache("accessors", timeIndex, float[].class);
if (times == null) {
times = loadAccessorData(timeIndex, floatArrayPopulator);
addToCache("accessors", timeIndex, times, accessors.size());
}
if (animData.times == null) {
animData.times = times;
} else {
//check if we are loading the same time array
if (animData.times != times) {
throw new AssetLoadException("Channel has different input accessors for samplers");
}
}
if (animData.length == null) {
//animation length is the last timestamp
animData.length = times[times.length - 1];
}
if (targetPath.equals("translation")) {
Vector3f[] translations = loadAccessorData(dataIndex, vector3fArrayPopulator);
animData.translations = translations;
} else if (targetPath.equals("scale")) {
Vector3f[] scales = loadAccessorData(dataIndex, vector3fArrayPopulator);
animData.scales = scales;
} else if (targetPath.equals("rotation")) {
Quaternion[] rotations = loadAccessorData(dataIndex, quaternionArrayPopulator);
animData.rotations = rotations;
}
}
for (int i = 0; i < animatedNodes.length; i++) {
AnimData animData = animatedNodes[i];
if (animData == null) {
continue;
}
Object node = fetchFromCache("nodes", i, Object.class);
if (node instanceof Spatial) {
Spatial s = (Spatial) node;
AnimControl control = s.getControl(AnimControl.class);
if (control == null) {
control = new AnimControl();
s.addControl(control);
}
if (name == null) {
name = s.getName() + "_anim_" + control.getAnimationNames().size();
}
Animation anim = new Animation(name, animData.length);
anim.addTrack(new SpatialTrack(animData.times, animData.translations, animData.rotations, animData.scales));
control.addAnim(anim);
} else {
//At some pont we'll have bone animation
//TODO support for bone animation.
}
}
}
//private void readAnimationSampler()
private void readSampler(int samplerIndex, Texture2D texture) { private void readSampler(int samplerIndex, Texture2D texture) {
if (samplers == null) { if (samplers == null) {
throw new AssetLoadException("No samplers defined"); throw new AssetLoadException("No samplers defined");
@ -529,5 +630,112 @@ public class GltfLoader implements AssetLoader {
data[index] = object; data[index] = object;
} }
private class AnimData {
Float length;
float[] times;
Vector3f[] translations;
Quaternion[] rotations;
Vector3f[] scales;
float[] weights;
}
private interface Populator<T> {
T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException;
}
private class VertexBufferPopulator implements Populator<VertexBuffer> {
VertexBuffer.Type bufferType;
public VertexBufferPopulator(VertexBuffer.Type bufferType) {
this.bufferType = bufferType;
}
@Override
public VertexBuffer populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException {
VertexBuffer vb = new VertexBuffer(bufferType);
VertexBuffer.Format format = getVertexBufferFormat(componentType);
int numComponents = getNumberOfComponents(type);
Buffer buff = VertexBuffer.createBuffer(format, numComponents, count);
int bufferSize = numComponents * count;
if (bufferViewIndex == null) {
//no referenced buffer, specs says to pad the buffer with zeros.
padBuffer(buff, bufferSize);
} else {
readBuffer(bufferViewIndex, byteOffset, bufferSize, buff, numComponents);
}
if (bufferType == VertexBuffer.Type.Index) {
numComponents = 3;
}
vb.setupData(VertexBuffer.Usage.Dynamic, numComponents, format, buff);
return vb;
}
}
private class FloatArrayPopulator implements Populator<float[]> {
@Override
public float[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException {
int numComponents = getNumberOfComponents(type);
int dataSize = numComponents * count;
float[] data = new float[count];
if (bufferViewIndex == null) {
//no referenced buffer, specs says to pad the data with zeros.
padBuffer(data, dataSize);
} else {
readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents);
}
return data;
}
}
private class Vector3fArrayPopulator implements Populator<Vector3f[]> {
@Override
public Vector3f[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException {
int numComponents = getNumberOfComponents(type);
int dataSize = numComponents * count;
Vector3f[] data = new Vector3f[count];
if (bufferViewIndex == null) {
//no referenced buffer, specs says to pad the data with zeros.
padBuffer(data, dataSize);
} else {
readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents);
}
return data;
}
}
private class QuaternionArrayPopulator implements Populator<Quaternion[]> {
@Override
public Quaternion[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException {
int numComponents = getNumberOfComponents(type);
int dataSize = numComponents * count;
Quaternion[] data = new Quaternion[count];
if (bufferViewIndex == null) {
//no referenced buffer, specs says to pad the data with zeros.
padBuffer(data, dataSize);
} else {
readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents);
}
return data;
}
}
} }

@ -3,6 +3,8 @@ package com.jme3.scene.plugins.gltf;
import com.google.gson.*; import com.google.gson.*;
import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetLoadException;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Mesh; import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
@ -109,7 +111,7 @@ public class GltfUtils {
return VertexBuffer.Type.Color; return VertexBuffer.Type.Color;
case "JOINTS_0": case "JOINTS_0":
return VertexBuffer.Type.BoneIndex; return VertexBuffer.Type.BoneIndex;
case "WEIGHT_0": case "WEIGHTS_0":
return VertexBuffer.Type.BoneWeight; return VertexBuffer.Type.BoneWeight;
default: default:
throw new AssetLoadException("Unsupported buffer attribute: " + attribute); throw new AssetLoadException("Unsupported buffer attribute: " + attribute);
@ -166,48 +168,79 @@ public class GltfUtils {
} }
} }
public static void padBuffer(Buffer buffer, int bufferSize) { public static void padBuffer(Object store, int bufferSize) {
buffer.clear(); if (store instanceof Buffer) {
if (buffer instanceof IntBuffer) { Buffer buffer = (Buffer) store;
IntBuffer ib = (IntBuffer) buffer; buffer.clear();
for (int i = 0; i < bufferSize; i++) { if (buffer instanceof IntBuffer) {
ib.put(0); IntBuffer ib = (IntBuffer) buffer;
for (int i = 0; i < bufferSize; i++) {
ib.put(0);
}
} else if (buffer instanceof FloatBuffer) {
FloatBuffer fb = (FloatBuffer) buffer;
for (int i = 0; i < bufferSize; i++) {
fb.put(0);
}
} else if (buffer instanceof ShortBuffer) {
ShortBuffer sb = (ShortBuffer) buffer;
for (int i = 0; i < bufferSize; i++) {
sb.put((short) 0);
}
} else if (buffer instanceof ByteBuffer) {
ByteBuffer bb = (ByteBuffer) buffer;
for (int i = 0; i < bufferSize; i++) {
bb.put((byte) 0);
}
} }
} else if (buffer instanceof FloatBuffer) { buffer.rewind();
FloatBuffer fb = (FloatBuffer) buffer; }
for (int i = 0; i < bufferSize; i++) { if (store instanceof float[]) {
fb.put(0); float[] array = (float[]) store;
for (int i = 0; i < array.length; i++) {
array[i] = 0;
} }
} else if (buffer instanceof ShortBuffer) { } else if (store instanceof Vector3f[]) {
ShortBuffer sb = (ShortBuffer) buffer; Vector3f[] array = (Vector3f[]) store;
for (int i = 0; i < bufferSize; i++) { for (int i = 0; i < array.length; i++) {
sb.put((short) 0); array[i] = new Vector3f();
} }
} else if (buffer instanceof ByteBuffer) { } else if (store instanceof Quaternion[]) {
ByteBuffer bb = (ByteBuffer) buffer; Quaternion[] array = (Quaternion[]) store;
for (int i = 0; i < bufferSize; i++) { for (int i = 0; i < array.length; i++) {
bb.put((byte) 0); array[i] = new Quaternion();
} }
} }
buffer.rewind();
} }
public static void populateBuffer(Buffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents) throws IOException { public static void populateBuffer(Object store, byte[] source, int length, int byteOffset, int byteStride, int numComponents) throws IOException {
buffer.clear();
if (buffer instanceof ByteBuffer) { if (store instanceof Buffer) {
populateByteBuffer((ByteBuffer) buffer, source, length, byteOffset, byteStride, numComponents); Buffer buffer = (Buffer) store;
buffer.clear();
if (buffer instanceof ByteBuffer) {
populateByteBuffer((ByteBuffer) buffer, source, length, byteOffset, byteStride, numComponents);
return;
}
LittleEndien stream = getStream(source);
if (buffer instanceof ShortBuffer) {
populateShortBuffer((ShortBuffer) buffer, stream, length, byteOffset, byteStride, numComponents);
} else if (buffer instanceof IntBuffer) {
populateIntBuffer((IntBuffer) buffer, stream, length, byteOffset, byteStride, numComponents);
} else if (buffer instanceof FloatBuffer) {
populateFloatBuffer((FloatBuffer) buffer, stream, length, byteOffset, byteStride, numComponents);
}
buffer.rewind();
return; return;
} }
LittleEndien stream = getStream(source); LittleEndien stream = getStream(source);
if (buffer instanceof ShortBuffer) { if (store instanceof float[]) {
populateShortBuffer((ShortBuffer) buffer, stream, length, byteOffset, byteStride, numComponents); populateFloatArray((float[]) store, stream, length, byteOffset, byteStride, numComponents);
} else if (buffer instanceof IntBuffer) { } else if (store instanceof Vector3f[]) {
populateIntBuffer((IntBuffer) buffer, stream, length, byteOffset, byteStride, numComponents); populateVector3fArray((Vector3f[]) store, stream, length, byteOffset, byteStride, numComponents);
} else if (buffer instanceof FloatBuffer) { } else if (store instanceof Quaternion[]) {
populateFloatBuffer((FloatBuffer) buffer, stream, length, byteOffset, byteStride, numComponents); populateQuaternionArray((Quaternion[]) store, stream, length, byteOffset, byteStride, numComponents);
} }
buffer.rewind();
} }
private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents) { private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents) {
@ -260,6 +293,61 @@ public class GltfUtils {
} }
} }
private static void populateFloatArray(float[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException {
int index = byteOffset;
int componentSize = 4;
int end = length * componentSize + byteOffset;
stream.skipBytes(byteOffset);
int arrayIndex = 0;
while (index < end) {
for (int i = 0; i < numComponents; i++) {
array[arrayIndex] = stream.readFloat();
arrayIndex++;
}
index += Math.max(componentSize * numComponents, byteStride);
}
}
private static void populateVector3fArray(Vector3f[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException {
int index = byteOffset;
int componentSize = 4;
int end = length * componentSize + byteOffset;
stream.skipBytes(byteOffset);
int arrayIndex = 0;
while (index < end) {
array[arrayIndex] = new Vector3f(
stream.readFloat(),
stream.readFloat(),
stream.readFloat()
);
arrayIndex++;
index += Math.max(componentSize * numComponents, byteStride);
}
}
private static void populateQuaternionArray(Quaternion[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException {
int index = byteOffset;
int componentSize = 4;
int end = length * componentSize + byteOffset;
stream.skipBytes(byteOffset);
int arrayIndex = 0;
while (index < end) {
array[arrayIndex] = new Quaternion(
stream.readFloat(),
stream.readFloat(),
stream.readFloat(),
stream.readFloat()
);
arrayIndex++;
index += Math.max(componentSize * numComponents, byteStride);
}
}
private static LittleEndien getStream(byte[] buffer) { private static LittleEndien getStream(byte[] buffer) {
return new LittleEndien(new DataInputStream(new ByteArrayInputStream(buffer))); return new LittleEndien(new DataInputStream(new ByteArrayInputStream(buffer)));
} }

Loading…
Cancel
Save