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

gradle-4.1
Nehon 7 years ago
parent ff75671162
commit 2b014d194c
  1. 5
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
  2. 4
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java
  3. 58
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GlbLoader.java
  4. 72
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  5. 27
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java
  6. 4
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java

@ -4,6 +4,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetLoadException;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; 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); T output = readExtension(name, el, input);
output = readExtras(name, el, output); output = readExtras(name, el, output);
return 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"); JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
if (extensions == null) { if (extensions == null) {
return input; return input;

@ -2,6 +2,8 @@ package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import java.io.IOException;
/** /**
* Base Interface for extension loading implementation. * 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 * @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 * @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;
} }

@ -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);
}
}

@ -75,6 +75,11 @@ public class GltfLoader implements AssetLoader {
@Override @Override
public Object load(AssetInfo assetInfo) throws IOException { public Object load(AssetInfo assetInfo) throws IOException {
return loadFromStream(assetInfo, assetInfo.openStream());
}
protected Object loadFromStream(AssetInfo assetInfo, InputStream stream) throws IOException {
try { try {
dataCache.clear(); dataCache.clear();
info = assetInfo; info = assetInfo;
@ -87,7 +92,7 @@ public class GltfLoader implements AssetLoader {
defaultMat.setFloat("Roughness", 1f); 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(); JsonObject asset = docRoot.getAsJsonObject().get("asset").getAsJsonObject();
String generator = getAsString(asset, "generator"); String generator = getAsString(asset, "generator");
@ -455,7 +460,7 @@ public class GltfLoader implements AssetLoader {
return data; 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(); JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject();
Integer bufferIndex = getAsInteger(bufferView, "buffer"); Integer bufferIndex = getAsInteger(bufferView, "buffer");
@ -473,8 +478,17 @@ public class GltfLoader implements AssetLoader {
data = customContentManager.readExtensionAndExtras("bufferView", bufferView, data); 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); populateBuffer(store, data, count, byteOffset + bvByteOffset, byteStride, numComponents, format);
return store;
} }
public byte[] readData(int bufferIndex) throws IOException { public byte[] readData(int bufferIndex) throws IOException {
@ -489,6 +503,17 @@ public class GltfLoader implements AssetLoader {
if (data != null) { if (data != null) {
return data; 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 != null) {
if (uri.startsWith("data:")) { if (uri.startsWith("data:")) {
//base 64 embed data //base 64 embed data
@ -505,19 +530,13 @@ public class GltfLoader implements AssetLoader {
input.read(data); input.read(data);
} }
} else { } else {
//no URI we are in a binary file so the data is in the 2nd chunk //no URI this should not happen in a gltf file, only in glb files.
//TODO handle binary GLTF (GLB) throw new AssetLoadException("Buffer " + bufferIndex + " has no uri");
throw new AssetLoadException("Binary gltf is not supported yet");
} }
data = customContentManager.readExtensionAndExtras("buffer", buffer, data);
addToCache("buffers", bufferIndex, data, buffers.size());
return data; 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"); assertNotNull(materials, "There is no material defined yet a mesh references one");
JsonObject matData = materials.get(materialIndex).getAsJsonObject(); JsonObject matData = materials.get(materialIndex).getAsJsonObject();
@ -571,7 +590,7 @@ public class GltfLoader implements AssetLoader {
return adapter.getMaterial(); return adapter.getMaterial();
} }
public void readCameras() { public void readCameras() throws IOException {
if (cameras == null) { if (cameras == null) {
return; 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); return readTexture(texture, false);
} }
public Texture2D readTexture(JsonObject texture, boolean flip) { public Texture2D readTexture(JsonObject texture, boolean flip) throws IOException {
if (texture == null) { if (texture == null) {
return null; return null;
} }
@ -646,18 +665,24 @@ public class GltfLoader implements AssetLoader {
return texture2d; return texture2d;
} }
public Texture2D readImage(int sourceIndex, boolean flip) { public Texture2D readImage(int sourceIndex, boolean flip) throws IOException {
if (images == null) { if (images == null) {
throw new AssetLoadException("No image defined"); throw new AssetLoadException("No image defined");
} }
JsonObject image = images.get(sourceIndex).getAsJsonObject(); JsonObject image = images.get(sourceIndex).getAsJsonObject();
String uri = getAsString(image, "uri"); String uri = getAsString(image, "uri");
Integer bufferView = getAsInteger(image, "bufferView");
String mimeType = getAsString(image, "mimeType");
Texture2D result; Texture2D result;
if (uri == null) { if (uri == null) {
//Image is embed in a buffer not supported yet assertNotNull(bufferView, "Image " + sourceIndex + " should either have an uri or a bufferView");
//TODO support images embed in a buffer assertNotNull(mimeType, "Image " + sourceIndex + " should have a mimeType");
throw new AssetLoadException("Images embed in a buffer are not supported yet"); 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:")) { } else if (uri.startsWith("data:")) {
//base64 encoded image //base64 encoded image
String[] uriInfo = uri.split(","); String[] uriInfo = uri.split(",");
@ -672,11 +697,7 @@ public class GltfLoader implements AssetLoader {
Texture tex = info.getManager().loadTexture(key); Texture tex = info.getManager().loadTexture(key);
result = (Texture2D) tex; result = (Texture2D) tex;
} }
result = customContentManager.readExtensionAndExtras("image", image, result);
return result; return result;
} }
public void readAnimation(int animationIndex) throws IOException { 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) { if (samplers == null) {
throw new AssetLoadException("No samplers defined"); throw new AssetLoadException("No samplers defined");
} }
@ -1225,6 +1246,10 @@ public class GltfLoader implements AssetLoader {
} }
} }
private class TextureData {
byte[] data;
}
private interface Populator<T> { private interface Populator<T> {
T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException; 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()); return new SkinBuffers(data, format.getComponentSize());
} }
} }
} }

@ -253,10 +253,11 @@ public class GltfUtils {
return; return;
} }
LittleEndien stream = getStream(source); 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); populateShortArray((short[]) store, stream, count, byteOffset, byteStride, numComponents, format);
} else } else if (store instanceof float[]) {
if (store instanceof float[]) {
populateFloatArray((float[]) store, stream, count, byteOffset, byteStride, numComponents, format); populateFloatArray((float[]) store, stream, count, byteOffset, byteStride, numComponents, format);
} else if (store instanceof Vector3f[]) { } else if (store instanceof Vector3f[]) {
populateVector3fArray((Vector3f[]) store, stream, count, byteOffset, byteStride, numComponents, format); 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 { 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 componentSize = format.getComponentSize();
int index = byteOffset; int index = byteOffset;

@ -3,6 +3,8 @@ package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.jme3.asset.AssetKey; 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.getAsColor;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
@ -15,7 +17,7 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader {
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter(); private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
@Override @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; MaterialAdapter adapter = materialAdapter;
AssetKey key = loader.getInfo().getKey(); AssetKey key = loader.getInfo().getKey();
//check for a custom adapter for spec/gloss pipeline //check for a custom adapter for spec/gloss pipeline

Loading…
Cancel
Save