From 716690690803c12d407dd59f4a85528b2315b7ad Mon Sep 17 00:00:00 2001 From: Nehon Date: Thu, 31 Aug 2017 00:40:00 +0200 Subject: [PATCH] Added support for loading "extras" from gltf files. Added some documentation --- .../plugins/gltf/CustomContentManager.java | 33 +++++++++++++- .../scene/plugins/gltf/ExtensionLoader.java | 14 +++++- .../jme3/scene/plugins/gltf/ExtrasLoader.java | 25 +++++++++++ .../jme3/scene/plugins/gltf/GltfLoader.java | 45 +++++++++++-------- .../jme3/scene/plugins/gltf/GltfModelKey.java | 37 +++++++++++++++ .../gltf/PBRSpecGlossExtensionLoader.java | 28 ++++++++---- 6 files changed, 153 insertions(+), 29 deletions(-) create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtrasLoader.java 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 a635aa0ea..5581e1f17 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 @@ -56,7 +56,13 @@ public class CustomContentManager { } } - public T readExtension(JsonElement el, T input) throws AssetLoadException { + public T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException { + T output = readExtension(name, el, input); + output = readExtras(name, el, output); + return output; + } + + private T readExtension(String name, JsonElement el, T input) throws AssetLoadException { JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions"); if (extensions == null) { return input; @@ -77,7 +83,7 @@ public class CustomContentManager { } try { - return (T) loader.handleExtension(gltfLoader, el, ext.getValue(), input); + return (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input); } catch (ClassCastException e) { throw new AssetLoadException("Extension loader " + loader.getClass().getName() + " for extension " + ext.getKey() + " is incompatible with type " + input.getClass(), e); } @@ -86,4 +92,27 @@ public class CustomContentManager { return input; } + private T readExtras(String name, JsonElement el, T input) throws AssetLoadException { + if (key == null) { + return input; + } + ExtrasLoader loader; + loader = key.getExtrasLoader(); + if (loader == null) { + return input; + } + JsonElement extras = el.getAsJsonObject().getAsJsonObject("extras"); + if (extras == null) { + return input; + } + + try { + return (T) loader.handleExtras(gltfLoader, name, el, extras, input); + } catch (ClassCastException e) { + throw new AssetLoadException("Extra loader " + loader.getClass().getName() + " for " + name + " is incompatible with type " + input.getClass(), e); + } + + } + + } 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 8e563962e..d0dc85125 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 @@ -3,10 +3,22 @@ package com.jme3.scene.plugins.gltf; import com.google.gson.JsonElement; /** + * Base Interface for extension loading implementation. + * * Created by Nehon on 20/08/2017. */ public interface ExtensionLoader { - Object handleExtension(GltfLoader loader, JsonElement parent, JsonElement extension, Object input); + /** + * Implement this methods to handle a gltf extension reading + * + * @param loader the GltfLoader with all the data structure. + * @param parentName the name of the element being read + * @param parent the element being read + * @param extension the content of the extension found in the element being read + * @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); } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtrasLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtrasLoader.java new file mode 100644 index 000000000..1f0118b57 --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtrasLoader.java @@ -0,0 +1,25 @@ +package com.jme3.scene.plugins.gltf; + +import com.google.gson.JsonElement; + +/** + * Base Interface for extra loading implementation. + * Created by Nehon on 30/08/2017. + */ +public interface ExtrasLoader { + + /** + * Implement this methods to handle gltf extra reading + * Note that this method will be invoked every time an "extras" element will be found in the gltf file. + * You can check the parentName to know where the "extras" element has been found. + * + * @param loader the GltfLoader with all the data structure. + * @param parentName the name of the element being read + * @param parent the element being read + * @param extras the content of the extras found in the element being read + * @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 extras + */ + Object handleExtras(GltfLoader loader, String parentName, JsonElement parent, JsonElement extras, Object input); + +} 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 6bc0805e3..973e4ccf8 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 @@ -121,7 +121,7 @@ public class GltfLoader implements AssetLoader { readScenes(defaultScene, rootNode); - rootNode = customContentManager.readExtension(docRoot, rootNode); + rootNode = customContentManager.readExtensionAndExtras("root", docRoot, rootNode); setupControls(); @@ -163,7 +163,7 @@ public class GltfLoader implements AssetLoader { sceneNode.setName(getAsString(scene.getAsJsonObject(), "name")); JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes"); - sceneNode = customContentManager.readExtension(scene, sceneNode); + sceneNode = customContentManager.readExtensionAndExtras("scene", scene, sceneNode); rootNode.attachChild(sceneNode); for (JsonElement node : sceneNodes) { readChild(sceneNode, node); @@ -248,7 +248,7 @@ public class GltfLoader implements AssetLoader { spatial.setName(getAsString(nodeData.getAsJsonObject(), "name")); } - spatial = customContentManager.readExtension(nodeData, spatial); + spatial = customContentManager.readExtensionAndExtras("node", nodeData, spatial); addToCache("nodes", nodeIndex, spatial, nodes.size()); return spatial; @@ -384,7 +384,7 @@ public class GltfLoader implements AssetLoader { mesh.generateBindPose(); } - mesh = customContentManager.readExtension(meshObject, mesh); + mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh); Geometry geom = new Geometry(null, mesh); @@ -415,7 +415,7 @@ public class GltfLoader implements AssetLoader { //TODO targets(morph anim...) } - geomArray = customContentManager.readExtension(meshData, geomArray); + geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray); addToCache("meshes", meshIndex, geomArray, meshes.size()); return geomArray; @@ -454,7 +454,7 @@ public class GltfLoader implements AssetLoader { //TODO extras? R data = populator.populate(bufferViewIndex, componentType, type, count, byteOffset, normalized); - data = customContentManager.readExtension(accessor, data); + data = customContentManager.readExtensionAndExtras("accessor", accessor, data); return data; } @@ -474,7 +474,7 @@ public class GltfLoader implements AssetLoader { byte[] data = readData(bufferIndex); - data = customContentManager.readExtension(bufferView, data); + data = customContentManager.readExtensionAndExtras("bufferView", bufferView, data); populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, format); @@ -515,7 +515,7 @@ public class GltfLoader implements AssetLoader { throw new AssetLoadException("Binary gltf is not supported yet"); } - data = customContentManager.readExtension(buffer, data); + data = customContentManager.readExtensionAndExtras("buffer", buffer, data); addToCache("buffers", bufferIndex, data, buffers.size()); return data; @@ -539,7 +539,7 @@ public class GltfLoader implements AssetLoader { adapter.init(info.getManager()); } - adapter = customContentManager.readExtension(matData, adapter); + adapter = customContentManager.readExtensionAndExtras("material", matData, adapter); if (adapter == null) { logger.log(Level.WARNING, "Couldn't find any matching material definition for material " + materialIndex); @@ -599,7 +599,7 @@ public class GltfLoader implements AssetLoader { Float zfar = getAsFloat(camData, "zfar", znear * 1000f); cam.setFrustumPerspective(yfov * FastMath.RAD_TO_DEG, aspectRatio, znear, zfar); - cam = customContentManager.readExtension(camData, cam); + cam = customContentManager.readExtensionAndExtras("camera.perspective", camData, cam); } else { Float xmag = getAsFloat(camData, "xmag"); @@ -609,14 +609,14 @@ public class GltfLoader implements AssetLoader { Float znear = getAsFloat(camData, "znear"); assertNotNull(znear, "No znear for orthographic camere"); Float zfar = getAsFloat(camData, "zfar", znear * 1000f); - assertNotNull(zfar, "No zfar for orthographic camere"); + assertNotNull(zfar, "No zfar for orthographic camera"); cam.setParallelProjection(true); cam.setFrustum(znear, zfar, -xmag, xmag, ymag, -ymag); - cam = customContentManager.readExtension(camData, cam); + cam = customContentManager.readExtensionAndExtras("camera.orthographic", camData, cam); } - + cam = customContentManager.readExtensionAndExtras("camera", camObj, cam); addToCache("cameras", i, cam, cameras.size()); } } @@ -639,9 +639,9 @@ public class GltfLoader implements AssetLoader { Integer samplerIndex = getAsInteger(textureData, "sampler"); Texture2D texture2d = readImage(sourceIndex, flip); - readSampler(samplerIndex, texture2d); + texture2d = readSampler(samplerIndex, texture2d); - texture2d = customContentManager.readExtension(texture, texture2d); + texture2d = customContentManager.readExtensionAndExtras("texture", texture, texture2d); return texture2d; } @@ -673,7 +673,7 @@ public class GltfLoader implements AssetLoader { result = (Texture2D) tex; } - result = customContentManager.readExtension(image, result); + result = customContentManager.readExtensionAndExtras("image", image, result); return result; @@ -728,6 +728,8 @@ public class GltfLoader implements AssetLoader { logger.log(Level.WARNING, "JME only supports linear interpolation for animations"); } + animData = customContentManager.readExtensionAndExtras("animation.sampler", sampler, animData); + float[] times = fetchFromCache("accessors", timeIndex, float[].class); if (times == null) { times = readAccessorData(timeIndex, floatArrayPopulator); @@ -757,6 +759,7 @@ public class GltfLoader implements AssetLoader { Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator); animData.rotations = rotations; } + animatedNodes[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, animData); } if (name == null) { @@ -802,7 +805,7 @@ public class GltfLoader implements AssetLoader { } } - anim = customContentManager.readExtension(animation, anim); + anim = customContentManager.readExtensionAndExtras("animations", animation, anim); if (skinIndex != -1) { //we have a bone animation. @@ -841,7 +844,7 @@ public class GltfLoader implements AssetLoader { } } - public void readSampler(int samplerIndex, Texture2D texture) { + public Texture2D readSampler(int samplerIndex, Texture2D texture) { if (samplers == null) { throw new AssetLoadException("No samplers defined"); } @@ -859,6 +862,10 @@ public class GltfLoader implements AssetLoader { } texture.setWrap(Texture.WrapAxis.S, wrapS); texture.setWrap(Texture.WrapAxis.T, wrapT); + + texture = customContentManager.readExtensionAndExtras("texture.sampler", sampler, texture); + + return texture; } public void readSkins() throws IOException { @@ -917,6 +924,8 @@ public class GltfLoader implements AssetLoader { skeleton.updateWorldVectors(); } + skeleton = customContentManager.readExtensionAndExtras("skin", skin, skeleton); + SkinData skinData = new SkinData(); skinData.skeletonControl = new SkeletonControl(skeleton); addToCache("skins", index, skinData, nodes.size()); diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java index ff12b4d90..c2f13e9c0 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java @@ -6,12 +6,23 @@ import java.util.HashMap; import java.util.Map; /** + * An optional key to use when loading a glTF file + * It allows to specify custom data loader replacing the default ones. + * + * MaterialAdapters: Allows to map glTF standard material model to a non stock material. + * ExtensionLoaders: Allows to provide or override a loader for a given gltf extension. + * ExtrasLoader: Allows to load any extras, application specific data of the gltf file. + * + * For more information, please see glTF 2.0 specifications + * https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md + * * Created by Nehon on 08/08/2017. */ public class GltfModelKey extends ModelKey { private Map materialAdapters = new HashMap<>(); private static Map extensionLoaders = new HashMap<>(); + private ExtrasLoader extrasLoader; private boolean keepSkeletonPose = false; public GltfModelKey(String name) { @@ -21,10 +32,25 @@ public class GltfModelKey extends ModelKey { public GltfModelKey() { } + /** + * Registers a MaterialAdapter for the given materialName. + * The materialName must be "pbrMetallicRoughness" or any name from KHR_materials glTF Extension (for example "pbrSpecularGlossiness" for "KHR_materials_pbrSpecularGlossiness" extension) + * + * @param gltfMaterialName the name of the gltf material + * @param adapter the material adapter + */ public void registerMaterialAdapter(String gltfMaterialName, MaterialAdapter adapter) { materialAdapters.put(gltfMaterialName, adapter); } + /** + * Registers and extension loader for th given extension name. + * For more information on extension please see glTF 2.0 extensions registry + * https://github.com/KhronosGroup/glTF/blob/master/extensions/README.md + * + * @param extensionName the name of the extension + * @param loader the Extension loader + */ public void registerExtensionLoader(String extensionName, ExtensionLoader loader) { extensionLoaders.put(extensionName, loader); } @@ -45,5 +71,16 @@ public class GltfModelKey extends ModelKey { this.keepSkeletonPose = keepSkeletonPose; } + public ExtrasLoader getExtrasLoader() { + return extrasLoader; + } + /** + * Sets the ExtrasLoader for reading any extra information from the gltf file. + * + * @param extrasLoader + */ + public void setExtrasLoader(ExtrasLoader extrasLoader) { + this.extrasLoader = extrasLoader; + } } 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 645779877..2052c9a5d 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 @@ -1,6 +1,7 @@ package com.jme3.scene.plugins.gltf; import com.google.gson.JsonElement; +import com.jme3.asset.AssetKey; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat; @@ -14,15 +15,26 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader { private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter(); @Override - public Object handleExtension(GltfLoader loader, JsonElement parent, JsonElement extension, Object input) { - materialAdapter.init(loader.getInfo().getManager()); + public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { + MaterialAdapter adapter = materialAdapter; + AssetKey key = loader.getInfo().getKey(); + //check for a custom adapter for spec/gloss pipeline + if (key instanceof GltfModelKey) { + GltfModelKey gltfKey = (GltfModelKey) key; + MaterialAdapter ma = gltfKey.getAdapterForMaterial("pbrSpecularGlossiness"); + if (ma != null) { + adapter = ma; + } + } - materialAdapter.setParam("diffuseFactor", getAsColor(extension.getAsJsonObject(), "diffuseFactor")); - materialAdapter.setParam("specularFactor", getAsColor(extension.getAsJsonObject(), "specularFactor")); - materialAdapter.setParam("glossinessFactor", getAsFloat(extension.getAsJsonObject(), "glossinessFactor")); - materialAdapter.setParam("diffuseTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("diffuseTexture"))); - materialAdapter.setParam("specularGlossinessTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("specularGlossinessTexture"))); + adapter.init(loader.getInfo().getManager()); - return materialAdapter; + adapter.setParam("diffuseFactor", getAsColor(extension.getAsJsonObject(), "diffuseFactor")); + adapter.setParam("specularFactor", getAsColor(extension.getAsJsonObject(), "specularFactor")); + adapter.setParam("glossinessFactor", getAsFloat(extension.getAsJsonObject(), "glossinessFactor")); + adapter.setParam("diffuseTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("diffuseTexture"))); + adapter.setParam("specularGlossinessTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("specularGlossinessTexture"))); + + return adapter; } }