Skip to content

Commit ff683b9

Browse files
committed
Throw exception when unsupported extension is required
1 parent a8b89f8 commit ff683b9

File tree

2 files changed

+161
-47
lines changed

2 files changed

+161
-47
lines changed

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

Lines changed: 126 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,22 @@
3131
*/
3232
package com.jme3.scene.plugins.gltf;
3333

34-
import com.jme3.plugins.json.JsonArray;
35-
import com.jme3.plugins.json.JsonElement;
36-
import com.jme3.asset.AssetLoadException;
3734
import java.io.IOException;
3835
import java.lang.reflect.InvocationTargetException;
36+
import java.util.ArrayList;
37+
import java.util.Collections;
3938
import java.util.HashMap;
39+
import java.util.List;
4040
import java.util.Map;
4141
import java.util.concurrent.ConcurrentHashMap;
4242
import java.util.logging.Level;
4343
import java.util.logging.Logger;
4444

45+
import com.jme3.asset.AssetLoadException;
46+
import com.jme3.plugins.json.JsonArray;
47+
import com.jme3.plugins.json.JsonElement;
48+
import com.jme3.plugins.json.JsonObject;
49+
4550
/**
4651
* Created by Nehon on 20/08/2017.
4752
*/
@@ -56,6 +61,10 @@ public class CustomContentManager {
5661
private GltfLoader gltfLoader;
5762

5863

64+
/**
65+
* The mapping from glTF extension names to the classes that
66+
* represent the respective laoders.
67+
*/
5968
static final Map<String, Class<? extends ExtensionLoader>> defaultExtensionLoaders = new ConcurrentHashMap<>();
6069
static {
6170
defaultExtensionLoaders.put("KHR_materials_pbrSpecularGlossiness", PBRSpecGlossExtensionLoader.class);
@@ -64,9 +73,13 @@ public class CustomContentManager {
6473
defaultExtensionLoaders.put("KHR_texture_transform", TextureTransformExtensionLoader.class);
6574
defaultExtensionLoaders.put("KHR_materials_emissive_strength", PBREmissiveStrengthExtensionLoader.class);
6675
defaultExtensionLoaders.put("KHR_draco_mesh_compression", DracoMeshCompressionExtensionLoader.class);
67-
6876
}
6977

78+
/**
79+
* The mapping from glTF extension names to the actual loader instances
80+
* that have been lazily created from the defaultExtensionLoaders,
81+
* in {@link #findExtensionLoader(String)}
82+
*/
7083
private final Map<String, ExtensionLoader> loadedExtensionLoaders = new HashMap<>();
7184

7285
public CustomContentManager() {
@@ -104,30 +117,82 @@ void init(GltfLoader gltfLoader) {
104117
this.key = (GltfModelKey) gltfLoader.getInfo().getKey();
105118
}
106119

107-
JsonArray extensionUsed = gltfLoader.getDocRoot().getAsJsonArray("extensionsUsed");
108-
if (extensionUsed != null) {
109-
for (JsonElement extElem : extensionUsed) {
110-
String ext = extElem.getAsString();
111-
if (ext != null) {
112-
if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) {
113-
logger.log(Level.WARNING, "Extension " + ext + " is not supported, please provide your own implementation in the GltfModelKey");
114-
}
115-
}
120+
// For extensions that are USED but not supported, print a warning
121+
List<String> extensionsUsed = getArrayAsStringList(gltfLoader.getDocRoot(), "extensionsUsed");
122+
for (String extensionName : extensionsUsed) {
123+
if (!isExtensionSupported(extensionName)) {
124+
logger.log(Level.WARNING, "Extension " + extensionName
125+
+ " is not supported, please provide your own implementation in the GltfModelKey");
116126
}
117127
}
118-
JsonArray extensionRequired = gltfLoader.getDocRoot().getAsJsonArray("extensionsRequired");
119-
if (extensionRequired != null) {
120-
for (JsonElement extElem : extensionRequired) {
121-
String ext = extElem.getAsString();
122-
if (ext != null) {
123-
if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) {
124-
logger.log(Level.SEVERE, "Extension " + ext + " is mandatory for this file, the loaded scene result will be unexpected.");
125-
}
128+
129+
// For extensions that are REQUIRED but not supported,
130+
// throw an AssetLoadException by default
131+
// If the GltfModelKey#isStrict returns false, then
132+
// still print an error message, at least
133+
List<String> extensionsRequired = getArrayAsStringList(gltfLoader.getDocRoot(), "extensionsRequired");
134+
for (String extensionName : extensionsRequired) {
135+
if (!isExtensionSupported(extensionName)) {
136+
if (this.key != null && this.key.isStrict()) {
137+
throw new AssetLoadException(
138+
"Extension " + extensionName + " is required for this file.");
139+
} else {
140+
logger.log(Level.SEVERE, "Extension " + extensionName
141+
+ " is required for this file. The behavior of the loader is unspecified.");
126142
}
127143
}
128144
}
129145
}
130146

147+
/**
148+
* Returns a (possibly unmodifiable) list of the string representations of the elements in the specified
149+
* array, or an empty list if the specified array does not exist.
150+
*
151+
* @param jsonObject
152+
* The JSON object
153+
* @param property
154+
* The property name of the array property
155+
* @return The list
156+
*/
157+
private static List<String> getArrayAsStringList(JsonObject jsonObject, String property) {
158+
JsonArray jsonArray = jsonObject.getAsJsonArray(property);
159+
if (jsonArray == null) {
160+
return Collections.emptyList();
161+
}
162+
List<String> list = new ArrayList<String>();
163+
for (JsonElement jsonElement : jsonArray) {
164+
String string = jsonElement.getAsString();
165+
if (string != null) {
166+
list.add(string);
167+
}
168+
}
169+
return list;
170+
}
171+
172+
/**
173+
* Returns whether the specified glTF extension is supported.
174+
*
175+
* The given string is the name of the extension, e.g. <code>KHR_texture_transform</code>.
176+
*
177+
* This will return whether there is a default extension loader for the given extension registered in the
178+
* {@link #defaultExtensionLoaders}, or whether the <code>GltfModelKey</code> that was obtained from the
179+
* <code>GltfLoader</code> contains a custom extension loader that was registered via
180+
* {@link GltfModelKey#registerExtensionLoader(String, ExtensionLoader)}.
181+
*
182+
* @param ext
183+
* The glTF extension name
184+
* @return Whether the given extension is supported
185+
*/
186+
private boolean isExtensionSupported(String ext) {
187+
if (defaultExtensionLoaders.containsKey(ext)) {
188+
return true;
189+
}
190+
if (this.key != null && this.key.getExtensionLoader(ext) != null) {
191+
return true;
192+
}
193+
return false;
194+
}
195+
131196
public <T> T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException, IOException {
132197
T output = readExtension(name, el, input);
133198
output = readExtras(name, el, output);
@@ -142,36 +207,11 @@ private <T> T readExtension(String name, JsonElement el, T input) throws AssetLo
142207
}
143208

144209
for (Map.Entry<String, JsonElement> ext : extensions.getAsJsonObject().entrySet()) {
145-
ExtensionLoader loader = null;
146-
147-
if (key != null) {
148-
loader = key.getExtensionLoader(ext.getKey());
149-
}
150-
151-
if (loader == null) {
152-
loader = loadedExtensionLoaders.get(ext.getKey());
153-
if (loader == null) {
154-
try {
155-
Class<? extends ExtensionLoader> clz = defaultExtensionLoaders.get(ext.getKey());
156-
if (clz != null) {
157-
loader = clz.getDeclaredConstructor().newInstance();
158-
}
159-
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
160-
logger.log(Level.WARNING, "Could not instantiate loader", e);
161-
}
162-
163-
if (loader != null) {
164-
loadedExtensionLoaders.put(ext.getKey(), loader);
165-
}
166-
}
167-
}
168-
169-
210+
ExtensionLoader loader = findExtensionLoader(ext.getKey());
170211
if (loader == null) {
171212
logger.log(Level.WARNING, "Could not find loader for extension " + ext.getKey());
172213
continue;
173214
}
174-
175215
try {
176216
return (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input);
177217
} catch (ClassCastException e) {
@@ -182,6 +222,45 @@ private <T> T readExtension(String name, JsonElement el, T input) throws AssetLo
182222
return input;
183223
}
184224

225+
/**
226+
* Returns the <code>ExtensionLoader</code> for the given glTF extension name.
227+
*
228+
* The extension name is a name like <code>KHR_texture_transform</code>. This method will first try to
229+
* return the custom extension loader that was registered in the GltfModelKey.
230+
*
231+
* If it does not exist, it will return an instance of the default extension loader that was registered
232+
* for the given extension, lazily creating the instance based on the registered defaultExtensionLoaders.
233+
*
234+
* @param extensionName
235+
* The extension name
236+
* @return The loader, or <code>null</code> if no loader could be found or instantiated
237+
*/
238+
private ExtensionLoader findExtensionLoader(String extensionName) {
239+
if (key != null) {
240+
ExtensionLoader loader = key.getExtensionLoader(extensionName);
241+
if (loader != null) {
242+
return loader;
243+
}
244+
}
245+
246+
ExtensionLoader loader = loadedExtensionLoaders.get(extensionName);
247+
if (loader != null) {
248+
return loader;
249+
}
250+
try {
251+
Class<? extends ExtensionLoader> clz = defaultExtensionLoaders.get(extensionName);
252+
if (clz != null) {
253+
loader = clz.getDeclaredConstructor().newInstance();
254+
loadedExtensionLoaders.put(extensionName, loader);
255+
}
256+
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
257+
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
258+
logger.log(Level.WARNING, "Could not instantiate loader", e);
259+
}
260+
return loader;
261+
262+
}
263+
185264
@SuppressWarnings("unchecked")
186265
private <T> T readExtras(String name, JsonElement el, T input) throws AssetLoadException {
187266
ExtrasLoader loader = null;

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,46 @@ public class GltfModelKey extends ModelKey {
5757
private boolean keepSkeletonPose = false;
5858
private ExtrasLoader extrasLoader;
5959

60+
/**
61+
* The flag indicating whether the loader should perform stricter consistency checks of the supported glTF
62+
* extensions.
63+
*
64+
* When this is <code>true</code>, then the loader will cause an <code>AssetLoadException</code> when it
65+
* encounters an asset that contains an extension in its <code>extensionsRequired</code> declaration that
66+
* is not supported.
67+
*/
68+
private boolean strictExtensionCheck;
69+
6070
public GltfModelKey(String name) {
6171
super(name);
72+
this.strictExtensionCheck = true;
6273
}
6374

6475
public GltfModelKey() {
76+
this.strictExtensionCheck = true;
77+
}
78+
79+
/**
80+
* Set whether the loader should perform stricter consistency checks when loading a model. Details are not
81+
* specified for now.
82+
*
83+
* The default value is <code>true</code>.
84+
*
85+
* @param strict
86+
* The flag
87+
*/
88+
public void setStrict(boolean strict) {
89+
this.strictExtensionCheck = strict;
90+
}
91+
92+
/**
93+
* Returns whether the loader should perform stricter consistency checks when loading a model. Details are
94+
* not specified for now.
95+
*
96+
* @return The flag
97+
*/
98+
public boolean isStrict() {
99+
return strictExtensionCheck;
65100
}
66101

67102
/**

0 commit comments

Comments
 (0)