Prechádzať zdrojové kódy

Implemented texture loading and PBR metalness/roughness pipeline support
Fixed some mesh loading issues.

Nehon 8 rokov pred
rodič
commit
40c4f7936d

+ 28 - 19
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag

@@ -9,9 +9,7 @@ varying vec2 texCoord;
   varying vec2 texCoord2;
 #endif
 
-#ifndef BASECOLORMAP
-    varying vec4 Color;
-#endif
+varying vec4 Color;
 
 uniform vec4 g_LightData[NB_LIGHTS];
 
@@ -33,11 +31,16 @@ varying vec3 wPosition;
 #ifdef BASECOLORMAP
   uniform sampler2D m_BaseColorMap;
 #endif
-#ifdef METALLICMAP
-  uniform sampler2D m_MetallicMap;
-#endif
-#ifdef ROUGHNESSMAP
-  uniform sampler2D m_RoughnessMap;
+
+#ifdef USE_PACKED_MR
+     uniform sampler2D m_MetallicRoughnessMap;
+#else
+    #ifdef METALLICMAP
+      uniform sampler2D m_MetallicMap;
+    #endif
+    #ifdef ROUGHNESSMAP
+      uniform sampler2D m_RoughnessMap;
+    #endif
 #endif
 
 #ifdef EMISSIVE
@@ -109,19 +112,26 @@ void main(){
     #endif
     
     #ifdef BASECOLORMAP
-        vec4 albedo = texture2D(m_BaseColorMap, newTexCoord);
+        vec4 albedo = texture2D(m_BaseColorMap, newTexCoord) * Color;
     #else
         vec4 albedo = Color;
     #endif
-    #ifdef ROUGHNESSMAP
-        float Roughness = texture2D(m_RoughnessMap, newTexCoord).r * max(m_Roughness, 1e-8);
-    #else
-        float Roughness =  max(m_Roughness, 1e-8);
-    #endif
-    #ifdef METALLICMAP   
-        float Metallic = texture2D(m_MetallicMap, newTexCoord).r;
+
+    #ifdef USE_PACKED_MR
+        vec2 rm = texture2D(m_MetallicRoughnessMap, newTexCoord).gb;
+        float Roughness = rm.x * max(m_Roughness, 1e-8);
+        float Metallic = rm.y * max(m_Metallic, 0.0);
     #else
-        float Metallic =  max(m_Metallic, 0.0);
+        #ifdef ROUGHNESSMAP
+            float Roughness = texture2D(m_RoughnessMap, newTexCoord).r * max(m_Roughness, 1e-8);
+        #else
+            float Roughness =  max(m_Roughness, 1e-8);
+        #endif
+        #ifdef METALLICMAP
+            float Metallic = texture2D(m_MetallicMap, newTexCoord).r * max(m_Metallic, 0.0);
+        #else
+            float Metallic =  max(m_Metallic, 0.0);
+        #endif
     #endif
  
     float alpha = albedo.a;
@@ -141,7 +151,7 @@ void main(){
       //as it's complient with normal maps generated with blender.
       //see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898
       //for more explanation.
-      vec3 normal = normalize((normalHeight.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0)));
+      vec3 normal = normalize((normalHeight.xyz * vec3(2.0, NORMAL_TYPE * 2.0, 2.0) - vec3(1.0, NORMAL_TYPE * 1.0, 1.0)));
       normal = normalize(tbnMat * normal);
       //normal = normalize(normal * inverse(tbnMat));
     #else
@@ -238,6 +248,5 @@ void main(){
     #endif
            
     gl_FragColor.a = alpha;
-    
    
 }

+ 12 - 5
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md

@@ -6,26 +6,29 @@ MaterialDef PBR Lighting {
         Float AlphaDiscardThreshold (AlphaTestFallOff)
 
         //metalness of the material
-        Float Metallic : 0.0
+        Float Metallic : 1.0
         //Roughness of the material
         Float Roughness : 1.0        
         // Base material color
-        Color BaseColor
+        Color BaseColor : 1.0 1.0 1.0 1.0
         // The emissive color of the object
         Color Emissive        
         // the emissive power
         Float EmissivePower : 3.0        
         // the emissive intensity
-        Float EmissiveIntensity : 1.0
+        Float EmissiveIntensity : 2.0
 
         // BaseColor map
         Texture2D BaseColorMap
 
-        // Specular/gloss map
+        // Metallic map
         Texture2D MetallicMap -LINEAR
         
         // Roughness Map
         Texture2D RoughnessMap -LINEAR
+
+        //Metallic and Roughness are packed respectively in the b and g channel of a single map
+        Texture2D MetallicRoughnessMap -LINEAR
         
         // Texture of the emissive parts of the material
         Texture2D EmissiveMap
@@ -33,6 +36,9 @@ MaterialDef PBR Lighting {
         // Normal map
         Texture2D NormalMap -LINEAR
 
+        //The type of normal map: -1.0 (DirectX), 1.0 (OpenGl)
+        Float NormalType : -1.0
+
         // For Spec gloss pipeline
         Texture2D SpecularMap
         Texture2D GlossMap
@@ -138,7 +144,8 @@ MaterialDef PBR Lighting {
             DISCARD_ALPHA : AlphaDiscardThreshold                        
             NUM_BONES : NumberOfBones                        
             INSTANCING : UseInstancing
-            //INDIRECT_LIGHTING : IntegrateBRDF
+            USE_PACKED_MR: MetallicRoughnessMap
+            NORMAL_TYPE: NormalType
             VERTEX_COLOR : UseVertexColor
         }
     }

+ 2 - 6
jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert

@@ -13,9 +13,7 @@ varying vec2 texCoord;
   attribute vec2 inTexCoord2;
 #endif
 
-#ifndef BASECOLORMAP
-    varying vec4 Color;
-#endif
+varying vec4 Color;
 
 attribute vec3 inPosition;
 attribute vec2 inTexCoord;
@@ -61,9 +59,7 @@ void main(){
       wTangent = vec4(TransformWorldNormal(modelSpaceTan),inTangent.w);
     #endif
 
-    #ifndef BASECOLORMAP
-        Color = m_BaseColor;
-    #endif
+    Color = m_BaseColor;
     
     #ifdef VERTEX_COLOR                    
         Color *= inColor;

+ 2 - 2
jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib

@@ -143,8 +143,8 @@ vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 Spe
 
 vec3 ApproximateSpecularIBLPolynomial(samplerCube envMap, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){
     //TODO magic values should be replaced by defines.
-    float Lod = log2(Roughness) * 1.5 + 6.0 - 1.0;
-    vec3 PrefilteredColor =  textureCubeLod(envMap, refVec.xyz, Lod).rgb;    
+    float Lod = log2(Roughness) * 1.6 + 5.0;
+    vec3 PrefilteredColor =  textureCubeLod(envMap, refVec.xyz, Lod).rgb;
     return PrefilteredColor * EnvDFGPolynomial(SpecularColor, Roughness, ndotv);
 }
 

+ 102 - 21
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

@@ -8,6 +8,8 @@ import com.jme3.material.RenderState;
 import com.jme3.math.*;
 import com.jme3.renderer.queue.RenderQueue;
 import com.jme3.scene.*;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
 
 import java.io.*;
 import java.nio.Buffer;
@@ -35,6 +37,9 @@ public class GltfLoader implements AssetLoader {
     private JsonArray bufferViews;
     private JsonArray buffers;
     private JsonArray materials;
+    private JsonArray textures;
+    private JsonArray images;
+    private JsonArray samplers;
     private Material defaultMat;
     private AssetInfo info;
 
@@ -75,6 +80,9 @@ public class GltfLoader implements AssetLoader {
             bufferViews = root.getAsJsonArray("bufferViews");
             buffers = root.getAsJsonArray("buffers");
             materials = root.getAsJsonArray("materials");
+            textures = root.getAsJsonArray("textures");
+            images = root.getAsJsonArray("images");
+            samplers = root.getAsJsonArray("samplers");
 
             JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene");
 
@@ -123,9 +131,11 @@ public class GltfLoader implements AssetLoader {
             activeChild = defaultScene.getAsInt();
         }
         root.getChild(activeChild).setCullHint(Spatial.CullHint.Inherit);
+        System.err.println(nbPrim + " Geoms loaded");
         return root;
     }
 
+    int nbPrim = 0;
     private Spatial loadNode(int nodeIndex) throws IOException {
         Spatial spatial = fetchFromCache("nodes", nodeIndex, Spatial.class);
         if (spatial != null) {
@@ -134,20 +144,18 @@ public class GltfLoader implements AssetLoader {
             return spatial.clone();
         }
         JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject();
+        JsonArray children = nodeData.getAsJsonArray("children");
         Integer meshIndex = getAsInteger(nodeData, "mesh");
         if (meshIndex != null) {
             if (meshes == null) {
                 throw new AssetLoadException("Can't find any mesh data, yet a node references a mesh");
             }
 
-            //TODO material
-            Material mat = defaultMat;
-
             //there is a mesh in this node, however gltf can split meshes in primitives (some kind of sub meshes),
             //We don't have this in JME so we have to make one mesh and one Geometry for each primitive.
             Geometry[] primitives = loadMeshPrimitives(meshIndex);
-            if (primitives.length > 1) {
-                //only one geometry, let's not wrap it in another node.
+            if (primitives.length == 1 && children == null) {
+                //only one geometry, let's not wrap it in another node unless the node has children.
                 spatial = primitives[0];
             } else {
                 //several geometries, let's make a parent Node and attach them to it
@@ -157,21 +165,23 @@ public class GltfLoader implements AssetLoader {
                 }
                 spatial = node;
             }
-
+            nbPrim += primitives.length;
             spatial.setName(loadMeshName(meshIndex));
 
         } else {
             //no mesh, we have a node. Can be a camera node or a regular node.
             //TODO handle camera nodes?
             Node node = new Node();
-            JsonArray children = nodeData.getAsJsonArray("children");
-            if (children != null) {
-                for (JsonElement child : children) {
-                    node.attachChild(loadNode(child.getAsInt()));
-                }
-            }
+
             spatial = node;
         }
+
+        if (children != null) {
+            for (JsonElement child : children) {
+                ((Node) spatial).attachChild(loadNode(child.getAsInt()));
+            }
+        }
+
         if (spatial.getName() == null) {
             spatial.setName(getAsString(nodeData.getAsJsonObject(), "name"));
         }
@@ -236,6 +246,7 @@ public class GltfLoader implements AssetLoader {
         if (primitives == null) {
             throw new AssetLoadException("Can't find any primitives in mesh " + meshIndex);
         }
+        String name = getAsString(meshData, "name");
 
         geomArray = new Geometry[primitives.size()];
         int index = 0;
@@ -267,11 +278,15 @@ public class GltfLoader implements AssetLoader {
                 }
             }
 
+            if (name != null) {
+                geom.setName(name + (primitives.size() > 1 ? ("_" + index) : ""));
+            }
+
             geom.updateModelBound();
             geomArray[index] = geom;
             index++;
 
-            //TODO material, targets(morph anim...)
+            //TODO targets(morph anim...)
         }
 
         addToCache("meshes", meshIndex, geomArray, meshes.size());
@@ -404,20 +419,86 @@ public class GltfLoader implements AssetLoader {
         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.setParam(mat, "alphaMode", getAsString(matData, "alphaMode"));
-        adapter.setParam(mat, "alphaCutoff", getAsFloat(matData, "alphaCutoff"));
+        String alphaMode = getAsString(matData, "alphaMode");
+        adapter.setParam(mat, "alphaMode", alphaMode);
+        if (alphaMode != null && alphaMode.equals("MASK")) {
+            adapter.setParam(mat, "alphaCutoff", getAsFloat(matData, "alphaCutoff"));
+        }
         adapter.setParam(mat, "doubleSided", getAsBoolean(matData, "doubleSided"));
 
-        //TODO textures
-        //adapter.setParam(mat, "baseColorTexture", readTexture);
-        //adapter.setParam(mat, "metallicRoughnessTexture", readTexture);
-        //adapter.setParam(mat, "normalTexture", readTexture);
-        //adapter.setParam(mat, "occlusionTexture", readTexture);
-        //adapter.setParam(mat, "emissiveTexture", readTexture);
+        adapter.setParam(mat, "baseColorTexture", readTexture(pbrMat.getAsJsonObject("baseColorTexture")));
+        adapter.setParam(mat, "metallicRoughnessTexture", readTexture(pbrMat.getAsJsonObject("metallicRoughnessTexture")));
+        adapter.setParam(mat, "normalTexture", readTexture(matData.getAsJsonObject("normalTexture")));
+        adapter.setParam(mat, "occlusionTexture", readTexture(matData.getAsJsonObject("occlusionTexture")));
+        adapter.setParam(mat, "emissiveTexture", readTexture(matData.getAsJsonObject("emissiveTexture")));
 
         return mat;
     }
 
+    private Texture2D readTexture(JsonObject texture) {
+        if (texture == null) {
+            return null;
+        }
+        Integer textureIndex = getAsInteger(texture, "index");
+        if (textureIndex == null) {
+            throw new AssetLoadException("Texture as no index");
+        }
+        if (textures == null) {
+            throw new AssetLoadException("There are no textures, yet one is referenced by a material");
+        }
+        JsonObject textureData = textures.get(textureIndex).getAsJsonObject();
+        Integer sourceIndex = getAsInteger(textureData, "source");
+        Integer samplerIndex = getAsInteger(textureData, "sampler");
+
+        Texture2D texture2d = loadImage(sourceIndex);
+        readSampler(samplerIndex, texture2d);
+
+        return texture2d;
+    }
+
+    private Texture2D loadImage(int sourceIndex) {
+        if (images == null) {
+            throw new AssetLoadException("No image defined");
+        }
+
+        JsonObject image = images.get(sourceIndex).getAsJsonObject();
+        String uri = getAsString(image, "uri");
+        if (uri == null) {
+            //Image is embed in a buffer not supported yet
+            //TODO support images embed in a buffer
+            throw new AssetLoadException("Images embed in a buffer are not supported yet");
+        } else if (uri.startsWith("data:")) {
+            //base64 encoded image, not supported yet
+            //TODO support base64 encoded images
+            throw new AssetLoadException("Base64 encoded embed images are not supported yet");
+        } else {
+            TextureKey key = new TextureKey(info.getKey().getFolder() + uri, false);
+            Texture tex = info.getManager().loadTexture(key);
+            return (Texture2D) tex;
+        }
+
+    }
+
+    private void readSampler(int samplerIndex, Texture2D texture) {
+        if (samplers == null) {
+            throw new AssetLoadException("No samplers defined");
+        }
+        JsonObject sampler = samplers.get(samplerIndex).getAsJsonObject();
+        Texture.MagFilter magFilter = getMagFilter(getAsInteger(sampler, "magFilter"));
+        Texture.MinFilter minFilter = getMinFilter(getAsInteger(sampler, "minFilter"));
+        Texture.WrapMode wrapS = getWrapMode(getAsInteger(sampler, "wrapS"));
+        Texture.WrapMode wrapT = getWrapMode(getAsInteger(sampler, "wrapT"));
+
+        if (magFilter != null) {
+            texture.setMagFilter(magFilter);
+        }
+        if (minFilter != null) {
+            texture.setMinFilter(minFilter);
+        }
+        texture.setWrap(Texture.WrapAxis.S, wrapS);
+        texture.setWrap(Texture.WrapAxis.T, wrapT);
+    }
+
     private String loadMeshName(int meshIndex) {
         JsonObject meshData = meshes.get(meshIndex).getAsJsonObject();
         return getAsString(meshData, "name");

+ 51 - 1
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

@@ -5,6 +5,7 @@ import com.jme3.asset.AssetLoadException;
 import com.jme3.math.ColorRGBA;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
+import com.jme3.texture.Texture;
 import com.jme3.util.LittleEndien;
 
 import java.io.*;
@@ -116,6 +117,55 @@ public class GltfUtils {
         }
     }
 
+    public static Texture.MagFilter getMagFilter(Integer value) {
+        if (value == null) {
+            return null;
+        }
+        switch (value) {
+            case 9728:
+                return Texture.MagFilter.Nearest;
+            case 9729:
+                return Texture.MagFilter.Bilinear;
+        }
+        return null;
+    }
+
+    public static Texture.MinFilter getMinFilter(Integer value) {
+        if (value == null) {
+            return null;
+        }
+        switch (value) {
+            case 9728:
+                return Texture.MinFilter.NearestNoMipMaps;
+            case 9729:
+                return Texture.MinFilter.BilinearNoMipMaps;
+            case 9984:
+                return Texture.MinFilter.NearestNearestMipMap;
+            case 9985:
+                return Texture.MinFilter.BilinearNearestMipMap;
+            case 9986:
+                return Texture.MinFilter.NearestLinearMipMap;
+            case 9987:
+                return Texture.MinFilter.Trilinear;
+
+        }
+        return null;
+    }
+
+    public static Texture.WrapMode getWrapMode(Integer value) {
+        if (value == null) {
+            return Texture.WrapMode.Repeat;
+        }
+        switch (value) {
+            case 33071:
+                return Texture.WrapMode.EdgeClamp;
+            case 33648:
+                return Texture.WrapMode.MirroredRepeat;
+            default:
+                return Texture.WrapMode.Repeat;
+        }
+    }
+
     public static void padBuffer(Buffer buffer, int bufferSize) {
         buffer.clear();
         if (buffer instanceof IntBuffer) {
@@ -255,7 +305,7 @@ public class GltfUtils {
             return null;
         }
         JsonArray color = el.getAsJsonArray();
-        return new ColorRGBA(color.get(0).getAsFloat(), color.get(1).getAsFloat(), color.get(2).getAsFloat(), color.get(3).getAsFloat());
+        return new ColorRGBA(color.get(0).getAsFloat(), color.get(1).getAsFloat(), color.get(2).getAsFloat(), color.size() > 3 ? color.get(3).getAsFloat() : 1f);
     }
 
     public static ColorRGBA getAsColor(JsonObject parent, String name, ColorRGBA defaultValue) {

+ 1 - 0
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java

@@ -81,6 +81,7 @@ public abstract class MaterialAdapter {
         if (value instanceof Vector2f) return VarType.Vector2;
         if (value instanceof Matrix3f) return VarType.Matrix3;
         if (value instanceof Matrix4f) return VarType.Matrix4;
+        if (value instanceof String) return VarType.Boolean;
         throw new AssetLoadException("Unsupported material parameter type : " + value.getClass().getSimpleName());
     }
 }

+ 6 - 6
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java

@@ -16,10 +16,10 @@ public class PBRMaterialAdapter extends MaterialAdapter {
         addParamMapping("metallicRoughnessTexture", "MetallicRoughnessMap");
         addParamMapping("normalTexture", "NormalMap");
         addParamMapping("occlusionTexture", "LightMap");
-        addParamMapping("emisiveTexture", "EmissiveMap");
-        addParamMapping("emisiveFactor", "Emissive");
+        addParamMapping("emissiveTexture", "EmissiveMap");
+        addParamMapping("emissiveFactor", "Emissive");
         addParamMapping("alphaMode", "alpha");
-        addParamMapping("alphaCutoff", "AlphaDiscardThreshold");
+        //   addParamMapping("alphaCutoff", "AlphaDiscardThreshold");
         addParamMapping("doubleSided", "doubleSided");
     }
 
@@ -47,9 +47,9 @@ public class PBRMaterialAdapter extends MaterialAdapter {
             }
             return null;
         }
-        if (param.getName().equals("MetallicRoughnessMap")) {
-            //use packed Metallic/Roughness
-            mat.setBoolean("UsePackedMR", true);
+        if (param.getName().equals("NormalMap")) {
+            //Set the normal map type to OpenGl
+            mat.setFloat("NormalType", 1.0f);
         }