Implemented texture loading and PBR metalness/roughness pipeline support
Fixed some mesh loading issues.
This commit is contained in:
parent
3bbfabed5e
commit
40c4f7936d
@ -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);
|
||||
|
||||
#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 Roughness = max(m_Roughness, 1e-8);
|
||||
#endif
|
||||
#ifdef METALLICMAP
|
||||
float Metallic = texture2D(m_MetallicMap, newTexCoord).r;
|
||||
#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;
|
||||
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user