Sfoglia il codice sorgente

Added support for extensions, implemented PBR spec gloss extension

Nehon 8 anni fa
parent
commit
f14acf305b

+ 29 - 5
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;

+ 9 - 2
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
         }

+ 89 - 0
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<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;
+    }
+
+}

+ 12 - 0
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);
+
+}

+ 161 - 90
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<String, MaterialAdapter> 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> 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> 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");
 
@@ -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> 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);
         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;

+ 11 - 0
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<String, MaterialAdapter> materialAdapters = new HashMap<>();
+    private static Map<String, ExtensionLoader> 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;
     }
+
+
 }

+ 22 - 9
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<String, String> 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());
             }
         }
     }

+ 5 - 10
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);
         }
 
 

+ 16 - 0
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");
+    }
+}

+ 28 - 0
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;
+    }
+}

+ 24 - 0
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);
+    }
+}