3131 */
3232package 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 ;
3734import java .io .IOException ;
3835import java .lang .reflect .InvocationTargetException ;
36+ import java .util .ArrayList ;
37+ import java .util .Collections ;
3938import java .util .HashMap ;
39+ import java .util .List ;
4040import java .util .Map ;
4141import java .util .concurrent .ConcurrentHashMap ;
4242import java .util .logging .Level ;
4343import 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 ;
0 commit comments