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 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 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); + } + +}