Added support for loading "extras" from gltf files. Added some documentation

fix-456
Nehon 8 years ago committed by Rémy Bouquet
parent 5e79ae3c36
commit 7166906908
  1. 33
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
  2. 14
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java
  3. 25
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtrasLoader.java
  4. 45
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  5. 37
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java
  6. 28
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java

@ -56,7 +56,13 @@ public class CustomContentManager {
} }
} }
public <T> T readExtension(JsonElement el, T input) throws AssetLoadException { public <T> 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> T readExtension(String name, JsonElement el, T input) throws AssetLoadException {
JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions"); JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
if (extensions == null) { if (extensions == null) {
return input; return input;
@ -77,7 +83,7 @@ public class CustomContentManager {
} }
try { try {
return (T) loader.handleExtension(gltfLoader, el, ext.getValue(), input); return (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input);
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new AssetLoadException("Extension loader " + loader.getClass().getName() + " for extension " + ext.getKey() + " is incompatible with type " + input.getClass(), 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; return input;
} }
private <T> 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);
}
}
} }

@ -3,10 +3,22 @@ package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
/** /**
* Base Interface for extension loading implementation.
*
* Created by Nehon on 20/08/2017. * Created by Nehon on 20/08/2017.
*/ */
public interface ExtensionLoader { 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);
} }

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

@ -121,7 +121,7 @@ public class GltfLoader implements AssetLoader {
readScenes(defaultScene, rootNode); readScenes(defaultScene, rootNode);
rootNode = customContentManager.readExtension(docRoot, rootNode); rootNode = customContentManager.readExtensionAndExtras("root", docRoot, rootNode);
setupControls(); setupControls();
@ -163,7 +163,7 @@ public class GltfLoader implements AssetLoader {
sceneNode.setName(getAsString(scene.getAsJsonObject(), "name")); sceneNode.setName(getAsString(scene.getAsJsonObject(), "name"));
JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes"); JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes");
sceneNode = customContentManager.readExtension(scene, sceneNode); sceneNode = customContentManager.readExtensionAndExtras("scene", scene, sceneNode);
rootNode.attachChild(sceneNode); rootNode.attachChild(sceneNode);
for (JsonElement node : sceneNodes) { for (JsonElement node : sceneNodes) {
readChild(sceneNode, node); readChild(sceneNode, node);
@ -248,7 +248,7 @@ public class GltfLoader implements AssetLoader {
spatial.setName(getAsString(nodeData.getAsJsonObject(), "name")); spatial.setName(getAsString(nodeData.getAsJsonObject(), "name"));
} }
spatial = customContentManager.readExtension(nodeData, spatial); spatial = customContentManager.readExtensionAndExtras("node", nodeData, spatial);
addToCache("nodes", nodeIndex, spatial, nodes.size()); addToCache("nodes", nodeIndex, spatial, nodes.size());
return spatial; return spatial;
@ -384,7 +384,7 @@ public class GltfLoader implements AssetLoader {
mesh.generateBindPose(); mesh.generateBindPose();
} }
mesh = customContentManager.readExtension(meshObject, mesh); mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh);
Geometry geom = new Geometry(null, mesh); Geometry geom = new Geometry(null, mesh);
@ -415,7 +415,7 @@ public class GltfLoader implements AssetLoader {
//TODO targets(morph anim...) //TODO targets(morph anim...)
} }
geomArray = customContentManager.readExtension(meshData, geomArray); geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray);
addToCache("meshes", meshIndex, geomArray, meshes.size()); addToCache("meshes", meshIndex, geomArray, meshes.size());
return geomArray; return geomArray;
@ -454,7 +454,7 @@ public class GltfLoader implements AssetLoader {
//TODO extras? //TODO extras?
R data = populator.populate(bufferViewIndex, componentType, type, count, byteOffset, normalized); R data = populator.populate(bufferViewIndex, componentType, type, count, byteOffset, normalized);
data = customContentManager.readExtension(accessor, data); data = customContentManager.readExtensionAndExtras("accessor", accessor, data);
return data; return data;
} }
@ -474,7 +474,7 @@ public class GltfLoader implements AssetLoader {
byte[] data = readData(bufferIndex); byte[] data = readData(bufferIndex);
data = customContentManager.readExtension(bufferView, data); data = customContentManager.readExtensionAndExtras("bufferView", bufferView, data);
populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, format); 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"); 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()); addToCache("buffers", bufferIndex, data, buffers.size());
return data; return data;
@ -539,7 +539,7 @@ public class GltfLoader implements AssetLoader {
adapter.init(info.getManager()); adapter.init(info.getManager());
} }
adapter = customContentManager.readExtension(matData, adapter); adapter = customContentManager.readExtensionAndExtras("material", matData, adapter);
if (adapter == null) { if (adapter == null) {
logger.log(Level.WARNING, "Couldn't find any matching material definition for material " + materialIndex); 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); Float zfar = getAsFloat(camData, "zfar", znear * 1000f);
cam.setFrustumPerspective(yfov * FastMath.RAD_TO_DEG, aspectRatio, znear, zfar); cam.setFrustumPerspective(yfov * FastMath.RAD_TO_DEG, aspectRatio, znear, zfar);
cam = customContentManager.readExtension(camData, cam); cam = customContentManager.readExtensionAndExtras("camera.perspective", camData, cam);
} else { } else {
Float xmag = getAsFloat(camData, "xmag"); Float xmag = getAsFloat(camData, "xmag");
@ -609,14 +609,14 @@ public class GltfLoader implements AssetLoader {
Float znear = getAsFloat(camData, "znear"); Float znear = getAsFloat(camData, "znear");
assertNotNull(znear, "No znear for orthographic camere"); assertNotNull(znear, "No znear for orthographic camere");
Float zfar = getAsFloat(camData, "zfar", znear * 1000f); 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.setParallelProjection(true);
cam.setFrustum(znear, zfar, -xmag, xmag, ymag, -ymag); 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()); addToCache("cameras", i, cam, cameras.size());
} }
} }
@ -639,9 +639,9 @@ public class GltfLoader implements AssetLoader {
Integer samplerIndex = getAsInteger(textureData, "sampler"); Integer samplerIndex = getAsInteger(textureData, "sampler");
Texture2D texture2d = readImage(sourceIndex, flip); 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; return texture2d;
} }
@ -673,7 +673,7 @@ public class GltfLoader implements AssetLoader {
result = (Texture2D) tex; result = (Texture2D) tex;
} }
result = customContentManager.readExtension(image, result); result = customContentManager.readExtensionAndExtras("image", image, result);
return result; return result;
@ -728,6 +728,8 @@ public class GltfLoader implements AssetLoader {
logger.log(Level.WARNING, "JME only supports linear interpolation for animations"); 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); float[] times = fetchFromCache("accessors", timeIndex, float[].class);
if (times == null) { if (times == null) {
times = readAccessorData(timeIndex, floatArrayPopulator); times = readAccessorData(timeIndex, floatArrayPopulator);
@ -757,6 +759,7 @@ public class GltfLoader implements AssetLoader {
Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator); Quaternion[] rotations = readAccessorData(dataIndex, quaternionArrayPopulator);
animData.rotations = rotations; animData.rotations = rotations;
} }
animatedNodes[targetNode] = customContentManager.readExtensionAndExtras("channel", channel, animData);
} }
if (name == null) { 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) { if (skinIndex != -1) {
//we have a bone animation. //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) { if (samplers == null) {
throw new AssetLoadException("No samplers defined"); 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.S, wrapS);
texture.setWrap(Texture.WrapAxis.T, wrapT); texture.setWrap(Texture.WrapAxis.T, wrapT);
texture = customContentManager.readExtensionAndExtras("texture.sampler", sampler, texture);
return texture;
} }
public void readSkins() throws IOException { public void readSkins() throws IOException {
@ -917,6 +924,8 @@ public class GltfLoader implements AssetLoader {
skeleton.updateWorldVectors(); skeleton.updateWorldVectors();
} }
skeleton = customContentManager.readExtensionAndExtras("skin", skin, skeleton);
SkinData skinData = new SkinData(); SkinData skinData = new SkinData();
skinData.skeletonControl = new SkeletonControl(skeleton); skinData.skeletonControl = new SkeletonControl(skeleton);
addToCache("skins", index, skinData, nodes.size()); addToCache("skins", index, skinData, nodes.size());

@ -6,12 +6,23 @@ import java.util.HashMap;
import java.util.Map; 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. * Created by Nehon on 08/08/2017.
*/ */
public class GltfModelKey extends ModelKey { public class GltfModelKey extends ModelKey {
private Map<String, MaterialAdapter> materialAdapters = new HashMap<>(); private Map<String, MaterialAdapter> materialAdapters = new HashMap<>();
private static Map<String, ExtensionLoader> extensionLoaders = new HashMap<>(); private static Map<String, ExtensionLoader> extensionLoaders = new HashMap<>();
private ExtrasLoader extrasLoader;
private boolean keepSkeletonPose = false; private boolean keepSkeletonPose = false;
public GltfModelKey(String name) { public GltfModelKey(String name) {
@ -21,10 +32,25 @@ public class GltfModelKey extends ModelKey {
public GltfModelKey() { 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) { public void registerMaterialAdapter(String gltfMaterialName, MaterialAdapter adapter) {
materialAdapters.put(gltfMaterialName, 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) { public void registerExtensionLoader(String extensionName, ExtensionLoader loader) {
extensionLoaders.put(extensionName, loader); extensionLoaders.put(extensionName, loader);
} }
@ -45,5 +71,16 @@ public class GltfModelKey extends ModelKey {
this.keepSkeletonPose = keepSkeletonPose; 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;
}
} }

@ -1,6 +1,7 @@
package com.jme3.scene.plugins.gltf; package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonElement; 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.getAsColor;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
@ -14,15 +15,26 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader {
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter(); private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
@Override @Override
public Object handleExtension(GltfLoader loader, JsonElement parent, JsonElement extension, Object input) { public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) {
materialAdapter.init(loader.getInfo().getManager()); 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")); adapter.init(loader.getInfo().getManager());
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")));
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;
} }
} }

Loading…
Cancel
Save