Added support for extensions, implemented PBR spec gloss extension

fix-456
Nehon 8 years ago committed by Rémy Bouquet
parent e9869e5298
commit f14acf305b
  1. 34
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag
  2. 11
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md
  3. 89
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
  4. 12
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java
  5. 251
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
  6. 11
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java
  7. 31
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java
  8. 15
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java
  9. 16
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java
  10. 28
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java
  11. 24
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java

@ -55,8 +55,15 @@ varying vec3 wPosition;
#endif #endif
#ifdef SPECGLOSSPIPELINE #ifdef SPECGLOSSPIPELINE
uniform sampler2D m_SpecularMap;
uniform sampler2D m_GlossMap; uniform vec4 m_Specular;
uniform float m_Glossiness;
#ifdef USE_PACKED_SG
uniform sampler2D m_SpecularGlossinessMap;
#else
uniform sampler2D m_SpecularMap;
uniform sampler2D m_GlossinessMap;
#endif
#endif #endif
#ifdef PARALLAXMAP #ifdef PARALLAXMAP
@ -160,9 +167,26 @@ void main(){
float specular = 0.5; float specular = 0.5;
#ifdef SPECGLOSSPIPELINE #ifdef SPECGLOSSPIPELINE
vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
vec4 diffuseColor = albedo; #ifdef USE_PACKED_SG
Roughness = 1.0 - texture2D(m_GlossMap, newTexCoord).r; vec4 specularColor = texture2D(m_SpecularGlossinessMap, newTexCoord);
float glossiness = specularColor.a * m_Glossiness;
specularColor *= m_Specular;
#else
#ifdef SPECULARMAP
vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
#else
vec4 specularColor = vec4(1.0);
#endif
#ifdef GLOSSINESSMAP
float glossiness = texture2D(m_GlossinesMap, newTexCoord).r * m_Glossiness;
#else
float glossiness = m_Glossiness;
#endif
specularColor *= m_Specular;
#endif
vec4 diffuseColor = albedo * (1.0 - max(max(specularColor.r, specularColor.g), specularColor.b));
Roughness = 1.0 - glossiness;
#else #else
float nonMetalSpec = 0.08 * specular; float nonMetalSpec = 0.08 * specular;
vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic; vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic;

@ -40,8 +40,12 @@ MaterialDef PBR Lighting {
Float NormalType : -1.0 Float NormalType : -1.0
// For Spec gloss pipeline // For Spec gloss pipeline
Boolean UseSpecGloss
Texture2D SpecularMap Texture2D SpecularMap
Texture2D GlossMap Texture2D GlossinessMap
Texture2D SpecularGlossinessMap
Color Specular : 1.0 1.0 1.0 1.0
Float Glossiness : 1.0
Vector4 ProbeData Vector4 ProbeData
@ -135,7 +139,7 @@ MaterialDef PBR Lighting {
ROUGHNESSMAP : RoughnessMap ROUGHNESSMAP : RoughnessMap
EMISSIVEMAP : EmissiveMap EMISSIVEMAP : EmissiveMap
EMISSIVE : Emissive EMISSIVE : Emissive
SPECGLOSSPIPELINE : SpecularMap SPECGLOSSPIPELINE : UseSpecGloss
PARALLAXMAP : ParallaxMap PARALLAXMAP : ParallaxMap
NORMALMAP_PARALLAX : PackedNormalParallax NORMALMAP_PARALLAX : PackedNormalParallax
STEEP_PARALLAX : SteepParallax STEEP_PARALLAX : SteepParallax
@ -145,6 +149,9 @@ MaterialDef PBR Lighting {
NUM_BONES : NumberOfBones NUM_BONES : NumberOfBones
INSTANCING : UseInstancing INSTANCING : UseInstancing
USE_PACKED_MR: MetallicRoughnessMap USE_PACKED_MR: MetallicRoughnessMap
USE_PACKED_SG: SpecularGlossinessMap
SPECULARMAP : SpecularMap
GLOSSINESSMAP : GlossinessMap
NORMAL_TYPE: NormalType NORMAL_TYPE: NormalType
VERTEX_COLOR : UseVertexColor VERTEX_COLOR : UseVertexColor
} }

@ -0,0 +1,89 @@
package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.jme3.asset.AssetLoadException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by Nehon on 20/08/2017.
*/
public class CustomContentManager {
private final static Logger logger = Logger.getLogger(CustomContentManager.class.getName());
private GltfModelKey key;
private GltfLoader gltfLoader;
private static Map<String, ExtensionLoader> defaultExtensionLoaders = new HashMap<>();
static {
defaultExtensionLoaders.put("KHR_materials_pbrSpecularGlossiness", new PBRSpecGlossExtensionLoader());
}
void init(GltfLoader gltfLoader) {
this.gltfLoader = gltfLoader;
if (gltfLoader.getInfo().getKey() instanceof GltfModelKey) {
this.key = (GltfModelKey) gltfLoader.getInfo().getKey();
}
JsonArray extensionUsed = gltfLoader.getDocRoot().getAsJsonArray("extensionsUsed");
if (extensionUsed != null) {
for (JsonElement extElem : extensionUsed) {
String ext = extElem.getAsString();
if (ext != null) {
if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) {
logger.log(Level.WARNING, "Extension " + ext + " is not supported, please provide your own implementation in the GltfModelKey");
}
}
}
}
JsonArray extensionRequired = gltfLoader.getDocRoot().getAsJsonArray("extensionsRequired");
if (extensionRequired != null) {
for (JsonElement extElem : extensionRequired) {
String ext = extElem.getAsString();
if (ext != null) {
if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) {
logger.log(Level.SEVERE, "Extension " + ext + " is mandatory for this file, the loaded scene result will be unexpected.");
}
}
}
}
}
public <T> T readExtension(JsonElement el, T input) throws AssetLoadException {
JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
if (extensions == null) {
return input;
}
for (Map.Entry<String, JsonElement> ext : extensions.getAsJsonObject().entrySet()) {
ExtensionLoader loader = null;
if (key != null) {
loader = key.getExtensionLoader(ext.getKey());
}
if (loader == null) {
loader = defaultExtensionLoaders.get(ext.getKey());
}
if (loader == null) {
logger.log(Level.WARNING, "Could not find loader for extension " + ext.getKey());
continue;
}
try {
return (T) loader.handleExtension(gltfLoader, 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);
}
}
return input;
}
}

@ -0,0 +1,12 @@
package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonElement;
/**
* Created by Nehon on 20/08/2017.
*/
public interface ExtensionLoader {
Object handleExtension(GltfLoader loader, JsonElement parent, JsonElement extension, Object input);
}

@ -48,12 +48,15 @@ public class GltfLoader implements AssetLoader {
private Material defaultMat; private Material defaultMat;
private AssetInfo info; private AssetInfo info;
private JsonObject docRoot;
private Node rootNode;
private FloatArrayPopulator floatArrayPopulator = new FloatArrayPopulator(); private FloatArrayPopulator floatArrayPopulator = new FloatArrayPopulator();
private Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator(); private Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator();
private QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator(); private QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator();
private Matrix4fArrayPopulator matrix4fArrayPopulator = new Matrix4fArrayPopulator(); private Matrix4fArrayPopulator matrix4fArrayPopulator = new Matrix4fArrayPopulator();
private static Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>(); private static Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>();
private CustomContentManager customContentManager = new CustomContentManager();
private boolean useNormalsFlag = false; private boolean useNormalsFlag = false;
private Quaternion tmpQuat = new Quaternion(); private Quaternion tmpQuat = new Quaternion();
private Transform tmpTransforms = new Transform(); private Transform tmpTransforms = new Transform();
@ -64,7 +67,7 @@ public class GltfLoader implements AssetLoader {
IntMap<SkinBuffers> skinBuffers = new IntMap<>(); IntMap<SkinBuffers> skinBuffers = new IntMap<>();
static { static {
defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMaterialAdapter()); defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMetalRoughMaterialAdapter());
} }
@Override @Override
@ -72,6 +75,7 @@ public class GltfLoader implements AssetLoader {
try { try {
dataCache.clear(); dataCache.clear();
info = assetInfo; info = assetInfo;
rootNode = new Node();
if (defaultMat == null) { if (defaultMat == null) {
defaultMat = new Material(assetInfo.getManager(), "Common/MatDefs/Light/PBRLighting.j3md"); defaultMat = new Material(assetInfo.getManager(), "Common/MatDefs/Light/PBRLighting.j3md");
@ -80,9 +84,9 @@ public class GltfLoader implements AssetLoader {
defaultMat.setFloat("Roughness", 1f); defaultMat.setFloat("Roughness", 1f);
} }
JsonObject root = new JsonParser().parse(new JsonReader(new InputStreamReader(assetInfo.openStream()))).getAsJsonObject(); docRoot = new JsonParser().parse(new JsonReader(new InputStreamReader(assetInfo.openStream()))).getAsJsonObject();
JsonObject asset = root.getAsJsonObject().get("asset").getAsJsonObject(); JsonObject asset = docRoot.getAsJsonObject().get("asset").getAsJsonObject();
String generator = getAsString(asset, "generator"); String generator = getAsString(asset, "generator");
String version = getAsString(asset, "version"); String version = getAsString(asset, "version");
String minVersion = getAsString(asset, "minVersion"); String minVersion = getAsString(asset, "minVersion");
@ -91,51 +95,62 @@ public class GltfLoader implements AssetLoader {
throw new AssetLoadException("Gltf Loader doesn't support this gltf version: " + version + (minVersion != null ? ("/" + minVersion) : "")); throw new AssetLoadException("Gltf Loader doesn't support this gltf version: " + version + (minVersion != null ? ("/" + minVersion) : ""));
} }
scenes = root.getAsJsonArray("scenes"); scenes = docRoot.getAsJsonArray("scenes");
nodes = root.getAsJsonArray("nodes"); nodes = docRoot.getAsJsonArray("nodes");
meshes = root.getAsJsonArray("meshes"); meshes = docRoot.getAsJsonArray("meshes");
accessors = root.getAsJsonArray("accessors"); accessors = docRoot.getAsJsonArray("accessors");
bufferViews = root.getAsJsonArray("bufferViews"); bufferViews = docRoot.getAsJsonArray("bufferViews");
buffers = root.getAsJsonArray("buffers"); buffers = docRoot.getAsJsonArray("buffers");
materials = root.getAsJsonArray("materials"); materials = docRoot.getAsJsonArray("materials");
textures = root.getAsJsonArray("textures"); textures = docRoot.getAsJsonArray("textures");
images = root.getAsJsonArray("images"); images = docRoot.getAsJsonArray("images");
samplers = root.getAsJsonArray("samplers"); samplers = docRoot.getAsJsonArray("samplers");
animations = root.getAsJsonArray("animations"); animations = docRoot.getAsJsonArray("animations");
skins = root.getAsJsonArray("skins"); skins = docRoot.getAsJsonArray("skins");
customContentManager.init(this);
readSkins(); readSkins();
JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene"); JsonPrimitive defaultScene = docRoot.getAsJsonPrimitive("scene");
readScenes(defaultScene, rootNode);
Node n = readScenes(defaultScene); rootNode = customContentManager.readExtension(docRoot, rootNode);
setupControls(); setupControls();
//only one scene let's not return the root. //only one scene let's not return the root.
if (n.getChildren().size() == 1) { if (rootNode.getChildren().size() == 1) {
n = (Node) n.getChild(0); rootNode = (Node) rootNode.getChild(0);
} }
//no name for the scene... let's set the file name. //no name for the scene... let's set the file name.
if (n.getName() == null) { if (rootNode.getName() == null) {
n.setName(assetInfo.getKey().getName()); rootNode.setName(assetInfo.getKey().getName());
} }
return n; return rootNode;
} catch (Exception e) { } catch (Exception e) {
throw new AssetLoadException("An error occurred loading " + assetInfo.getKey().getName(), e); throw new AssetLoadException("An error occurred loading " + assetInfo.getKey().getName(), e);
} }
} }
private void setDefaultParams(Material mat) {
mat.setColor("BaseColor", ColorRGBA.White);
mat.setFloat("Metallic", 0f);
mat.setFloat("Roughness", 1f);
}
private boolean isSupported(String version, String minVersion) { private boolean isSupported(String version, String minVersion) {
return "2.0".equals(version); return "2.0".equals(version);
} }
private Node readScenes(JsonPrimitive defaultScene) throws IOException { public void readScenes(JsonPrimitive defaultScene, Node rootNode) throws IOException {
if (scenes == null) { if (scenes == null) {
//no scene... lets handle this later... //no scene... lets handle this later...
throw new AssetLoadException("Gltf files with no scene is not yet supported"); throw new AssetLoadException("Gltf files with no scene is not yet supported");
} }
Node root = new Node();
for (JsonElement scene : scenes) { for (JsonElement scene : scenes) {
Node sceneNode = new Node(); Node sceneNode = new Node();
//specs says that only the default scene should be rendered, //specs says that only the default scene should be rendered,
@ -144,10 +159,12 @@ 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");
root.attachChild(sceneNode); sceneNode = customContentManager.readExtension(scene, sceneNode);
rootNode.attachChild(sceneNode);
for (JsonElement node : sceneNodes) { for (JsonElement node : sceneNodes) {
readChild(sceneNode, node); readChild(sceneNode, node);
} }
} }
//Loading animations //Loading animations
@ -162,11 +179,10 @@ public class GltfLoader implements AssetLoader {
if (defaultScene != null) { if (defaultScene != null) {
activeChild = defaultScene.getAsInt(); activeChild = defaultScene.getAsInt();
} }
root.getChild(activeChild).setCullHint(Spatial.CullHint.Inherit); rootNode.getChild(activeChild).setCullHint(Spatial.CullHint.Inherit);
return root;
} }
private Object readNode(int nodeIndex) throws IOException { public Object readNode(int nodeIndex) throws IOException {
Object obj = fetchFromCache("nodes", nodeIndex, Object.class); Object obj = fetchFromCache("nodes", nodeIndex, Object.class);
if (obj != null) { if (obj != null) {
if (obj instanceof BoneWrapper) { if (obj instanceof BoneWrapper) {
@ -222,6 +238,8 @@ public class GltfLoader implements AssetLoader {
spatial.setName(getAsString(nodeData.getAsJsonObject(), "name")); spatial.setName(getAsString(nodeData.getAsJsonObject(), "name"));
} }
spatial = customContentManager.readExtension(nodeData, spatial);
addToCache("nodes", nodeIndex, spatial, nodes.size()); addToCache("nodes", nodeIndex, spatial, nodes.size());
return spatial; return spatial;
} }
@ -248,7 +266,7 @@ public class GltfLoader implements AssetLoader {
} }
private Transform readTransforms(JsonObject nodeData) { public Transform readTransforms(JsonObject nodeData) {
Transform transform = new Transform(); Transform transform = new Transform();
JsonArray matrix = nodeData.getAsJsonArray("matrix"); JsonArray matrix = nodeData.getAsJsonArray("matrix");
if (matrix != null) { if (matrix != null) {
@ -288,7 +306,7 @@ public class GltfLoader implements AssetLoader {
return transform; return transform;
} }
private Geometry[] readMeshPrimitives(int meshIndex) throws IOException { public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
Geometry[] geomArray = (Geometry[]) fetchFromCache("meshes", meshIndex, Object.class); Geometry[] geomArray = (Geometry[]) fetchFromCache("meshes", meshIndex, Object.class);
if (geomArray != null) { if (geomArray != null) {
//cloning the geoms. //cloning the geoms.
@ -355,6 +373,8 @@ public class GltfLoader implements AssetLoader {
mesh.generateBindPose(); mesh.generateBindPose();
} }
mesh = customContentManager.readExtension(meshObject, mesh);
Geometry geom = new Geometry(null, mesh); Geometry geom = new Geometry(null, mesh);
Integer materialIndex = getAsInteger(meshObject, "material"); Integer materialIndex = getAsInteger(meshObject, "material");
@ -384,21 +404,12 @@ public class GltfLoader implements AssetLoader {
//TODO targets(morph anim...) //TODO targets(morph anim...)
} }
geomArray = customContentManager.readExtension(meshData, geomArray);
addToCache("meshes", meshIndex, geomArray, meshes.size()); addToCache("meshes", meshIndex, geomArray, meshes.size());
return geomArray; return geomArray;
} }
public static class WeightData {
float value;
short index;
int componentSize;
public WeightData(float value, short index, int componentSize) {
this.value = value;
this.index = index;
this.componentSize = componentSize;
}
}
private SkinBuffers getSkinBuffers(String bufferType) { private SkinBuffers getSkinBuffers(String bufferType) {
int bufIndex = getIndex(bufferType); int bufIndex = getIndex(bufferType);
@ -410,7 +421,7 @@ public class GltfLoader implements AssetLoader {
return buffs; return buffs;
} }
private <R> R readAccessorData(int accessorIndex, Populator<R> populator) throws IOException { public <R> R readAccessorData(int accessorIndex, Populator<R> populator) throws IOException {
assertNotNull(accessors, "No accessor attribute in the gltf file"); assertNotNull(accessors, "No accessor attribute in the gltf file");
@ -432,10 +443,12 @@ public class GltfLoader implements AssetLoader {
//TODO extensions? //TODO extensions?
//TODO extras? //TODO extras?
return populator.populate(bufferViewIndex, componentType, type, count, byteOffset); R data = populator.populate(bufferViewIndex, componentType, type, count, byteOffset);
data = customContentManager.readExtension(accessor, data);
return data;
} }
private void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Object store, int numComponents, int componentSize) throws IOException { public void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Object store, int numComponents, int componentSize) throws IOException {
JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject();
Integer bufferIndex = getAsInteger(bufferView, "buffer"); Integer bufferIndex = getAsInteger(bufferView, "buffer");
@ -450,14 +463,17 @@ public class GltfLoader implements AssetLoader {
//int target = getAsInteger(bufferView, "target", 0); //int target = getAsInteger(bufferView, "target", 0);
byte[] data = readData(bufferIndex); byte[] data = readData(bufferIndex);
data = customContentManager.readExtension(bufferView, data);
populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, componentSize); populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, componentSize);
//TODO extensions?
//TODO extras? //TODO extras?
} }
private byte[] readData(int bufferIndex) throws IOException { public byte[] readData(int bufferIndex) throws IOException {
assertNotNull(buffers, "No buffer defined"); assertNotNull(buffers, "No buffer defined");
@ -465,25 +481,23 @@ public class GltfLoader implements AssetLoader {
String uri = getAsString(buffer, "uri"); String uri = getAsString(buffer, "uri");
Integer bufferLength = getAsInteger(buffer, "byteLength"); Integer bufferLength = getAsInteger(buffer, "byteLength");
assertNotNull(bufferLength, "No byteLength defined for buffer " + bufferIndex); assertNotNull(bufferLength, "No byteLength defined for buffer " + bufferIndex);
byte[] data = (byte[]) fetchFromCache("buffers", bufferIndex, Object.class);
if (data != null) {
return data;
}
if (uri != null) { if (uri != null) {
if (uri.startsWith("data:")) { if (uri.startsWith("data:")) {
//base 64 embed data //base 64 embed data
return DatatypeConverter.parseBase64Binary(uri.substring(uri.indexOf(",") + 1)); data = DatatypeConverter.parseBase64Binary(uri.substring(uri.indexOf(",") + 1));
} else { } else {
//external file let's load it //external file let's load it
if (!uri.endsWith(".bin")) { if (!uri.endsWith(".bin")) {
throw new AssetLoadException("Cannot load " + uri + ", a .bin extension is required."); throw new AssetLoadException("Cannot load " + uri + ", a .bin extension is required.");
} }
byte[] data = (byte[]) fetchFromCache("buffers", bufferIndex, Object.class);
if (data != null) {
return data;
}
InputStream input = (InputStream) info.getManager().loadAsset(info.getKey().getFolder() + uri); InputStream input = (InputStream) info.getManager().loadAsset(info.getKey().getFolder() + uri);
data = new byte[bufferLength]; data = new byte[bufferLength];
input.read(data); input.read(data);
addToCache("buffers", bufferIndex, data, buffers.size());
return data;
} }
} else { } else {
//no URI we are in a binary file so the data is in the 2nd chunk //no URI we are in a binary file so the data is in the 2nd chunk
@ -491,52 +505,73 @@ 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);
addToCache("buffers", bufferIndex, data, buffers.size());
return data;
} }
private Material readMaterial(int materialIndex) { public Material readMaterial(int materialIndex) {
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();
JsonObject pbrMat = matData.getAsJsonObject("pbrMetallicRoughness"); JsonObject pbrMat = matData.getAsJsonObject("pbrMetallicRoughness");
if (pbrMat == null) {
logger.log(Level.WARNING, "Unable to find any pbrMetallicRoughness material entry in material " + materialIndex + ". Only PBR material is supported for now"); MaterialAdapter adapter = null;
return defaultMat;
if (pbrMat != null) {
adapter = getAdapterForMaterial(info, "pbrMetallicRoughness");
if (adapter == null) {
adapter = defaultMaterialAdapters.get("pbrMetallicRoughness");
}
adapter.init(info.getManager());
} }
MaterialAdapter adapter = getAdapterForMaterial(info, "pbrMetallicRoughness"); adapter = customContentManager.readExtension(matData, adapter);
if (adapter == null) { if (adapter == null) {
logger.log(Level.WARNING, "Couldn't find any matching material definition for material " + materialIndex);
adapter = defaultMaterialAdapters.get("pbrMetallicRoughness"); adapter = defaultMaterialAdapters.get("pbrMetallicRoughness");
adapter.init(info.getManager());
setDefaultParams(adapter.getMaterial());
} }
Material mat = adapter.getMaterial(info.getManager()); if (pbrMat != null) {
mat.setName(getAsString(matData, "name")); adapter.setParam("baseColorFactor", getAsColor(pbrMat, "baseColorFactor", ColorRGBA.White));
adapter.setParam("metallicFactor", getAsFloat(pbrMat, "metallicFactor", 1f));
adapter.setParam("roughnessFactor", getAsFloat(pbrMat, "roughnessFactor", 1f));
adapter.setParam("baseColorTexture", readTexture(pbrMat.getAsJsonObject("baseColorTexture")));
adapter.setParam("metallicRoughnessTexture", readTexture(pbrMat.getAsJsonObject("metallicRoughnessTexture")));
}
adapter.setParam(mat, "baseColorFactor", getAsColor(pbrMat, "baseColorFactor", ColorRGBA.White)); adapter.getMaterial().setName(getAsString(matData, "name"));
adapter.setParam(mat, "metallicFactor", getAsFloat(pbrMat, "metallicFactor", 1f)); adapter.setParam("emissiveFactor", getAsColor(matData, "emissiveFactor", ColorRGBA.Black));
adapter.setParam(mat, "roughnessFactor", getAsFloat(pbrMat, "roughnessFactor", 1f));
adapter.setParam(mat, "emissiveFactor", getAsColor(matData, "emissiveFactor", ColorRGBA.Black));
String alphaMode = getAsString(matData, "alphaMode"); String alphaMode = getAsString(matData, "alphaMode");
adapter.setParam(mat, "alphaMode", alphaMode); adapter.setParam("alphaMode", alphaMode);
if (alphaMode != null && alphaMode.equals("MASK")) { if (alphaMode != null && alphaMode.equals("MASK")) {
adapter.setParam(mat, "alphaCutoff", getAsFloat(matData, "alphaCutoff")); adapter.setParam("alphaCutoff", getAsFloat(matData, "alphaCutoff"));
} }
adapter.setParam(mat, "doubleSided", getAsBoolean(matData, "doubleSided")); adapter.setParam("doubleSided", getAsBoolean(matData, "doubleSided"));
adapter.setParam(mat, "baseColorTexture", readTexture(pbrMat.getAsJsonObject("baseColorTexture")));
adapter.setParam(mat, "metallicRoughnessTexture", readTexture(pbrMat.getAsJsonObject("metallicRoughnessTexture")));
Texture2D normal = readTexture(matData.getAsJsonObject("normalTexture")); Texture2D normal = readTexture(matData.getAsJsonObject("normalTexture"));
adapter.setParam(mat, "normalTexture", normal); adapter.setParam("normalTexture", normal);
if (normal != null) { if (normal != null) {
useNormalsFlag = true; useNormalsFlag = true;
} }
adapter.setParam(mat, "occlusionTexture", readTexture(matData.getAsJsonObject("occlusionTexture"))); adapter.setParam("occlusionTexture", readTexture(matData.getAsJsonObject("occlusionTexture")));
adapter.setParam(mat, "emissiveTexture", readTexture(matData.getAsJsonObject("emissiveTexture"))); adapter.setParam("emissiveTexture", readTexture(matData.getAsJsonObject("emissiveTexture")));
return adapter.getMaterial();
}
public Texture2D readTexture(JsonObject texture) {
return readTexture(texture, false);
return mat;
} }
private Texture2D readTexture(JsonObject texture) { public Texture2D readTexture(JsonObject texture, boolean flip) {
if (texture == null) { if (texture == null) {
return null; return null;
} }
@ -548,19 +583,22 @@ public class GltfLoader implements AssetLoader {
Integer sourceIndex = getAsInteger(textureData, "source"); Integer sourceIndex = getAsInteger(textureData, "source");
Integer samplerIndex = getAsInteger(textureData, "sampler"); Integer samplerIndex = getAsInteger(textureData, "sampler");
Texture2D texture2d = readImage(sourceIndex); Texture2D texture2d = readImage(sourceIndex, flip);
readSampler(samplerIndex, texture2d); readSampler(samplerIndex, texture2d);
texture2d = customContentManager.readExtension(texture, texture2d);
return texture2d; return texture2d;
} }
private Texture2D readImage(int sourceIndex) { public Texture2D readImage(int sourceIndex, boolean flip) {
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");
Texture2D result;
if (uri == null) { if (uri == null) {
//Image is embed in a buffer not supported yet //Image is embed in a buffer not supported yet
//TODO support images embed in a buffer //TODO support images embed in a buffer
@ -571,18 +609,22 @@ public class GltfLoader implements AssetLoader {
byte[] data = DatatypeConverter.parseBase64Binary(uriInfo[1]); byte[] data = DatatypeConverter.parseBase64Binary(uriInfo[1]);
String headerInfo = uriInfo[0].split(";")[0]; String headerInfo = uriInfo[0].split(";")[0];
String extension = headerInfo.split("/")[1]; String extension = headerInfo.split("/")[1];
TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, false); TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, flip);
return (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data)); result = (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data));
} else { } else {
//external file image //external file image
TextureKey key = new TextureKey(info.getKey().getFolder() + uri, false); TextureKey key = new TextureKey(info.getKey().getFolder() + uri, flip);
Texture tex = info.getManager().loadTexture(key); Texture tex = info.getManager().loadTexture(key);
return (Texture2D) tex; result = (Texture2D) tex;
} }
result = customContentManager.readExtension(image, result);
return result;
} }
private void readAnimation(int animationIndex) throws IOException { public void readAnimation(int animationIndex) throws IOException {
JsonObject animation = animations.get(animationIndex).getAsJsonObject(); JsonObject animation = animations.get(animationIndex).getAsJsonObject();
JsonArray channels = animation.getAsJsonArray("channels"); JsonArray channels = animation.getAsJsonArray("channels");
JsonArray samplers = animation.getAsJsonArray("samplers"); JsonArray samplers = animation.getAsJsonArray("samplers");
@ -705,6 +747,8 @@ public class GltfLoader implements AssetLoader {
} }
} }
anim = customContentManager.readExtension(animation, anim);
if (skinIndex != -1) { if (skinIndex != -1) {
//we have a bone animation. //we have a bone animation.
SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class); SkinData skin = fetchFromCache("skins", skinIndex, SkinData.class);
@ -742,7 +786,7 @@ public class GltfLoader implements AssetLoader {
} }
} }
private void readSampler(int samplerIndex, Texture2D texture) { public void readSampler(int samplerIndex, Texture2D texture) {
if (samplers == null) { if (samplers == null) {
throw new AssetLoadException("No samplers defined"); throw new AssetLoadException("No samplers defined");
} }
@ -762,7 +806,7 @@ public class GltfLoader implements AssetLoader {
texture.setWrap(Texture.WrapAxis.T, wrapT); texture.setWrap(Texture.WrapAxis.T, wrapT);
} }
private void readSkins() throws IOException { public void readSkins() throws IOException {
if (skins == null) { if (skins == null) {
//no skins, no bone animation. //no skins, no bone animation.
return; return;
@ -870,7 +914,7 @@ public class GltfLoader implements AssetLoader {
return null; return null;
} }
private Bone readNodeAsBone(int nodeIndex, int boneIndex, int skinIndex, Matrix4f modelBindMatrix) throws IOException { public Bone readNodeAsBone(int nodeIndex, int boneIndex, int skinIndex, Matrix4f modelBindMatrix) throws IOException {
BoneWrapper boneWrapper = fetchFromCache("nodes", nodeIndex, BoneWrapper.class); BoneWrapper boneWrapper = fetchFromCache("nodes", nodeIndex, BoneWrapper.class);
if (boneWrapper != null) { if (boneWrapper != null) {
@ -927,8 +971,11 @@ public class GltfLoader implements AssetLoader {
spatial.removeControl(animControl); spatial.removeControl(animControl);
} }
spatial.addControl(skinData.animControl); if (skinData.animControl != null) {
spatial.addControl(skinData.skeletonControl); spatial.addControl(skinData.animControl);
spatial.addControl(skinData.skeletonControl);
}
} }
} }
@ -937,7 +984,7 @@ public class GltfLoader implements AssetLoader {
return getAsString(meshData, "name"); return getAsString(meshData, "name");
} }
private <T> T fetchFromCache(String name, int index, Class<T> type) { public <T> T fetchFromCache(String name, int index, Class<T> type) {
Object[] data = dataCache.get(name); Object[] data = dataCache.get(name);
if (data == null) { if (data == null) {
return null; return null;
@ -945,7 +992,7 @@ public class GltfLoader implements AssetLoader {
return type.cast(data[index]); return type.cast(data[index]);
} }
private void addToCache(String name, int index, Object object, int maxLength) { public void addToCache(String name, int index, Object object, int maxLength) {
Object[] data = dataCache.get(name); Object[] data = dataCache.get(name);
if (data == null) { if (data == null) {
data = new Object[maxLength]; data = new Object[maxLength];
@ -954,6 +1001,30 @@ public class GltfLoader implements AssetLoader {
data[index] = object; data[index] = object;
} }
public AssetInfo getInfo() {
return info;
}
public JsonObject getDocRoot() {
return docRoot;
}
public Node getRootNode() {
return rootNode;
}
public static class WeightData {
float value;
short index;
int componentSize;
public WeightData(float value, short index, int componentSize) {
this.value = value;
this.index = index;
this.componentSize = componentSize;
}
}
private class AnimData { private class AnimData {
Float length; Float length;
float[] times; float[] times;

@ -11,6 +11,7 @@ import java.util.Map;
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 boolean keepSkeletonPose = false; private boolean keepSkeletonPose = false;
public GltfModelKey(String name) { public GltfModelKey(String name) {
@ -24,10 +25,18 @@ public class GltfModelKey extends ModelKey {
materialAdapters.put(gltfMaterialName, adapter); materialAdapters.put(gltfMaterialName, adapter);
} }
public void registerExtensionLoader(String extensionName, ExtensionLoader loader) {
extensionLoaders.put(extensionName, loader);
}
public MaterialAdapter getAdapterForMaterial(String gltfMaterialName) { public MaterialAdapter getAdapterForMaterial(String gltfMaterialName) {
return materialAdapters.get(gltfMaterialName); return materialAdapters.get(gltfMaterialName);
} }
public ExtensionLoader getExtensionLoader(String extensionName) {
return extensionLoaders.get(extensionName);
}
public boolean isKeepSkeletonPose() { public boolean isKeepSkeletonPose() {
return keepSkeletonPose; return keepSkeletonPose;
} }
@ -35,4 +44,6 @@ public class GltfModelKey extends ModelKey {
public void setKeepSkeletonPose(boolean keepSkeletonPose) { public void setKeepSkeletonPose(boolean keepSkeletonPose) {
this.keepSkeletonPose = keepSkeletonPose; this.keepSkeletonPose = keepSkeletonPose;
} }
} }

@ -20,6 +20,8 @@ import java.util.Map;
public abstract class MaterialAdapter { public abstract class MaterialAdapter {
private Map<String, String> paramsMapping = new HashMap<>(); private Map<String, String> paramsMapping = new HashMap<>();
private Material mat;
private AssetManager assetManager;
/** /**
* Should return the material definition used by this material adapter * Should return the material definition used by this material adapter
@ -28,13 +30,24 @@ public abstract class MaterialAdapter {
*/ */
protected abstract String getMaterialDefPath(); protected abstract String getMaterialDefPath();
protected abstract MatParam adaptMatParam(Material mat, MatParam param); protected abstract MatParam adaptMatParam(MatParam param);
public Material getMaterial(AssetManager assetManager) { protected void init(AssetManager assetManager) {
return new Material(assetManager, getMaterialDefPath()); this.assetManager = assetManager;
} }
public void setParam(Material mat, String gltfParamName, Object value) { void reset() {
mat = null;
}
protected Material getMaterial() {
if (mat == null) {
mat = new Material(assetManager, getMaterialDefPath());
}
return mat;
}
public void setParam(String gltfParamName, Object value) {
String name = getJmeParamName(gltfParamName); String name = getJmeParamName(gltfParamName);
if (name == null || value == null) { if (name == null || value == null) {
//no mapping registered or value is null, let's ignore this param //no mapping registered or value is null, let's ignore this param
@ -42,7 +55,7 @@ public abstract class MaterialAdapter {
} }
MatParam param; MatParam param;
if (value instanceof Texture) { if (value instanceof Texture) {
MatParam defParam = mat.getMaterialDef().getMaterialParam(name); MatParam defParam = getMaterial().getMaterialDef().getMaterialParam(name);
if (defParam == null) { if (defParam == null) {
throw new AssetLoadException("Material definition " + getMaterialDefPath() + " has not param with name" + name); throw new AssetLoadException("Material definition " + getMaterialDefPath() + " has not param with name" + name);
} }
@ -50,15 +63,15 @@ public abstract class MaterialAdapter {
throw new AssetLoadException("param with name" + name + "in material definition " + getMaterialDefPath() + " should be a texture param"); throw new AssetLoadException("param with name" + name + "in material definition " + getMaterialDefPath() + " should be a texture param");
} }
param = new MatParamTexture(VarType.Texture2D, name, (Texture) value, ((MatParamTexture) defParam).getColorSpace()); param = new MatParamTexture(VarType.Texture2D, name, (Texture) value, ((MatParamTexture) defParam).getColorSpace());
param = adaptMatParam(mat, param); param = adaptMatParam(param);
if (param != null) { if (param != null) {
mat.setTextureParam(param.getName(), param.getVarType(), (Texture) param.getValue()); getMaterial().setTextureParam(param.getName(), param.getVarType(), (Texture) param.getValue());
} }
} else { } else {
param = new MatParam(getVarType(value), name, value); param = new MatParam(getVarType(value), name, value);
param = adaptMatParam(mat, param); param = adaptMatParam(param);
if (param != null) { if (param != null) {
mat.setParam(param.getName(), param.getVarType(), param.getValue()); getMaterial().setParam(param.getName(), param.getVarType(), param.getValue());
} }
} }
} }

@ -5,15 +5,10 @@ import com.jme3.material.*;
/** /**
* Created by Nehon on 08/08/2017. * Created by Nehon on 08/08/2017.
*/ */
public class PBRMaterialAdapter extends MaterialAdapter { public abstract class PBRMaterialAdapter extends MaterialAdapter {
public PBRMaterialAdapter() { public PBRMaterialAdapter() {
addParamMapping("baseColorFactor", "BaseColor");
addParamMapping("baseColorTexture", "BaseColorMap");
addParamMapping("metallicFactor", "Metallic");
addParamMapping("roughnessFactor", "Roughness");
addParamMapping("metallicRoughnessTexture", "MetallicRoughnessMap");
addParamMapping("normalTexture", "NormalMap"); addParamMapping("normalTexture", "NormalMap");
addParamMapping("occlusionTexture", "LightMap"); addParamMapping("occlusionTexture", "LightMap");
addParamMapping("emissiveTexture", "EmissiveMap"); addParamMapping("emissiveTexture", "EmissiveMap");
@ -29,13 +24,13 @@ public class PBRMaterialAdapter extends MaterialAdapter {
} }
@Override @Override
protected MatParam adaptMatParam(Material mat, MatParam param) { protected MatParam adaptMatParam(MatParam param) {
if (param.getName().equals("alpha")) { if (param.getName().equals("alpha")) {
String alphaMode = (String) param.getValue(); String alphaMode = (String) param.getValue();
switch (alphaMode) { switch (alphaMode) {
case "MASK": case "MASK":
case "BLEND": case "BLEND":
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); getMaterial().getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
} }
return null; return null;
} }
@ -43,13 +38,13 @@ public class PBRMaterialAdapter extends MaterialAdapter {
boolean doubleSided = (boolean) param.getValue(); boolean doubleSided = (boolean) param.getValue();
if (doubleSided) { if (doubleSided) {
//Note that this is not completely right as normals on the back side will be in the wrong direction. //Note that this is not completely right as normals on the back side will be in the wrong direction.
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off); getMaterial().getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
} }
return null; return null;
} }
if (param.getName().equals("NormalMap")) { if (param.getName().equals("NormalMap")) {
//Set the normal map type to OpenGl //Set the normal map type to OpenGl
mat.setFloat("NormalType", 1.0f); getMaterial().setFloat("NormalType", 1.0f);
} }

@ -0,0 +1,16 @@
package com.jme3.scene.plugins.gltf;
/**
* Created by Nehon on 20/08/2017.
*/
public class PBRMetalRoughMaterialAdapter extends PBRMaterialAdapter {
public PBRMetalRoughMaterialAdapter() {
super();
addParamMapping("baseColorFactor", "BaseColor");
addParamMapping("baseColorTexture", "BaseColorMap");
addParamMapping("metallicFactor", "Metallic");
addParamMapping("roughnessFactor", "Roughness");
addParamMapping("metallicRoughnessTexture", "MetallicRoughnessMap");
}
}

@ -0,0 +1,28 @@
package com.jme3.scene.plugins.gltf;
import com.google.gson.JsonElement;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
/**
* Material adapter for PBR Specular Glossiness pipeline
* Created by Nehon on 20/08/2017.
*/
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());
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")));
return materialAdapter;
}
}

@ -0,0 +1,24 @@
package com.jme3.scene.plugins.gltf;
import com.jme3.material.MatParam;
/**
* Created by Nehon on 20/08/2017.
*/
public class PBRSpecGlossMaterialAdapter extends PBRMaterialAdapter {
public PBRSpecGlossMaterialAdapter() {
super();
addParamMapping("diffuseFactor", "BaseColor");
addParamMapping("diffuseTexture", "BaseColorMap");
addParamMapping("specularFactor", "Specular");
addParamMapping("glossinessFactor", "Glossiness");
addParamMapping("specularGlossinessTexture", "SpecularGlossinessMap");
}
@Override
protected MatParam adaptMatParam(MatParam param) {
getMaterial().setBoolean("UseSpecGloss", true);
return super.adaptMatParam(param);
}
}
Loading…
Cancel
Save