glTF. support for glb files (binary self contained gltf)
This commit is contained in:
parent
ff75671162
commit
2b014d194c
@ -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…
x
Reference in New Issue
Block a user