Browse Source

Fixes #202 Import the UserData for GLTF

MeFisto94 5 years ago
parent
commit
5264848673

+ 11 - 0
jme3-blender/src/com/jme3/gde/blender/GLTFDataObject.java

@@ -31,7 +31,10 @@
  */
 package com.jme3.gde.blender;
 
+import com.jme3.asset.ModelKey;
 import com.jme3.gde.core.assets.SpatialAssetDataObject;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.gltf.GltfModelKey;
 import java.io.IOException;
 import org.openide.awt.ActionID;
 import org.openide.awt.ActionReference;
@@ -93,4 +96,12 @@ public class GLTFDataObject extends SpatialAssetDataObject {
     public GLTFDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
         super(pf, loader);
     }
+
+    @Override
+    public synchronized ModelKey getAssetKey() {
+        GltfModelKey gltf = new GltfModelKey(super.getAssetKey().getName());
+        gltf.setExtrasLoader(GltfExtrasLoader.INSTANCE);
+        return gltf;
+    }
+    
 }

+ 183 - 0
jme3-blender/src/com/jme3/gde/blender/GltfExtrasLoader.java

@@ -0,0 +1,183 @@
+/*
+ * $Id$
+ * 
+ * Copyright (c) 2019, Simsilica, LLC
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions 
+ * are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer.
+ * 
+ * 2. 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.
+ * 
+ * 3. Neither the name of the copyright holder 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 HOLDER 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.gde.blender;
+
+import java.lang.reflect.Array;
+import java.util.*;
+
+import org.slf4j.*;
+
+import com.google.gson.*;
+
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.gltf.*;
+
+/**
+ *  Attaches GLTF "extras" data to objects as appropriate.<br />
+ *  <b>Note:</b> Taken by MeFisto94 and modified to match for the SDK.
+ *
+ *  @author    Paul Speed
+ */
+public class GltfExtrasLoader implements ExtrasLoader {
+    static Logger log = LoggerFactory.getLogger(GltfExtrasLoader.class);
+    
+    public static final GltfExtrasLoader INSTANCE = new GltfExtrasLoader();
+    
+    public GltfExtrasLoader() {
+    }
+    
+    /**
+     *  Utility method to create a ModelKey that is configured to use this
+     *  ExtrasLoader.
+     */
+    public static GltfModelKey createModelKey( String path ) {
+        GltfModelKey key = new GltfModelKey(path);
+        key.setExtrasLoader(INSTANCE);
+        return key;
+    } 
+    
+    @Override
+    public Object handleExtras( GltfLoader loader, String parentName, 
+                                JsonElement parent, JsonElement extras, Object input ) {
+ 
+        log.debug("handleExtras(" + loader + ", " + parentName + ", " + parent + ", " + extras + ", " + input + ")");     
+
+        // Only interested in composite objects
+        if( !extras.isJsonObject() ) {
+            log.warn("Skipping extras:" + extras);
+            return input; 
+        }
+        JsonObject jo = extras.getAsJsonObject();
+        apply(input, jo);
+        return input;
+    }
+    
+    protected void apply( Object input, JsonObject extras ) {
+        if( input == null ) {
+            return;
+        }
+        if( input.getClass().isArray() ) {
+            applyToArray(input, extras);   
+        } else if( input instanceof Spatial ) {
+            applyToSpatial((Spatial)input, extras); 
+        } else {
+            log.warn("Unhandled input type:" + input.getClass());
+        }
+    }
+    
+    protected void applyToArray( Object array, JsonObject extras ) {
+        int size = Array.getLength(array);
+        for( int i = 0; i < size; i++ ) {
+            Object o = Array.get(array, i);            
+            log.debug("processing array[" + i + "]:" + o);
+            apply(o, extras);
+        }
+    }
+    
+    protected void applyToSpatial( Spatial spatial, JsonObject extras ) {
+        for( Map.Entry<String, JsonElement> el : extras.entrySet() ) {
+            log.debug(el.toString());
+            Object val = toAttribute(el.getValue(), false);
+            if( log.isDebugEnabled() ) {
+                log.debug("setUserData(" + el.getKey() + ", " + val + ")");
+            }            
+            spatial.setUserData(el.getKey(), val);
+        }         
+    }
+ 
+    protected Object toAttribute( JsonElement el, boolean nested ) {
+        if( el.isJsonObject() ) {
+            return toAttribute(el.getAsJsonObject(), nested);
+        } else if( el.isJsonArray() ) {
+            return toAttribute(el.getAsJsonArray(), nested);
+        } else if( el.isJsonPrimitive() ) {
+            return toAttribute(el.getAsJsonPrimitive(), nested);
+        } else if( el.isJsonNull() ) {
+            return null;
+        }
+        log.warn("Unhandled extras element:" + el);
+        return null;       
+    }
+    
+    protected Object toAttribute( JsonObject jo, boolean nested ) {
+        Map<String, Object> result = new HashMap<>();
+        for( Map.Entry<String, JsonElement> el : jo.entrySet() ) {
+            result.put(el.getKey(), toAttribute(el.getValue(), true)); 
+        }
+        return result;       
+    }
+    
+    protected Object toAttribute( JsonArray ja, boolean nested ) {
+        List<Object> result = new ArrayList<>();
+        for( JsonElement el : ja ) {
+            result.add(toAttribute(el, true));
+        }
+        return result;
+    }
+    
+    protected Object toAttribute( JsonPrimitive jp, boolean nested ) {
+        if( jp.isBoolean() ) {
+            return jp.getAsBoolean();
+        } else if( jp.isNumber() ) {
+            // JME doesn't save Maps properly and treats them as two
+            // separate Lists... and it doesn't like saving Doubles
+            // in lists so we'll just return strings in the case where
+            // the value would end up in a map.  If users someday really
+            // need properly typed map values and JME map storage hasn't
+            // been fixed then perhaps we give the users the option of
+            // flattening the nested properties into dot notation, ie:
+            // all directly on UserData with no Map children.
+            if( nested ) {
+                return jp.getAsString();
+            }
+            Number num = jp.getAsNumber();
+            // JME doesn't like to save GSON's LazilyParsedNumber so we'll
+            // convert it into a real number.  I don't think we can reliably
+            // guess what type of number the user intended.  It would take
+            // some expirimentation to determine if things like 0.0 are preserved
+            // during export or just get exported as 0.
+            // Rather than randomly flip-flop between number types depending
+            // on the inclusion (or not) of a decimal point, we'll just always
+            // return Double. 
+            return num.doubleValue();
+        } else if( jp.isString() ) {
+            return jp.getAsString();
+        }
+        log.warn("Unhandled primitive:" + jp);
+        return null;
+    }
+}