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..211ad735dd 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());
}
@@ -526,9 +535,10 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
geom.setMaterial(defaultMat);
} 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 +550,6 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
}
}
- if (useVertexColors) {
- geom.getMaterial().setBoolean("UseVertexColor", useVertexColors);
- }
-
geom.setName(name + "_" + index);
geom.updateModelBound();
@@ -802,7 +808,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);
+ }
+
+ 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) throws IOException {
assertNotNull(materials, "There is no material defined yet a mesh references one");
JsonObject matData = materials.get(materialIndex).getAsJsonObject();
@@ -922,6 +996,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 +1793,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/GltfMaterialFactory.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java
new file mode 100644
index 0000000000..7d4eabf5f4
--- /dev/null
+++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+/**
+ * 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 at the {@link GltfLoader} by using one of its
+ * static register methods.
+ *
+ */
+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..cea372e7df 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);
}
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..a29691a669 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
@@ -33,7 +33,13 @@
import com.jme3.asset.AssetKey;
import com.jme3.plugins.json.JsonElement;
+import com.jme3.plugins.json.JsonObject;
+
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,11 +47,37 @@
* @author codex
*/
public class PBREmissiveStrengthExtensionLoader implements ExtensionLoader {
-
+
+ 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());
+
+ @Deprecated
private PBREmissiveStrengthMaterialAdapter materialAdapter = new PBREmissiveStrengthMaterialAdapter();
-
+
@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(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;
AssetKey key = loader.getInfo().getKey();
//check for a custom adapter for emissive strength
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/PBREmissiveStrengthMaterialAdapter.java
index 5d078c97f7..59386c0a8b 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/PBREmissiveStrengthMaterialAdapter.java
@@ -35,7 +35,11 @@
* Adapter for converting GLTF emissive strength to JME emissive intensity.
*
* @author codex
+ *
+ * @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 PBREmissiveStrengthMaterialAdapter extends PBRMaterialAdapter {
public PBREmissiveStrengthMaterialAdapter() {
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..5dcf6761c2
--- /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));
+
+ 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..6e73560548 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 {
/**
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..9b32dc8fce 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() {
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);
+ }
+
+}