From f14acf305bbd46d44a047d6b8b21db7e35a2eb21 Mon Sep 17 00:00:00 2001 From: Nehon Date: Mon, 21 Aug 2017 23:20:28 +0200 Subject: [PATCH] Added support for extensions, implemented PBR spec gloss extension --- .../Common/MatDefs/Light/PBRLighting.frag | 34 ++- .../Common/MatDefs/Light/PBRLighting.j3md | 11 +- .../plugins/gltf/CustomContentManager.java | 89 +++++++ .../scene/plugins/gltf/ExtensionLoader.java | 12 + .../jme3/scene/plugins/gltf/GltfLoader.java | 251 +++++++++++------- .../jme3/scene/plugins/gltf/GltfModelKey.java | 11 + .../scene/plugins/gltf/MaterialAdapter.java | 31 ++- .../plugins/gltf/PBRMaterialAdapter.java | 15 +- .../gltf/PBRMetalRoughMaterialAdapter.java | 16 ++ .../gltf/PBRSpecGlossExtensionLoader.java | 28 ++ .../gltf/PBRSpecGlossMaterialAdapter.java | 24 ++ 11 files changed, 406 insertions(+), 116 deletions(-) create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index afd891885..5503aeaab 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -55,8 +55,15 @@ varying vec3 wPosition; #endif #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 #ifdef PARALLAXMAP @@ -160,9 +167,26 @@ void main(){ float specular = 0.5; #ifdef SPECGLOSSPIPELINE - vec4 specularColor = texture2D(m_SpecularMap, newTexCoord); - vec4 diffuseColor = albedo; - Roughness = 1.0 - texture2D(m_GlossMap, newTexCoord).r; + + #ifdef USE_PACKED_SG + 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 float nonMetalSpec = 0.08 * specular; vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md index 4108ba53c..584bf388f 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md @@ -40,8 +40,12 @@ MaterialDef PBR Lighting { Float NormalType : -1.0 // For Spec gloss pipeline + Boolean UseSpecGloss Texture2D SpecularMap - Texture2D GlossMap + Texture2D GlossinessMap + Texture2D SpecularGlossinessMap + Color Specular : 1.0 1.0 1.0 1.0 + Float Glossiness : 1.0 Vector4 ProbeData @@ -135,7 +139,7 @@ MaterialDef PBR Lighting { ROUGHNESSMAP : RoughnessMap EMISSIVEMAP : EmissiveMap EMISSIVE : Emissive - SPECGLOSSPIPELINE : SpecularMap + SPECGLOSSPIPELINE : UseSpecGloss PARALLAXMAP : ParallaxMap NORMALMAP_PARALLAX : PackedNormalParallax STEEP_PARALLAX : SteepParallax @@ -145,6 +149,9 @@ MaterialDef PBR Lighting { NUM_BONES : NumberOfBones INSTANCING : UseInstancing USE_PACKED_MR: MetallicRoughnessMap + USE_PACKED_SG: SpecularGlossinessMap + SPECULARMAP : SpecularMap + GLOSSINESSMAP : GlossinessMap NORMAL_TYPE: NormalType VERTEX_COLOR : UseVertexColor } 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 new file mode 100644 index 000000000..a635aa0ea --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java @@ -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 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 readExtension(JsonElement el, T input) throws AssetLoadException { + JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions"); + if (extensions == null) { + return input; + } + + for (Map.Entry 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; + } + +} 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 new file mode 100644 index 000000000..8e563962e --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ExtensionLoader.java @@ -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); + +} 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 5902eb416..efdd9ae4d 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 @@ -48,12 +48,15 @@ public class GltfLoader implements AssetLoader { private Material defaultMat; private AssetInfo info; + private JsonObject docRoot; + private Node rootNode; private FloatArrayPopulator floatArrayPopulator = new FloatArrayPopulator(); private Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator(); private QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator(); private Matrix4fArrayPopulator matrix4fArrayPopulator = new Matrix4fArrayPopulator(); private static Map defaultMaterialAdapters = new HashMap<>(); + private CustomContentManager customContentManager = new CustomContentManager(); private boolean useNormalsFlag = false; private Quaternion tmpQuat = new Quaternion(); private Transform tmpTransforms = new Transform(); @@ -64,7 +67,7 @@ public class GltfLoader implements AssetLoader { IntMap skinBuffers = new IntMap<>(); static { - defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMaterialAdapter()); + defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMetalRoughMaterialAdapter()); } @Override @@ -72,6 +75,7 @@ public class GltfLoader implements AssetLoader { try { dataCache.clear(); info = assetInfo; + rootNode = new Node(); if (defaultMat == null) { defaultMat = new Material(assetInfo.getManager(), "Common/MatDefs/Light/PBRLighting.j3md"); @@ -80,9 +84,9 @@ public class GltfLoader implements AssetLoader { 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 version = getAsString(asset, "version"); 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) : "")); } - scenes = root.getAsJsonArray("scenes"); - nodes = root.getAsJsonArray("nodes"); - meshes = root.getAsJsonArray("meshes"); - accessors = root.getAsJsonArray("accessors"); - bufferViews = root.getAsJsonArray("bufferViews"); - buffers = root.getAsJsonArray("buffers"); - materials = root.getAsJsonArray("materials"); - textures = root.getAsJsonArray("textures"); - images = root.getAsJsonArray("images"); - samplers = root.getAsJsonArray("samplers"); - animations = root.getAsJsonArray("animations"); - skins = root.getAsJsonArray("skins"); + scenes = docRoot.getAsJsonArray("scenes"); + nodes = docRoot.getAsJsonArray("nodes"); + meshes = docRoot.getAsJsonArray("meshes"); + accessors = docRoot.getAsJsonArray("accessors"); + bufferViews = docRoot.getAsJsonArray("bufferViews"); + buffers = docRoot.getAsJsonArray("buffers"); + materials = docRoot.getAsJsonArray("materials"); + textures = docRoot.getAsJsonArray("textures"); + images = docRoot.getAsJsonArray("images"); + samplers = docRoot.getAsJsonArray("samplers"); + animations = docRoot.getAsJsonArray("animations"); + skins = docRoot.getAsJsonArray("skins"); + + + customContentManager.init(this); readSkins(); - JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene"); + JsonPrimitive defaultScene = docRoot.getAsJsonPrimitive("scene"); + + readScenes(defaultScene, rootNode); - Node n = readScenes(defaultScene); + rootNode = customContentManager.readExtension(docRoot, rootNode); setupControls(); //only one scene let's not return the root. - if (n.getChildren().size() == 1) { - n = (Node) n.getChild(0); + if (rootNode.getChildren().size() == 1) { + rootNode = (Node) rootNode.getChild(0); } //no name for the scene... let's set the file name. - if (n.getName() == null) { - n.setName(assetInfo.getKey().getName()); + if (rootNode.getName() == null) { + rootNode.setName(assetInfo.getKey().getName()); } - return n; + return rootNode; } catch (Exception 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) { return "2.0".equals(version); } - private Node readScenes(JsonPrimitive defaultScene) throws IOException { + public void readScenes(JsonPrimitive defaultScene, Node rootNode) throws IOException { if (scenes == null) { //no scene... lets handle this later... throw new AssetLoadException("Gltf files with no scene is not yet supported"); } - Node root = new Node(); + for (JsonElement scene : scenes) { Node sceneNode = new Node(); //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")); JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes"); - root.attachChild(sceneNode); + sceneNode = customContentManager.readExtension(scene, sceneNode); + rootNode.attachChild(sceneNode); for (JsonElement node : sceneNodes) { readChild(sceneNode, node); } + } //Loading animations @@ -162,11 +179,10 @@ public class GltfLoader implements AssetLoader { if (defaultScene != null) { activeChild = defaultScene.getAsInt(); } - root.getChild(activeChild).setCullHint(Spatial.CullHint.Inherit); - return root; + rootNode.getChild(activeChild).setCullHint(Spatial.CullHint.Inherit); } - private Object readNode(int nodeIndex) throws IOException { + public Object readNode(int nodeIndex) throws IOException { Object obj = fetchFromCache("nodes", nodeIndex, Object.class); if (obj != null) { if (obj instanceof BoneWrapper) { @@ -222,6 +238,8 @@ public class GltfLoader implements AssetLoader { spatial.setName(getAsString(nodeData.getAsJsonObject(), "name")); } + spatial = customContentManager.readExtension(nodeData, spatial); + addToCache("nodes", nodeIndex, spatial, nodes.size()); 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(); JsonArray matrix = nodeData.getAsJsonArray("matrix"); if (matrix != null) { @@ -288,7 +306,7 @@ public class GltfLoader implements AssetLoader { return transform; } - private Geometry[] readMeshPrimitives(int meshIndex) throws IOException { + public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { Geometry[] geomArray = (Geometry[]) fetchFromCache("meshes", meshIndex, Object.class); if (geomArray != null) { //cloning the geoms. @@ -355,6 +373,8 @@ public class GltfLoader implements AssetLoader { mesh.generateBindPose(); } + mesh = customContentManager.readExtension(meshObject, mesh); + Geometry geom = new Geometry(null, mesh); Integer materialIndex = getAsInteger(meshObject, "material"); @@ -384,21 +404,12 @@ public class GltfLoader implements AssetLoader { //TODO targets(morph anim...) } + geomArray = customContentManager.readExtension(meshData, geomArray); + addToCache("meshes", meshIndex, geomArray, meshes.size()); 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) { int bufIndex = getIndex(bufferType); @@ -410,7 +421,7 @@ public class GltfLoader implements AssetLoader { return buffs; } - private R readAccessorData(int accessorIndex, Populator populator) throws IOException { + public R readAccessorData(int accessorIndex, Populator populator) throws IOException { assertNotNull(accessors, "No accessor attribute in the gltf file"); @@ -432,10 +443,12 @@ public class GltfLoader implements AssetLoader { //TODO extensions? //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(); Integer bufferIndex = getAsInteger(bufferView, "buffer"); @@ -450,14 +463,17 @@ public class GltfLoader implements AssetLoader { //int target = getAsInteger(bufferView, "target", 0); byte[] data = readData(bufferIndex); + + data = customContentManager.readExtension(bufferView, data); + populateBuffer(store, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents, componentSize); - //TODO extensions? + //TODO extras? } - private byte[] readData(int bufferIndex) throws IOException { + public byte[] readData(int bufferIndex) throws IOException { assertNotNull(buffers, "No buffer defined"); @@ -465,25 +481,23 @@ public class GltfLoader implements AssetLoader { String uri = getAsString(buffer, "uri"); Integer bufferLength = getAsInteger(buffer, "byteLength"); 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.startsWith("data:")) { //base 64 embed data - return DatatypeConverter.parseBase64Binary(uri.substring(uri.indexOf(",") + 1)); + data = DatatypeConverter.parseBase64Binary(uri.substring(uri.indexOf(",") + 1)); } else { //external file let's load it if (!uri.endsWith(".bin")) { 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); data = new byte[bufferLength]; input.read(data); - addToCache("buffers", bufferIndex, data, buffers.size()); - - return data; } } else { //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"); } + 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"); JsonObject matData = materials.get(materialIndex).getAsJsonObject(); 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"); - return defaultMat; + + MaterialAdapter adapter = null; + + 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) { + logger.log(Level.WARNING, "Couldn't find any matching material definition for material " + materialIndex); adapter = defaultMaterialAdapters.get("pbrMetallicRoughness"); + adapter.init(info.getManager()); + setDefaultParams(adapter.getMaterial()); } - Material mat = adapter.getMaterial(info.getManager()); - mat.setName(getAsString(matData, "name")); + if (pbrMat != null) { + 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.setParam(mat, "metallicFactor", getAsFloat(pbrMat, "metallicFactor", 1f)); - adapter.setParam(mat, "roughnessFactor", getAsFloat(pbrMat, "roughnessFactor", 1f)); - adapter.setParam(mat, "emissiveFactor", getAsColor(matData, "emissiveFactor", ColorRGBA.Black)); + adapter.getMaterial().setName(getAsString(matData, "name")); + adapter.setParam("emissiveFactor", getAsColor(matData, "emissiveFactor", ColorRGBA.Black)); String alphaMode = getAsString(matData, "alphaMode"); - adapter.setParam(mat, "alphaMode", alphaMode); + adapter.setParam("alphaMode", alphaMode); 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(mat, "baseColorTexture", readTexture(pbrMat.getAsJsonObject("baseColorTexture"))); - adapter.setParam(mat, "metallicRoughnessTexture", readTexture(pbrMat.getAsJsonObject("metallicRoughnessTexture"))); + adapter.setParam("doubleSided", getAsBoolean(matData, "doubleSided")); Texture2D normal = readTexture(matData.getAsJsonObject("normalTexture")); - adapter.setParam(mat, "normalTexture", normal); + adapter.setParam("normalTexture", normal); if (normal != null) { useNormalsFlag = true; } - adapter.setParam(mat, "occlusionTexture", readTexture(matData.getAsJsonObject("occlusionTexture"))); - adapter.setParam(mat, "emissiveTexture", readTexture(matData.getAsJsonObject("emissiveTexture"))); + adapter.setParam("occlusionTexture", readTexture(matData.getAsJsonObject("occlusionTexture"))); + 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) { return null; } @@ -548,19 +583,22 @@ public class GltfLoader implements AssetLoader { Integer sourceIndex = getAsInteger(textureData, "source"); Integer samplerIndex = getAsInteger(textureData, "sampler"); - Texture2D texture2d = readImage(sourceIndex); + Texture2D texture2d = readImage(sourceIndex, flip); readSampler(samplerIndex, texture2d); + texture2d = customContentManager.readExtension(texture, texture2d); + return texture2d; } - private Texture2D readImage(int sourceIndex) { + public Texture2D readImage(int sourceIndex, boolean flip) { if (images == null) { throw new AssetLoadException("No image defined"); } JsonObject image = images.get(sourceIndex).getAsJsonObject(); String uri = getAsString(image, "uri"); + Texture2D result; if (uri == null) { //Image is embed in a buffer not supported yet //TODO support images embed in a buffer @@ -571,18 +609,22 @@ public class GltfLoader implements AssetLoader { byte[] data = DatatypeConverter.parseBase64Binary(uriInfo[1]); String headerInfo = uriInfo[0].split(";")[0]; String extension = headerInfo.split("/")[1]; - TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, false); - return (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data)); + TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, flip); + result = (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data)); } else { //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); - 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(); JsonArray channels = animation.getAsJsonArray("channels"); JsonArray samplers = animation.getAsJsonArray("samplers"); @@ -705,6 +747,8 @@ public class GltfLoader implements AssetLoader { } } + anim = customContentManager.readExtension(animation, anim); + if (skinIndex != -1) { //we have a bone animation. 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) { throw new AssetLoadException("No samplers defined"); } @@ -762,7 +806,7 @@ public class GltfLoader implements AssetLoader { texture.setWrap(Texture.WrapAxis.T, wrapT); } - private void readSkins() throws IOException { + public void readSkins() throws IOException { if (skins == null) { //no skins, no bone animation. return; @@ -870,7 +914,7 @@ public class GltfLoader implements AssetLoader { 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); if (boneWrapper != null) { @@ -927,8 +971,11 @@ public class GltfLoader implements AssetLoader { spatial.removeControl(animControl); } - spatial.addControl(skinData.animControl); - spatial.addControl(skinData.skeletonControl); + if (skinData.animControl != null) { + spatial.addControl(skinData.animControl); + spatial.addControl(skinData.skeletonControl); + } + } } @@ -937,7 +984,7 @@ public class GltfLoader implements AssetLoader { return getAsString(meshData, "name"); } - private T fetchFromCache(String name, int index, Class type) { + public T fetchFromCache(String name, int index, Class type) { Object[] data = dataCache.get(name); if (data == null) { return null; @@ -945,7 +992,7 @@ public class GltfLoader implements AssetLoader { 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); if (data == null) { data = new Object[maxLength]; @@ -954,6 +1001,30 @@ public class GltfLoader implements AssetLoader { 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 { Float length; float[] times; 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 a75ca38b3..ff12b4d90 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 @@ -11,6 +11,7 @@ import java.util.Map; public class GltfModelKey extends ModelKey { private Map materialAdapters = new HashMap<>(); + private static Map extensionLoaders = new HashMap<>(); private boolean keepSkeletonPose = false; public GltfModelKey(String name) { @@ -24,10 +25,18 @@ public class GltfModelKey extends ModelKey { materialAdapters.put(gltfMaterialName, adapter); } + public void registerExtensionLoader(String extensionName, ExtensionLoader loader) { + extensionLoaders.put(extensionName, loader); + } + public MaterialAdapter getAdapterForMaterial(String gltfMaterialName) { return materialAdapters.get(gltfMaterialName); } + public ExtensionLoader getExtensionLoader(String extensionName) { + return extensionLoaders.get(extensionName); + } + public boolean isKeepSkeletonPose() { return keepSkeletonPose; } @@ -35,4 +44,6 @@ public class GltfModelKey extends ModelKey { public void setKeepSkeletonPose(boolean keepSkeletonPose) { this.keepSkeletonPose = keepSkeletonPose; } + + } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java index 0ba4416cd..9835b84a3 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java @@ -20,6 +20,8 @@ import java.util.Map; public abstract class MaterialAdapter { private Map paramsMapping = new HashMap<>(); + private Material mat; + private AssetManager assetManager; /** * Should return the material definition used by this material adapter @@ -28,13 +30,24 @@ public abstract class MaterialAdapter { */ protected abstract String getMaterialDefPath(); - protected abstract MatParam adaptMatParam(Material mat, MatParam param); + protected abstract MatParam adaptMatParam(MatParam param); - public Material getMaterial(AssetManager assetManager) { - return new Material(assetManager, getMaterialDefPath()); + protected void init(AssetManager assetManager) { + 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); if (name == null || value == null) { //no mapping registered or value is null, let's ignore this param @@ -42,7 +55,7 @@ public abstract class MaterialAdapter { } MatParam param; if (value instanceof Texture) { - MatParam defParam = mat.getMaterialDef().getMaterialParam(name); + MatParam defParam = getMaterial().getMaterialDef().getMaterialParam(name); if (defParam == null) { 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"); } param = new MatParamTexture(VarType.Texture2D, name, (Texture) value, ((MatParamTexture) defParam).getColorSpace()); - param = adaptMatParam(mat, param); + param = adaptMatParam(param); if (param != null) { - mat.setTextureParam(param.getName(), param.getVarType(), (Texture) param.getValue()); + getMaterial().setTextureParam(param.getName(), param.getVarType(), (Texture) param.getValue()); } } else { param = new MatParam(getVarType(value), name, value); - param = adaptMatParam(mat, param); + param = adaptMatParam(param); if (param != null) { - mat.setParam(param.getName(), param.getVarType(), param.getValue()); + getMaterial().setParam(param.getName(), param.getVarType(), param.getValue()); } } } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java index 39a32bd43..2a4d96d51 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java @@ -5,15 +5,10 @@ import com.jme3.material.*; /** * Created by Nehon on 08/08/2017. */ -public class PBRMaterialAdapter extends MaterialAdapter { +public abstract class PBRMaterialAdapter extends MaterialAdapter { public PBRMaterialAdapter() { - addParamMapping("baseColorFactor", "BaseColor"); - addParamMapping("baseColorTexture", "BaseColorMap"); - addParamMapping("metallicFactor", "Metallic"); - addParamMapping("roughnessFactor", "Roughness"); - addParamMapping("metallicRoughnessTexture", "MetallicRoughnessMap"); addParamMapping("normalTexture", "NormalMap"); addParamMapping("occlusionTexture", "LightMap"); addParamMapping("emissiveTexture", "EmissiveMap"); @@ -29,13 +24,13 @@ public class PBRMaterialAdapter extends MaterialAdapter { } @Override - protected MatParam adaptMatParam(Material mat, MatParam param) { + protected MatParam adaptMatParam(MatParam param) { if (param.getName().equals("alpha")) { String alphaMode = (String) param.getValue(); switch (alphaMode) { case "MASK": case "BLEND": - mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); + getMaterial().getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); } return null; } @@ -43,13 +38,13 @@ public class PBRMaterialAdapter extends MaterialAdapter { boolean doubleSided = (boolean) param.getValue(); if (doubleSided) { //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; } if (param.getName().equals("NormalMap")) { //Set the normal map type to OpenGl - mat.setFloat("NormalType", 1.0f); + getMaterial().setFloat("NormalType", 1.0f); } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java new file mode 100644 index 000000000..a40e514d1 --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java @@ -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"); + } +} 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 new file mode 100644 index 000000000..645779877 --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java @@ -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; + } +} diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java new file mode 100644 index 000000000..8edff4148 --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java @@ -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); + } +}