diff --git a/jme3-blender/build.xml b/jme3-blender/build.xml
new file mode 100644
index 000000000..535b63fb0
--- /dev/null
+++ b/jme3-blender/build.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+ Builds, tests, and runs the project com.jme3.gde.blender.
+
+
diff --git a/jme3-blender/manifest.mf b/jme3-blender/manifest.mf
new file mode 100644
index 000000000..3e10413d0
--- /dev/null
+++ b/jme3-blender/manifest.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module: com.jme3.gde.blender
+OpenIDE-Module-Implementation-Version: 0
+OpenIDE-Module-Localizing-Bundle: com/jme3/gde/blender/Bundle.properties
+
diff --git a/jme3-blender/nbproject/build-impl.xml b/jme3-blender/nbproject/build-impl.xml
new file mode 100644
index 000000000..7232c5d6f
--- /dev/null
+++ b/jme3-blender/nbproject/build-impl.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ You must set 'suite.dir' to point to your containing module suite
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jme3-blender/nbproject/genfiles.properties b/jme3-blender/nbproject/genfiles.properties
new file mode 100644
index 000000000..3f9c08a70
--- /dev/null
+++ b/jme3-blender/nbproject/genfiles.properties
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=46157a9c
+build.xml.script.CRC32=0f77a514
+build.xml.stylesheet.CRC32=15ca8a54@2.79
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=46157a9c
+nbproject/build-impl.xml.script.CRC32=5c5042d6
+nbproject/build-impl.xml.stylesheet.CRC32=49aa68b0@2.79
diff --git a/jme3-blender/nbproject/project.properties b/jme3-blender/nbproject/project.properties
new file mode 100644
index 000000000..c67aa1b97
--- /dev/null
+++ b/jme3-blender/nbproject/project.properties
@@ -0,0 +1,8 @@
+#Thu, 25 Aug 2011 20:26:50 +0200
+javac.source=21
+javac.compilerargs=-Xlint -Xlint\:-serial
+license.file=../license-jme.txt
+nbm.homepage=https://www.jmonkeyengine.org
+nbm.module.author=Kaelthas
+nbm.needs.restart=true
+spec.version.base=3.5.2
diff --git a/jme3-blender/nbproject/project.xml b/jme3-blender/nbproject/project.xml
new file mode 100644
index 000000000..f9dc8f1ee
--- /dev/null
+++ b/jme3-blender/nbproject/project.xml
@@ -0,0 +1,173 @@
+
+
+ org.netbeans.modules.apisupport.project
+
+
+ com.jme3.gde.blender
+
+
+
+ com.jme3.gde.core
+
+
+
+ 1
+ 3.6.1
+
+
+
+ com.jme3.gde.core.baselibs
+
+
+
+ 1
+ 3.6.1
+
+
+
+ com.jme3.gde.core.libraries
+
+
+
+ 1
+ 3.6.1
+
+
+
+ org.netbeans.api.progress
+
+
+
+ 1
+ 1.46.1
+
+
+
+ org.netbeans.api.progress.nb
+
+
+
+ 1.46.1
+
+
+
+ org.netbeans.api.templates
+
+
+
+ 1.6.1
+
+
+
+ org.netbeans.modules.projectapi
+
+
+
+ 1
+ 1.60.2
+
+
+
+ org.netbeans.modules.websvc.jaxws21api
+
+
+
+ 1
+ 1.34.1
+
+
+
+ org.openide.awt
+
+
+
+ 7.62.1
+
+
+
+ org.openide.dialogs
+
+
+
+ 7.38.1
+
+
+
+ org.openide.filesystems
+
+
+
+ 9.7.1
+
+
+
+ org.openide.filesystems.nb
+
+
+
+ 9.7.1
+
+
+
+ org.openide.loaders
+
+
+
+ 7.57.2
+
+
+
+ org.openide.modules
+
+
+
+ 7.43.1
+
+
+
+ org.openide.nodes
+
+
+
+ 7.39.1
+
+
+
+ org.openide.util
+
+
+
+ 8.39.1
+
+
+
+ org.openide.util.lookup
+
+
+
+ 8.25.1
+
+
+
+ org.openide.util.ui
+
+
+
+ 9.4.1
+
+
+
+ org.openide.windows
+
+
+
+ 6.71.1
+
+
+
+
+ com.jme3.gde.blender
+
+
+
+
diff --git a/jme3-blender/nbproject/suite.properties b/jme3-blender/nbproject/suite.properties
new file mode 100644
index 000000000..29d7cc9bd
--- /dev/null
+++ b/jme3-blender/nbproject/suite.properties
@@ -0,0 +1 @@
+suite.dir=${basedir}/..
diff --git a/jme3-blender/src/com/jme3/gde/blender/Bundle.properties b/jme3-blender/src/com/jme3/gde/blender/Bundle.properties
new file mode 100644
index 000000000..8ecfde7d0
--- /dev/null
+++ b/jme3-blender/src/com/jme3/gde/blender/Bundle.properties
@@ -0,0 +1,5 @@
+OpenIDE-Module-Display-Category=jMonkeyEngine
+OpenIDE-Module-Long-Description=\
+ This plugin adds support for GLTF's .gltf .glb and .bin files
+OpenIDE-Module-Name=GLTF Support
+OpenIDE-Module-Short-Description=Adds support for GLTF's .gltf .glb and .bin files
\ No newline at end of file
diff --git a/jme3-blender/src/com/jme3/gde/blender/GLTFDataObject.java b/jme3-blender/src/com/jme3/gde/blender/GLTFDataObject.java
new file mode 100644
index 000000000..a428f26aa
--- /dev/null
+++ b/jme3-blender/src/com/jme3/gde/blender/GLTFDataObject.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2009-2020 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.gde.blender;
+
+import com.jme3.asset.ModelKey;
+import com.jme3.gde.core.assets.SpatialAssetDataObject;
+import com.jme3.scene.plugins.gltf.GltfModelKey;
+import java.io.IOException;
+import org.openide.awt.ActionID;
+import org.openide.awt.ActionReference;
+import org.openide.awt.ActionReferences;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.MIMEResolver;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectExistsException;
+import org.openide.loaders.MultiFileLoader;
+import org.openide.util.NbBundle.Messages;
+
+@Messages({
+ "LBL_GLTF_LOADER=GLTF Files"
+})
+@MIMEResolver.ExtensionRegistration(
+ displayName="#LBL_GLTF_LOADER",
+ mimeType="model/gltf+json",
+ //mimeType="model/gltf-binary",
+ // glb has another mimeType but for us, this is irrelevant. The Actions are the same
+ extension={ "gltf", "glb" }
+)
+@DataObject.Registration(
+ mimeType = "model/gltf+json",
+ iconBase = "com/jme3/gde/blender/glTF.svg",
+ displayName="#LBL_GLTF_LOADER",
+ position=300
+)
+@ActionReferences(value = {
+ @ActionReference(id =
+ @ActionID(category = "jMonkeyPlatform", id = "com.jme3.gde.core.assets.actions.ConvertModel"), path = "Loaders/model/gltf+json/Actions", position = 10),
+ @ActionReference(id =
+ @ActionID(category = "jMonkeyPlatform", id = "com.jme3.gde.core.assets.actions.OpenModel"), path = "Loaders/model/gltf+json/Actions", position = 20),
+ @ActionReference(id =
+ @ActionID(category = "Edit", id = "org.openide.actions.CutAction"), path = "Loaders/model/gltf+json/Actions", position = 200, separatorBefore = 100),
+ @ActionReference(id =
+ @ActionID(category = "Edit", id = "org.openide.actions.CopyAction"), path = "Loaders/model/gltf+json/Actions", position = 300, separatorAfter = 400),
+ @ActionReference(id =
+ @ActionID(category = "Edit", id = "org.openide.actions.DeleteAction"), path = "Loaders/model/gltf+json/Actions", position = 500),
+ @ActionReference(id =
+ @ActionID(category = "System", id = "org.openide.actions.RenameAction"), path = "Loaders/model/gltf+json/Actions", position = 600, separatorAfter = 700),
+ @ActionReference(id =
+ @ActionID(category = "System", id = "org.openide.actions.SaveAsTemplateAction"), path = "Loaders/model/gltf+json/Actions", position = 800, separatorAfter = 900),
+ @ActionReference(id =
+ @ActionID(category = "System", id = "org.openide.actions.FileSystemAction"), path = "Loaders/model/gltf+json/Actions", position = 1000, separatorAfter = 1100),
+ @ActionReference(id =
+ @ActionID(category = "System", id = "org.openide.actions.ToolsAction"), path = "Loaders/model/gltf+json/Actions", position = 1200),
+ @ActionReference(id =
+ @ActionID(category = "System", id = "org.openide.actions.PropertiesAction"), path = "Loaders/model/gltf+json/Actions", position = 1300)
+})
+
+/**
+ * This class enables the SDK to open and convert .gltf and
+ * glb files.
+ *
+ * @author MeFisto94
+ */
+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;
+ }
+
+}
diff --git a/jme3-blender/src/com/jme3/gde/blender/GltfExtrasLoader.java b/jme3-blender/src/com/jme3/gde/blender/GltfExtrasLoader.java
new file mode 100644
index 000000000..a4ddbf7e3
--- /dev/null
+++ b/jme3-blender/src/com/jme3/gde/blender/GltfExtrasLoader.java
@@ -0,0 +1,199 @@
+/*
+ * $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 com.jme3.plugins.json.JsonArray;
+import com.jme3.plugins.json.JsonElement;
+import com.jme3.plugins.json.JsonObject;
+import com.jme3.plugins.json.JsonPrimitive;
+import java.lang.reflect.Array;
+import java.util.*;
+
+import org.slf4j.*;
+
+import com.jme3.scene.Spatial;
+import com.jme3.scene.plugins.gltf.*;
+
+/**
+ * Attaches GLTF "extras" data to objects as appropriate.
+ * Note: 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
+ JsonObject jo = null;
+ try {
+ jo = extras.getAsJsonObject();
+ }
+ catch(Exception e) {
+ log.warn("Skipping extras:" + extras, e);
+ }
+
+ if(jo == null) {
+ log.warn("Skipping extras:" + extras);
+ return input;
+ }
+
+ 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 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 ) {
+ try {
+ return toAttribute(el.getAsJsonObject(), nested);
+ } catch (Exception e) {
+ }
+
+ try {
+ return toAttribute(el.getAsJsonArray(), nested);
+ } catch (Exception e) {
+ }
+
+ try {
+ return toAttribute(el.getAsJsonPrimitive(), nested);
+ } catch (Exception e) {
+ }
+
+ log.warn("Unhandled extras element:" + el);
+ return null;
+ }
+
+ protected Object toAttribute( JsonObject jo, boolean nested ) {
+ Map result = new HashMap<>();
+ for( Map.Entry el : jo.entrySet() ) {
+ result.put(el.getKey(), toAttribute(el.getValue(), true));
+ }
+ return result;
+ }
+
+ protected Object toAttribute( JsonArray ja, boolean nested ) {
+ List