From cac51f542a7bb16281031c35073465b1609a9420 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sat, 26 Aug 2017 17:50:06 +0200 Subject: [PATCH] Added support for packed float data --- .../jme3/scene/plugins/gltf/GltfLoader.java | 51 ++++--- .../jme3/scene/plugins/gltf/GltfUtils.java | 130 +++++++++++------- 2 files changed, 111 insertions(+), 70 deletions(-) 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 e7f0de43d..6bc0805e3 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 @@ -285,7 +285,8 @@ public class GltfLoader implements AssetLoader { for (int i = 0; i < tmpArray.length; i++) { tmpArray[i] = matrix.get(i).getAsFloat(); } - Matrix4f mat = toRowMajor(tmpArray); + //creates a row major matrix from color major data + Matrix4f mat = new Matrix4f(tmpArray); transform.fromTransformMatrix(mat); return transform; } @@ -446,19 +447,18 @@ public class GltfLoader implements AssetLoader { assertNotNull(type, "No type attribute defined for accessor " + accessorIndex); boolean normalized = getAsBoolean(accessor, "normalized", false); - //Some float data can be packed into short buffers, "normalized" means they have to be unpacked. + //TODO support packed data //TODO min / max //TODO sparse - //TODO extensions? //TODO extras? - R data = populator.populate(bufferViewIndex, componentType, type, count, byteOffset); + R data = populator.populate(bufferViewIndex, componentType, type, count, byteOffset, normalized); data = customContentManager.readExtension(accessor, data); return data; } - public void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Object store, int numComponents, int componentSize) throws IOException { + public void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Object store, int numComponents, VertexBuffer.Format format) throws IOException { JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); Integer bufferIndex = getAsInteger(bufferView, "buffer"); @@ -476,7 +476,7 @@ public class GltfLoader implements AssetLoader { data = customContentManager.readExtension(bufferView, data); - populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, componentSize); + populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, format); //TODO extras? @@ -1219,7 +1219,7 @@ public class GltfLoader implements AssetLoader { } private interface Populator { - T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException; + T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException; } private class VertexBufferPopulator implements Populator { @@ -1230,15 +1230,22 @@ public class GltfLoader implements AssetLoader { } @Override - public VertexBuffer populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException { + public VertexBuffer populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { if (bufferType == null) { logger.log(Level.WARNING, "could not assign data to any VertexBuffer type for buffer view " + bufferViewIndex); return null; } + VertexBuffer vb = new VertexBuffer(bufferType); VertexBuffer.Format format = getVertexBufferFormat(componentType); + VertexBuffer.Format originalFormat = format; + if (normalized) { + //Some float data can be packed into short buffers, "normalized" means they have to be unpacked. + //In that case the buffer is a FloatBuffer + format = VertexBuffer.Format.Float; + } int numComponents = getNumberOfComponents(type); Buffer buff = VertexBuffer.createBuffer(format, numComponents, count); @@ -1247,7 +1254,7 @@ public class GltfLoader implements AssetLoader { //no referenced buffer, specs says to pad the buffer with zeros. padBuffer(buff, bufferSize); } else { - readBuffer(bufferViewIndex, byteOffset, bufferSize, buff, numComponents, format.getComponentSize()); + readBuffer(bufferViewIndex, byteOffset, bufferSize, buff, numComponents, originalFormat); } if (bufferType == VertexBuffer.Type.Index) { @@ -1263,7 +1270,7 @@ public class GltfLoader implements AssetLoader { private class FloatArrayPopulator implements Populator { @Override - public float[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException { + public float[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; @@ -1273,7 +1280,7 @@ public class GltfLoader implements AssetLoader { //no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, 4); + readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, getVertexBufferFormat(componentType)); } return data; @@ -1284,7 +1291,7 @@ public class GltfLoader implements AssetLoader { private class Vector3fArrayPopulator implements Populator { @Override - public Vector3f[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException { + public Vector3f[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; @@ -1294,7 +1301,7 @@ public class GltfLoader implements AssetLoader { //no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, 4); + readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, getVertexBufferFormat(componentType)); } return data; } @@ -1303,7 +1310,7 @@ public class GltfLoader implements AssetLoader { private class QuaternionArrayPopulator implements Populator { @Override - public Quaternion[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException { + public Quaternion[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; @@ -1313,7 +1320,7 @@ public class GltfLoader implements AssetLoader { //no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, 4); + readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, getVertexBufferFormat(componentType)); } return data; @@ -1323,7 +1330,7 @@ public class GltfLoader implements AssetLoader { private class Matrix4fArrayPopulator implements Populator { @Override - public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException { + public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; @@ -1333,7 +1340,7 @@ public class GltfLoader implements AssetLoader { //no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, 4); + readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, getVertexBufferFormat(componentType)); } return data; @@ -1343,14 +1350,14 @@ public class GltfLoader implements AssetLoader { private class JointArrayPopulator implements Populator { @Override - public SkinBuffers populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset) throws IOException { + public SkinBuffers populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { int numComponents = getNumberOfComponents(type); //can be bytes or shorts. - int componentSize = 1; + VertexBuffer.Format format = VertexBuffer.Format.Byte; if (componentType == 5123) { - componentSize = 2; + format = VertexBuffer.Format.Short; } int dataSize = numComponents * count; @@ -1360,10 +1367,10 @@ public class GltfLoader implements AssetLoader { //no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, componentSize); + readBuffer(bufferViewIndex, byteOffset, dataSize, data, numComponents, format); } - return new SkinBuffers(data, componentSize); + return new SkinBuffers(data, format.getComponentSize()); } } } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java index 9e54366f2..d2e742687 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java @@ -237,42 +237,43 @@ public class GltfUtils { } } - public static void populateBuffer(Object store, byte[] source, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + public static void populateBuffer(Object store, byte[] source, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { if (store instanceof Buffer) { Buffer buffer = (Buffer) store; buffer.clear(); if (buffer instanceof ByteBuffer) { - populateByteBuffer((ByteBuffer) buffer, source, length, byteOffset, byteStride, numComponents, componentSize); + populateByteBuffer((ByteBuffer) buffer, source, length, byteOffset, byteStride, numComponents, format); return; } LittleEndien stream = getStream(source); if (buffer instanceof ShortBuffer) { - populateShortBuffer((ShortBuffer) buffer, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateShortBuffer((ShortBuffer) buffer, stream, length, byteOffset, byteStride, numComponents, format); } else if (buffer instanceof IntBuffer) { - populateIntBuffer((IntBuffer) buffer, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateIntBuffer((IntBuffer) buffer, stream, length, byteOffset, byteStride, numComponents, format); } else if (buffer instanceof FloatBuffer) { - populateFloatBuffer((FloatBuffer) buffer, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateFloatBuffer((FloatBuffer) buffer, stream, length, byteOffset, byteStride, numComponents, format); } buffer.rewind(); return; } LittleEndien stream = getStream(source); if (store instanceof short[]) { - populateShortArray((short[]) store, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateShortArray((short[]) store, stream, length, byteOffset, byteStride, numComponents, format); } else if (store instanceof float[]) { - populateFloatArray((float[]) store, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateFloatArray((float[]) store, stream, length, byteOffset, byteStride, numComponents, format); } else if (store instanceof Vector3f[]) { - populateVector3fArray((Vector3f[]) store, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateVector3fArray((Vector3f[]) store, stream, length, byteOffset, byteStride, numComponents, format); } else if (store instanceof Quaternion[]) { - populateQuaternionArray((Quaternion[]) store, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateQuaternionArray((Quaternion[]) store, stream, length, byteOffset, byteStride, numComponents, format); } else if (store instanceof Matrix4f[]) { - populateMatrix4fArray((Matrix4f[]) store, stream, length, byteOffset, byteStride, numComponents, componentSize); + populateMatrix4fArray((Matrix4f[]) store, stream, length, byteOffset, byteStride, numComponents, format); } } - private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents, int componentSize) { + private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) { + int componentSize = format.getComponentSize(); int index = byteOffset; while (index < length + byteOffset) { for (int i = 0; i < numComponents; i++) { @@ -282,7 +283,8 @@ public class GltfUtils { } } - private static void populateShortBuffer(ShortBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateShortBuffer(ShortBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; int end = length * componentSize + byteOffset; stream.skipBytes(byteOffset); @@ -294,7 +296,8 @@ public class GltfUtils { } } - private static void populateIntBuffer(IntBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateIntBuffer(IntBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; int end = length * componentSize + byteOffset; stream.skipBytes(byteOffset); @@ -306,19 +309,50 @@ public class GltfUtils { } } - private static void populateFloatBuffer(FloatBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateFloatBuffer(FloatBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; int end = length * componentSize + byteOffset; stream.skipBytes(byteOffset); while (index < end) { for (int i = 0; i < numComponents; i++) { - buffer.put(stream.readFloat()); + buffer.put(readAsFloat(stream, format)); } index += Math.max(componentSize * numComponents, byteStride); } } - private static void populateShortArray(short[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static float readAsFloat(LittleEndien stream, VertexBuffer.Format format) throws IOException { + //We may have packed data so depending on the format, we need to read data differently and unpack it + // Implementations must use following equations to get corresponding floating-point value f from a normalized integer c and vise-versa: + // accessor.componentType int-to-float float-to-int + // 5120 (BYTE) f = max(c / 127.0, -1.0) c = round(f * 127.0) + // 5121 (UNSIGNED_BYTE) f = c / 255.0 c = round(f * 255.0) + // 5122 (SHORT) f = max(c / 32767.0, -1.0) c = round(f * 32767.0) + // 5123 (UNSIGNED_SHORT) f = c / 65535.0 c = round(f * 65535.0) + byte b; + switch (format) { + case Byte: + b = stream.readByte(); + return Math.max((float) b / 127f, -1f); + case UnsignedByte: + b = stream.readByte(); + return (float) b / 255f; + case Short: + b = stream.readByte(); + return Math.max((float) b / 32767f, -1f); + case UnsignedShort: + b = stream.readByte(); + return (float) b / 65535f; + default: + //we have a regular float + return stream.readFloat(); + } + + } + + private static void populateShortArray(short[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; int end = length * componentSize + byteOffset; stream.skipBytes(byteOffset); @@ -404,14 +438,15 @@ public class GltfUtils { mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, BufferUtils.createFloatBuffer(weightsArray)); } - private static void populateFloatArray(float[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateFloatArray(float[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; 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(); + array[arrayIndex] = readAsFloat(stream, format); arrayIndex++; } @@ -419,16 +454,17 @@ public class GltfUtils { } } - private static void populateVector3fArray(Vector3f[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateVector3fArray(Vector3f[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; int end = length * componentSize + byteOffset; stream.skipBytes(byteOffset); int arrayIndex = 0; while (index < end) { array[arrayIndex] = new Vector3f( - stream.readFloat(), - stream.readFloat(), - stream.readFloat() + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format) ); arrayIndex++; @@ -437,17 +473,18 @@ public class GltfUtils { } } - private static void populateQuaternionArray(Quaternion[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateQuaternionArray(Quaternion[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; 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() + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format) ); arrayIndex++; @@ -456,7 +493,8 @@ public class GltfUtils { } } - private static void populateMatrix4fArray(Matrix4f[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, int componentSize) throws IOException { + private static void populateMatrix4fArray(Matrix4f[] array, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); int index = byteOffset; int end = length * componentSize + byteOffset; stream.skipBytes(byteOffset); @@ -464,22 +502,22 @@ public class GltfUtils { while (index < end) { array[arrayIndex] = toRowMajor( - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat(), - stream.readFloat() + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format), + readAsFloat(stream, format) ); //gltf matrix are column major, JME ones are row major. @@ -496,10 +534,6 @@ public class GltfUtils { return new Matrix4f(m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32, m03, m13, m23, m33); } - public static Matrix4f toRowMajor(float[] a) { - return new Matrix4f(a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[5], a[10], a[14], a[3], a[7], a[11], a[15]); - } - public static GltfModelKey getKey(AssetInfo info) { if (info.getKey() instanceof GltfModelKey) { return (GltfModelKey) info.getKey();