diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
index d9c4cbf7d8..5898887e58 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java
@@ -214,7 +214,7 @@ private T readExtension(String name, JsonElement el, T input) throws AssetLo
continue;
}
try {
- return (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input);
+ input = (T) loader.handleExtension(gltfLoader, name, 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);
}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
index 69b0696df3..bbca2de105 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java
@@ -31,6 +31,7 @@
*/
package com.jme3.scene.plugins.gltf;
+import static com.jme3.scene.plugins.gltf.GltfMaterialData.*;
import static com.jme3.scene.plugins.gltf.GltfUtils.assertNotNull;
import static com.jme3.scene.plugins.gltf.GltfUtils.findCommonAncestor;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAdapterForMaterial;
@@ -82,7 +83,7 @@
import com.jme3.asset.AssetLoader;
import com.jme3.asset.TextureKey;
import com.jme3.material.Material;
-import com.jme3.material.RenderState;
+import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f;
@@ -109,6 +110,7 @@
import com.jme3.util.BufferInputStream;
import com.jme3.util.BufferUtils;
import com.jme3.util.IntMap;
+import com.jme3.util.SafeArrayList;
import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
/**
@@ -143,13 +145,20 @@ public class GltfLoader implements AssetLoader {
private final Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator();
private final QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator();
private final Matrix4fArrayPopulator matrix4fArrayPopulator = new Matrix4fArrayPopulator();
- private final Map defaultMaterialAdapters = new HashMap<>();
+ @Deprecated private final Map defaultMaterialAdapters = new HashMap<>();
private final CustomContentManager customContentManager = new CustomContentManager();
private boolean useNormalsFlag = false;
Map> skinnedSpatials = new HashMap<>();
private final IntMap skinBuffers = new IntMap<>();
+ private static SafeArrayList materialFactoryList = new SafeArrayList<>(GltfMaterialFactory.class);
+
+ static {
+ materialFactoryList.add(new UnshadedMaterialFactory());
+ materialFactoryList.add(new PBRLightingMaterialFactory());
+ }
+
public GltfLoader() {
defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMetalRoughMaterialAdapter());
}
@@ -523,12 +532,17 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
Integer materialIndex = getAsInteger(meshObject, "material");
if (materialIndex == null) {
- geom.setMaterial(defaultMat);
+ // Create a new default material
+ Material material = defaultMat.clone();
+ material.setBoolean("UseVertexColor", useVertexColors);
+ geom.setMaterial(material);
+
} else {
useNormalsFlag = false;
- geom.setMaterial(readMaterial(materialIndex));
- if (geom.getMaterial().getAdditionalRenderState()
- .getBlendMode() == RenderState.BlendMode.Alpha) {
+ Material material = readMaterial(materialIndex, useVertexColors);
+ geom.setMaterial(material);
+ BlendMode blendMode = material.getAdditionalRenderState().getBlendMode();
+ if (blendMode == BlendMode.Alpha || blendMode == BlendMode.AlphaAdditive) {
// Alpha blending is enabled for this material. Let's place the geom in the
// transparent bucket.
geom.setQueueBucket(RenderQueue.Bucket.Transparent);
@@ -540,10 +554,6 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
}
}
- if (useVertexColors) {
- geom.getMaterial().setBoolean("UseVertexColor", useVertexColors);
- }
-
geom.setName(name + "_" + index);
geom.updateModelBound();
@@ -802,7 +812,75 @@ protected ByteBuffer getBytes(int bufferIndex, String uri, Integer bufferLength)
return data;
}
- public Material readMaterial(int materialIndex) throws IOException {
+ public Material readMaterial(int materialIndex, boolean usesVertexColors) throws IOException {
+ // Fallback to the old material adapter system, if the legacy flag is set.
+ if (GltfUtils.isMaterialAdaptersEnabled(info)) {
+ return readMaterialUsingMaterialAdapters(materialIndex, usesVertexColors);
+ }
+
+ assertNotNull(materials, "There is no material defined yet a mesh references one");
+ JsonObject materialJson = materials.get(materialIndex).getAsJsonObject();
+
+ GltfMaterialData gltfMaterialData = readStandardMaterialParameters(materialJson);
+ gltfMaterialData.setHasVertexColors(usesVertexColors);
+ gltfMaterialData = customContentManager.readExtensionAndExtras("material", materialJson, gltfMaterialData);
+ return createMaterial(gltfMaterialData, materialIndex);
+ }
+
+ protected GltfMaterialData readStandardMaterialParameters(JsonObject materialJson) throws IOException {
+ GltfMaterialData gltfMaterialData = new GltfMaterialData();
+ gltfMaterialData.setGltfParam(MATERIAL_NAME_PARAM, getAsString(materialJson, "name"));
+
+ JsonObject pbrMetallicRoughnessJson = materialJson.getAsJsonObject("pbrMetallicRoughness");
+ if (pbrMetallicRoughnessJson != null) {
+ gltfMaterialData.setGltfParam(BASE_COLOR_PARAM, getAsColor(pbrMetallicRoughnessJson, "baseColorFactor"));
+ gltfMaterialData.setGltfParam(BASE_COLOR_TEXTURE_PARAM, getAsTexture2D(pbrMetallicRoughnessJson, "baseColorTexture"));
+ gltfMaterialData.setGltfParam(METALLIC_FACTOR_PARAM, getAsFloat(pbrMetallicRoughnessJson, "metallicFactor"));
+ gltfMaterialData.setGltfParam(ROUGHNESS_FACTOR_PARAM, getAsFloat(pbrMetallicRoughnessJson, "roughnessFactor"));
+ gltfMaterialData.setGltfParam(METALLIC_ROUGHNESS_TEXTURE_PARAM, getAsTexture2D(pbrMetallicRoughnessJson, "metallicRoughnessTexture"));
+ }
+
+ JsonObject normalTextureJson = materialJson.getAsJsonObject("normalTexture");
+ if (normalTextureJson != null) {
+ gltfMaterialData.setGltfParam(NORMAL_TEXTURE_PARAM, readTexture(normalTextureJson));
+ gltfMaterialData.setGltfParam(NORMAL_SCALE_PARAM, getAsFloat(normalTextureJson, "scale"));
+ useNormalsFlag = true;
+ }
+
+ JsonObject occlusionTextureJson = materialJson.getAsJsonObject("occlusionTexture");
+ if (occlusionTextureJson != null) {
+ gltfMaterialData.setGltfParam(OCCLUSION_TEXTURE_PARAM, readTexture(occlusionTextureJson));
+ gltfMaterialData.setGltfParam(OCCLUSION_TEXTURE_STRENGTH_PARAM, getAsFloat(occlusionTextureJson, "strength"));
+ }
+
+ gltfMaterialData.setGltfParam(EMISSIVE_TEXTURE_PARAM, getAsTexture2D(materialJson, "emissiveTexture"));
+ gltfMaterialData.setGltfParam(EMISSIVE_COLOR_PARAM, getAsColor(materialJson, "emissiveFactor"));
+
+ String alphaMode = getAsString(materialJson, "alphaMode");
+ gltfMaterialData.setGltfParam(ALPHA_MODE_PARAM, alphaMode);
+ if ("MASK".equals(alphaMode)) {
+ gltfMaterialData.setGltfParam(ALPHA_CUTOFF_PARAM, getAsFloat(materialJson, "alphaCutoff"));
+ }
+
+ gltfMaterialData.setGltfParam(DOUBLE_SIDED_PARAM, getAsBoolean(materialJson, "doubleSided"));
+
+ return gltfMaterialData;
+ }
+
+ protected Material createMaterial(GltfMaterialData gltfMaterialData, int materialIndex) {
+ for (GltfMaterialFactory gltfMaterialFactory : materialFactoryList) {
+ if (gltfMaterialFactory.accepts(info.getKey(), gltfMaterialData)) {
+ return gltfMaterialFactory.createMaterial(info.getManager(), info.getKey(), gltfMaterialData);
+ }
+ }
+
+ logger.log(Level.WARNING, "Couldn't find any matching GltfMaterialFactory for material " + materialIndex);
+ useNormalsFlag = false;
+ return defaultMat;
+ }
+
+ @Deprecated
+ protected Material readMaterialUsingMaterialAdapters(int materialIndex, boolean usesVertexColors) throws IOException {
assertNotNull(materials, "There is no material defined yet a mesh references one");
JsonObject matData = materials.get(materialIndex).getAsJsonObject();
@@ -875,6 +953,8 @@ public Material readMaterial(int materialIndex) throws IOException {
adapter.setParam("emissiveTexture", readTexture(matData.getAsJsonObject("emissiveTexture")));
+ adapter.setParam("usesVertexColors", usesVertexColors);
+
return adapter.getMaterial();
}
@@ -922,6 +1002,10 @@ public void readCameras() throws IOException {
}
}
+ protected Texture2D getAsTexture2D(JsonObject jsonObject, String textureName) throws IOException {
+ return readTexture(jsonObject.getAsJsonObject(textureName));
+ }
+
public Texture2D readTexture(JsonObject texture) throws IOException {
return readTexture(texture, false);
}
@@ -1715,4 +1799,44 @@ public static void registerDefaultExtrasLoader(Class extends ExtrasLoader> loa
public static void unregisterDefaultExtrasLoader() {
CustomContentManager.defaultExtraLoaderClass = UserDataLoader.class;
}
+
+ /**
+ * Registers a new material factory and places it before all existing factories.
+ * The ordering of these factories defines their priority. When a new material needs to be created,
+ * the loader searches for the first material factory that accepts the given material data.
+ *
+ * @param materialFactory The {@link GltfMaterialFactory} to register.
+ */
+ public static void registerMaterialFactoryFirst(GltfMaterialFactory materialFactory) {
+ unregisterMaterialFactory(materialFactory.getClass());
+ materialFactoryList.add(0, materialFactory);
+ }
+
+ /**
+ * Registers a new material factory and places it behind all existing factories.
+ * The ordering of these factories defines their priority. When a new material needs to be created,
+ * the loader searches for the first material factory that accepts the given material data.
+ *
+ * @param materialFactory The {@link GltfMaterialFactory} to register.
+ */
+ public static void registerMaterialFactoryLast(GltfMaterialFactory materialFactory) {
+ unregisterMaterialFactory(materialFactory.getClass());
+ materialFactoryList.add(materialFactory);
+ }
+
+ /**
+ * Unregisters a material factory by its class.
+ *
+ * @param materialFactoryClass The class of the {@link GltfMaterialFactory} to unregister.
+ */
+ public static void unregisterMaterialFactory(Class extends GltfMaterialFactory> materialFactoryClass) {
+ materialFactoryList.removeIf(materialFactoryClass::isInstance);
+ }
+
+ /**
+ * Unregisters all material factories.
+ */
+ public static void unregisterAllMaterialFactories() {
+ materialFactoryList.clear();
+ }
}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialData.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialData.java
new file mode 100644
index 0000000000..490bc2973f
--- /dev/null
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialData.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.gltf;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Stores all data of a single material from a GLTF file.
+ * This data can then be used by a {@link GltfMaterialFactory} to create a new material.
+ *
+ *
Parameter naming convention
+ *
+ * -
+ * Parameter names should use single dots '.' as a separator.
+ *
+ * -
+ * All standard parameter names are directly taken from the
+ * GLTF specs document
+ * (ยง 5.19. Material and following).
+ *
+ * -
+ * All additional parameter names from GLTF extensions should start with {@link #MATERIAL_EXTENSION_PARAM_PREFIX}
+ * followed by the name of the extension (e.g. "KHR_materials_unlit") and finished by the parameter name.
+ *
+ * -
+ * All additional parameter names from GLTF extras should start with {@link #MATERIAL_EXTRA_PARAM_PREFIX}.
+ *
+ *
+ *
+ */
+public class GltfMaterialData {
+
+ public static final String MATERIAL_NAME_PARAM = "material.name";
+
+ public static final String BASE_COLOR_PARAM = "material.pbrMetallicRoughness.baseColorFactor";
+
+ public static final String BASE_COLOR_TEXTURE_PARAM = "material.pbrMetallicRoughness.baseColorTexture";
+
+ public static final String METALLIC_FACTOR_PARAM = "material.pbrMetallicRoughness.metallicFactor";
+
+ public static final String ROUGHNESS_FACTOR_PARAM = "material.pbrMetallicRoughness.roughnessFactor";
+
+ public static final String METALLIC_ROUGHNESS_TEXTURE_PARAM = "material.pbrMetallicRoughness.metallicRoughnessTexture";
+
+ public static final String NORMAL_TEXTURE_PARAM = "material.normalTexture";
+
+ public static final String NORMAL_SCALE_PARAM = "material.normalTextureInfo.scale";
+
+ public static final String OCCLUSION_TEXTURE_PARAM = "material.occlusionTexture";
+
+ public static final String OCCLUSION_TEXTURE_STRENGTH_PARAM = "material.occlusionTextureInfo.strength";
+
+ public static final String EMISSIVE_TEXTURE_PARAM = "material.emissiveTexture";
+
+ public static final String EMISSIVE_COLOR_PARAM = "material.emissiveFactor";
+
+ public static final String ALPHA_MODE_PARAM = "material.alphaMode";
+
+ public static final String ALPHA_CUTOFF_PARAM = "material.alphaCutoff";
+
+ public static final String DOUBLE_SIDED_PARAM = "material.doubleSided";
+
+ public static final String MATERIAL_EXTENSION_PARAM_PREFIX = "material.extension.";
+
+ public static final String MATERIAL_EXTRA_PARAM_PREFIX = "material.extra.";
+
+
+ private Map gltfParamMap = new HashMap<>();
+
+ private Set gltfExtensions = new HashSet<>();
+
+ /**
+ * Indicates the existence of a vertex color buffer.
+ */
+ private boolean hasVertexColors;
+
+
+ /**
+ * Checks if the material provides the given GLTF extension.
+ *
+ * @param gltfExtension The GLTF extension name.
+ * @return true if the material provides the given GLTF extension, otherwise false.
+ */
+ public boolean hasGltfExtension(String gltfExtension) {
+ return gltfExtensions.contains(gltfExtension);
+ }
+
+ /**
+ * Adds the given GLTF extension name.
+ *
+ * @param gltfExtension The GLTF extension name.
+ */
+ public void addGltfExtension(String gltfExtension) {
+ gltfExtensions.add(gltfExtension);
+ }
+
+ /**
+ * Removes the given GLTF extension name.
+ *
+ * @param gltfExtension The GLTF extension name.
+ */
+ public void removeGltfExtension(String gltfExtension) {
+ gltfExtensions.remove(gltfExtension);
+ }
+
+
+ /**
+ * Checks if the material provides a material parameter with the given name.
+ *
+ * @param gltfParamName The GLTF parameter name.
+ * @return true if the material provides a material parameter with the given name, otherwise false.
+ */
+ public boolean containsGltfParam(String gltfParamName) {
+ return gltfParamMap.containsKey(gltfParamName);
+ }
+
+ /**
+ * Gets the material parameter with the given name.
+ *
+ * @param gltfParamName The GLTF parameter name.
+ * @return The value of the material parameter with the given name, or null if no such parameter exists.
+ */
+ public Object getGltfParam(String gltfParamName) {
+ return gltfParamMap.get(gltfParamName);
+ }
+
+ /**
+ * Adds a material parameter with the given name and value.
+ *
+ * @param gltfParamName The GLTF parameter name.
+ * @param value The value of the material parameter. Does nothing, if value is null.
+ */
+ public void setGltfParam(String gltfParamName, Object value) {
+ if (value != null) {
+ gltfParamMap.put(gltfParamName, value);
+ }
+ }
+
+ /**
+ * Removes the material parameter with the given name.
+ *
+ * @param gltfParamName The GLTF parameter name.
+ * @return The previous value of the material parameter, or null if there was no such parameter.
+ */
+ public Object removeGltfParam(String gltfParamName) {
+ return gltfParamMap.remove(gltfParamName);
+ }
+
+
+ /**
+ * @return Indicates the existence of a vertex color buffer.
+ */
+ public boolean hasVertexColors() {
+ return hasVertexColors;
+ }
+
+ /**
+ * Sets the vertex color flag.
+ *
+ * @param hasVertexColors The value to set.
+ */
+ public void setHasVertexColors(boolean hasVertexColors) {
+ this.hasVertexColors = hasVertexColors;
+ }
+
+}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java
similarity index 51%
rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthMaterialAdapter.java
rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java
index 5d078c97f7..32554b9919 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthMaterialAdapter.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2023 jMonkeyEngine
+ * Copyright (c) 2009-2026 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,16 +31,38 @@
*/
package com.jme3.scene.plugins.gltf;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+
/**
- * Adapter for converting GLTF emissive strength to JME emissive intensity.
- *
- * @author codex
+ * A material factory creates {@link Material}s based on the data of a single material from a GLTF file.
+ *
+ * All material factories have bo be registered with the {@link GltfLoader} by using one of its
+ * static register methods.
+ *
*/
-public class PBREmissiveStrengthMaterialAdapter extends PBRMaterialAdapter {
-
- public PBREmissiveStrengthMaterialAdapter() {
- super();
- addParamMapping("emissiveStrength", "EmissiveIntensity");
- }
-
+public interface GltfMaterialFactory {
+
+ /**
+ * Checks, if the factory is able to create a new material from the given material data.
+ * If it accepts the material data, the {@link #createMaterial(AssetManager, AssetKey, GltfMaterialData)} method
+ * can be used to create a new material.
+ *
+ * @param assetKey The {@link AssetKey} used for loading the GLTF model.
+ * @param gltfMaterialData The {@link GltfMaterialData} containing all available GLTF material data.
+ * @return true if the factory is able to create a material from the given material data, otherwise false.
+ */
+ boolean accepts(AssetKey> assetKey, GltfMaterialData gltfMaterialData);
+
+ /**
+ * Creates a new material from the given material data.
+ *
+ * @param assetManager The {@link AssetManager} instance.
+ * @param assetKey The {@link AssetKey} used for loading the GLTF model.
+ * @param gltfMaterialData The {@link GltfMaterialData} containing all available GLTF material data.
+ * @return The new created {@link Material}.
+ */
+ Material createMaterial(AssetManager assetManager, AssetKey> assetKey, GltfMaterialData gltfMaterialData);
+
}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java
index 2490243a00..f045a5a7b3 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java
@@ -52,7 +52,15 @@
*/
public class GltfModelKey extends ModelKey {
+ /**
+ * Enables or disables the legacy material adapter system.
+ * This should only be used in older projects for backward compatibility.
+ */
+ private boolean materialAdaptersEnabled = false;
+
+ @Deprecated
private Map materialAdapters = new HashMap<>();
+
private static Map extensionLoaders = new HashMap<>();
private boolean keepSkeletonPose = false;
private ExtrasLoader extrasLoader;
@@ -101,13 +109,31 @@ public boolean isStrict() {
return strictExtensionCheck;
}
+ public boolean isMaterialAdaptersEnabled() {
+ return materialAdaptersEnabled;
+ }
+
+ /**
+ * Enables or disables the legacy material adapter system.
+ * This should only be used in older projects for backward compatibility.
+ *
+ * @param materialAdaptersEnabled The value to set.
+ */
+ public void setMaterialAdaptersEnabled(boolean materialAdaptersEnabled) {
+ this.materialAdaptersEnabled = materialAdaptersEnabled;
+ }
+
/**
* Registers a MaterialAdapter for the given materialName.
* The materialName must be "pbrMetallicRoughness" or any name from KHR_materials glTF Extension (for example "pbrSpecularGlossiness" for "KHR_materials_pbrSpecularGlossiness" extension)
*
* @param gltfMaterialName the name of the gltf material
* @param adapter the material adapter
+ *
+ * @deprecated This will be removed in a future version of the engine. To migrate,
+ * create a custom {@link GltfMaterialFactory} and register it with the {@link GltfLoader}.
*/
+ @Deprecated
public void registerMaterialAdapter(String gltfMaterialName, MaterialAdapter adapter) {
materialAdapters.put(gltfMaterialName, adapter);
}
@@ -124,6 +150,7 @@ public void registerExtensionLoader(String extensionName, ExtensionLoader loader
extensionLoaders.put(extensionName, loader);
}
+ @Deprecated
public MaterialAdapter getAdapterForMaterial(String gltfMaterialName) {
return materialAdapters.get(gltfMaterialName);
}
@@ -169,15 +196,17 @@ public boolean equals(Object object) {
|| !Objects.equals(extrasLoader, other.extrasLoader)) {
return false;
}
- return keepSkeletonPose == other.keepSkeletonPose;
+ return keepSkeletonPose == other.keepSkeletonPose
+ && materialAdaptersEnabled == other.materialAdaptersEnabled;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + materialAdapters.hashCode();
- hash = 37 * hash + Objects.hashCode(this.extrasLoader);
- hash = 37 * hash + (this.keepSkeletonPose ? 1 : 0);
+ hash = 37 * hash + Objects.hashCode(extrasLoader);
+ hash = 37 * hash + (keepSkeletonPose ? 1 : 0);
+ hash = 37 * hash + (materialAdaptersEnabled ? 1 : 0);
return hash;
}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java
index 677f15e6ea..e8f0735f67 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java
@@ -716,6 +716,12 @@ public static GltfModelKey getKey(AssetInfo info) {
return null;
}
+ public static boolean isMaterialAdaptersEnabled(AssetInfo info) {
+ GltfModelKey key = getKey(info);
+ return key != null && key.isMaterialAdaptersEnabled();
+ }
+
+ @Deprecated
public static MaterialAdapter getAdapterForMaterial(AssetInfo info, String defName) {
GltfModelKey key = getKey(info);
if (key == null) {
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java
index b6b10f9c83..d94d1662fa 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java
@@ -47,7 +47,11 @@
* It maps each gltf parameter to its matching parameter in the JME material,
* and allows for some conversion if the JME material model doesn't exactly match the gltf material model
* Created by Nehon on 08/08/2017.
+ *
+ * @deprecated This will be removed in a future version of the engine. To migrate,
+ * create a custom {@link GltfMaterialFactory} and register it with the {@link GltfLoader}.
*/
+@Deprecated
public abstract class MaterialAdapter {
private final Map paramsMapping = new HashMap<>();
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthExtensionLoader.java
index 71ab1b84a4..8fdf2934d7 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthExtensionLoader.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthExtensionLoader.java
@@ -32,8 +32,16 @@
package com.jme3.scene.plugins.gltf;
import com.jme3.asset.AssetKey;
+import com.jme3.material.MatParam;
import com.jme3.plugins.json.JsonElement;
+import com.jme3.plugins.json.JsonObject;
+import com.jme3.shader.VarType;
+
import java.io.IOException;
+import java.util.logging.Logger;
+
+import static com.jme3.scene.plugins.gltf.GltfMaterialData.MATERIAL_EXTENSION_PARAM_PREFIX;
+import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
/**
* Extension loader for "KHR_materials_emissive_strength".
@@ -41,12 +49,35 @@
* @author codex
*/
public class PBREmissiveStrengthExtensionLoader implements ExtensionLoader {
-
- private PBREmissiveStrengthMaterialAdapter materialAdapter = new PBREmissiveStrengthMaterialAdapter();
-
+
+ public static final String EXTENSION_NAME = "KHR_materials_emissive_strength";
+
+ public static final String EMISSIVE_STRENGTH_PARAM = MATERIAL_EXTENSION_PARAM_PREFIX + EXTENSION_NAME + ".emissiveStrength";
+
+ private static final Logger logger = Logger.getLogger(PBREmissiveStrengthExtensionLoader.class.getName());
+
@Override
public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException {
- MaterialAdapter adapter = materialAdapter;
+ if (input instanceof GltfMaterialData) {
+ GltfMaterialData gltfMaterialData = (GltfMaterialData) input;
+ gltfMaterialData.addGltfExtension(EXTENSION_NAME);
+
+ JsonObject extensionJson = extension.getAsJsonObject();
+ gltfMaterialData.setGltfParam(EMISSIVE_STRENGTH_PARAM, getAsFloat(extensionJson, "emissiveStrength"));
+
+ } else if (input instanceof MaterialAdapter) {
+ return handleExtensionForMaterialAdapter(loader, parentName, parent, extension, input);
+
+ } else {
+ logger.warning(EXTENSION_NAME + " extension added on unsupported element");
+ }
+
+ return input;
+ }
+
+ @Deprecated
+ private Object handleExtensionForMaterialAdapter(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException {
+ MaterialAdapter adapter = (MaterialAdapter) input;
AssetKey key = loader.getInfo().getKey();
//check for a custom adapter for emissive strength
if (key instanceof GltfModelKey) {
@@ -54,9 +85,16 @@ public Object handleExtension(GltfLoader loader, String parentName, JsonElement
if (custom != null) {
adapter = custom;
}
- }
- adapter.init(loader.getInfo().getManager());
- adapter.setParam("emissiveStrength", GltfUtils.getAsFloat(extension.getAsJsonObject(), "emissiveStrength"));
+ }
+
+ // Simply add a param mapping for the emissive strength, if the given material supports a
+ // material parameter with the exact name "EmissiveIntensity".
+ MatParam matParam = adapter.getMaterial().getMaterialDef().getMaterialParam("EmissiveIntensity");
+ if (matParam != null && matParam.getVarType() == VarType.Float) {
+ adapter.addParamMapping("emissiveStrength", "EmissiveIntensity");
+ adapter.setParam("emissiveStrength", GltfUtils.getAsFloat(extension.getAsJsonObject(), "emissiveStrength"));
+ }
+
return adapter;
}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRLightingMaterialFactory.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRLightingMaterialFactory.java
new file mode 100644
index 0000000000..334d21f1cb
--- /dev/null
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRLightingMaterialFactory.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.gltf;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.texture.Texture;
+
+import static com.jme3.scene.plugins.gltf.GltfMaterialData.*;
+import static com.jme3.scene.plugins.gltf.PBREmissiveStrengthExtensionLoader.EMISSIVE_STRENGTH_PARAM;
+import static com.jme3.scene.plugins.gltf.PBRSpecGlossExtensionLoader.*;
+
+/**
+ * This material factory creates jME3's standard "PBRLighting" materials.
+ */
+public class PBRLightingMaterialFactory implements GltfMaterialFactory {
+
+ @Override
+ public boolean accepts(AssetKey> assetKey, GltfMaterialData gltfMaterialData) {
+ // Since PBRLighting is the default material, it accepts all material data,
+ // making any subsequent material factories effectively unreachable.
+ return true;
+ }
+
+ @Override
+ public Material createMaterial(AssetManager assetManager, AssetKey> assetKey, GltfMaterialData gltfMaterialData) {
+ Material material = new Material(assetManager, getMaterialDefPath());
+ material.setName((String) gltfMaterialData.getGltfParam(MATERIAL_NAME_PARAM));
+
+ setStandardParams(material, gltfMaterialData);
+
+ if (gltfMaterialData.hasGltfExtension(PBRSpecGlossExtensionLoader.EXTENSION_NAME)) {
+ setSpecularGlossinessParams(material, gltfMaterialData);
+
+ } else {
+ setMetallicRoughnessParams(material, gltfMaterialData);
+ }
+
+ return material;
+ }
+
+ protected String getMaterialDefPath() {
+ return "Common/MatDefs/Light/PBRLighting.j3md";
+ }
+
+ protected void setStandardParams(Material material, GltfMaterialData gltfMaterialData) {
+ if (gltfMaterialData.containsGltfParam(NORMAL_TEXTURE_PARAM)) {
+ setParam(material, "NormalMap", gltfMaterialData.getGltfParam(NORMAL_TEXTURE_PARAM));
+ setParam(material, "NormalScale", gltfMaterialData.getGltfParam(NORMAL_SCALE_PARAM));
+ material.setFloat("NormalType", 1f);
+ }
+
+ if (gltfMaterialData.containsGltfParam(OCCLUSION_TEXTURE_PARAM)) {
+ // Gltf only supports AO maps (gray scales and only the r channel must be read)
+ material.setBoolean("LightMapAsAOMap", true);
+ setParam(material, "LightMap", gltfMaterialData.getGltfParam(OCCLUSION_TEXTURE_PARAM));
+ setParam(material, "AoStrength", gltfMaterialData.getGltfParam(OCCLUSION_TEXTURE_STRENGTH_PARAM));
+
+ // Check if the occlusion texture is actually the same instance as the metallic-roughness texture.
+ boolean isAoPackedInMRMap = false;
+ if (gltfMaterialData.containsGltfParam(METALLIC_ROUGHNESS_TEXTURE_PARAM)) {
+ Texture occlusionTexture = (Texture) gltfMaterialData.getGltfParam(OCCLUSION_TEXTURE_PARAM);
+ Texture metallicRoughnessTexture = (Texture) gltfMaterialData.getGltfParam(METALLIC_ROUGHNESS_TEXTURE_PARAM);
+ isAoPackedInMRMap = occlusionTexture == metallicRoughnessTexture;
+ }
+ material.setBoolean("AoPackedInMRMap", isAoPackedInMRMap);
+ }
+
+ setParam(material, "EmissiveMap", gltfMaterialData.getGltfParam(EMISSIVE_TEXTURE_PARAM));
+ setParam(material, "Emissive", gltfMaterialData.getGltfParam(EMISSIVE_COLOR_PARAM), ColorRGBA.Black);
+ setParam(material, "EmissiveIntensity", gltfMaterialData.getGltfParam(EMISSIVE_STRENGTH_PARAM), 1f);
+
+ if (gltfMaterialData.containsGltfParam(ALPHA_MODE_PARAM)) {
+ String alphaMode = (String) gltfMaterialData.getGltfParam(ALPHA_MODE_PARAM);
+ switch (alphaMode) {
+ case "MASK":
+ // "MASK" -> BlendMode.Off
+ setParam(material, "AlphaDiscardThreshold", gltfMaterialData.getGltfParam(ALPHA_CUTOFF_PARAM), 0.5f);
+ break;
+ case "BLEND":
+ material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
+ break;
+ }
+ }
+
+ if (gltfMaterialData.containsGltfParam(DOUBLE_SIDED_PARAM)) {
+ boolean doubleSided = (boolean) gltfMaterialData.getGltfParam(DOUBLE_SIDED_PARAM);
+ if (doubleSided) {
+ //Note that this is not completely right as normals on the back side will be in the wrong direction.
+ material.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
+ }
+ }
+
+ setParam(material, "UseVertexColor", gltfMaterialData.hasVertexColors());
+ }
+
+ protected void setMetallicRoughnessParams(Material material, GltfMaterialData gltfMaterialData) {
+ setParam(material, "BaseColor", gltfMaterialData.getGltfParam(BASE_COLOR_PARAM), ColorRGBA.White);
+ setParam(material, "BaseColorMap", gltfMaterialData.getGltfParam(BASE_COLOR_TEXTURE_PARAM));
+ setParam(material, "Metallic", gltfMaterialData.getGltfParam(METALLIC_FACTOR_PARAM), 1f);
+ setParam(material, "Roughness", gltfMaterialData.getGltfParam(ROUGHNESS_FACTOR_PARAM), 1f);
+ setParam(material, "MetallicRoughnessMap", gltfMaterialData.getGltfParam(METALLIC_ROUGHNESS_TEXTURE_PARAM));
+ }
+
+ protected void setSpecularGlossinessParams(Material material, GltfMaterialData gltfMaterialData) {
+ material.setBoolean("UseSpecGloss", true);
+ setParam(material, "BaseColor", gltfMaterialData.getGltfParam(DIFFUSE_COLOR_PARAM));
+ setParam(material, "BaseColorMap", gltfMaterialData.getGltfParam(DIFFUSE_TEXTURE_PARAM));
+ setParam(material, "Specular", gltfMaterialData.getGltfParam(SPECULAR_COLOR_PARAM));
+ setParam(material, "Glossiness", gltfMaterialData.getGltfParam(GLOSSINESS_FACTOR_PARAM));
+ setParam(material, "SpecularGlossinessMap", gltfMaterialData.getGltfParam(SPECULAR_GLOSSINESS_TEXTURE_PARAM));
+ }
+
+ protected void setParam(Material material, String paramName, Object value) {
+ if (value != null) {
+ material.setParam(paramName, value);
+ }
+ }
+
+ protected void setParam(Material material, String paramName, Object value, Object defaultValue) {
+ setParam(material, paramName, value != null ? value : defaultValue);
+ }
+
+}
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java
index 340cdb513b..185b961b0f 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java
@@ -37,7 +37,11 @@
* Adapts GLTF PBR materials to JME PBR materials.
*
* @author Nehon
+ *
+ * @deprecated This will be removed in a future version of the engine. To migrate,
+ * create a custom {@link GltfMaterialFactory} and register it with the {@link GltfLoader}.
*/
+@Deprecated
public abstract class PBRMaterialAdapter extends MaterialAdapter {
/**
@@ -55,6 +59,7 @@ public PBRMaterialAdapter() {
addParamMapping("alphaMode", "alpha");
addParamMapping("alphaCutoff", "AlphaDiscardThreshold");
addParamMapping("doubleSided", "doubleSided");
+ addParamMapping("usesVertexColors", "UseVertexColor");
}
@Override
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java
index fd744cffdd..c9c99c595f 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java
@@ -33,7 +33,11 @@
/**
* Created by Nehon on 20/08/2017.
+ *
+ * @deprecated This will be removed in a future version of the engine. To migrate,
+ * create a custom {@link GltfMaterialFactory} and register it with the {@link GltfLoader}.
*/
+@Deprecated
public class PBRMetalRoughMaterialAdapter extends PBRMaterialAdapter {
public PBRMetalRoughMaterialAdapter() {
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java
index abeda10cd7..984e649fb4 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java
@@ -34,7 +34,12 @@
import com.jme3.asset.AssetKey;
import java.io.IOException;
+import java.util.logging.Logger;
+
import com.jme3.plugins.json.JsonElement;
+import com.jme3.plugins.json.JsonObject;
+
+import static com.jme3.scene.plugins.gltf.GltfMaterialData.MATERIAL_EXTENSION_PARAM_PREFIX;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor;
import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat;
@@ -44,10 +49,48 @@
*/
public class PBRSpecGlossExtensionLoader implements ExtensionLoader {
+ public static final String EXTENSION_NAME = "KHR_materials_pbrSpecularGlossiness";
+
+ public static final String DIFFUSE_COLOR_PARAM = MATERIAL_EXTENSION_PARAM_PREFIX + EXTENSION_NAME + ".diffuseFactor";
+
+ public static final String SPECULAR_COLOR_PARAM = MATERIAL_EXTENSION_PARAM_PREFIX + EXTENSION_NAME + ".specularFactor";
+
+ public static final String GLOSSINESS_FACTOR_PARAM = MATERIAL_EXTENSION_PARAM_PREFIX + EXTENSION_NAME + ".glossinessFactor";
+
+ public static final String DIFFUSE_TEXTURE_PARAM = MATERIAL_EXTENSION_PARAM_PREFIX + EXTENSION_NAME + ".diffuseTexture";
+
+ public static final String SPECULAR_GLOSSINESS_TEXTURE_PARAM = MATERIAL_EXTENSION_PARAM_PREFIX + EXTENSION_NAME + ".specularGlossinessTexture";
+
+ private static final Logger logger = Logger.getLogger(PBRSpecGlossExtensionLoader.class.getName());
+
+ @Deprecated
private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter();
@Override
public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException {
+ if (input instanceof GltfMaterialData) {
+ GltfMaterialData gltfMaterialData = (GltfMaterialData) input;
+ gltfMaterialData.addGltfExtension(EXTENSION_NAME);
+
+ JsonObject extensionJson = extension.getAsJsonObject();
+ gltfMaterialData.setGltfParam(DIFFUSE_COLOR_PARAM, getAsColor(extensionJson, "diffuseFactor"));
+ gltfMaterialData.setGltfParam(SPECULAR_COLOR_PARAM, getAsColor(extensionJson, "specularFactor"));
+ gltfMaterialData.setGltfParam(GLOSSINESS_FACTOR_PARAM, getAsFloat(extensionJson, "glossinessFactor"));
+ gltfMaterialData.setGltfParam(DIFFUSE_TEXTURE_PARAM, loader.getAsTexture2D(extensionJson,"diffuseTexture"));
+ gltfMaterialData.setGltfParam(SPECULAR_GLOSSINESS_TEXTURE_PARAM, loader.getAsTexture2D(extensionJson,"specularGlossinessTexture"));
+
+ } else if (input instanceof MaterialAdapter) {
+ return handleExtensionForMaterialAdapter(loader, parentName, parent, extension, input);
+
+ } else {
+ logger.warning(EXTENSION_NAME + " extension added on unsupported element");
+ }
+
+ return input;
+ }
+
+ @Deprecated
+ private Object handleExtensionForMaterialAdapter(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException {
MaterialAdapter adapter = materialAdapter;
AssetKey key = loader.getInfo().getKey();
//check for a custom adapter for spec/gloss pipeline
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java
index bd4c25c8af..df74552ea6 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java
@@ -35,7 +35,11 @@
/**
* Created by Nehon on 20/08/2017.
+ *
+ * @deprecated This will be removed in a future version of the engine. To migrate,
+ * create a custom {@link GltfMaterialFactory} and register it with the {@link GltfLoader}.
*/
+@Deprecated
public class PBRSpecGlossMaterialAdapter extends PBRMaterialAdapter {
public PBRSpecGlossMaterialAdapter() {
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java
index 790d70b0cf..a95eda8960 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java
@@ -34,16 +34,39 @@
import com.jme3.plugins.json.JsonElement;
import com.jme3.asset.AssetKey;
+import java.util.logging.Logger;
+
/**
* Material adapter for the Unlit pipeline
* @author Markil 3
*/
public class UnlitExtensionLoader implements ExtensionLoader {
+ public static final String EXTENSION_NAME = "KHR_materials_unlit";
+
+ private static final Logger logger = Logger.getLogger(UnlitExtensionLoader.class.getName());
+
+ @Deprecated
private final UnlitMaterialAdapter materialAdapter = new UnlitMaterialAdapter();
@Override
public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) {
+ if (input instanceof GltfMaterialData) {
+ GltfMaterialData gltfMaterialData = (GltfMaterialData) input;
+ gltfMaterialData.addGltfExtension(EXTENSION_NAME);
+
+ } else if (input instanceof MaterialAdapter) {
+ return handleExtensionForMaterialAdapter(loader, parentName, parent, extension, input);
+
+ } else {
+ logger.warning(EXTENSION_NAME + " extension added on unsupported element");
+ }
+
+ return input;
+ }
+
+ @Deprecated
+ private Object handleExtensionForMaterialAdapter(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) {
MaterialAdapter adapter = materialAdapter;
AssetKey key = loader.getInfo().getKey();
//check for a custom adapter for spec/gloss pipeline
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java
index 65acdf7f2b..e921025dd5 100644
--- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java
@@ -37,7 +37,11 @@
/**
* @author Markil 3
+ *
+ * @deprecated This will be removed in a future version of the engine. To migrate,
+ * create a custom {@link GltfMaterialFactory} and register it with the {@link GltfLoader}.
*/
+@Deprecated
public class UnlitMaterialAdapter extends MaterialAdapter {
public UnlitMaterialAdapter() {
@@ -48,6 +52,7 @@ public UnlitMaterialAdapter() {
addParamMapping("alphaMode", "alpha");
addParamMapping("alphaCutoff", "AlphaDiscardThreshold");
addParamMapping("doubleSided", "doubleSided");
+ addParamMapping("usesVertexColors", "VertexColor");
}
@Override
diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnshadedMaterialFactory.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnshadedMaterialFactory.java
new file mode 100644
index 0000000000..0d89573392
--- /dev/null
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnshadedMaterialFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2026 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.scene.plugins.gltf;
+
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+
+import static com.jme3.scene.plugins.gltf.GltfMaterialData.*;
+
+/**
+ * This material factory creates jME3's standard "Unshaded" materials.
+ */
+public class UnshadedMaterialFactory implements GltfMaterialFactory {
+
+ @Override
+ public boolean accepts(AssetKey> assetKey, GltfMaterialData gltfMaterialData) {
+ return gltfMaterialData.hasGltfExtension(UnlitExtensionLoader.EXTENSION_NAME);
+ }
+
+ @Override
+ public Material createMaterial(AssetManager assetManager, AssetKey> assetKey, GltfMaterialData gltfMaterialData) {
+ Material material = new Material(assetManager,getMaterialDefPath());
+ material.setName((String) gltfMaterialData.getGltfParam(MATERIAL_NAME_PARAM));
+
+ setParam(material, "Color", gltfMaterialData.getGltfParam(BASE_COLOR_PARAM), ColorRGBA.White);
+ setParam(material, "ColorMap", gltfMaterialData.getGltfParam(BASE_COLOR_TEXTURE_PARAM));
+ setParam(material, "GlowColor", gltfMaterialData.getGltfParam(EMISSIVE_COLOR_PARAM), ColorRGBA.Black);
+ setParam(material, "GlowMap", gltfMaterialData.getGltfParam(EMISSIVE_TEXTURE_PARAM));
+
+ if (gltfMaterialData.containsGltfParam(ALPHA_MODE_PARAM)) {
+ String alphaMode = (String) gltfMaterialData.getGltfParam(ALPHA_MODE_PARAM);
+ switch (alphaMode) {
+ case "MASK":
+ // "MASK" -> BlendMode.Off
+ setParam(material, "AlphaDiscardThreshold", gltfMaterialData.getGltfParam(ALPHA_CUTOFF_PARAM), 0.5f);
+ break;
+ case "BLEND":
+ material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
+ break;
+ }
+ }
+
+ if (gltfMaterialData.containsGltfParam(DOUBLE_SIDED_PARAM)) {
+ boolean doubleSided = (boolean) gltfMaterialData.getGltfParam(DOUBLE_SIDED_PARAM);
+ if (doubleSided) {
+ //Note that this is not completely right as normals on the back side will be in the wrong direction.
+ material.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
+ }
+ }
+
+ setParam(material, "VertexColor", gltfMaterialData.hasVertexColors());
+
+ return material;
+ }
+
+ protected String getMaterialDefPath() {
+ return "Common/MatDefs/Misc/Unshaded.j3md";
+ }
+
+ protected void setParam(Material material, String paramName, Object value) {
+ if (value != null) {
+ material.setParam(paramName, value);
+ }
+ }
+
+ protected void setParam(Material material, String paramName, Object value, Object defaultValue) {
+ setParam(material, paramName, value != null ? value : defaultValue);
+ }
+
+}
diff --git a/jme3-plugins/src/test/java/com/jme3/scene/plugins/gltf/GltfLoaderTest.java b/jme3-plugins/src/test/java/com/jme3/scene/plugins/gltf/GltfLoaderTest.java
index d2662b9ec0..d485ec2f31 100644
--- a/jme3-plugins/src/test/java/com/jme3/scene/plugins/gltf/GltfLoaderTest.java
+++ b/jme3-plugins/src/test/java/com/jme3/scene/plugins/gltf/GltfLoaderTest.java
@@ -37,7 +37,12 @@
import com.jme3.light.Light;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
+import com.jme3.material.MatParam;
+import com.jme3.material.MatParamTexture;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
import com.jme3.material.plugin.TestMaterialWrite;
+import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
@@ -196,4 +201,276 @@ private void dumpScene(Spatial s, int indent) {
}
}
}
-}
\ No newline at end of file
+
+ @Test
+ public void testPBRMaterialNoTextures() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry pbrNoTexturesCube = (Geometry) sceneNode.getChild("pbrNoTexturesCube_0");
+ Material pbrMaterial = pbrNoTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(pbrMaterial, "PBR Lighting", "pbrNoTexturesMaterial");
+
+ assertMaterialParam(pbrMaterial, "BaseColor", new ColorRGBA(0.9f, 0.6f, 0.3f, 0f));
+ assertMaterialParam(pbrMaterial, "BaseColorMap", null);
+ assertMaterialParam(pbrMaterial, "Metallic", 0.4f);
+ assertMaterialParam(pbrMaterial, "Roughness", 0.6f);
+ assertMaterialParam(pbrMaterial, "MetallicRoughnessMap", null);
+
+ assertMaterialParam(pbrMaterial, "NormalMap", null);
+ assertMaterialParam(pbrMaterial, "NormalScale", null);
+ assertMaterialParam(pbrMaterial, "NormalType", -1f);
+
+ assertMaterialParam(pbrMaterial, "LightMapAsAOMap", null);
+ assertMaterialParam(pbrMaterial, "LightMap", null);
+ assertMaterialParam(pbrMaterial, "AoStrength", null);
+ assertMaterialParam(pbrMaterial, "AoPackedInMRMap", null);
+
+ assertMaterialParam(pbrMaterial, "EmissiveMap", null);
+ assertMaterialParam(pbrMaterial, "Emissive", new ColorRGBA(0.2f, 0.6f, 1.f, 1f));
+ assertMaterialParam(pbrMaterial, "EmissiveIntensity", 2.7f);
+
+ assertMaterialParam(pbrMaterial, "AlphaDiscardThreshold", 0.5f);
+ Assertions.assertEquals(RenderState.BlendMode.Off, pbrMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Off, pbrMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(pbrMaterial, "UseVertexColor", true);
+ }
+
+ @Test
+ public void testPBRMaterialWithTextures() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry pbrWithTexturesCube = (Geometry) sceneNode.getChild("pbrWithTexturesCube_0");
+ Material pbrMaterial = pbrWithTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(pbrMaterial, "PBR Lighting", "pbrWithTexturesMaterial");
+
+ assertMaterialParam(pbrMaterial, "BaseColor", ColorRGBA.White);
+ assertMaterialParam(pbrMaterial, "BaseColorMap", "gltf/ColorTexture.png");
+ assertMaterialParam(pbrMaterial, "Metallic", 1f);
+ assertMaterialParam(pbrMaterial, "Roughness", 1f);
+ assertMaterialParam(pbrMaterial, "MetallicRoughnessMap", "gltf/MetallicRoughnessOcclusionTexture.png");
+
+ assertMaterialParam(pbrMaterial, "NormalMap", "gltf/NormalTexture.png");
+ assertMaterialParam(pbrMaterial, "NormalScale", 1.8f);
+ assertMaterialParam(pbrMaterial, "NormalType", 1f);
+
+ assertMaterialParam(pbrMaterial, "LightMapAsAOMap", true);
+ assertMaterialParam(pbrMaterial, "LightMap", "gltf/MetallicRoughnessOcclusionTexture.png");
+ assertMaterialParam(pbrMaterial, "AoStrength", null);
+ assertMaterialParam(pbrMaterial, "AoPackedInMRMap", true);
+
+ assertMaterialParam(pbrMaterial, "EmissiveMap", "gltf/EmissiveTexture.png");
+ assertMaterialParam(pbrMaterial, "Emissive", ColorRGBA.White);
+ assertMaterialParam(pbrMaterial, "EmissiveIntensity", 2.7f);
+
+ assertMaterialParam(pbrMaterial, "AlphaDiscardThreshold", null);
+ Assertions.assertEquals(RenderState.BlendMode.Alpha, pbrMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Back, pbrMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(pbrMaterial, "UseVertexColor", false);
+ }
+
+ @Test
+ public void testUnlitMaterialNoTextures() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry unlitNoTexturesCube = (Geometry) sceneNode.getChild("unlitNoTexturesCube_0");
+ Material unlitMaterial = unlitNoTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(unlitMaterial, "Unshaded", "unlitNoTexturesMaterial");
+
+ assertMaterialParam(unlitMaterial, "Color", new ColorRGBA(0.2f, 0.4f, 0.6f, 0f));
+ assertMaterialParam(unlitMaterial, "ColorMap", null);
+
+ assertMaterialParam(unlitMaterial, "GlowMap", null);
+ assertMaterialParam(unlitMaterial, "GlowColor", ColorRGBA.Black);
+
+ assertMaterialParam(unlitMaterial, "AlphaDiscardThreshold", 0.5f);
+ Assertions.assertEquals(RenderState.BlendMode.Off, unlitMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Off, unlitMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(unlitMaterial, "VertexColor", true);
+ }
+
+ @Test
+ public void testUnlitMaterialWithTextures() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry unlitWithTexturesCube = (Geometry) sceneNode.getChild("unlitWithTexturesCube_0");
+ Material unlitMaterial = unlitWithTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(unlitMaterial, "Unshaded", "unlitWithTexturesMaterial");
+
+ assertMaterialParam(unlitMaterial, "Color", ColorRGBA.White);
+ assertMaterialParam(unlitMaterial, "ColorMap", "gltf/ColorTexture.png");
+
+ assertMaterialParam(unlitMaterial, "GlowMap", null);
+ assertMaterialParam(unlitMaterial, "GlowColor", ColorRGBA.Black);
+
+ assertMaterialParam(unlitMaterial, "AlphaDiscardThreshold", null);
+ Assertions.assertEquals(RenderState.BlendMode.Alpha, unlitMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Back, unlitMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(unlitMaterial, "VertexColor", false);
+ }
+
+ @Test
+ public void testPBRMaterialNoTextures_LegacyMechanism() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ gltfModelKey.setMaterialAdaptersEnabled(true);
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry pbrNoTexturesCube = (Geometry) sceneNode.getChild("pbrNoTexturesCube_0");
+ Material pbrMaterial = pbrNoTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(pbrMaterial, "PBR Lighting", "pbrNoTexturesMaterial");
+
+ assertMaterialParam(pbrMaterial, "BaseColor", new ColorRGBA(0.9f, 0.6f, 0.3f, 0f));
+ assertMaterialParam(pbrMaterial, "BaseColorMap", null);
+ assertMaterialParam(pbrMaterial, "Metallic", 0.4f);
+ assertMaterialParam(pbrMaterial, "Roughness", 0.6f);
+ assertMaterialParam(pbrMaterial, "MetallicRoughnessMap", null);
+
+ assertMaterialParam(pbrMaterial, "NormalMap", null);
+ assertMaterialParam(pbrMaterial, "NormalScale", null);
+ assertMaterialParam(pbrMaterial, "NormalType", -1f);
+
+ assertMaterialParam(pbrMaterial, "LightMapAsAOMap", null);
+ assertMaterialParam(pbrMaterial, "LightMap", null);
+ assertMaterialParam(pbrMaterial, "AoStrength", null);
+ assertMaterialParam(pbrMaterial, "AoPackedInMRMap", null);
+
+ assertMaterialParam(pbrMaterial, "EmissiveMap", null);
+ assertMaterialParam(pbrMaterial, "Emissive", new ColorRGBA(0.2f, 0.6f, 1.f, 1f));
+ assertMaterialParam(pbrMaterial, "EmissiveIntensity", 2.7f);
+
+ assertMaterialParam(pbrMaterial, "AlphaDiscardThreshold", 0.5f);
+ Assertions.assertEquals(RenderState.BlendMode.Off, pbrMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Off, pbrMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(pbrMaterial, "UseVertexColor", true);
+ }
+
+ @Test
+ public void testPBRMaterialWithTextures_LegacyMechanism() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ gltfModelKey.setMaterialAdaptersEnabled(true);
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry pbrWithTexturesCube = (Geometry) sceneNode.getChild("pbrWithTexturesCube_0");
+ Material pbrMaterial = pbrWithTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(pbrMaterial, "PBR Lighting", "pbrWithTexturesMaterial");
+
+ assertMaterialParam(pbrMaterial, "BaseColor", ColorRGBA.White);
+ assertMaterialParam(pbrMaterial, "BaseColorMap", "gltf/ColorTexture.png");
+ assertMaterialParam(pbrMaterial, "Metallic", 1f);
+ assertMaterialParam(pbrMaterial, "Roughness", 1f);
+ assertMaterialParam(pbrMaterial, "MetallicRoughnessMap", "gltf/MetallicRoughnessOcclusionTexture.png");
+
+ assertMaterialParam(pbrMaterial, "NormalMap", "gltf/NormalTexture.png");
+ assertMaterialParam(pbrMaterial, "NormalScale", 1.8f);
+ assertMaterialParam(pbrMaterial, "NormalType", 1f);
+
+ // Differences to new material system:
+ // - LightMap is not set to occlusion texture
+ // - LightMapAsAOMap is not set
+ assertMaterialParam(pbrMaterial, "LightMapAsAOMap", null);
+ assertMaterialParam(pbrMaterial, "LightMap", null);
+ assertMaterialParam(pbrMaterial, "AoStrength", null);
+ assertMaterialParam(pbrMaterial, "AoPackedInMRMap", true);
+
+ assertMaterialParam(pbrMaterial, "EmissiveMap", "gltf/EmissiveTexture.png");
+ assertMaterialParam(pbrMaterial, "Emissive", ColorRGBA.White);
+ assertMaterialParam(pbrMaterial, "EmissiveIntensity", 2.7f);
+
+ assertMaterialParam(pbrMaterial, "AlphaDiscardThreshold", null);
+ Assertions.assertEquals(RenderState.BlendMode.Alpha, pbrMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Back, pbrMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(pbrMaterial, "UseVertexColor", false);
+ }
+
+ @Test
+ public void testUnlitMaterialNoTextures_LegacyMechanism() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ gltfModelKey.setMaterialAdaptersEnabled(true);
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry unlitNoTexturesCube = (Geometry) sceneNode.getChild("unlitNoTexturesCube_0");
+ Material unlitMaterial = unlitNoTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(unlitMaterial, "Unshaded", "unlitNoTexturesMaterial");
+
+ assertMaterialParam(unlitMaterial, "Color", new ColorRGBA(0.2f, 0.4f, 0.6f, 0f));
+ assertMaterialParam(unlitMaterial, "ColorMap", null);
+
+ assertMaterialParam(unlitMaterial, "GlowMap", null);
+ assertMaterialParam(unlitMaterial, "GlowColor", ColorRGBA.Black);
+
+ // Differences to new material system:
+ // - UnlitMaterialAdapter translates alphaMode=MASK to BlendMode.Alpha
+ // - AlphaDiscardThreshold is also not set
+ assertMaterialParam(unlitMaterial, "AlphaDiscardThreshold", null);
+ Assertions.assertEquals(RenderState.BlendMode.Alpha, unlitMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Off, unlitMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(unlitMaterial, "VertexColor", true);
+ }
+
+ @Test
+ public void testUnlitMaterialWithTextures_LegacyMechanism() {
+ GltfModelKey gltfModelKey = new GltfModelKey("gltf/MaterialTestCubes.gltf");
+ gltfModelKey.setMaterialAdaptersEnabled(true);
+ Node sceneNode = (Node) assetManager.loadModel(gltfModelKey);
+
+ Geometry unlitWithTexturesCube = (Geometry) sceneNode.getChild("unlitWithTexturesCube_0");
+ Material unlitMaterial = unlitWithTexturesCube.getMaterial();
+
+ assertMaterialNameAndDefinition(unlitMaterial, "Unshaded", "unlitWithTexturesMaterial");
+
+ assertMaterialParam(unlitMaterial, "Color", ColorRGBA.White);
+ assertMaterialParam(unlitMaterial, "ColorMap", "gltf/ColorTexture.png");
+
+ assertMaterialParam(unlitMaterial, "GlowMap", null);
+ assertMaterialParam(unlitMaterial, "GlowColor", ColorRGBA.Black);
+
+ assertMaterialParam(unlitMaterial, "AlphaDiscardThreshold", null);
+ Assertions.assertEquals(RenderState.BlendMode.Alpha, unlitMaterial.getAdditionalRenderState().getBlendMode());
+ Assertions.assertEquals(RenderState.FaceCullMode.Back, unlitMaterial.getAdditionalRenderState().getFaceCullMode());
+
+ assertMaterialParam(unlitMaterial, "VertexColor", false);
+ }
+
+ private void assertMaterialNameAndDefinition(Material material, String expectedDefinitionName, String expectedMaterialName) {
+ Assertions.assertEquals(expectedMaterialName, material.getName(), "Wrong material name.");
+ Assertions.assertEquals(expectedDefinitionName, material.getMaterialDef().getName(), "Wrong material definition.");
+ }
+
+ private void assertMaterialParam(Material material, String paramName, Object expectedValue) {
+ MatParam matParam = material.getParam(paramName);
+ if (expectedValue == null) {
+ Assertions.assertNull(matParam, () -> "Material parameter '" + paramName + "' should not be set.");
+ return;
+
+ } else {
+ Assertions.assertNotNull(matParam, () -> "Missing material parameter '" + paramName + "'.");
+ }
+
+ if (matParam instanceof MatParamTexture) {
+ String imageName = ((MatParamTexture) matParam).getTextureValue().getName();
+ Assertions.assertEquals(expectedValue, imageName, () -> "Wrong value of texture parameter '" + paramName + "'.");
+
+ } else {
+ Object value = matParam.getValue();
+ Assertions.assertEquals(expectedValue, value, () -> "Wrong value of material parameter '" + paramName + "'.");
+ }
+ }
+
+}
diff --git a/jme3-plugins/src/test/resources/gltf/ColorTexture.png b/jme3-plugins/src/test/resources/gltf/ColorTexture.png
new file mode 100644
index 0000000000..9a6f42a6b2
Binary files /dev/null and b/jme3-plugins/src/test/resources/gltf/ColorTexture.png differ
diff --git a/jme3-plugins/src/test/resources/gltf/EmissiveTexture.png b/jme3-plugins/src/test/resources/gltf/EmissiveTexture.png
new file mode 100644
index 0000000000..22881453dd
Binary files /dev/null and b/jme3-plugins/src/test/resources/gltf/EmissiveTexture.png differ
diff --git a/jme3-plugins/src/test/resources/gltf/MaterialTestCubes.bin b/jme3-plugins/src/test/resources/gltf/MaterialTestCubes.bin
new file mode 100644
index 0000000000..c29da2f925
Binary files /dev/null and b/jme3-plugins/src/test/resources/gltf/MaterialTestCubes.bin differ
diff --git a/jme3-plugins/src/test/resources/gltf/MaterialTestCubes.gltf b/jme3-plugins/src/test/resources/gltf/MaterialTestCubes.gltf
new file mode 100644
index 0000000000..4000b4dca3
--- /dev/null
+++ b/jme3-plugins/src/test/resources/gltf/MaterialTestCubes.gltf
@@ -0,0 +1,523 @@
+{
+ "asset":{
+ "generator":"Khronos glTF Blender I/O v4.3.47",
+ "version":"2.0"
+ },
+ "extensionsUsed":[
+ "KHR_materials_emissive_strength",
+ "KHR_materials_unlit"
+ ],
+ "scene":0,
+ "scenes":[
+ {
+ "name":"Scene",
+ "nodes":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "nodes":[
+ {
+ "mesh":0,
+ "name":"pbrWithTexturesCube",
+ "translation":[
+ -2,
+ 0,
+ 0
+ ]
+ },
+ {
+ "mesh":1,
+ "name":"unlitWithTexturesCube",
+ "translation":[
+ 2,
+ 0,
+ 0
+ ]
+ },
+ {
+ "mesh":2,
+ "name":"pbrNoTexturesCube",
+ "translation":[
+ -2,
+ 0,
+ 3
+ ]
+ },
+ {
+ "mesh":3,
+ "name":"unlitNoTexturesCube",
+ "translation":[
+ 2,
+ 0,
+ 3
+ ]
+ }
+ ],
+ "materials":[
+ {
+ "alphaMode":"BLEND",
+ "emissiveFactor":[
+ 1,
+ 1,
+ 1
+ ],
+ "emissiveTexture":{
+ "index":0
+ },
+ "extensions":{
+ "KHR_materials_emissive_strength":{
+ "emissiveStrength":2.700000047683716
+ }
+ },
+ "name":"pbrWithTexturesMaterial",
+ "normalTexture":{
+ "index":1,
+ "scale":1.7999999523162842
+ },
+ "occlusionTexture":{
+ "index":2
+ },
+ "pbrMetallicRoughness":{
+ "baseColorTexture":{
+ "index":3
+ },
+ "metallicRoughnessTexture":{
+ "index":2
+ }
+ }
+ },
+ {
+ "alphaMode":"BLEND",
+ "extensions":{
+ "KHR_materials_unlit":{}
+ },
+ "name":"unlitWithTexturesMaterial",
+ "pbrMetallicRoughness":{
+ "baseColorTexture":{
+ "index":4
+ },
+ "metallicFactor":0,
+ "roughnessFactor":0.9
+ }
+ },
+ {
+ "alphaMode":"MASK",
+ "doubleSided":true,
+ "emissiveFactor":[
+ 0.20000000298023224,
+ 0.6000000238418579,
+ 1
+ ],
+ "extensions":{
+ "KHR_materials_emissive_strength":{
+ "emissiveStrength":2.700000047683716
+ }
+ },
+ "name":"pbrNoTexturesMaterial",
+ "pbrMetallicRoughness":{
+ "baseColorFactor":[
+ 0.8999999761581421,
+ 0.6000000238418579,
+ 0.30000001192092896,
+ 0
+ ],
+ "metallicFactor":0.4000000059604645,
+ "roughnessFactor":0.6000000238418579
+ }
+ },
+ {
+ "alphaMode":"MASK",
+ "doubleSided":true,
+ "extensions":{
+ "KHR_materials_unlit":{}
+ },
+ "name":"unlitNoTexturesMaterial",
+ "pbrMetallicRoughness":{
+ "baseColorFactor":[
+ 0.20000000298023224,
+ 0.4000000059604645,
+ 0.6000000238418579,
+ 0
+ ],
+ "metallicFactor":0,
+ "roughnessFactor":0.9
+ }
+ }
+ ],
+ "meshes":[
+ {
+ "name":"pbrWithTexturesCube",
+ "primitives":[
+ {
+ "attributes":{
+ "POSITION":0,
+ "NORMAL":1,
+ "TEXCOORD_0":2
+ },
+ "indices":3,
+ "material":0
+ }
+ ]
+ },
+ {
+ "name":"unlitWithTexturesCube",
+ "primitives":[
+ {
+ "attributes":{
+ "POSITION":4,
+ "NORMAL":5,
+ "TEXCOORD_0":6
+ },
+ "indices":3,
+ "material":1
+ }
+ ]
+ },
+ {
+ "name":"pbrNoTexturesCube",
+ "primitives":[
+ {
+ "attributes":{
+ "POSITION":7,
+ "NORMAL":8,
+ "TEXCOORD_0":9,
+ "COLOR_0":10,
+ "COLOR_1":11
+ },
+ "indices":3,
+ "material":2
+ }
+ ]
+ },
+ {
+ "name":"unlitNoTexturesCube",
+ "primitives":[
+ {
+ "attributes":{
+ "POSITION":12,
+ "NORMAL":13,
+ "TEXCOORD_0":14,
+ "COLOR_0":15,
+ "COLOR_1":16
+ },
+ "indices":3,
+ "material":3
+ }
+ ]
+ }
+ ],
+ "textures":[
+ {
+ "sampler":0,
+ "source":0
+ },
+ {
+ "sampler":0,
+ "source":1
+ },
+ {
+ "sampler":0,
+ "source":2
+ },
+ {
+ "sampler":0,
+ "source":3
+ },
+ {
+ "sampler":0,
+ "source":3
+ }
+ ],
+ "images":[
+ {
+ "mimeType":"image/png",
+ "name":"EmissiveTexture",
+ "uri":"EmissiveTexture.png"
+ },
+ {
+ "mimeType":"image/png",
+ "name":"NormalTexture",
+ "uri":"NormalTexture.png"
+ },
+ {
+ "mimeType":"image/png",
+ "name":"MetallicRoughnessOcclusionTexture",
+ "uri":"MetallicRoughnessOcclusionTexture.png"
+ },
+ {
+ "mimeType":"image/png",
+ "name":"ColorTexture",
+ "uri":"ColorTexture.png"
+ }
+ ],
+ "accessors":[
+ {
+ "bufferView":0,
+ "componentType":5126,
+ "count":24,
+ "max":[
+ 1,
+ 1,
+ 1
+ ],
+ "min":[
+ -1,
+ -1,
+ -1
+ ],
+ "type":"VEC3"
+ },
+ {
+ "bufferView":1,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC3"
+ },
+ {
+ "bufferView":2,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC2"
+ },
+ {
+ "bufferView":3,
+ "componentType":5123,
+ "count":36,
+ "type":"SCALAR"
+ },
+ {
+ "bufferView":4,
+ "componentType":5126,
+ "count":24,
+ "max":[
+ 1,
+ 1,
+ 1
+ ],
+ "min":[
+ -1,
+ -1,
+ -1
+ ],
+ "type":"VEC3"
+ },
+ {
+ "bufferView":5,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC3"
+ },
+ {
+ "bufferView":6,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC2"
+ },
+ {
+ "bufferView":7,
+ "componentType":5126,
+ "count":24,
+ "max":[
+ 1,
+ 1,
+ 1
+ ],
+ "min":[
+ -1,
+ -1,
+ -1
+ ],
+ "type":"VEC3"
+ },
+ {
+ "bufferView":8,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC3"
+ },
+ {
+ "bufferView":9,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC2"
+ },
+ {
+ "bufferView":10,
+ "componentType":5121,
+ "count":24,
+ "normalized":true,
+ "type":"VEC4"
+ },
+ {
+ "bufferView":11,
+ "componentType":5123,
+ "count":24,
+ "normalized":true,
+ "type":"VEC4"
+ },
+ {
+ "bufferView":12,
+ "componentType":5126,
+ "count":24,
+ "max":[
+ 1,
+ 1,
+ 1
+ ],
+ "min":[
+ -1,
+ -1,
+ -1
+ ],
+ "type":"VEC3"
+ },
+ {
+ "bufferView":13,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC3"
+ },
+ {
+ "bufferView":14,
+ "componentType":5126,
+ "count":24,
+ "type":"VEC2"
+ },
+ {
+ "bufferView":15,
+ "componentType":5121,
+ "count":24,
+ "normalized":true,
+ "type":"VEC4"
+ },
+ {
+ "bufferView":16,
+ "componentType":5123,
+ "count":24,
+ "normalized":true,
+ "type":"VEC4"
+ }
+ ],
+ "bufferViews":[
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":0,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":288,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":192,
+ "byteOffset":576,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":72,
+ "byteOffset":768,
+ "target":34963
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":840,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":1128,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":192,
+ "byteOffset":1416,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":1608,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":1896,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":192,
+ "byteOffset":2184,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":96,
+ "byteOffset":2376,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":192,
+ "byteOffset":2472,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":2664,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":288,
+ "byteOffset":2952,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":192,
+ "byteOffset":3240,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":96,
+ "byteOffset":3432,
+ "target":34962
+ },
+ {
+ "buffer":0,
+ "byteLength":192,
+ "byteOffset":3528,
+ "target":34962
+ }
+ ],
+ "samplers":[
+ {
+ "magFilter":9729,
+ "minFilter":9987,
+ "wrapS":33071,
+ "wrapT":33071
+ }
+ ],
+ "buffers":[
+ {
+ "byteLength":3720,
+ "uri":"MaterialTestCubes.bin"
+ }
+ ]
+}
diff --git a/jme3-plugins/src/test/resources/gltf/MaterialTextCubes.blend b/jme3-plugins/src/test/resources/gltf/MaterialTextCubes.blend
new file mode 100644
index 0000000000..ee16d8624e
Binary files /dev/null and b/jme3-plugins/src/test/resources/gltf/MaterialTextCubes.blend differ
diff --git a/jme3-plugins/src/test/resources/gltf/MetallicRoughnessOcclusionTexture.png b/jme3-plugins/src/test/resources/gltf/MetallicRoughnessOcclusionTexture.png
new file mode 100644
index 0000000000..2fde5a0820
Binary files /dev/null and b/jme3-plugins/src/test/resources/gltf/MetallicRoughnessOcclusionTexture.png differ
diff --git a/jme3-plugins/src/test/resources/gltf/NormalTexture.png b/jme3-plugins/src/test/resources/gltf/NormalTexture.png
new file mode 100644
index 0000000000..fea5cc1a48
Binary files /dev/null and b/jme3-plugins/src/test/resources/gltf/NormalTexture.png differ