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.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;

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

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

@ -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,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

Loading…
Cancel
Save