diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java index 5581e1f17..ab7a3bc74 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java @@ -4,6 +4,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.jme3.asset.AssetLoadException; +import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; @@ -56,13 +57,13 @@ public class CustomContentManager { } } - public T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException { + public T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException, IOException { T output = readExtension(name, el, input); output = readExtras(name, el, output); return output; } - private T readExtension(String name, JsonElement el, T input) throws AssetLoadException { + private T readExtension(String name, JsonElement el, T input) throws AssetLoadException, IOException { JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions"); if (extensions == null) { return input; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java index d0dc85125..3f32be4bd 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java @@ -2,6 +2,8 @@ package com.jme3.scene.plugins.gltf; import com.google.gson.JsonElement; +import java.io.IOException; + /** * Base Interface for extension loading implementation. * @@ -19,6 +21,6 @@ public interface ExtensionLoader { * @param input an object containing already loaded data from the element, this is most probably a JME object * @return An object of the same type as input, containing the data from the input object and the eventual additional data read from the extension */ - Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input); + Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException; } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GlbLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GlbLoader.java new file mode 100644 index 000000000..7ca499c9d --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GlbLoader.java @@ -0,0 +1,58 @@ +package com.jme3.scene.plugins.gltf; + +import com.jme3.asset.AssetInfo; +import com.jme3.util.LittleEndien; + +import java.io.*; +import java.util.ArrayList; + +/** + * Created by Nehon on 12/09/2017. + */ +public class GlbLoader extends GltfLoader { + + private static final int GLTF_MAGIC = 0x46546C67; + private static final int JSON_TYPE = 0x4E4F534A; + private static final int BIN_TYPE = 0x004E4942; + private ArrayList data = new ArrayList<>(); + + @Override + public Object load(AssetInfo assetInfo) throws IOException { + LittleEndien stream = new LittleEndien(new DataInputStream(assetInfo.openStream())); + int magic = stream.readInt(); + int version = stream.readInt(); + int length = stream.readInt(); + System.err.println(magic == GLTF_MAGIC ? "gltf" : "no no no"); + System.err.println(version); + System.err.println(length); + + byte[] json = null; + + //length is the total size, we have to remove the header size (3 integers = 12 bytes). + length -= 12; + + while (length > 0) { + int chunkLength = stream.readInt(); + int chunkType = stream.readInt(); + if (chunkType == JSON_TYPE) { + json = new byte[chunkLength]; + stream.read(json); + System.err.println(new String(json)); + } else { + byte[] bin = new byte[chunkLength]; + stream.read(bin); + data.add(bin); + } + //8 is the byte size of the 2 ints chunkLength and chunkType. + length -= chunkLength + 8; + } + + return loadFromStream(assetInfo, new ByteArrayInputStream(json)); + } + + @Override + protected byte[] getBytes(int bufferIndex, String uri, Integer bufferLength) throws IOException { + return data.get(bufferIndex); + } + +} 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 6536bdf2f..a97d40017 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 @@ -75,6 +75,11 @@ public class GltfLoader implements AssetLoader { @Override public Object load(AssetInfo assetInfo) throws IOException { + return loadFromStream(assetInfo, assetInfo.openStream()); + } + + + protected Object loadFromStream(AssetInfo assetInfo, InputStream stream) throws IOException { try { dataCache.clear(); info = assetInfo; @@ -87,7 +92,7 @@ public class GltfLoader implements AssetLoader { defaultMat.setFloat("Roughness", 1f); } - docRoot = new JsonParser().parse(new JsonReader(new InputStreamReader(assetInfo.openStream()))).getAsJsonObject(); + docRoot = new JsonParser().parse(new JsonReader(new InputStreamReader(stream))).getAsJsonObject(); JsonObject asset = docRoot.getAsJsonObject().get("asset").getAsJsonObject(); String generator = getAsString(asset, "generator"); @@ -455,7 +460,7 @@ public class GltfLoader implements AssetLoader { return data; } - public void readBuffer(Integer bufferViewIndex, int byteOffset, int count, Object store, int numComponents, VertexBuffer.Format format) throws IOException { + public Object readBuffer(Integer bufferViewIndex, int byteOffset, int count, Object store, int numComponents, VertexBuffer.Format format) throws IOException { JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); Integer bufferIndex = getAsInteger(bufferView, "buffer"); @@ -473,8 +478,17 @@ public class GltfLoader implements AssetLoader { data = customContentManager.readExtensionAndExtras("bufferView", bufferView, data); + if (store == null) { + store = new byte[byteLength]; + } + + if (count == -1) { + count = byteLength; + } + populateBuffer(store, data, count, byteOffset + bvByteOffset, byteStride, numComponents, format); + return store; } public byte[] readData(int bufferIndex) throws IOException { @@ -489,6 +503,17 @@ public class GltfLoader implements AssetLoader { if (data != null) { return data; } + data = getBytes(bufferIndex, uri, bufferLength); + + data = customContentManager.readExtensionAndExtras("buffer", buffer, data); + + addToCache("buffers", bufferIndex, data, buffers.size()); + return data; + + } + + protected byte[] getBytes(int bufferIndex, String uri, Integer bufferLength) throws IOException { + byte[] data; if (uri != null) { if (uri.startsWith("data:")) { //base 64 embed data @@ -505,19 +530,13 @@ public class GltfLoader implements AssetLoader { input.read(data); } } else { - //no URI we are in a binary file so the data is in the 2nd chunk - //TODO handle binary GLTF (GLB) - throw new AssetLoadException("Binary gltf is not supported yet"); + //no URI this should not happen in a gltf file, only in glb files. + throw new AssetLoadException("Buffer " + bufferIndex + " has no uri"); } - - data = customContentManager.readExtensionAndExtras("buffer", buffer, data); - - addToCache("buffers", bufferIndex, data, buffers.size()); return data; - } - public Material readMaterial(int materialIndex) { + public Material readMaterial(int materialIndex) throws IOException { assertNotNull(materials, "There is no material defined yet a mesh references one"); JsonObject matData = materials.get(materialIndex).getAsJsonObject(); @@ -571,7 +590,7 @@ public class GltfLoader implements AssetLoader { return adapter.getMaterial(); } - public void readCameras() { + public void readCameras() throws IOException { if (cameras == null) { return; } @@ -616,12 +635,12 @@ public class GltfLoader implements AssetLoader { } } - public Texture2D readTexture(JsonObject texture) { + public Texture2D readTexture(JsonObject texture) throws IOException { return readTexture(texture, false); } - public Texture2D readTexture(JsonObject texture, boolean flip) { + public Texture2D readTexture(JsonObject texture, boolean flip) throws IOException { if (texture == null) { return null; } @@ -646,18 +665,24 @@ public class GltfLoader implements AssetLoader { return texture2d; } - public Texture2D readImage(int sourceIndex, boolean flip) { + public Texture2D readImage(int sourceIndex, boolean flip) throws IOException { if (images == null) { throw new AssetLoadException("No image defined"); } JsonObject image = images.get(sourceIndex).getAsJsonObject(); String uri = getAsString(image, "uri"); + Integer bufferView = getAsInteger(image, "bufferView"); + String mimeType = getAsString(image, "mimeType"); Texture2D result; if (uri == null) { - //Image is embed in a buffer not supported yet - //TODO support images embed in a buffer - throw new AssetLoadException("Images embed in a buffer are not supported yet"); + assertNotNull(bufferView, "Image " + sourceIndex + " should either have an uri or a bufferView"); + assertNotNull(mimeType, "Image " + sourceIndex + " should have a mimeType"); + byte[] data = (byte[]) readBuffer(bufferView, 0, -1, null, 1, VertexBuffer.Format.Byte); + String extension = mimeType.split("/")[1]; + TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, flip); + result = (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data)); + } else if (uri.startsWith("data:")) { //base64 encoded image String[] uriInfo = uri.split(","); @@ -672,11 +697,7 @@ public class GltfLoader implements AssetLoader { Texture tex = info.getManager().loadTexture(key); result = (Texture2D) tex; } - - result = customContentManager.readExtensionAndExtras("image", image, result); - return result; - } public void readAnimation(int animationIndex) throws IOException { @@ -844,7 +865,7 @@ public class GltfLoader implements AssetLoader { } } - public Texture2D readSampler(int samplerIndex, Texture2D texture) { + public Texture2D readSampler(int samplerIndex, Texture2D texture) throws IOException { if (samplers == null) { throw new AssetLoadException("No samplers defined"); } @@ -1225,6 +1246,10 @@ public class GltfLoader implements AssetLoader { } } + private class TextureData { + byte[] data; + } + private interface Populator { T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException; } @@ -1380,5 +1405,6 @@ public class GltfLoader implements AssetLoader { 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 75d48d06c..b5fd0614a 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 @@ -253,10 +253,11 @@ public class GltfUtils { return; } LittleEndien stream = getStream(source); - if (store instanceof short[]) { + if (store instanceof byte[]) { + populateByteArray((byte[]) store, stream, count, byteOffset, byteStride, numComponents, format); + } else if (store instanceof short[]) { populateShortArray((short[]) store, stream, count, byteOffset, byteStride, numComponents, format); - } else - if (store instanceof float[]) { + } else if (store instanceof float[]) { populateFloatArray((float[]) store, stream, count, byteOffset, byteStride, numComponents, format); } else if (store instanceof Vector3f[]) { populateVector3fArray((Vector3f[]) store, stream, count, byteOffset, byteStride, numComponents, format); @@ -367,6 +368,26 @@ public class GltfUtils { } + private static void populateByteArray(byte[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + int componentSize = format.getComponentSize(); + int index = byteOffset; + int dataLength = componentSize * numComponents; + int stride = Math.max(dataLength, byteStride); + int end = count * stride + byteOffset; + stream.skipBytes(byteOffset); + int arrayIndex = 0; + while (index < end) { + for (int i = 0; i < numComponents; i++) { + array[arrayIndex] = stream.readByte(); + arrayIndex++; + } + if (dataLength < stride) { + stream.skipBytes(stride - dataLength); + } + index += stride; + } + } + private static void populateShortArray(short[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java index 2052c9a5d..f9a9c68ee 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java @@ -3,6 +3,8 @@ package com.jme3.scene.plugins.gltf; import com.google.gson.JsonElement; import com.jme3.asset.AssetKey; +import java.io.IOException; + import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat; @@ -15,7 +17,7 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader { private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter(); @Override - public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { + public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException { MaterialAdapter adapter = materialAdapter; AssetKey key = loader.getInfo().getKey(); //check for a custom adapter for spec/gloss pipeline