Added support for extensions, implemented PBR spec gloss extension
This commit is contained in:
parent
e9869e5298
commit
f14acf305b
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package com.jme3.scene.plugins.gltf;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.jme3.asset.AssetLoadException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Created by Nehon on 20/08/2017.
|
||||
*/
|
||||
public class CustomContentManager {
|
||||
|
||||
private final static Logger logger = Logger.getLogger(CustomContentManager.class.getName());
|
||||
|
||||
private GltfModelKey key;
|
||||
private GltfLoader gltfLoader;
|
||||
|
||||
private static Map<String, ExtensionLoader> defaultExtensionLoaders = new HashMap<>();
|
||||
|
||||
static {
|
||||
defaultExtensionLoaders.put("KHR_materials_pbrSpecularGlossiness", new PBRSpecGlossExtensionLoader());
|
||||
}
|
||||
|
||||
void init(GltfLoader gltfLoader) {
|
||||
this.gltfLoader = gltfLoader;
|
||||
|
||||
if (gltfLoader.getInfo().getKey() instanceof GltfModelKey) {
|
||||
this.key = (GltfModelKey) gltfLoader.getInfo().getKey();
|
||||
}
|
||||
|
||||
JsonArray extensionUsed = gltfLoader.getDocRoot().getAsJsonArray("extensionsUsed");
|
||||
if (extensionUsed != null) {
|
||||
for (JsonElement extElem : extensionUsed) {
|
||||
String ext = extElem.getAsString();
|
||||
if (ext != null) {
|
||||
if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) {
|
||||
logger.log(Level.WARNING, "Extension " + ext + " is not supported, please provide your own implementation in the GltfModelKey");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonArray extensionRequired = gltfLoader.getDocRoot().getAsJsonArray("extensionsRequired");
|
||||
if (extensionRequired != null) {
|
||||
for (JsonElement extElem : extensionRequired) {
|
||||
String ext = extElem.getAsString();
|
||||
if (ext != null) {
|
||||
if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) {
|
||||
logger.log(Level.SEVERE, "Extension " + ext + " is mandatory for this file, the loaded scene result will be unexpected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T readExtension(JsonElement el, T input) throws AssetLoadException {
|
||||
JsonElement extensions = el.getAsJsonObject().getAsJsonObject("extensions");
|
||||
if (extensions == null) {
|
||||
return input;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, JsonElement> ext : extensions.getAsJsonObject().entrySet()) {
|
||||
ExtensionLoader loader = null;
|
||||
if (key != null) {
|
||||
loader = key.getExtensionLoader(ext.getKey());
|
||||
}
|
||||
if (loader == null) {
|
||||
loader = defaultExtensionLoaders.get(ext.getKey());
|
||||
}
|
||||
|
||||
if (loader == null) {
|
||||
logger.log(Level.WARNING, "Could not find loader for extension " + ext.getKey());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return (T) loader.handleExtension(gltfLoader, el, ext.getValue(), input);
|
||||
} catch (ClassCastException e) {
|
||||
throw new AssetLoadException("Extension loader " + loader.getClass().getName() + " for extension " + ext.getKey() + " is incompatible with type " + input.getClass(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.jme3.scene.plugins.gltf;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
/**
|
||||
* Created by Nehon on 20/08/2017.
|
||||
*/
|
||||
public interface ExtensionLoader {
|
||||
|
||||
Object handleExtension(GltfLoader loader, JsonElement parent, JsonElement extension, Object input);
|
||||
|
||||
}
|
@ -48,12 +48,15 @@ public class GltfLoader implements AssetLoader {
|
||||
|
||||
private Material defaultMat;
|
||||
private 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");
|
||||
|
||||
Node n = readScenes(defaultScene);
|
||||
readScenes(defaultScene, rootNode);
|
||||
|
||||
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 mat;
|
||||
|
||||
return adapter.getMaterial();
|
||||
}
|
||||
|
||||
private Texture2D readTexture(JsonObject texture) {
|
||||
public Texture2D readTexture(JsonObject texture) {
|
||||
return readTexture(texture, false);
|
||||
|
||||
}
|
||||
|
||||
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,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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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,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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.jme3.scene.plugins.gltf;
|
||||
|
||||
/**
|
||||
* Created by Nehon on 20/08/2017.
|
||||
*/
|
||||
public class PBRMetalRoughMaterialAdapter extends PBRMaterialAdapter {
|
||||
|
||||
public PBRMetalRoughMaterialAdapter() {
|
||||
super();
|
||||
addParamMapping("baseColorFactor", "BaseColor");
|
||||
addParamMapping("baseColorTexture", "BaseColorMap");
|
||||
addParamMapping("metallicFactor", "Metallic");
|
||||
addParamMapping("roughnessFactor", "Roughness");
|
||||
addParamMapping("metallicRoughnessTexture", "MetallicRoughnessMap");
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.jme3.scene.plugins.gltf;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor;
|
||||
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
|
||||
|
||||
/**
|
||||
* Material adapter for PBR Specular Glossiness pipeline
|
||||
* Created by Nehon on 20/08/2017.
|
||||
*/
|
||||
public class PBRSpecGlossExtensionLoader implements ExtensionLoader {
|
||||
|
||||
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
|
||||
|
||||
@Override
|
||||
public Object handleExtension(GltfLoader loader, JsonElement parent, JsonElement extension, Object input) {
|
||||
materialAdapter.init(loader.getInfo().getManager());
|
||||
|
||||
materialAdapter.setParam("diffuseFactor", getAsColor(extension.getAsJsonObject(), "diffuseFactor"));
|
||||
materialAdapter.setParam("specularFactor", getAsColor(extension.getAsJsonObject(), "specularFactor"));
|
||||
materialAdapter.setParam("glossinessFactor", getAsFloat(extension.getAsJsonObject(), "glossinessFactor"));
|
||||
materialAdapter.setParam("diffuseTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("diffuseTexture")));
|
||||
materialAdapter.setParam("specularGlossinessTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("specularGlossinessTexture")));
|
||||
|
||||
return materialAdapter;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.jme3.scene.plugins.gltf;
|
||||
|
||||
import com.jme3.material.MatParam;
|
||||
|
||||
/**
|
||||
* Created by Nehon on 20/08/2017.
|
||||
*/
|
||||
public class PBRSpecGlossMaterialAdapter extends PBRMaterialAdapter {
|
||||
|
||||
public PBRSpecGlossMaterialAdapter() {
|
||||
super();
|
||||
addParamMapping("diffuseFactor", "BaseColor");
|
||||
addParamMapping("diffuseTexture", "BaseColorMap");
|
||||
addParamMapping("specularFactor", "Specular");
|
||||
addParamMapping("glossinessFactor", "Glossiness");
|
||||
addParamMapping("specularGlossinessTexture", "SpecularGlossinessMap");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MatParam adaptMatParam(MatParam param) {
|
||||
getMaterial().setBoolean("UseSpecGloss", true);
|
||||
return super.adaptMatParam(param);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user