Explorar el Código

glTF. support for glb files (binary self contained gltf)

Nehon hace 8 años
padre
commit
2b014d194c

+ 3 - 2
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> T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException {
+    public <T> 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> T readExtension(String name, JsonElement el, T input) throws AssetLoadException {
+    private <T> T readExtension(String name, JsonElement el, T input) throws AssetLoadException, IOException {
         JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
         if (extensions == null) {
             return input;

+ 3 - 1
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;
 
 }

+ 58 - 0
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<byte[]> 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);
+    }
+
+}

+ 49 - 23
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> {
         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());
         }
     }
+
 }
 

+ 24 - 3
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;

+ 3 - 1
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