Skip to content

Commit 64ffd13

Browse files
authored
Adding full support for custom materials to GltfLoader (#2725)
* Fixed multiple extension handling in CustomContentManager If multiple GLTF extensions are present on an element, the CustomContentManager now processes all supported ones instead of just the first one. * Added a flag to GltfModelKey to switch between the old and new material creation process * Implemented new material creation process in GltfLoader * Step 1: Collect all material data in the new GltfMaterialData object * Step 1.1: GltfLoader reads all standard GLTF parameters * Step 1.2: ExtensionLoader and ExtrasLoader can read additional GLTF parameters * Step 2: Find a matching GltfMaterialFactory and use it to create the material * Added handling of GltfMaterialData objects for all material-based ExtensionLoaders * Retains the old MaterialAdapter handling for backward compatibility * Added GltfMaterialFactory implementations for BPRLighting and Unshaded material * Added both material factories as the default ones to the GltfLoader * Move default values for material parameters to material factories Reason: Setting default values directly in GltfMaterialData obscures whether a parameter is actually defined in the glTF material. The presence of material parameters may be relevant for some material factories. Therefore, default values should be set by the material factories themselves. * Deprecating most parts of the old MaterialAdapter system * Fixed vertex color flag in GltfLoader * Fixed QueueBucket for BlendMode.AlphaAdditive in GltfLoader * Fixed typo in emissive constants in GltfMaterialData * Fixed missing vertex coloring flag in GltfLoader * Smaller fixes in some components for loading GLTF files * PBREmissiveStrengthExtensionLoader no longer replaces other MaterialAdapters * Added material parameter tests to GltfLoaderTest
1 parent b00c824 commit 64ffd13

24 files changed

Lines changed: 1597 additions & 34 deletions

jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ private <T> T readExtension(String name, JsonElement el, T input) throws AssetLo
214214
continue;
215215
}
216216
try {
217-
return (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input);
217+
input = (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input);
218218
} catch (ClassCastException e) {
219219
throw new AssetLoadException("Extension loader " + loader.getClass().getName() + " for extension " + ext.getKey() + " is incompatible with type " + input.getClass(), e);
220220
}

jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java

Lines changed: 135 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*/
3232
package com.jme3.scene.plugins.gltf;
3333

34+
import static com.jme3.scene.plugins.gltf.GltfMaterialData.*;
3435
import static com.jme3.scene.plugins.gltf.GltfUtils.assertNotNull;
3536
import static com.jme3.scene.plugins.gltf.GltfUtils.findCommonAncestor;
3637
import static com.jme3.scene.plugins.gltf.GltfUtils.getAdapterForMaterial;
@@ -82,7 +83,7 @@
8283
import com.jme3.asset.AssetLoader;
8384
import com.jme3.asset.TextureKey;
8485
import com.jme3.material.Material;
85-
import com.jme3.material.RenderState;
86+
import com.jme3.material.RenderState.BlendMode;
8687
import com.jme3.math.ColorRGBA;
8788
import com.jme3.math.FastMath;
8889
import com.jme3.math.Matrix4f;
@@ -109,6 +110,7 @@
109110
import com.jme3.util.BufferInputStream;
110111
import com.jme3.util.BufferUtils;
111112
import com.jme3.util.IntMap;
113+
import com.jme3.util.SafeArrayList;
112114
import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
113115

114116
/**
@@ -143,13 +145,20 @@ public class GltfLoader implements AssetLoader {
143145
private final Vector3fArrayPopulator vector3fArrayPopulator = new Vector3fArrayPopulator();
144146
private final QuaternionArrayPopulator quaternionArrayPopulator = new QuaternionArrayPopulator();
145147
private final Matrix4fArrayPopulator matrix4fArrayPopulator = new Matrix4fArrayPopulator();
146-
private final Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>();
148+
@Deprecated private final Map<String, MaterialAdapter> defaultMaterialAdapters = new HashMap<>();
147149
private final CustomContentManager customContentManager = new CustomContentManager();
148150
private boolean useNormalsFlag = false;
149151

150152
Map<SkinData, List<Spatial>> skinnedSpatials = new HashMap<>();
151153
private final IntMap<SkinBuffers> skinBuffers = new IntMap<>();
152154

155+
private static SafeArrayList<GltfMaterialFactory> materialFactoryList = new SafeArrayList<>(GltfMaterialFactory.class);
156+
157+
static {
158+
materialFactoryList.add(new UnshadedMaterialFactory());
159+
materialFactoryList.add(new PBRLightingMaterialFactory());
160+
}
161+
153162
public GltfLoader() {
154163
defaultMaterialAdapters.put("pbrMetallicRoughness", new PBRMetalRoughMaterialAdapter());
155164
}
@@ -523,12 +532,17 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
523532

524533
Integer materialIndex = getAsInteger(meshObject, "material");
525534
if (materialIndex == null) {
526-
geom.setMaterial(defaultMat);
535+
// Create a new default material
536+
Material material = defaultMat.clone();
537+
material.setBoolean("UseVertexColor", useVertexColors);
538+
geom.setMaterial(material);
539+
527540
} else {
528541
useNormalsFlag = false;
529-
geom.setMaterial(readMaterial(materialIndex));
530-
if (geom.getMaterial().getAdditionalRenderState()
531-
.getBlendMode() == RenderState.BlendMode.Alpha) {
542+
Material material = readMaterial(materialIndex, useVertexColors);
543+
geom.setMaterial(material);
544+
BlendMode blendMode = material.getAdditionalRenderState().getBlendMode();
545+
if (blendMode == BlendMode.Alpha || blendMode == BlendMode.AlphaAdditive) {
532546
// Alpha blending is enabled for this material. Let's place the geom in the
533547
// transparent bucket.
534548
geom.setQueueBucket(RenderQueue.Bucket.Transparent);
@@ -540,10 +554,6 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
540554
}
541555
}
542556

543-
if (useVertexColors) {
544-
geom.getMaterial().setBoolean("UseVertexColor", useVertexColors);
545-
}
546-
547557
geom.setName(name + "_" + index);
548558

549559
geom.updateModelBound();
@@ -802,7 +812,75 @@ protected ByteBuffer getBytes(int bufferIndex, String uri, Integer bufferLength)
802812
return data;
803813
}
804814

805-
public Material readMaterial(int materialIndex) throws IOException {
815+
public Material readMaterial(int materialIndex, boolean usesVertexColors) throws IOException {
816+
// Fallback to the old material adapter system, if the legacy flag is set.
817+
if (GltfUtils.isMaterialAdaptersEnabled(info)) {
818+
return readMaterialUsingMaterialAdapters(materialIndex, usesVertexColors);
819+
}
820+
821+
assertNotNull(materials, "There is no material defined yet a mesh references one");
822+
JsonObject materialJson = materials.get(materialIndex).getAsJsonObject();
823+
824+
GltfMaterialData gltfMaterialData = readStandardMaterialParameters(materialJson);
825+
gltfMaterialData.setHasVertexColors(usesVertexColors);
826+
gltfMaterialData = customContentManager.readExtensionAndExtras("material", materialJson, gltfMaterialData);
827+
return createMaterial(gltfMaterialData, materialIndex);
828+
}
829+
830+
protected GltfMaterialData readStandardMaterialParameters(JsonObject materialJson) throws IOException {
831+
GltfMaterialData gltfMaterialData = new GltfMaterialData();
832+
gltfMaterialData.setGltfParam(MATERIAL_NAME_PARAM, getAsString(materialJson, "name"));
833+
834+
JsonObject pbrMetallicRoughnessJson = materialJson.getAsJsonObject("pbrMetallicRoughness");
835+
if (pbrMetallicRoughnessJson != null) {
836+
gltfMaterialData.setGltfParam(BASE_COLOR_PARAM, getAsColor(pbrMetallicRoughnessJson, "baseColorFactor"));
837+
gltfMaterialData.setGltfParam(BASE_COLOR_TEXTURE_PARAM, getAsTexture2D(pbrMetallicRoughnessJson, "baseColorTexture"));
838+
gltfMaterialData.setGltfParam(METALLIC_FACTOR_PARAM, getAsFloat(pbrMetallicRoughnessJson, "metallicFactor"));
839+
gltfMaterialData.setGltfParam(ROUGHNESS_FACTOR_PARAM, getAsFloat(pbrMetallicRoughnessJson, "roughnessFactor"));
840+
gltfMaterialData.setGltfParam(METALLIC_ROUGHNESS_TEXTURE_PARAM, getAsTexture2D(pbrMetallicRoughnessJson, "metallicRoughnessTexture"));
841+
}
842+
843+
JsonObject normalTextureJson = materialJson.getAsJsonObject("normalTexture");
844+
if (normalTextureJson != null) {
845+
gltfMaterialData.setGltfParam(NORMAL_TEXTURE_PARAM, readTexture(normalTextureJson));
846+
gltfMaterialData.setGltfParam(NORMAL_SCALE_PARAM, getAsFloat(normalTextureJson, "scale"));
847+
useNormalsFlag = true;
848+
}
849+
850+
JsonObject occlusionTextureJson = materialJson.getAsJsonObject("occlusionTexture");
851+
if (occlusionTextureJson != null) {
852+
gltfMaterialData.setGltfParam(OCCLUSION_TEXTURE_PARAM, readTexture(occlusionTextureJson));
853+
gltfMaterialData.setGltfParam(OCCLUSION_TEXTURE_STRENGTH_PARAM, getAsFloat(occlusionTextureJson, "strength"));
854+
}
855+
856+
gltfMaterialData.setGltfParam(EMISSIVE_TEXTURE_PARAM, getAsTexture2D(materialJson, "emissiveTexture"));
857+
gltfMaterialData.setGltfParam(EMISSIVE_COLOR_PARAM, getAsColor(materialJson, "emissiveFactor"));
858+
859+
String alphaMode = getAsString(materialJson, "alphaMode");
860+
gltfMaterialData.setGltfParam(ALPHA_MODE_PARAM, alphaMode);
861+
if ("MASK".equals(alphaMode)) {
862+
gltfMaterialData.setGltfParam(ALPHA_CUTOFF_PARAM, getAsFloat(materialJson, "alphaCutoff"));
863+
}
864+
865+
gltfMaterialData.setGltfParam(DOUBLE_SIDED_PARAM, getAsBoolean(materialJson, "doubleSided"));
866+
867+
return gltfMaterialData;
868+
}
869+
870+
protected Material createMaterial(GltfMaterialData gltfMaterialData, int materialIndex) {
871+
for (GltfMaterialFactory gltfMaterialFactory : materialFactoryList) {
872+
if (gltfMaterialFactory.accepts(info.getKey(), gltfMaterialData)) {
873+
return gltfMaterialFactory.createMaterial(info.getManager(), info.getKey(), gltfMaterialData);
874+
}
875+
}
876+
877+
logger.log(Level.WARNING, "Couldn't find any matching GltfMaterialFactory for material " + materialIndex);
878+
useNormalsFlag = false;
879+
return defaultMat;
880+
}
881+
882+
@Deprecated
883+
protected Material readMaterialUsingMaterialAdapters(int materialIndex, boolean usesVertexColors) throws IOException {
806884
assertNotNull(materials, "There is no material defined yet a mesh references one");
807885

808886
JsonObject matData = materials.get(materialIndex).getAsJsonObject();
@@ -875,6 +953,8 @@ public Material readMaterial(int materialIndex) throws IOException {
875953

876954
adapter.setParam("emissiveTexture", readTexture(matData.getAsJsonObject("emissiveTexture")));
877955

956+
adapter.setParam("usesVertexColors", usesVertexColors);
957+
878958
return adapter.getMaterial();
879959
}
880960

@@ -922,6 +1002,10 @@ public void readCameras() throws IOException {
9221002
}
9231003
}
9241004

1005+
protected Texture2D getAsTexture2D(JsonObject jsonObject, String textureName) throws IOException {
1006+
return readTexture(jsonObject.getAsJsonObject(textureName));
1007+
}
1008+
9251009
public Texture2D readTexture(JsonObject texture) throws IOException {
9261010
return readTexture(texture, false);
9271011
}
@@ -1715,4 +1799,44 @@ public static void registerDefaultExtrasLoader(Class<? extends ExtrasLoader> loa
17151799
public static void unregisterDefaultExtrasLoader() {
17161800
CustomContentManager.defaultExtraLoaderClass = UserDataLoader.class;
17171801
}
1802+
1803+
/**
1804+
* Registers a new material factory and places it before all existing factories.<br/>
1805+
* The ordering of these factories defines their priority. When a new material needs to be created,
1806+
* the loader searches for the first material factory that accepts the given material data.
1807+
*
1808+
* @param materialFactory The {@link GltfMaterialFactory} to register.
1809+
*/
1810+
public static void registerMaterialFactoryFirst(GltfMaterialFactory materialFactory) {
1811+
unregisterMaterialFactory(materialFactory.getClass());
1812+
materialFactoryList.add(0, materialFactory);
1813+
}
1814+
1815+
/**
1816+
* Registers a new material factory and places it behind all existing factories.<br/>
1817+
* The ordering of these factories defines their priority. When a new material needs to be created,
1818+
* the loader searches for the first material factory that accepts the given material data.
1819+
*
1820+
* @param materialFactory The {@link GltfMaterialFactory} to register.
1821+
*/
1822+
public static void registerMaterialFactoryLast(GltfMaterialFactory materialFactory) {
1823+
unregisterMaterialFactory(materialFactory.getClass());
1824+
materialFactoryList.add(materialFactory);
1825+
}
1826+
1827+
/**
1828+
* Unregisters a material factory by its class.
1829+
*
1830+
* @param materialFactoryClass The class of the {@link GltfMaterialFactory} to unregister.
1831+
*/
1832+
public static void unregisterMaterialFactory(Class<? extends GltfMaterialFactory> materialFactoryClass) {
1833+
materialFactoryList.removeIf(materialFactoryClass::isInstance);
1834+
}
1835+
1836+
/**
1837+
* Unregisters all material factories.
1838+
*/
1839+
public static void unregisterAllMaterialFactories() {
1840+
materialFactoryList.clear();
1841+
}
17181842
}

0 commit comments

Comments
 (0)