diff --git a/modules/gltf/README.md b/modules/gltf/README.md
new file mode 100644
index 000000000..5d8966b20
--- /dev/null
+++ b/modules/gltf/README.md
@@ -0,0 +1,11 @@
+# Godot GLTF import and export module
+
+In a nutshell, the GLTF module works like this:
+
+* The [`structures/`](structures/) folder contains GLTF structures, the
+ small pieces that make up a GLTF file, represented as C++ classes.
+* The [`extensions/`](extensions/) folder contains GLTF extensions, which
+ are optional features that build on top of the base GLTF spec.
+* [`GLTFState`](gltf_state.h) holds collections of structures and extensions.
+* [`GLTFDocument`](gltf_document.h) operates on GLTFState and its elements.
+* The [`editor/`](editor/) folder uses GLTFDocument to import and export 3D models.
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub
index f23d5675c..a7cdeca2e 100644
--- a/modules/gltf/SCsub
+++ b/modules/gltf/SCsub
@@ -5,5 +5,10 @@ Import("env_modules")
env_gltf = env_modules.Clone()
-# Pandemonium's own source files
+# Godot source files
env_gltf.add_source_files(env.modules_sources, "*.cpp")
+env_gltf.add_source_files(env.modules_sources, "extensions/*.cpp")
+env_gltf.add_source_files(env.modules_sources, "structures/*.cpp")
+
+if env["tools"]:
+ env_gltf.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/gltf/config.py b/modules/gltf/config.py
index a4ee871ef..33e9652ae 100644
--- a/modules/gltf/config.py
+++ b/modules/gltf/config.py
@@ -1,5 +1,8 @@
+
+
def can_build(env, platform):
- return env["tools"] and not env["disable_3d"]
+ #return not env["disable_3d"]
+ return False
def configure(env):
@@ -8,12 +11,16 @@ def configure(env):
def get_doc_classes():
return [
- "EditorSceneImporterGLTF",
+ "EditorSceneFormatImporterBlend",
+ "EditorSceneFormatImporterFBX",
+ "EditorSceneFormatImporterGLTF",
"GLTFAccessor",
"GLTFAnimation",
"GLTFBufferView",
"GLTFCamera",
"GLTFDocument",
+ "GLTFDocumentExtension",
+ "GLTFDocumentExtensionConvertImporterMesh",
"GLTFLight",
"GLTFMesh",
"GLTFNode",
@@ -22,7 +29,6 @@ def get_doc_classes():
"GLTFSpecGloss",
"GLTFState",
"GLTFTexture",
- "PackedSceneGLTF",
]
diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml
new file mode 100644
index 000000000..ca8eb9854
--- /dev/null
+++ b/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml
@@ -0,0 +1,15 @@
+
+
+
+ Importer for Blender's [code].blend[/code] scene file format.
+
+
+ Imports Blender scenes in the [code].blend[/code] file format through the glTF 2.0 3D import pipeline. This importer requires Blender to be installed by the user, so that it can be used to export the scene as glTF 2.0.
+ The location of the Blender binary is set via the [code]filesystem/import/blender/blender3_path[/code] editor setting.
+ This importer is only used if [member ProjectSettings.filesystem/import/blender/enabled] is enabled, otherwise [code].blend[/code] files present in the project folder are not imported.
+ Blend import requires Blender 3.0.
+ Internally, the EditorSceneFormatImporterBlend uses the Blender glTF "Use Original" mode to reference external textures.
+
+
+
+
diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml
new file mode 100644
index 000000000..6754d963f
--- /dev/null
+++ b/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml
@@ -0,0 +1,13 @@
+
+
+
+ Importer for the [code].fbx[/code] scene file format.
+
+
+ Imports Autodesk FBX 3D scenes by way of converting them to glTF 2.0 using the FBX2glTF command line tool.
+ The location of the FBX2glTF binary is set via the [code]filesystem/import/fbx/fbx2gltf_path[/code] editor setting.
+ This importer is only used if [member ProjectSettings.filesystem/import/fbx/enabled] is enabled, otherwise [code].fbx[/code] files present in the project folder are not imported.
+
+
+
+
diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml
new file mode 100644
index 000000000..5a6a2f52d
--- /dev/null
+++ b/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml b/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml
deleted file mode 100644
index 60853edc1..000000000
--- a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [EditorSceneImporterGLTF] within a script will cause an error in an exported project.
-
-
-
-
-
-
-
-
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index 2052ec2ec..b73a4f8c5 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -1,14 +1,11 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFAccessor] within a script will cause an error in an exported project.
-
-
@@ -18,9 +15,9 @@
-
+
-
+
@@ -39,6 +36,4 @@
-
-
diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml
index 032794dd8..e2991170a 100644
--- a/modules/gltf/doc_classes/GLTFAnimation.xml
+++ b/modules/gltf/doc_classes/GLTFAnimation.xml
@@ -1,18 +1,13 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFAnimation] within a script will cause an error in an exported project.
-
-
-
-
diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml
index 7ebdc463d..00b6ccbe7 100644
--- a/modules/gltf/doc_classes/GLTFBufferView.xml
+++ b/modules/gltf/doc_classes/GLTFBufferView.xml
@@ -1,14 +1,11 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFBufferView] within a script will cause an error in an exported project.
-
-
@@ -21,6 +18,4 @@
-
-
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
index 3c8b0a776..9b9eff614 100644
--- a/modules/gltf/doc_classes/GLTFCamera.xml
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -1,24 +1,19 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFCamera] within a script will cause an error in an exported project.
-
-
+
+
+
+
-
-
-
-
-
-
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index bde28758a..cb0e3b675 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -1,14 +1,65 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFDocument] within a script will cause an error in an exported project.
+ Append a glTF2 3d format from a file, buffer or scene and then write to the filesystem, buffer or scene.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
new file mode 100644
index 000000000..3c28546ad
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml
new file mode 100644
index 000000000..70268fc0c
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml
index 7f9f7307d..354cd48a0 100644
--- a/modules/gltf/doc_classes/GLTFLight.xml
+++ b/modules/gltf/doc_classes/GLTFLight.xml
@@ -1,36 +1,31 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFLight] within a script will cause an error in an exported project.
-
-
-
+
The [Color] of the light. Defaults to white. A black color causes the light to have no effect.
The inner angle of the cone in a spotlight. Must be less than or equal to the outer cone angle.
- Within this angle, the light is at full brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. When creating a Pandemonium [SpotLight], the ratio between the inner and outer cone angles is used to calculate the attenuation of the light.
+ Within this angle, the light is at full brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. When creating a Godot [SpotLight3D], the ratio between the inner and outer cone angles is used to calculate the attenuation of the light.
- The intensity of the light. This is expressed in candelas (lumens per steradian) for point and spot lights, and lux (lumens per m²) for directional lights. When creating a Pandemonium light, this value is converted to a unitless multiplier.
+ The intensity of the light. This is expressed in candelas (lumens per steradian) for point and spot lights, and lux (lumens per m²) for directional lights. When creating a Godot light, this value is converted to a unitless multiplier.
+
+
+ The type of the light. The values accepted by Godot are "point", "spot", and "directional", which correspond to Godot's [OmniLight3D], [SpotLight3D], and [DirectionalLight3D] respectively.
The outer angle of the cone in a spotlight. Must be greater than or equal to the inner angle.
- At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Pandemonium [SpotLight], the outer cone angle is used as the angle of the spotlight.
+ At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight.
- The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Pandemonium light, the range is clamped to 4096.
-
-
- The type of the light. The values accepted by Pandemonium are "point", "spot", and "directional", which correspond to Pandemonium's [OmniLight], [SpotLight], and [DirectionalLight] respectively.
+ The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096.
-
-
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index a39cf88af..bac351cc2 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -1,22 +1,17 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFMesh] within a script will cause an error in an exported project.
-
-
-
+
-
+
-
+
-
-
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index dd73b9dc0..e933e6046 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -1,18 +1,15 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFNode] within a script will cause an error in an exported project.
-
-
-
+
@@ -24,19 +21,17 @@
-
+
-
+
+
+
-
-
-
+
-
-
diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml
index 83b3fd48b..dad985e88 100644
--- a/modules/gltf/doc_classes/GLTFSkeleton.xml
+++ b/modules/gltf/doc_classes/GLTFSkeleton.xml
@@ -1,15 +1,14 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFSkeleton] within a script will cause an error in an exported project.
-
+
@@ -19,13 +18,13 @@
-
+
-
-
+
+
@@ -34,9 +33,9 @@
-
+
-
+
@@ -48,11 +47,9 @@
-
+
-
+
-
-
diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml
index 2c5bc9a1f..b6a2bdb95 100644
--- a/modules/gltf/doc_classes/GLTFSkin.xml
+++ b/modules/gltf/doc_classes/GLTFSkin.xml
@@ -1,5 +1,5 @@
-
+
@@ -42,21 +42,19 @@
-
+
-
+
-
+
-
+
-
+
-
-
diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml
index 96c69193b..8433cf8dd 100644
--- a/modules/gltf/doc_classes/GLTFSpecGloss.xml
+++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml
@@ -1,16 +1,13 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFSpecGloss] within a script will cause an error in an exported project.
-
-
-
+
@@ -18,9 +15,7 @@
-
+
-
-
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 6e917672a..44a172356 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -1,9 +1,8 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFState] within a script will cause an error in an exported project.
@@ -193,9 +192,11 @@
-
+
-
+
+
+
@@ -203,13 +204,11 @@
-
+
-
-
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index c81406119..c0bc42416 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -1,18 +1,13 @@
-
+
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [GLTFTexture] within a script will cause an error in an exported project.
-
-
-
-
diff --git a/modules/gltf/doc_classes/PackedSceneGLTF.xml b/modules/gltf/doc_classes/PackedSceneGLTF.xml
deleted file mode 100644
index d618160c0..000000000
--- a/modules/gltf/doc_classes/PackedSceneGLTF.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
- [b]Note:[/b] This class is only compiled in editor builds. Run-time glTF loading and saving is [i]not[/i] available in exported projects. References to [PackedSceneGLTF] within a script will cause an error in an exported project.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
similarity index 61%
rename from modules/gltf/editor_scene_exporter_gltf_plugin.cpp
rename to modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
index 841687186..95db1c096 100644
--- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
@@ -32,9 +32,18 @@
#include "editor_scene_exporter_gltf_plugin.h"
+#include "../gltf_document.h"
+#include "../gltf_state.h"
+
+#include "core/config/project_settings.h"
+#include "core/error/error_list.h"
+#include "core/object/object.h"
+#include "core/templates/vector.h"
#include "editor/editor_file_dialog.h"
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/gui/check_box.h"
#include "scene/main/node.h"
String SceneExporterGLTFPlugin::get_name() const {
@@ -45,50 +54,58 @@ bool SceneExporterGLTFPlugin::has_main_screen() const {
return false;
}
-SceneExporterGLTFPlugin::SceneExporterGLTFPlugin(EditorNode *p_node) {
- editor = p_node;
- convert_gltf2.instance();
+SceneExporterGLTFPlugin::SceneExporterGLTFPlugin() {
file_export_lib = memnew(EditorFileDialog);
- editor->get_gui_base()->add_child(file_export_lib);
- file_export_lib->connect("file_selected", this, "_gltf2_dialog_action");
+ EditorNode::get_singleton()->get_gui_base()->add_child(file_export_lib);
+ file_export_lib->connect("file_selected", callable_mp(this, &SceneExporterGLTFPlugin::_gltf2_dialog_action));
file_export_lib->set_title(TTR("Export Library"));
- file_export_lib->set_mode(EditorFileDialog::MODE_SAVE_FILE);
+ file_export_lib->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_export_lib->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
file_export_lib->clear_filters();
file_export_lib->add_filter("*.glb");
file_export_lib->add_filter("*.gltf");
- file_export_lib->set_title(TTR("Export Mesh GLTF2"));
- String gltf_scene_name = TTR("Export GLTF...");
- add_tool_menu_item(gltf_scene_name, this, "convert_scene_to_gltf2", DEFVAL(Variant()));
+ file_export_lib->set_title(TTR("Export Scene to glTF 2.0 File"));
+
+ PopupMenu *menu = get_export_as_menu();
+ int idx = menu->get_item_count();
+ menu->add_item(TTR("glTF 2.0 Scene..."));
+ menu->set_item_metadata(idx, callable_mp(this, &SceneExporterGLTFPlugin::convert_scene_to_gltf2));
}
void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) {
- Node *root = editor->get_tree()->get_edited_scene_root();
+ Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root();
if (!root) {
- editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
+ EditorNode::get_singleton()->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
return;
}
List deps;
- convert_gltf2->save_scene(root, p_file, p_file, 0, 1000.0f, &deps);
- EditorFileSystem::get_singleton()->scan_changes();
+ Ref doc;
+ doc.instantiate();
+ Ref state;
+ state.instantiate();
+ int32_t flags = 0;
+ flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
+ Error err = doc->append_from_scene(root, state, flags, 30.0f);
+ if (err != OK) {
+ ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
+ }
+ err = doc->write_to_filesystem(state, p_file);
+ if (err != OK) {
+ ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
+ }
}
-void SceneExporterGLTFPlugin::_bind_methods() {
- ClassDB::bind_method(D_METHOD("convert_scene_to_gltf2"), &SceneExporterGLTFPlugin::convert_scene_to_gltf2);
- ClassDB::bind_method(D_METHOD("_gltf2_dialog_action", "file"), &SceneExporterGLTFPlugin::_gltf2_dialog_action);
-}
-
-void SceneExporterGLTFPlugin::convert_scene_to_gltf2(Variant p_null) {
- Node *root = editor->get_tree()->get_edited_scene_root();
+void SceneExporterGLTFPlugin::convert_scene_to_gltf2() {
+ Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root();
if (!root) {
- editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
+ EditorNode::get_singleton()->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
return;
}
- String filename = String(root->get_filename().get_file().get_basename());
- if (filename.empty()) {
+ String filename = String(root->get_scene_file_path().get_file().get_basename());
+ if (filename.is_empty()) {
filename = root->get_name();
}
- file_export_lib->set_current_file(filename + ".gltf");
+ file_export_lib->set_current_file(filename + String(".gltf"));
file_export_lib->popup_centered_ratio();
}
diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
similarity index 88%
rename from modules/gltf/editor_scene_exporter_gltf_plugin.h
rename to modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
index 97dc17982..5af46bc75 100644
--- a/modules/gltf/editor_scene_exporter_gltf_plugin.h
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
@@ -1,5 +1,3 @@
-#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
-#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
/*************************************************************************/
/* editor_scene_exporter_gltf_plugin.h */
/*************************************************************************/
@@ -30,27 +28,27 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "editor/editor_file_dialog.h"
-#include "editor/editor_plugin.h"
+#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
+#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
-#include "packed_scene_gltf.h"
+#ifdef TOOLS_ENABLED
+
+#include "editor/editor_plugin.h"
+#include "editor_scene_importer_gltf.h"
class SceneExporterGLTFPlugin : public EditorPlugin {
GDCLASS(SceneExporterGLTFPlugin, EditorPlugin);
- Ref convert_gltf2;
- EditorNode *editor = nullptr;
EditorFileDialog *file_export_lib = nullptr;
void _gltf2_dialog_action(String p_file);
- void convert_scene_to_gltf2(Variant p_null);
-
-protected:
- static void _bind_methods();
+ void convert_scene_to_gltf2();
public:
- virtual String get_name() const;
- bool has_main_screen() const;
- SceneExporterGLTFPlugin(class EditorNode *p_node);
+ virtual String get_name() const override;
+ bool has_main_screen() const override;
+ SceneExporterGLTFPlugin();
};
+#endif // TOOLS_ENABLED
+
#endif // EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
new file mode 100644
index 000000000..8002c185c
--- /dev/null
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -0,0 +1,575 @@
+/*************************************************************************/
+/* editor_scene_importer_blend.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor_scene_importer_blend.h"
+
+#ifdef TOOLS_ENABLED
+
+#include "../gltf_document.h"
+#include "../gltf_state.h"
+
+#include "core/config/project_settings.h"
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
+#include "main/main.h"
+#include "scene/main/node.h"
+#include "scene/resources/animation.h"
+
+#ifdef WINDOWS_ENABLED
+// Code by Pedro Estebanez (https://github.com/godotengine/godot/pull/59766)
+#include
+#endif
+
+uint32_t EditorSceneFormatImporterBlend::get_import_flags() const {
+ return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
+}
+
+void EditorSceneFormatImporterBlend::get_extensions(List *r_extensions) const {
+ r_extensions->push_back("blend");
+}
+
+Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags,
+ const HashMap &p_options, int p_bake_fps,
+ List *r_missing_deps, Error *r_err) {
+ // Get global paths for source and sink.
+
+ // Escape paths to be valid Python strings to embed in the script.
+ const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape();
+ const String sink = ProjectSettings::get_singleton()->get_imported_files_path().plus_file(
+ vformat("%s-%s.gltf", p_path.get_file().get_basename(), p_path.md5_text()));
+ const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape();
+
+ // Handle configuration options.
+
+ String parameters_arg;
+
+ if (p_options.has(SNAME("blender/nodes/custom_properties")) && p_options[SNAME("blender/nodes/custom_properties")]) {
+ parameters_arg += "export_extras=True,";
+ } else {
+ parameters_arg += "export_extras=False,";
+ }
+ if (p_options.has(SNAME("blender/meshes/skins")) && p_options[SNAME("blender/meshes/skins")]) {
+ int32_t skins = p_options["blender/meshes/skins"];
+ if (skins == BLEND_BONE_INFLUENCES_NONE) {
+ parameters_arg += "export_all_influences=False,";
+ } else if (skins == BLEND_BONE_INFLUENCES_COMPATIBLE) {
+ parameters_arg += "export_all_influences=False,";
+ } else if (skins == BLEND_BONE_INFLUENCES_ALL) {
+ parameters_arg += "export_all_influences=True,";
+ }
+ parameters_arg += "export_skins=True,";
+ } else {
+ parameters_arg += "export_skins=False,";
+ }
+ if (p_options.has(SNAME("blender/materials/export_materials")) && p_options[SNAME("blender/materials/export_materials")]) {
+ int32_t exports = p_options["blender/materials/export_materials"];
+ if (exports == BLEND_MATERIAL_EXPORT_PLACEHOLDER) {
+ parameters_arg += "export_materials='PLACEHOLDER',";
+ } else if (exports == BLEND_MATERIAL_EXPORT_EXPORT) {
+ parameters_arg += "export_materials='EXPORT',";
+ }
+ } else {
+ parameters_arg += "export_materials='PLACEHOLDER',";
+ }
+ if (p_options.has(SNAME("blender/nodes/cameras")) && p_options[SNAME("blender/nodes/cameras")]) {
+ parameters_arg += "export_cameras=True,";
+ } else {
+ parameters_arg += "export_cameras=False,";
+ }
+ if (p_options.has(SNAME("blender/nodes/punctual_lights")) && p_options[SNAME("blender/nodes/punctual_lights")]) {
+ parameters_arg += "export_lights=True,";
+ } else {
+ parameters_arg += "export_lights=False,";
+ }
+ if (p_options.has(SNAME("blender/meshes/colors")) && p_options[SNAME("blender/meshes/colors")]) {
+ parameters_arg += "export_colors=True,";
+ } else {
+ parameters_arg += "export_colors=False,";
+ }
+ if (p_options.has(SNAME("blender/nodes/visible")) && p_options[SNAME("blender/nodes/visible")]) {
+ int32_t visible = p_options["blender/nodes/visible"];
+ if (visible == BLEND_VISIBLE_VISIBLE_ONLY) {
+ parameters_arg += "use_visible=True,";
+ } else if (visible == BLEND_VISIBLE_RENDERABLE) {
+ parameters_arg += "use_renderable=True,";
+ } else if (visible == BLEND_VISIBLE_ALL) {
+ parameters_arg += "use_visible=False,use_renderable=False,";
+ }
+ } else {
+ parameters_arg += "use_visible=False,use_renderable=False,";
+ }
+
+ if (p_options.has(SNAME("blender/meshes/uvs")) && p_options[SNAME("blender/meshes/uvs")]) {
+ parameters_arg += "export_texcoords=True,";
+ } else {
+ parameters_arg += "export_texcoords=False,";
+ }
+ if (p_options.has(SNAME("blender/meshes/normals")) && p_options[SNAME("blender/meshes/normals")]) {
+ parameters_arg += "export_normals=True,";
+ } else {
+ parameters_arg += "export_normals=False,";
+ }
+ if (p_options.has(SNAME("blender/meshes/tangents")) && p_options[SNAME("blender/meshes/tangents")]) {
+ parameters_arg += "export_tangents=True,";
+ } else {
+ parameters_arg += "export_tangents=False,";
+ }
+ if (p_options.has(SNAME("blender/animation/group_tracks")) && p_options[SNAME("blender/animation/group_tracks")]) {
+ parameters_arg += "export_nla_strips=True,";
+ } else {
+ parameters_arg += "export_nla_strips=False,";
+ }
+ if (p_options.has(SNAME("blender/animation/limit_playback")) && p_options[SNAME("blender/animation/limit_playback")]) {
+ parameters_arg += "export_frame_range=True,";
+ } else {
+ parameters_arg += "export_frame_range=False,";
+ }
+ if (p_options.has(SNAME("blender/animation/always_sample")) && p_options[SNAME("blender/animation/always_sample")]) {
+ parameters_arg += "export_force_sampling=True,";
+ } else {
+ parameters_arg += "export_force_sampling=False,";
+ }
+ if (p_options.has(SNAME("blender/meshes/export_bones_deforming_mesh_only")) && p_options[SNAME("blender/meshes/export_bones_deforming_mesh_only")]) {
+ parameters_arg += "export_def_bones=True,";
+ } else {
+ parameters_arg += "export_def_bones=False,";
+ }
+ if (p_options.has(SNAME("blender/nodes/modifiers")) && p_options[SNAME("blender/nodes/modifiers")]) {
+ parameters_arg += "export_apply=True";
+ } else {
+ parameters_arg += "export_apply=False";
+ }
+
+ String unpack_all;
+ if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) {
+ unpack_all = "bpy.ops.file.unpack_all(method='USE_LOCAL');";
+ }
+
+ // Prepare Blender export script.
+
+ String common_args = vformat("filepath='%s',", sink_global) +
+ "export_format='GLTF_SEPARATE',"
+ "export_yup=True," +
+ parameters_arg;
+ String script =
+ String("import bpy, sys;") +
+ "print('Blender 3.0 or higher is required.', file=sys.stderr) if bpy.app.version < (3, 0, 0) else None;" +
+ vformat("bpy.ops.wm.open_mainfile(filepath='%s');", source_global) +
+ unpack_all +
+ vformat("bpy.ops.export_scene.gltf(export_keep_originals=True,%s);", common_args);
+ print_verbose(script);
+
+ // Run script with configured Blender binary.
+
+ String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path");
+
+#ifdef WINDOWS_ENABLED
+ blender_path = blender_path.plus_file("blender.exe");
+#else
+ blender_path = blender_path.plus_file("blender");
+#endif
+
+ List args;
+ args.push_back("--background");
+ args.push_back("--python-expr");
+ args.push_back(script);
+
+ String standard_out;
+ int ret;
+ OS::get_singleton()->execute(blender_path, args, &standard_out, &ret, true);
+ print_verbose(blender_path);
+ print_verbose(standard_out);
+
+ if (ret != 0) {
+ if (r_err) {
+ *r_err = ERR_SCRIPT_FAILED;
+ }
+ ERR_PRINT(vformat("Blend export to glTF failed with error: %d.", ret));
+ return nullptr;
+ }
+
+ // Import the generated glTF.
+
+ // Use GLTFDocument instead of glTF importer to keep image references.
+ Ref gltf;
+ gltf.instantiate();
+ Ref state;
+ state.instantiate();
+ String base_dir;
+ if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) {
+ base_dir = sink.get_base_dir();
+ }
+ Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, p_bake_fps, base_dir);
+ if (err != OK) {
+ if (r_err) {
+ *r_err = FAILED;
+ }
+ return nullptr;
+ }
+ return gltf->generate_scene(state, p_bake_fps);
+}
+
+Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
+ const HashMap &p_options) {
+ if (p_path.get_extension().to_lower() != "blend") {
+ return true;
+ }
+
+ if (p_option.begins_with("animation/")) {
+ if (p_option != "animation/import" && !bool(p_options["animation/import"])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, List *r_options) {
+ if (p_path.get_extension().to_lower() != "blend") {
+ return;
+ }
+#define ADD_OPTION_BOOL(PATH, VALUE) \
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, SNAME(PATH)), VALUE));
+#define ADD_OPTION_ENUM(PATH, ENUM_HINT, VALUE) \
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, SNAME(PATH), PROPERTY_HINT_ENUM, ENUM_HINT), VALUE));
+
+ ADD_OPTION_ENUM("blender/nodes/visible", "Visible Only,Renderable,All", BLEND_VISIBLE_ALL);
+ ADD_OPTION_BOOL("blender/nodes/punctual_lights", true);
+ ADD_OPTION_BOOL("blender/nodes/cameras", true);
+ ADD_OPTION_BOOL("blender/nodes/custom_properties", true);
+ ADD_OPTION_ENUM("blender/nodes/modifiers", "No Modifiers,All Modifiers", BLEND_MODIFIERS_ALL);
+ ADD_OPTION_BOOL("blender/meshes/colors", false);
+ ADD_OPTION_BOOL("blender/meshes/uvs", true);
+ ADD_OPTION_BOOL("blender/meshes/normals", true);
+ ADD_OPTION_BOOL("blender/meshes/tangents", true);
+ ADD_OPTION_ENUM("blender/meshes/skins", "None,4 Influences (Compatible),All Influences", BLEND_BONE_INFLUENCES_ALL);
+ ADD_OPTION_BOOL("blender/meshes/export_bones_deforming_mesh_only", false);
+ ADD_OPTION_BOOL("blender/materials/unpack_enabled", true);
+ ADD_OPTION_ENUM("blender/materials/export_materials", "Placeholder,Export", BLEND_MATERIAL_EXPORT_EXPORT);
+ ADD_OPTION_BOOL("blender/animation/limit_playback", true);
+ ADD_OPTION_BOOL("blender/animation/always_sample", true);
+ ADD_OPTION_BOOL("blender/animation/group_tracks", true);
+
+#undef ADD_OPTION_BOOL
+#undef ADD_OPTION_ENUM
+}
+
+///////////////////////////
+
+static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
+ String path = p_path;
+#ifdef WINDOWS_ENABLED
+ path = path.plus_file("blender.exe");
+#else
+ path = path.plus_file("blender");
+#endif
+
+#if defined(MACOS_ENABLED)
+ if (!FileAccess::exists(path)) {
+ path = path.plus_file("Blender");
+ }
+#endif
+
+ if (!FileAccess::exists(path)) {
+ if (r_err) {
+ *r_err = TTR("Path does not contain a Blender installation.");
+ }
+ return false;
+ }
+ List args;
+ args.push_back("--version");
+ String pipe;
+ Error err = OS::get_singleton()->execute(path, args, &pipe);
+ if (err != OK) {
+ if (r_err) {
+ *r_err = TTR("Can't execute Blender binary.");
+ }
+ return false;
+ }
+
+ if (pipe.find("Blender ") != 0) {
+ if (r_err) {
+ *r_err = vformat(TTR("Unexpected --version output from Blender binary at: %s"), path);
+ }
+ return false;
+ }
+ pipe = pipe.replace_first("Blender ", "");
+ int pp = pipe.find(".");
+ if (pp == -1) {
+ if (r_err) {
+ *r_err = TTR("Path supplied lacks a Blender binary.");
+ }
+ return false;
+ }
+ String v = pipe.substr(0, pp);
+ int version = v.to_int();
+ if (version < 3) {
+ if (r_err) {
+ *r_err = TTR("This Blender installation is too old for this importer (not 3.0+).");
+ }
+ return false;
+ }
+ if (version > 3) {
+ if (r_err) {
+ *r_err = TTR("This Blender installation is too new for this importer (not 3.x).");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool EditorFileSystemImportFormatSupportQueryBlend::is_active() const {
+ bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled");
+
+ String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path");
+
+ if (blend_enabled && !_test_blender_path(blender_path)) {
+ // Intending to import Blender, but blend not configured.
+ return true;
+ }
+
+ return false;
+}
+Vector EditorFileSystemImportFormatSupportQueryBlend::get_file_extensions() const {
+ Vector ret;
+ ret.push_back("blend");
+ return ret;
+}
+
+void EditorFileSystemImportFormatSupportQueryBlend::_validate_path(String p_path) {
+ String error;
+ bool success = false;
+ if (p_path == "") {
+ error = TTR("Path is empty.");
+ } else {
+ if (_test_blender_path(p_path, &error)) {
+ success = true;
+ if (auto_detected_path == p_path) {
+ error = TTR("Path to Blender installation is valid (Autodetected).");
+ } else {
+ error = TTR("Path to Blender installation is valid.");
+ }
+ }
+ }
+
+ path_status->set_text(error);
+
+ if (success) {
+ path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("success_color"), SNAME("Editor")));
+ configure_blender_dialog->get_ok_button()->set_disabled(false);
+ } else {
+ path_status->add_theme_color_override("font_color", path_status->get_theme_color(SNAME("error_color"), SNAME("Editor")));
+ configure_blender_dialog->get_ok_button()->set_disabled(true);
+ }
+}
+
+bool EditorFileSystemImportFormatSupportQueryBlend::_autodetect_path(String p_path) {
+ if (_test_blender_path(p_path)) {
+ auto_detected_path = p_path;
+ return true;
+ }
+ return false;
+}
+
+void EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed() {
+ confirmed = true;
+}
+
+void EditorFileSystemImportFormatSupportQueryBlend::_select_install(String p_path) {
+ blender_path->set_text(p_path);
+ _validate_path(p_path);
+}
+void EditorFileSystemImportFormatSupportQueryBlend::_browse_install() {
+ if (blender_path->get_text() != String()) {
+ browse_dialog->set_current_dir(blender_path->get_text());
+ }
+
+ browse_dialog->popup_centered_ratio();
+}
+
+bool EditorFileSystemImportFormatSupportQueryBlend::query() {
+ if (!configure_blender_dialog) {
+ configure_blender_dialog = memnew(ConfirmationDialog);
+ configure_blender_dialog->set_title(TTR("Configure Blender Importer"));
+ configure_blender_dialog->set_flag(Window::FLAG_BORDERLESS, true); // Avoid closing accidentally .
+ configure_blender_dialog->set_close_on_escape(false);
+
+ VBoxContainer *vb = memnew(VBoxContainer);
+ vb->add_child(memnew(Label(TTR("Blender 3.0+ is required to import '.blend' files.\nPlease provide a valid path to a Blender installation:"))));
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+
+ blender_path = memnew(LineEdit);
+ blender_path->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hb->add_child(blender_path);
+ blender_path_browse = memnew(Button);
+ hb->add_child(blender_path_browse);
+ blender_path_browse->set_text(TTR("Browse"));
+ blender_path_browse->connect("pressed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_browse_install));
+ hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ hb->set_custom_minimum_size(Size2(400 * EDSCALE, 0));
+
+ vb->add_child(hb);
+
+ path_status = memnew(Label);
+ vb->add_child(path_status);
+
+ configure_blender_dialog->add_child(vb);
+
+ blender_path->connect("text_changed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_validate_path));
+
+ EditorNode::get_singleton()->get_gui_base()->add_child(configure_blender_dialog);
+
+ configure_blender_dialog->set_ok_button_text(TTR("Confirm Path"));
+ configure_blender_dialog->set_cancel_button_text(TTR("Disable '.blend' Import"));
+ configure_blender_dialog->get_cancel_button()->set_tooltip(TTR("Disables Blender '.blend' files import for this project. Can be re-enabled in Project Settings."));
+ configure_blender_dialog->connect("confirmed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed));
+
+ browse_dialog = memnew(EditorFileDialog);
+ browse_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ browse_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
+ browse_dialog->connect("dir_selected", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_select_install));
+
+ EditorNode::get_singleton()->get_gui_base()->add_child(browse_dialog);
+ }
+
+ String path = EDITOR_GET("filesystem/import/blender/blender3_path");
+
+ if (path == "") {
+ // Autodetect
+ auto_detected_path = "";
+
+#if defined(MACOS_ENABLED)
+
+ {
+ Vector mdfind_paths;
+ {
+ List mdfind_args;
+ mdfind_args.push_back("kMDItemCFBundleIdentifier=org.blenderfoundation.blender");
+
+ String output;
+ Error err = OS::get_singleton()->execute("mdfind", mdfind_args, &output);
+ if (err == OK) {
+ mdfind_paths = output.split("\n");
+ }
+ }
+
+ bool found = false;
+ for (const String &path : mdfind_paths) {
+ found = _autodetect_path(path.plus_file("Contents/MacOS"));
+ if (found) {
+ break;
+ }
+ }
+ if (!found) {
+ found = _autodetect_path("/opt/homebrew/bin");
+ }
+ if (!found) {
+ found = _autodetect_path("/opt/local/bin");
+ }
+ if (!found) {
+ found = _autodetect_path("/usr/local/bin");
+ }
+ if (!found) {
+ found = _autodetect_path("/usr/local/opt");
+ }
+ if (!found) {
+ found = _autodetect_path("/Applications/Blender.app/Contents/MacOS");
+ }
+ }
+#elif defined(WINDOWS_ENABLED)
+ {
+ char blender_opener_path[MAX_PATH];
+ DWORD path_len = MAX_PATH;
+ HRESULT res = AssocQueryString(0, ASSOCSTR_EXECUTABLE, ".blend", "open", blender_opener_path, &path_len);
+ if (res == S_OK && _autodetect_path(String(blender_opener_path).get_base_dir())) {
+ // Good.
+ } else if (_autodetect_path("C:\\Program Files\\Blender Foundation")) {
+ // Good.
+ } else {
+ _autodetect_path("C:\\Program Files (x86)\\Blender Foundation");
+ }
+ }
+
+#elif defined(UNIX_ENABLED)
+ if (_autodetect_path("/usr/bin")) {
+ // Good.
+ } else if (_autodetect_path("/usr/local/bin")) {
+ // Good
+ } else {
+ _autodetect_path("/opt/blender/bin");
+ }
+#endif
+ if (auto_detected_path != "") {
+ path = auto_detected_path;
+ }
+ }
+
+ blender_path->set_text(path);
+
+ _validate_path(path);
+
+ configure_blender_dialog->popup_centered();
+ confirmed = false;
+
+ while (true) {
+ OS::get_singleton()->delay_usec(1);
+ DisplayServer::get_singleton()->process_events();
+ Main::iteration();
+ if (!configure_blender_dialog->is_visible() || confirmed) {
+ break;
+ }
+ }
+
+ if (confirmed) {
+ // Can only confirm a valid path.
+ EditorSettings::get_singleton()->set("filesystem/import/blender/blender3_path", blender_path->get_text());
+ EditorSettings::get_singleton()->save();
+ } else {
+ // Disable Blender import
+ ProjectSettings::get_singleton()->set("filesystem/import/blender/enabled", false);
+ ProjectSettings::get_singleton()->save();
+
+ if (EditorNode::immediate_confirmation_dialog(TTR("Disabling '.blend' file import requires restarting the editor."), TTR("Save & Restart"), TTR("Restart"))) {
+ EditorNode::get_singleton()->save_all_scenes();
+ }
+ EditorNode::get_singleton()->restart_editor();
+ return true;
+ }
+
+ return false;
+}
+
+EditorFileSystemImportFormatSupportQueryBlend::EditorFileSystemImportFormatSupportQueryBlend() {
+}
+
+#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h
new file mode 100644
index 000000000..dd1c1b988
--- /dev/null
+++ b/modules/gltf/editor/editor_scene_importer_blend.h
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* editor_scene_importer_blend.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef EDITOR_SCENE_IMPORTER_BLEND_H
+#define EDITOR_SCENE_IMPORTER_BLEND_H
+
+#ifdef TOOLS_ENABLED
+
+#include "editor/editor_file_system.h"
+#include "editor/import/resource_importer_scene.h"
+
+class Animation;
+class Node;
+class ConfirmationDialog;
+
+class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter {
+ GDCLASS(EditorSceneFormatImporterBlend, EditorSceneFormatImporter);
+
+public:
+ enum {
+ BLEND_VISIBLE_VISIBLE_ONLY,
+ BLEND_VISIBLE_RENDERABLE,
+ BLEND_VISIBLE_ALL
+ };
+ enum {
+ BLEND_BONE_INFLUENCES_NONE,
+ BLEND_BONE_INFLUENCES_COMPATIBLE,
+ BLEND_BONE_INFLUENCES_ALL
+ };
+ enum {
+ BLEND_MATERIAL_EXPORT_PLACEHOLDER,
+ BLEND_MATERIAL_EXPORT_EXPORT
+ };
+ enum {
+ BLEND_MODIFIERS_NONE,
+ BLEND_MODIFIERS_ALL
+ };
+
+ virtual uint32_t get_import_flags() const override;
+ virtual void get_extensions(List *r_extensions) const override;
+ virtual Node *import_scene(const String &p_path, uint32_t p_flags,
+ const HashMap &p_options, int p_bake_fps,
+ List *r_missing_deps, Error *r_err = nullptr) override;
+ virtual void get_import_options(const String &p_path,
+ List *r_options) override;
+ virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
+ const HashMap &p_options) override;
+};
+
+class LineEdit;
+class Button;
+class EditorFileDialog;
+class Label;
+
+class EditorFileSystemImportFormatSupportQueryBlend : public EditorFileSystemImportFormatSupportQuery {
+ GDCLASS(EditorFileSystemImportFormatSupportQueryBlend, EditorFileSystemImportFormatSupportQuery);
+
+ ConfirmationDialog *configure_blender_dialog = nullptr;
+ LineEdit *blender_path = nullptr;
+ Button *blender_path_browse = nullptr;
+ EditorFileDialog *browse_dialog = nullptr;
+ Label *path_status = nullptr;
+ bool confirmed = false;
+
+ String auto_detected_path;
+ void _validate_path(String p_path);
+
+ bool _autodetect_path(String p_path);
+
+ void _path_confirmed();
+
+ void _select_install(String p_path);
+ void _browse_install();
+
+public:
+ virtual bool is_active() const override;
+ virtual Vector get_file_extensions() const override;
+ virtual bool query() override;
+
+ EditorFileSystemImportFormatSupportQueryBlend();
+};
+
+#endif // TOOLS_ENABLED
+
+#endif // EDITOR_SCENE_IMPORTER_BLEND_H
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp
new file mode 100644
index 000000000..faad2d315
--- /dev/null
+++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp
@@ -0,0 +1,117 @@
+/*************************************************************************/
+/* editor_scene_importer_fbx.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor_scene_importer_fbx.h"
+
+#ifdef TOOLS_ENABLED
+
+#include "../gltf_document.h"
+#include "../gltf_state.h"
+
+#include "core/config/project_settings.h"
+#include "editor/editor_settings.h"
+#include "scene/main/node.h"
+#include "scene/resources/animation.h"
+
+uint32_t EditorSceneFormatImporterFBX::get_import_flags() const {
+ return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
+}
+
+void EditorSceneFormatImporterFBX::get_extensions(List *r_extensions) const {
+ r_extensions->push_back("fbx");
+}
+
+Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags,
+ const HashMap &p_options, int p_bake_fps,
+ List *r_missing_deps, Error *r_err) {
+ // Get global paths for source and sink.
+
+ // Don't use `c_escape()` as it can generate broken paths. These paths will be
+ // enclosed in double quotes by OS::execute(), so we only need to escape those.
+ // `c_escape_multiline()` seems to do this (escapes `\` and `"` only).
+ const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape_multiline();
+ const String sink = ProjectSettings::get_singleton()->get_imported_files_path().plus_file(
+ vformat("%s-%s.glb", p_path.get_file().get_basename(), p_path.md5_text()));
+ const String sink_global = ProjectSettings::get_singleton()->globalize_path(sink).c_escape_multiline();
+
+ // Run fbx2gltf.
+
+ String fbx2gltf_path = EDITOR_GET("filesystem/import/fbx/fbx2gltf_path");
+
+ List args;
+ args.push_back("--pbr-metallic-roughness");
+ args.push_back("--input");
+ args.push_back(source_global);
+ args.push_back("--output");
+ args.push_back(sink_global);
+ args.push_back("--binary");
+
+ String standard_out;
+ int ret;
+ OS::get_singleton()->execute(fbx2gltf_path, args, &standard_out, &ret, true);
+ print_verbose(fbx2gltf_path);
+ print_verbose(standard_out);
+
+ if (ret != 0) {
+ if (r_err) {
+ *r_err = ERR_SCRIPT_FAILED;
+ }
+ ERR_PRINT(vformat("FBX conversion to glTF failed with error: %d.", ret));
+ return nullptr;
+ }
+
+ // Import the generated glTF.
+
+ // Use GLTFDocument instead of glTF importer to keep image references.
+ Ref gltf;
+ gltf.instantiate();
+ Ref state;
+ state.instantiate();
+ print_verbose(vformat("glTF path: %s", sink));
+ Error err = gltf->append_from_file(sink, state, p_flags, p_bake_fps);
+ if (err != OK) {
+ if (r_err) {
+ *r_err = FAILED;
+ }
+ return nullptr;
+ }
+ return gltf->generate_scene(state, p_bake_fps);
+}
+
+Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation,
+ const String &p_option, const HashMap &p_options) {
+ return true;
+}
+
+void EditorSceneFormatImporterFBX::get_import_options(const String &p_path,
+ List *r_options) {
+}
+
+#endif // TOOLS_ENABLED
diff --git a/modules/gltf/packed_scene_gltf.h b/modules/gltf/editor/editor_scene_importer_fbx.h
similarity index 65%
rename from modules/gltf/packed_scene_gltf.h
rename to modules/gltf/editor/editor_scene_importer_fbx.h
index dc0960d49..b0039b1c8 100644
--- a/modules/gltf/packed_scene_gltf.h
+++ b/modules/gltf/editor/editor_scene_importer_fbx.h
@@ -1,7 +1,5 @@
-#ifndef PACKED_SCENE_GLTF_H
-#define PACKED_SCENE_GLTF_H
/*************************************************************************/
-/* packed_scene_gltf.h */
+/* editor_scene_importer_fbx.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -30,36 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef EDITOR_SCENE_IMPORTER_FBX_H
+#define EDITOR_SCENE_IMPORTER_FBX_H
+
#ifdef TOOLS_ENABLED
-#include "scene/main/node.h"
-#include "scene/resources/packed_scene.h"
+#include "editor/import/resource_importer_scene.h"
-#include "gltf_state.h"
+class Animation;
+class Node;
-class PackedSceneGLTF : public PackedScene {
- GDCLASS(PackedSceneGLTF, PackedScene);
-
-protected:
- static void _bind_methods();
+class EditorSceneFormatImporterFBX : public EditorSceneFormatImporter {
+ GDCLASS(EditorSceneFormatImporterFBX, EditorSceneFormatImporter);
public:
- virtual void save_scene(Node *p_node, const String &p_path, const String &p_src_path,
- uint32_t p_flags, int p_bake_fps,
- List *r_missing_deps, Error *r_err = nullptr);
- virtual void _build_parent_hierachy(Ref state);
- virtual Error export_gltf(Node *p_root, String p_path, int32_t p_flags = 0,
- real_t p_bake_fps = 1000.0f);
+ virtual uint32_t get_import_flags() const override;
+ virtual void get_extensions(List *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- int p_bake_fps, uint32_t p_compress_flags,
- List *r_missing_deps,
- Error *r_err,
- Ref r_state);
- virtual Node *import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, uint32_t p_compress_flags, Ref r_state = Ref());
- virtual void pack_gltf(String p_path, int32_t p_flags = 0,
- real_t p_bake_fps = 1000.0f, uint32_t p_compress_flags = Mesh::ARRAY_COMPRESS_DEFAULT, Ref r_state = Ref());
+ const HashMap &p_options, int p_bake_fps,
+ List *r_missing_deps, Error *r_err = nullptr) override;
+ virtual void get_import_options(const String &p_path,
+ List *r_options) override;
+ virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
+ const HashMap &p_options) override;
};
-#endif // PACKED_SCENE_GLTF_H
-
#endif // TOOLS_ENABLED
+
+#endif // EDITOR_SCENE_IMPORTER_FBX_H
diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
similarity index 77%
rename from modules/gltf/editor_scene_importer_gltf.cpp
rename to modules/gltf/editor/editor_scene_importer_gltf.cpp
index af4bc9261..3fadec516 100644
--- a/modules/gltf/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -31,34 +31,36 @@
#ifdef TOOLS_ENABLED
#include "editor_scene_importer_gltf.h"
+
+#include "../gltf_document.h"
+#include "../gltf_state.h"
+
#include "scene/resources/animation.h"
-#include "scene/resources/skin.h"
-#include "gltf_state.h"
-#include "packed_scene_gltf.h"
-
-uint32_t EditorSceneImporterGLTF::get_import_flags() const {
+uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const {
return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
}
-void EditorSceneImporterGLTF::get_extensions(List *r_extensions) const {
+void EditorSceneFormatImporterGLTF::get_extensions(List *r_extensions) const {
r_extensions->push_back("gltf");
r_extensions->push_back("glb");
}
-Node *EditorSceneImporterGLTF::import_scene(const String &p_path,
- uint32_t p_flags, int p_bake_fps, uint32_t p_compress_flags,
- List *r_missing_deps,
- Error *r_err) {
- Ref importer;
- importer.instance();
- return importer->import_scene(p_path, p_flags, p_bake_fps, p_compress_flags, r_missing_deps, r_err, Ref());
-}
-
-Ref EditorSceneImporterGLTF::import_animation(const String &p_path,
- uint32_t p_flags,
- int p_bake_fps) {
- return Ref();
+Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags,
+ const HashMap &p_options, int p_bake_fps,
+ List *r_missing_deps, Error *r_err) {
+ Ref doc;
+ doc.instantiate();
+ Ref state;
+ state.instantiate();
+ Error err = doc->append_from_file(p_path, state, p_flags, p_bake_fps);
+ if (err != OK) {
+ if (r_err) {
+ *r_err = err;
+ }
+ return nullptr;
+ }
+ return doc->generate_scene(state, p_bake_fps);
}
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h
similarity index 83%
rename from modules/gltf/editor_scene_importer_gltf.h
rename to modules/gltf/editor/editor_scene_importer_gltf.h
index dc222cc84..b17a1e4ea 100644
--- a/modules/gltf/editor_scene_importer_gltf.h
+++ b/modules/gltf/editor/editor_scene_importer_gltf.h
@@ -1,5 +1,3 @@
-#ifndef EDITOR_SCENE_IMPORTER_GLTF_H
-#define EDITOR_SCENE_IMPORTER_GLTF_H
/*************************************************************************/
/* editor_scene_importer_gltf.h */
/*************************************************************************/
@@ -30,27 +28,30 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef EDITOR_SCENE_IMPORTER_GLTF_H
+#define EDITOR_SCENE_IMPORTER_GLTF_H
+
#ifdef TOOLS_ENABLED
+#include "../gltf_document_extension.h"
+#include "../gltf_state.h"
+
#include "editor/import/resource_importer_scene.h"
-#include "gltf_document.h"
-#include "gltf_state.h"
+class Animation;
+class Node;
-class EditorSceneImporterGLTF : public EditorSceneImporter {
- GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter);
+class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter {
+ GDCLASS(EditorSceneFormatImporterGLTF, EditorSceneFormatImporter);
public:
- virtual uint32_t get_import_flags() const;
- virtual void get_extensions(List *r_extensions) const;
+ virtual uint32_t get_import_flags() const override;
+ virtual void get_extensions(List *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- int p_bake_fps, uint32_t p_compress_flags,
- List *r_missing_deps = nullptr,
- Error *r_err = nullptr);
- virtual Ref import_animation(const String &p_path,
- uint32_t p_flags, int p_bake_fps);
+ const HashMap &p_options, int p_bake_fps,
+ List *r_missing_deps, Error *r_err = nullptr) override;
};
-#endif // EDITOR_SCENE_IMPORTER_GLTF_H
-
#endif // TOOLS_ENABLED
+
+#endif // EDITOR_SCENE_IMPORTER_GLTF_H
diff --git a/modules/gltf/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp
similarity index 82%
rename from modules/gltf/gltf_light.cpp
rename to modules/gltf/extensions/gltf_light.cpp
index 05f0c7cc9..af21a4e80 100644
--- a/modules/gltf/gltf_light.cpp
+++ b/modules/gltf/extensions/gltf_light.cpp
@@ -35,8 +35,8 @@ void GLTFLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFLight::set_color);
ClassDB::bind_method(D_METHOD("get_intensity"), &GLTFLight::get_intensity);
ClassDB::bind_method(D_METHOD("set_intensity", "intensity"), &GLTFLight::set_intensity);
- ClassDB::bind_method(D_METHOD("get_type"), &GLTFLight::get_type);
- ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFLight::set_type);
+ ClassDB::bind_method(D_METHOD("get_light_type"), &GLTFLight::get_light_type);
+ ClassDB::bind_method(D_METHOD("set_light_type", "light_type"), &GLTFLight::set_light_type);
ClassDB::bind_method(D_METHOD("get_range"), &GLTFLight::get_range);
ClassDB::bind_method(D_METHOD("set_range", "range"), &GLTFLight::set_range);
ClassDB::bind_method(D_METHOD("get_inner_cone_angle"), &GLTFLight::get_inner_cone_angle);
@@ -45,11 +45,11 @@ void GLTFLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_outer_cone_angle", "outer_cone_angle"), &GLTFLight::set_outer_cone_angle);
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); // Color
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "intensity"), "set_intensity", "get_intensity"); // float
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "type"), "set_type", "get_type"); // String
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "range"), "set_range", "get_range"); // float
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_cone_angle"), "set_inner_cone_angle", "get_inner_cone_angle"); // float
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity"), "set_intensity", "get_intensity"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "light_type"), "set_light_type", "get_light_type"); // String
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range"), "set_range", "get_range"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_cone_angle"), "set_inner_cone_angle", "get_inner_cone_angle"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float
}
Color GLTFLight::get_color() {
@@ -68,12 +68,12 @@ void GLTFLight::set_intensity(float p_intensity) {
intensity = p_intensity;
}
-String GLTFLight::get_type() {
- return type;
+String GLTFLight::get_light_type() {
+ return light_type;
}
-void GLTFLight::set_type(String p_type) {
- type = p_type;
+void GLTFLight::set_light_type(String p_light_type) {
+ light_type = p_light_type;
}
float GLTFLight::get_range() {
diff --git a/modules/gltf/gltf_light.h b/modules/gltf/extensions/gltf_light.h
similarity index 94%
rename from modules/gltf/gltf_light.h
rename to modules/gltf/extensions/gltf_light.h
index 5076917ed..58fa299df 100644
--- a/modules/gltf/gltf_light.h
+++ b/modules/gltf/extensions/gltf_light.h
@@ -1,5 +1,3 @@
-#ifndef GLTF_LIGHT_H
-#define GLTF_LIGHT_H
/*************************************************************************/
/* gltf_light.h */
/*************************************************************************/
@@ -30,7 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/resource.h"
+#ifndef GLTF_LIGHT_H
+#define GLTF_LIGHT_H
+
+#include "core/config/engine.h"
+#include "core/io/resource.h"
+#include "scene/3d/light_3d.h"
class GLTFLight : public Resource {
GDCLASS(GLTFLight, Resource)
@@ -42,7 +45,7 @@ protected:
private:
Color color = Color(1.0f, 1.0f, 1.0f);
float intensity = 1.0f;
- String type;
+ String light_type;
float range = INFINITY;
float inner_cone_angle = 0.0f;
float outer_cone_angle = Math_TAU / 8.0f;
@@ -54,8 +57,8 @@ public:
float get_intensity();
void set_intensity(float p_intensity);
- String get_type();
- void set_type(String p_type);
+ String get_light_type();
+ void set_light_type(String p_light_type);
float get_range();
void set_range(float p_range);
diff --git a/modules/gltf/gltf_spec_gloss.cpp b/modules/gltf/extensions/gltf_spec_gloss.cpp
similarity index 97%
rename from modules/gltf/gltf_spec_gloss.cpp
rename to modules/gltf/extensions/gltf_spec_gloss.cpp
index ff688c5da..83af91bfc 100644
--- a/modules/gltf/gltf_spec_gloss.cpp
+++ b/modules/gltf/extensions/gltf_spec_gloss.cpp
@@ -44,7 +44,7 @@ void GLTFSpecGloss::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_img"), "set_diffuse_img", "get_diffuse_img"); // Ref
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "diffuse_factor"), "set_diffuse_factor", "get_diffuse_factor"); // Color
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "gloss_factor"), "set_gloss_factor", "get_gloss_factor"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gloss_factor"), "set_gloss_factor", "get_gloss_factor"); // float
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_factor"), "set_specular_factor", "get_specular_factor"); // Color
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "spec_gloss_img"), "set_spec_gloss_img", "get_spec_gloss_img"); // Ref
}
diff --git a/modules/gltf/gltf_spec_gloss.h b/modules/gltf/extensions/gltf_spec_gloss.h
similarity index 98%
rename from modules/gltf/gltf_spec_gloss.h
rename to modules/gltf/extensions/gltf_spec_gloss.h
index 3a69f431c..a45fa4296 100644
--- a/modules/gltf/gltf_spec_gloss.h
+++ b/modules/gltf/extensions/gltf_spec_gloss.h
@@ -1,5 +1,3 @@
-#ifndef GLTF_SPEC_GLOSS_H
-#define GLTF_SPEC_GLOSS_H
/*************************************************************************/
/* gltf_spec_gloss.h */
/*************************************************************************/
@@ -30,8 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/image.h"
-#include "core/resource.h"
+#ifndef GLTF_SPEC_GLOSS_H
+#define GLTF_SPEC_GLOSS_H
+
+#include "core/io/image.h"
+#include "core/io/resource.h"
class GLTFSpecGloss : public Resource {
GDCLASS(GLTFSpecGloss, Resource);
diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h
new file mode 100644
index 000000000..c20c87f79
--- /dev/null
+++ b/modules/gltf/gltf_defines.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* gltf_defines.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GLTF_DEFINES_H
+#define GLTF_DEFINES_H
+
+// This file should only be included by other headers.
+
+// Godot classes used by GLTF headers.
+class BoneAttachment3D;
+class CSGShape3D;
+class DirectionalLight3D;
+class GridMap;
+class Light3D;
+class MultiMeshInstance3D;
+class Skeleton3D;
+class Skin;
+
+// GLTF classes.
+struct GLTFAccessor;
+class GLTFAnimation;
+class GLTFBufferView;
+class GLTFCamera;
+class GLTFDocument;
+class GLTFDocumentExtension;
+class GLTFLight;
+class GLTFMesh;
+class GLTFNode;
+class GLTFSkeleton;
+class GLTFSkin;
+class GLTFSpecGloss;
+class GLTFState;
+class GLTFTexture;
+
+// GLTF index aliases.
+using GLTFAccessorIndex = int;
+using GLTFAnimationIndex = int;
+using GLTFBufferIndex = int;
+using GLTFBufferViewIndex = int;
+using GLTFCameraIndex = int;
+using GLTFImageIndex = int;
+using GLTFMaterialIndex = int;
+using GLTFMeshIndex = int;
+using GLTFLightIndex = int;
+using GLTFNodeIndex = int;
+using GLTFSkeletonIndex = int;
+using GLTFSkinIndex = int;
+using GLTFTextureIndex = int;
+
+enum GLTFType {
+ TYPE_SCALAR,
+ TYPE_VEC2,
+ TYPE_VEC3,
+ TYPE_VEC4,
+ TYPE_MAT2,
+ TYPE_MAT3,
+ TYPE_MAT4,
+};
+
+#endif // GLTF_DEFINES_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index edcda55b1..7e90f198f 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -30,81 +30,99 @@
#include "gltf_document.h"
-#include "gltf_accessor.h"
-#include "gltf_animation.h"
-#include "gltf_camera.h"
-#include "gltf_light.h"
-#include "gltf_mesh.h"
-#include "gltf_node.h"
-#include "gltf_skeleton.h"
-#include "gltf_skin.h"
-#include "gltf_spec_gloss.h"
+#include "extensions/gltf_spec_gloss.h"
+#include "gltf_document_extension.h"
+#include "gltf_document_extension_convert_importer_mesh.h"
#include "gltf_state.h"
-#include "gltf_texture.h"
-#include "core/bind/core_bind.h" // FIXME: Shouldn't use _Directory but DirAccess.
#include "core/crypto/crypto_core.h"
+#include "core/error/error_macros.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
+#include "core/io/file_access_memory.h"
#include "core/io/json.h"
+#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
-#include "core/os/file_access.h"
-#include "core/variant.h"
+#include "core/math/vector2.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/typed_array.h"
+#include "core/variant/variant.h"
#include "core/version.h"
#include "drivers/png/png_driver_common.h"
#include "scene/2d/node_2d.h"
-#include "scene/3d/bone_attachment.h"
-#include "scene/3d/mesh_instance.h"
-#include "scene/3d/multimesh_instance.h"
-#include "scene/3d/spatial.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/multimesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
#include "scene/animation/animation_player.h"
-#include "scene/main/node.h"
-#include "scene/resources/animation.h"
+#include "scene/resources/importer_mesh.h"
+#include "scene/resources/material.h"
+#include "scene/resources/mesh.h"
#include "scene/resources/multimesh.h"
-#include "scene/resources/skin.h"
#include "scene/resources/surface_tool.h"
-#include "modules/modules_enabled.gen.h" // For csg, gridmap, regex.
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
-#ifdef MODULE_REGEX_ENABLED
-#include "modules/regex/regex.h"
-#endif // MODULE_REGEX_ENABLED
+#ifdef MODULE_CSG_ENABLED
+#include "modules/csg/csg_shape.h"
+#endif // MODULE_CSG_ENABLED
+#ifdef MODULE_GRIDMAP_ENABLED
+#include "modules/gridmap/grid_map.h"
+#endif // MODULE_GRIDMAP_ENABLED
-Ref _mesh_to_array_mesh(Ref p_mesh) {
- Ref array_mesh = p_mesh;
- if (array_mesh.is_valid()) {
- return array_mesh;
- }
- array_mesh.instance();
+// FIXME: Hardcoded to avoid editor dependency.
+#define GLTF_IMPORT_USE_NAMED_SKIN_BINDS 16
+#define GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS 32
+
+#include
+#include
+#include
+#include
+
+static Ref _mesh_to_importer_mesh(Ref p_mesh) {
+ Ref importer_mesh;
+ importer_mesh.instantiate();
if (p_mesh.is_null()) {
- return array_mesh;
+ return importer_mesh;
}
+ Ref array_mesh = p_mesh;
+ if (p_mesh->get_blend_shape_count()) {
+ ArrayMesh::BlendShapeMode shape_mode = ArrayMesh::BLEND_SHAPE_MODE_NORMALIZED;
+ if (array_mesh.is_valid()) {
+ shape_mode = array_mesh->get_blend_shape_mode();
+ }
+ importer_mesh->set_blend_shape_mode(shape_mode);
+ for (int morph_i = 0; morph_i < p_mesh->get_blend_shape_count(); morph_i++) {
+ importer_mesh->add_blend_shape(p_mesh->get_blend_shape_name(morph_i));
+ }
+ }
for (int32_t surface_i = 0; surface_i < p_mesh->get_surface_count(); surface_i++) {
- Mesh::PrimitiveType primitive_type = p_mesh->surface_get_primitive_type(surface_i);
- Array arrays = p_mesh->surface_get_arrays(surface_i);
+ Array array = p_mesh->surface_get_arrays(surface_i);
Ref mat = p_mesh->surface_get_material(surface_i);
- int32_t mat_idx = array_mesh->get_surface_count();
- array_mesh->add_surface_from_arrays(primitive_type, arrays);
- array_mesh->surface_set_material(mat_idx, mat);
+ String mat_name;
+ if (mat.is_valid()) {
+ mat_name = mat->get_name();
+ } else {
+ // Assign default material when no material is assigned.
+ mat = Ref(memnew(StandardMaterial3D));
+ }
+ importer_mesh->add_surface(p_mesh->surface_get_primitive_type(surface_i),
+ array, p_mesh->surface_get_blend_shape_arrays(surface_i), p_mesh->surface_get_lods(surface_i), mat,
+ mat_name, p_mesh->surface_get_format(surface_i));
}
-
- return array_mesh;
+ return importer_mesh;
}
-Error GLTFDocument::serialize(Ref state, Node *p_root, const String &p_path) {
- uint64_t begin_time = OS::get_singleton()->get_ticks_usec();
-
- state->skeleton3d_to_gltf_skeleton.clear();
- state->skin_and_skeleton3d_to_gltf_skin.clear();
-
- _convert_scene_node(state, p_root, -1, -1);
+Error GLTFDocument::_serialize(Ref state, const String &p_path) {
if (!state->buffers.size()) {
state->buffers.push_back(Vector());
}
- /* STEP 1 CONVERT MESH INSTANCES */
+ /* STEP CONVERT MESH INSTANCES */
_convert_mesh_instances(state);
- /* STEP 2 SERIALIZE CAMERAS */
+ /* STEP SERIALIZE CAMERAS */
Error err = _serialize_cameras(state);
if (err != OK) {
return Error::FAILED;
@@ -116,37 +134,37 @@ Error GLTFDocument::serialize(Ref state, Node *p_root, const String &
return Error::FAILED;
}
- /* STEP 5 SERIALIZE MESHES (we have enough info now) */
+ /* STEP SERIALIZE MESHES (we have enough info now) */
err = _serialize_meshes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 6 SERIALIZE TEXTURES */
+ /* STEP SERIALIZE TEXTURES */
err = _serialize_materials(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 7 SERIALIZE ANIMATIONS */
+ /* STEP SERIALIZE ANIMATIONS */
err = _serialize_animations(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 8 SERIALIZE ACCESSORS */
+ /* STEP SERIALIZE ACCESSORS */
err = _encode_accessors(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 9 SERIALIZE IMAGES */
+ /* STEP SERIALIZE IMAGES */
err = _serialize_images(state, p_path);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 10 SERIALIZE TEXTURES */
+ /* STEP SERIALIZE TEXTURES */
err = _serialize_textures(state);
if (err != OK) {
return Error::FAILED;
@@ -156,65 +174,68 @@ Error GLTFDocument::serialize(Ref state, Node *p_root, const String &
state->buffer_views.write[i]->buffer = 0;
}
- /* STEP 11 SERIALIZE BUFFER VIEWS */
+ /* STEP SERIALIZE BUFFER VIEWS */
err = _encode_buffer_views(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 12 SERIALIZE NODES */
+ /* STEP SERIALIZE NODES */
err = _serialize_nodes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 13 SERIALIZE SCENE */
+ /* STEP SERIALIZE SCENE */
err = _serialize_scenes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 14 SERIALIZE SCENE */
+ /* STEP SERIALIZE SCENE */
err = _serialize_lights(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 15 SERIALIZE EXTENSIONS */
+ /* STEP SERIALIZE EXTENSIONS */
err = _serialize_extensions(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 16 SERIALIZE VERSION */
+ /* STEP SERIALIZE VERSION */
err = _serialize_version(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 17 SERIALIZE FILE */
- err = _serialize_file(state, p_path);
- if (err != OK) {
- return Error::FAILED;
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->export_post(state);
+ ERR_FAIL_COND_V(err != OK, err);
}
- uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time;
- float elapsed_sec = double(elapsed) / 1000000.0;
- elapsed_sec = Math::stepify(elapsed_sec, 0.01f);
- print_line("glTF: Export time elapsed seconds " + rtos(elapsed_sec).pad_decimals(2));
return OK;
}
Error GLTFDocument::_serialize_extensions(Ref state) const {
- const String texture_transform = "KHR_texture_transform";
- const String punctual_lights = "KHR_lights_punctual";
Array extensions_used;
- extensions_used.push_back(punctual_lights);
- extensions_used.push_back(texture_transform);
- state->json["extensionsUsed"] = extensions_used;
Array extensions_required;
- extensions_required.push_back(texture_transform);
- state->json["extensionsRequired"] = extensions_required;
+ if (!state->lights.is_empty()) {
+ extensions_used.push_back("KHR_lights_punctual");
+ }
+ if (state->use_khr_texture_transform) {
+ extensions_used.push_back("KHR_texture_transform");
+ extensions_required.push_back("KHR_texture_transform");
+ }
+ if (!extensions_used.is_empty()) {
+ state->json["extensionsUsed"] = extensions_used;
+ }
+ if (!extensions_required.is_empty()) {
+ state->json["extensionsRequired"] = extensions_required;
+ }
return OK;
}
@@ -225,7 +246,7 @@ Error GLTFDocument::_serialize_scenes(Ref state) {
if (state->nodes.size()) {
Dictionary s;
- if (!state->scene_name.empty()) {
+ if (!state->scene_name.is_empty()) {
s["name"] = state->scene_name;
}
@@ -241,42 +262,36 @@ Error GLTFDocument::_serialize_scenes(Ref state) {
Error GLTFDocument::_parse_json(const String &p_path, Ref state) {
Error err;
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (!f) {
+ Ref f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (f.is_null()) {
return err;
}
Vector array;
- array.resize(f->get_len());
+ array.resize(f->get_length());
f->get_buffer(array.ptrw(), array.size());
String text;
text.parse_utf8((const char *)array.ptr(), array.size());
- String err_txt;
- int err_line;
- Variant v;
- err = JSON::parse(text, v, err_txt, err_line);
+ JSON json;
+ err = json.parse(text);
if (err != OK) {
- _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
return err;
}
- state->json = v;
+ state->json = json.get_data();
return OK;
}
-Error GLTFDocument::_parse_glb(const String &p_path, Ref state) {
- Error err;
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (!f) {
- return err;
- }
-
+Error GLTFDocument::_parse_glb(Ref f, Ref state) {
+ ERR_FAIL_NULL_V(f, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(f->get_position() != 0, ERR_FILE_CANT_READ);
uint32_t magic = f->get_32();
ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF
f->get_32(); // version
f->get_32(); // length
-
uint32_t chunk_length = f->get_32();
uint32_t chunk_type = f->get_32();
@@ -289,16 +304,14 @@ Error GLTFDocument::_parse_glb(const String &p_path, Ref state) {
String text;
text.parse_utf8((const char *)json_data.ptr(), json_data.size());
- String err_txt;
- int err_line;
- Variant v;
- err = JSON::parse(text, v, err_txt, err_line);
+ JSON json;
+ Error err = json.parse(text);
if (err != OK) {
- _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("", "", json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
return err;
}
- state->json = v;
+ state->json = json.get_data();
//data?
@@ -332,47 +345,47 @@ static Vector3 _arr_to_vec3(const Array &p_array) {
return Vector3(p_array[0], p_array[1], p_array[2]);
}
-static Array _quat_to_array(const Quat &p_quat) {
+static Array _quaternion_to_array(const Quaternion &p_quaternion) {
Array array;
array.resize(4);
- array[0] = p_quat.x;
- array[1] = p_quat.y;
- array[2] = p_quat.z;
- array[3] = p_quat.w;
+ array[0] = p_quaternion.x;
+ array[1] = p_quaternion.y;
+ array[2] = p_quaternion.z;
+ array[3] = p_quaternion.w;
return array;
}
-static Quat _arr_to_quat(const Array &p_array) {
- ERR_FAIL_COND_V(p_array.size() != 4, Quat());
- return Quat(p_array[0], p_array[1], p_array[2], p_array[3]);
+static Quaternion _arr_to_quaternion(const Array &p_array) {
+ ERR_FAIL_COND_V(p_array.size() != 4, Quaternion());
+ return Quaternion(p_array[0], p_array[1], p_array[2], p_array[3]);
}
-static Transform _arr_to_xform(const Array &p_array) {
- ERR_FAIL_COND_V(p_array.size() != 16, Transform());
+static Transform3D _arr_to_xform(const Array &p_array) {
+ ERR_FAIL_COND_V(p_array.size() != 16, Transform3D());
- Transform xform;
- xform.basis.set_axis(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2]));
- xform.basis.set_axis(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6]));
- xform.basis.set_axis(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10]));
+ Transform3D xform;
+ xform.basis.set_column(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2]));
+ xform.basis.set_column(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6]));
+ xform.basis.set_column(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10]));
xform.set_origin(Vector3(p_array[12], p_array[13], p_array[14]));
return xform;
}
-static Vector _xform_to_array(const Transform p_transform) {
+static Vector _xform_to_array(const Transform3D p_transform) {
Vector array;
array.resize(16);
- Vector3 axis_x = p_transform.get_basis().get_axis(Vector3::AXIS_X);
+ Vector3 axis_x = p_transform.get_basis().get_column(Vector3::AXIS_X);
array.write[0] = axis_x.x;
array.write[1] = axis_x.y;
array.write[2] = axis_x.z;
array.write[3] = 0.0f;
- Vector3 axis_y = p_transform.get_basis().get_axis(Vector3::AXIS_Y);
+ Vector3 axis_y = p_transform.get_basis().get_column(Vector3::AXIS_Y);
array.write[4] = axis_y.x;
array.write[5] = axis_y.y;
array.write[6] = axis_y.z;
array.write[7] = 0.0f;
- Vector3 axis_z = p_transform.get_basis().get_axis(Vector3::AXIS_Z);
+ Vector3 axis_z = p_transform.get_basis().get_column(Vector3::AXIS_Z);
array.write[8] = axis_z.x;
array.write[9] = axis_z.y;
array.write[10] = axis_z.z;
@@ -392,7 +405,7 @@ Error GLTFDocument::_serialize_nodes(Ref state) {
Ref n = state->nodes[i];
Dictionary extensions;
node["extensions"] = extensions;
- if (!n->get_name().empty()) {
+ if (!n->get_name().is_empty()) {
node["name"] = n->get_name();
}
if (n->camera != -1) {
@@ -411,20 +424,20 @@ Error GLTFDocument::_serialize_nodes(Ref state) {
}
if (n->skeleton != -1 && n->skin < 0) {
}
- if (n->xform != Transform()) {
+ if (n->xform != Transform3D()) {
node["matrix"] = _xform_to_array(n->xform);
}
- if (!n->rotation.is_equal_approx(Quat())) {
- node["rotation"] = _quat_to_array(n->rotation);
+ if (!n->rotation.is_equal_approx(Quaternion())) {
+ node["rotation"] = _quaternion_to_array(n->rotation);
}
if (!n->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) {
node["scale"] = _vec3_to_arr(n->scale);
}
- if (!n->translation.is_equal_approx(Vector3())) {
- node["translation"] = _vec3_to_arr(n->translation);
+ if (!n->position.is_equal_approx(Vector3())) {
+ node["translation"] = _vec3_to_arr(n->position);
}
if (n->children.size()) {
Array children;
@@ -433,37 +446,23 @@ Error GLTFDocument::_serialize_nodes(Ref state) {
}
node["children"] = children;
}
+
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ ERR_CONTINUE(!state->scene_nodes.find(i));
+ Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]);
+ ERR_CONTINUE(err != OK);
+ }
+
nodes.push_back(node);
}
state->json["nodes"] = nodes;
return OK;
}
-String GLTFDocument::_sanitize_scene_name(Ref state, const String &p_name) {
- if (state->use_legacy_names) {
-#ifdef MODULE_REGEX_ENABLED
- RegEx regex("([^a-zA-Z0-9_ -]+)");
- String s_name = regex.sub(p_name, "", true);
- return s_name;
-#else
- WARN_PRINT("GLTF: Legacy scene names are not supported without the RegEx module. Falling back to new names.");
-#endif // MODULE_REGEX_ENABLED
- }
- return p_name.validate_node_name();
-}
-
-String GLTFDocument::_legacy_validate_node_name(const String &p_name) {
- String invalid_character = ". : @ / \"";
- String name = p_name;
- Vector chars = invalid_character.split(" ");
- for (int i = 0; i < chars.size(); i++) {
- name = name.replace(chars[i], "");
- }
- return name;
-}
-
String GLTFDocument::_gen_unique_name(Ref state, const String &p_name) {
- const String s_name = _sanitize_scene_name(state, p_name);
+ const String s_name = p_name.validate_node_name();
String name;
int index = 1;
@@ -471,9 +470,6 @@ String GLTFDocument::_gen_unique_name(Ref state, const String &p_name
name = s_name;
if (index > 1) {
- if (state->use_legacy_names) {
- name += " ";
- }
name += itos(index);
}
if (!state->unique_names.has(name)) {
@@ -520,39 +516,18 @@ String GLTFDocument::_gen_unique_animation_name(Ref state, const Stri
return name;
}
-String GLTFDocument::_sanitize_bone_name(Ref state, const String &p_name) {
- if (state->use_legacy_names) {
-#ifdef MODULE_REGEX_ENABLED
- String name = p_name.camelcase_to_underscore(true);
- RegEx pattern_del("([^a-zA-Z0-9_ ])+");
-
- name = pattern_del.sub(name, "", true);
-
- RegEx pattern_nospace(" +");
- name = pattern_nospace.sub(name, "_", true);
-
- RegEx pattern_multiple("_+");
- name = pattern_multiple.sub(name, "_", true);
-
- RegEx pattern_padded("0+(\\d+)");
- name = pattern_padded.sub(name, "$1", true);
-
- return name;
-#else
- WARN_PRINT("GLTF: Legacy bone names are not supported without the RegEx module. Falling back to new names.");
-#endif // MODULE_REGEX_ENABLED
- }
+String GLTFDocument::_sanitize_bone_name(const String &p_name) {
String name = p_name;
name = name.replace(":", "_");
name = name.replace("/", "_");
- if (name.empty()) {
- name = "bone";
- }
return name;
}
String GLTFDocument::_gen_unique_bone_name(Ref state, const GLTFSkeletonIndex skel_i, const String &p_name) {
- String s_name = _sanitize_bone_name(state, p_name);
+ String s_name = _sanitize_bone_name(p_name);
+ if (s_name.is_empty()) {
+ s_name = "bone";
+ }
String name;
int index = 1;
while (true) {
@@ -591,10 +566,10 @@ Error GLTFDocument::_parse_scenes(Ref state) {
state->root_nodes.push_back(nodes[j]);
}
- if (s.has("name") && !String(s["name"]).empty() && !((String)s["name"]).begins_with("Scene")) {
- state->scene_name = s["name"];
+ if (s.has("name") && !String(s["name"]).is_empty() && !((String)s["name"]).begins_with("Scene")) {
+ state->scene_name = _gen_unique_name(state, s["name"]);
} else {
- state->scene_name = state->filename;
+ state->scene_name = _gen_unique_name(state, state->filename);
}
}
@@ -606,7 +581,7 @@ Error GLTFDocument::_parse_nodes(Ref state) {
const Array &nodes = state->json["nodes"];
for (int i = 0; i < nodes.size(); i++) {
Ref node;
- node.instance();
+ node.instantiate();
const Dictionary &n = nodes[i];
if (n.has("name")) {
@@ -625,17 +600,17 @@ Error GLTFDocument::_parse_nodes(Ref state) {
node->xform = _arr_to_xform(n["matrix"]);
} else {
if (n.has("translation")) {
- node->translation = _arr_to_vec3(n["translation"]);
+ node->position = _arr_to_vec3(n["translation"]);
}
if (n.has("rotation")) {
- node->rotation = _arr_to_quat(n["rotation"]);
+ node->rotation = _arr_to_quaternion(n["rotation"]);
}
if (n.has("scale")) {
node->scale = _arr_to_vec3(n["scale"]);
}
- node->xform.basis.set_quat_scale(node->rotation, node->scale);
- node->xform.origin = node->translation;
+ node->xform.basis.set_quaternion_scale(node->rotation, node->scale);
+ node->xform.origin = node->position;
}
if (n.has("extensions")) {
@@ -701,7 +676,7 @@ static Vector _parse_base64_uri(const String &uri) {
int start = uri.find(",");
ERR_FAIL_COND_V(start == -1, Vector());
- CharString substr = uri.right(start + 1).ascii();
+ CharString substr = uri.substr(start + 1).ascii();
int strlen = substr.length();
@@ -736,8 +711,8 @@ Error GLTFDocument::_encode_buffer_glb(Ref state, const String &p_pat
String filename = p_path.get_basename().get_file() + itos(i) + ".bin";
String path = p_path.get_base_dir() + "/" + filename;
Error err;
- FileAccessRef f = FileAccess::open(path, FileAccess::WRITE, &err);
- if (!f) {
+ Ref f = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (f.is_null()) {
return err;
}
if (buffer_data.size() == 0) {
@@ -745,14 +720,10 @@ Error GLTFDocument::_encode_buffer_glb(Ref state, const String &p_pat
}
f->create(FileAccess::ACCESS_RESOURCES);
f->store_buffer(buffer_data.ptr(), buffer_data.size());
- f->close();
gltf_buffer["uri"] = filename;
gltf_buffer["byteLength"] = buffer_data.size();
buffers.push_back(gltf_buffer);
}
- if (!buffers.size()) {
- return OK;
- }
state->json["buffers"] = buffers;
return OK;
@@ -772,8 +743,8 @@ Error GLTFDocument::_encode_buffer_bins(Ref state, const String &p_pa
String filename = p_path.get_basename().get_file() + itos(i) + ".bin";
String path = p_path.get_base_dir() + "/" + filename;
Error err;
- FileAccessRef f = FileAccess::open(path, FileAccess::WRITE, &err);
- if (!f) {
+ Ref f = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (f.is_null()) {
return err;
}
if (buffer_data.size() == 0) {
@@ -781,7 +752,6 @@ Error GLTFDocument::_encode_buffer_bins(Ref state, const String &p_pa
}
f->create(FileAccess::ACCESS_RESOURCES);
f->store_buffer(buffer_data.ptr(), buffer_data.size());
- f->close();
gltf_buffer["uri"] = filename;
gltf_buffer["byteLength"] = buffer_data.size();
buffers.push_back(gltf_buffer);
@@ -815,7 +785,8 @@ Error GLTFDocument::_parse_buffers(Ref state, const String &p_base_pa
}
buffer_data = _parse_base64_uri(uri);
} else { // Relative path to an external image file.
- uri = uri.http_unescape();
+ ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
+ uri = uri.uri_decode();
uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows.
buffer_data = FileAccess::get_file_as_array(uri);
ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri);
@@ -874,7 +845,7 @@ Error GLTFDocument::_parse_buffer_views(Ref state) {
const Dictionary &d = buffers[i];
Ref buffer_view;
- buffer_view.instance();
+ buffer_view.instantiate();
ERR_FAIL_COND_V(!d.has("buffer"), ERR_PARSE_ERROR);
buffer_view->buffer = d["buffer"];
@@ -913,18 +884,8 @@ Error GLTFDocument::_encode_accessors(Ref state) {
d["type"] = _get_accessor_type_name(accessor->type);
d["byteOffset"] = accessor->byte_offset;
d["normalized"] = accessor->normalized;
- Array max;
- max.resize(accessor->max.size());
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- max[max_i] = accessor->max[max_i];
- }
- d["max"] = max;
- Array min;
- min.resize(accessor->min.size());
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- min[min_i] = accessor->min[min_i];
- }
- d["min"] = min;
+ d["max"] = accessor->max;
+ d["min"] = accessor->min;
d["bufferView"] = accessor->buffer_view; //optional because it may be sparse...
// Dictionary s;
@@ -970,58 +931,58 @@ Error GLTFDocument::_encode_accessors(Ref state) {
return OK;
}
-String GLTFDocument::_get_accessor_type_name(const GLTFDocument::GLTFType p_type) {
- if (p_type == GLTFDocument::TYPE_SCALAR) {
+String GLTFDocument::_get_accessor_type_name(const GLTFType p_type) {
+ if (p_type == GLTFType::TYPE_SCALAR) {
return "SCALAR";
}
- if (p_type == GLTFDocument::TYPE_VEC2) {
+ if (p_type == GLTFType::TYPE_VEC2) {
return "VEC2";
}
- if (p_type == GLTFDocument::TYPE_VEC3) {
+ if (p_type == GLTFType::TYPE_VEC3) {
return "VEC3";
}
- if (p_type == GLTFDocument::TYPE_VEC4) {
+ if (p_type == GLTFType::TYPE_VEC4) {
return "VEC4";
}
- if (p_type == GLTFDocument::TYPE_MAT2) {
+ if (p_type == GLTFType::TYPE_MAT2) {
return "MAT2";
}
- if (p_type == GLTFDocument::TYPE_MAT3) {
+ if (p_type == GLTFType::TYPE_MAT3) {
return "MAT3";
}
- if (p_type == GLTFDocument::TYPE_MAT4) {
+ if (p_type == GLTFType::TYPE_MAT4) {
return "MAT4";
}
ERR_FAIL_V("SCALAR");
}
-GLTFDocument::GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
+GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
if (p_string == "SCALAR") {
- return GLTFDocument::TYPE_SCALAR;
+ return GLTFType::TYPE_SCALAR;
}
if (p_string == "VEC2") {
- return GLTFDocument::TYPE_VEC2;
+ return GLTFType::TYPE_VEC2;
}
if (p_string == "VEC3") {
- return GLTFDocument::TYPE_VEC3;
+ return GLTFType::TYPE_VEC3;
}
if (p_string == "VEC4") {
- return GLTFDocument::TYPE_VEC4;
+ return GLTFType::TYPE_VEC4;
}
if (p_string == "MAT2") {
- return GLTFDocument::TYPE_MAT2;
+ return GLTFType::TYPE_MAT2;
}
if (p_string == "MAT3") {
- return GLTFDocument::TYPE_MAT3;
+ return GLTFType::TYPE_MAT3;
}
if (p_string == "MAT4") {
- return GLTFDocument::TYPE_MAT4;
+ return GLTFType::TYPE_MAT4;
}
- ERR_FAIL_V(GLTFDocument::TYPE_SCALAR);
+ ERR_FAIL_V(GLTFType::TYPE_SCALAR);
}
Error GLTFDocument::_parse_accessors(Ref state) {
@@ -1033,7 +994,7 @@ Error GLTFDocument::_parse_accessors(Ref state) {
const Dictionary &d = accessors[i];
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
ERR_FAIL_COND_V(!d.has("componentType"), ERR_PARSE_ERROR);
accessor->component_type = d["componentType"];
@@ -1055,21 +1016,11 @@ Error GLTFDocument::_parse_accessors(Ref state) {
}
if (d.has("max")) {
- Array max = d["max"];
- accessor->max.resize(max.size());
- PoolVector::Write max_write = accessor->max.write();
- for (int32_t max_i = 0; max_i < accessor->max.size(); max_i++) {
- max_write[max_i] = max[max_i];
- }
+ accessor->max = d["max"];
}
if (d.has("min")) {
- Array min = d["min"];
- accessor->min.resize(min.size());
- PoolVector::Write min_write = accessor->min.write();
- for (int32_t min_i = 0; min_i < accessor->min.size(); min_i++) {
- min_write[min_i] = min[min_i];
- }
+ accessor->min = d["min"];
}
if (d.has("sparse")) {
@@ -1185,7 +1136,7 @@ Error GLTFDocument::_encode_buffer_view(Ref state, const double *src,
}
Ref bv;
- bv.instance();
+ bv.instantiate();
const uint32_t offset = bv->byte_offset = byte_offset;
Vector &gltf_buffer = state->buffers.write[0];
@@ -1561,7 +1512,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref state, c
Vector type_min;
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
- attribs.write[i] = Math::stepify(p_attribs[i], 1.0);
+ attribs.write[i] = Math::snapped(p_attribs[i], 1.0);
if (i == 0) {
for (int32_t type_i = 0; type_i < element_count; type_i++) {
type_max.write[type_i] = attribs[(i * element_count) + type_i];
@@ -1579,26 +1530,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref state, c
ERR_FAIL_COND_V(attribs.size() == 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_INT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = ret_size;
accessor->type = type;
@@ -1667,34 +1606,22 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref state, c
for (int i = 0; i < p_attribs.size(); i++) {
Vector2 attrib = p_attribs[i];
- attribs.write[(i * element_count) + 0] = Math::stepify(attrib.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 1] = Math::stepify(attrib.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.y, CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC2;
+ const GLTFType type = GLTFType::TYPE_VEC2;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -1725,10 +1652,10 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref state,
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
Color attrib = p_attribs[i];
- attribs.write[(i * element_count) + 0] = Math::stepify(attrib.r, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 1] = Math::stepify(attrib.g, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 2] = Math::stepify(attrib.b, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 3] = Math::stepify(attrib.a, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
@@ -1736,25 +1663,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref state,
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -1801,10 +1717,10 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref state
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
Color attrib = p_attribs[i];
- attribs.write[(i * element_count) + 0] = Math::stepify(attrib.r, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 1] = Math::stepify(attrib.g, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 2] = Math::stepify(attrib.b, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 3] = Math::stepify(attrib.a, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
@@ -1812,26 +1728,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref state
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -1862,35 +1766,23 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref state,
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
Color attrib = p_attribs[i];
- attribs.write[(i * element_count) + 0] = Math::stepify(attrib.r, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 1] = Math::stepify(attrib.g, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 2] = Math::stepify(attrib.b, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 3] = Math::stepify(attrib.a, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -1905,7 +1797,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref state,
return state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref state, const Vector p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref state, const Vector p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1920,11 +1812,11 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref state,
Vector type_min;
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
- Quat quat = p_attribs[i];
- attribs.write[(i * element_count) + 0] = Math::stepify(quat.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 1] = Math::stepify(quat.y, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 2] = Math::stepify(quat.z, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 3] = Math::stepify(quat.w, CMP_NORMALIZE_TOLERANCE);
+ Quaternion quaternion = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(quaternion.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(quaternion.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(quaternion.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(quaternion.w, CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
@@ -1932,26 +1824,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quats(Ref state,
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const GLTFType type = GLTFType::TYPE_VEC4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -2001,7 +1881,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref state,
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
- attribs.write[i] = Math::stepify(p_attribs[i], CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i] = Math::snapped(p_attribs[i], CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
@@ -2009,26 +1889,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref state,
ERR_FAIL_COND_V(!attribs.size(), -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ const GLTFType type = GLTFType::TYPE_SCALAR;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = ret_size;
accessor->type = type;
@@ -2058,35 +1926,23 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref state, c
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
Vector3 attrib = p_attribs[i];
- attribs.write[(i * element_count) + 0] = Math::stepify(attrib.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 1] = Math::stepify(attrib.y, CMP_NORMALIZE_TOLERANCE);
- attribs.write[(i * element_count) + 2] = Math::stepify(attrib.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.z, CMP_NORMALIZE_TOLERANCE);
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
}
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC3;
+ const GLTFType type = GLTFType::TYPE_VEC3;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -2101,7 +1957,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref state, c
return state->accessors.size() - 1;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state, const Vector p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state, const Vector p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -2115,31 +1971,31 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state,
Vector type_min;
type_min.resize(element_count);
for (int i = 0; i < p_attribs.size(); i++) {
- Transform attrib = p_attribs[i];
+ Transform3D attrib = p_attribs[i];
Basis basis = attrib.get_basis();
- Vector3 axis_0 = basis.get_axis(Vector3::AXIS_X);
+ Vector3 axis_0 = basis.get_column(Vector3::AXIS_X);
- attribs.write[i * element_count + 0] = Math::stepify(axis_0.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 1] = Math::stepify(axis_0.y, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 2] = Math::stepify(axis_0.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 0] = Math::snapped(axis_0.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 1] = Math::snapped(axis_0.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 2] = Math::snapped(axis_0.z, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 3] = 0.0;
- Vector3 axis_1 = basis.get_axis(Vector3::AXIS_Y);
- attribs.write[i * element_count + 4] = Math::stepify(axis_1.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 5] = Math::stepify(axis_1.y, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 6] = Math::stepify(axis_1.z, CMP_NORMALIZE_TOLERANCE);
+ Vector3 axis_1 = basis.get_column(Vector3::AXIS_Y);
+ attribs.write[i * element_count + 4] = Math::snapped(axis_1.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 5] = Math::snapped(axis_1.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 6] = Math::snapped(axis_1.z, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 7] = 0.0;
- Vector3 axis_2 = basis.get_axis(Vector3::AXIS_Z);
- attribs.write[i * element_count + 8] = Math::stepify(axis_2.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 9] = Math::stepify(axis_2.y, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 10] = Math::stepify(axis_2.z, CMP_NORMALIZE_TOLERANCE);
+ Vector3 axis_2 = basis.get_column(Vector3::AXIS_Z);
+ attribs.write[i * element_count + 8] = Math::snapped(axis_2.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 9] = Math::snapped(axis_2.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 10] = Math::snapped(axis_2.z, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 11] = 0.0;
Vector3 origin = attrib.get_origin();
- attribs.write[i * element_count + 12] = Math::stepify(origin.x, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 13] = Math::stepify(origin.y, CMP_NORMALIZE_TOLERANCE);
- attribs.write[i * element_count + 14] = Math::stepify(origin.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 12] = Math::snapped(origin.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 13] = Math::snapped(origin.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 14] = Math::snapped(origin.z, CMP_NORMALIZE_TOLERANCE);
attribs.write[i * element_count + 15] = 1.0;
_calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
@@ -2147,26 +2003,14 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref state,
ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
Ref accessor;
- accessor.instance();
+ accessor.instantiate();
GLTFBufferIndex buffer_view_i;
int64_t size = state->buffers[0].size();
- const GLTFDocument::GLTFType type = GLTFDocument::TYPE_MAT4;
+ const GLTFType type = GLTFType::TYPE_MAT4;
const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
- PoolVector max;
- max.resize(type_max.size());
- PoolVector::Write write_max = max.write();
- for (int32_t max_i = 0; max_i < max.size(); max_i++) {
- write_max[max_i] = type_max[max_i];
- }
- accessor->max = max;
- PoolVector min;
- min.resize(type_min.size());
- PoolVector::Write write_min = min.write();
- for (int32_t min_i = 0; min_i < min.size(); min_i++) {
- write_min[min_i] = type_min[min_i];
- }
- accessor->min = min;
+ accessor->max = type_max;
+ accessor->min = type_min;
accessor->normalized = false;
accessor->count = p_attribs.size();
accessor->type = type;
@@ -2227,9 +2071,9 @@ Vector GLTFDocument::_decode_accessor_as_color(Ref state, cons
}
return ret;
}
-Vector GLTFDocument::_decode_accessor_as_quat(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector GLTFDocument::_decode_accessor_as_quaternion(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
const Vector attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector ret;
+ Vector ret;
if (attribs.size() == 0) {
return ret;
@@ -2241,7 +2085,7 @@ Vector GLTFDocument::_decode_accessor_as_quat(Ref state, const
ret.resize(ret_size);
{
for (int i = 0; i < ret_size; i++) {
- ret.write[i] = Quat(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]).normalized();
+ ret.write[i] = Quaternion(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]).normalized();
}
}
return ret;
@@ -2274,16 +2118,16 @@ Vector GLTFDocument::_decode_accessor_as_basis(Ref state, cons
ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret);
ret.resize(attribs.size() / 9);
for (int i = 0; i < ret.size(); i++) {
- ret.write[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2]));
- ret.write[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5]));
- ret.write[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8]));
+ ret.write[i].set_column(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2]));
+ ret.write[i].set_column(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5]));
+ ret.write[i].set_column(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8]));
}
return ret;
}
-Vector GLTFDocument::_decode_accessor_as_xform(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+Vector GLTFDocument::_decode_accessor_as_xform(Ref state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
const Vector attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector ret;
+ Vector ret;
if (attribs.size() == 0) {
return ret;
@@ -2292,9 +2136,9 @@ Vector GLTFDocument::_decode_accessor_as_xform(Ref state,
ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret);
ret.resize(attribs.size() / 16);
for (int i = 0; i < ret.size(); i++) {
- ret.write[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2]));
- ret.write[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6]));
- ret.write[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10]));
+ ret.write[i].basis.set_column(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2]));
+ ret.write[i].basis.set_column(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6]));
+ ret.write[i].basis.set_column(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10]));
ret.write[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14]));
}
return ret;
@@ -2304,7 +2148,7 @@ Error GLTFDocument::_serialize_meshes(Ref state) {
Array meshes;
for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < state->meshes.size(); gltf_mesh_i++) {
print_verbose("glTF: Serializing mesh: " + itos(gltf_mesh_i));
- Ref import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh();
+ Ref import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh();
if (import_mesh.is_null()) {
continue;
}
@@ -2319,7 +2163,7 @@ Error GLTFDocument::_serialize_meshes(Ref state) {
for (int surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) {
Array targets;
Dictionary primitive;
- Mesh::PrimitiveType primitive_type = import_mesh->surface_get_primitive_type(surface_i);
+ Mesh::PrimitiveType primitive_type = import_mesh->get_surface_primitive_type(surface_i);
switch (primitive_type) {
case Mesh::PRIMITIVE_POINTS: {
primitive["mode"] = 0;
@@ -2354,12 +2198,15 @@ Error GLTFDocument::_serialize_meshes(Ref state) {
}
}
- Array array = import_mesh->surface_get_arrays(surface_i);
+ Array array = import_mesh->get_surface_arrays(surface_i);
+ uint32_t format = import_mesh->get_surface_format(surface_i);
+ int32_t vertex_num = 0;
Dictionary attributes;
{
Vector a = array[Mesh::ARRAY_VERTEX];
ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA);
attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true);
+ vertex_num = a.size();
}
{
Vector a = array[Mesh::ARRAY_TANGENT];
@@ -2402,13 +2249,65 @@ Error GLTFDocument::_serialize_meshes(Ref state) {
attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(state, a, true);
}
}
+ for (int custom_i = 0; custom_i < 3; custom_i++) {
+ Vector a = array[Mesh::ARRAY_CUSTOM0 + custom_i];
+ if (a.size()) {
+ int num_channels = 4;
+ int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS;
+ switch ((format >> custom_shift) & Mesh::ARRAY_FORMAT_CUSTOM_MASK) {
+ case Mesh::ARRAY_CUSTOM_R_FLOAT:
+ num_channels = 1;
+ break;
+ case Mesh::ARRAY_CUSTOM_RG_FLOAT:
+ num_channels = 2;
+ break;
+ case Mesh::ARRAY_CUSTOM_RGB_FLOAT:
+ num_channels = 3;
+ break;
+ case Mesh::ARRAY_CUSTOM_RGBA_FLOAT:
+ num_channels = 4;
+ break;
+ }
+ int texcoord_i = 2 + 2 * custom_i;
+ String gltf_texcoord_key;
+ for (int prev_texcoord_i = 0; prev_texcoord_i < texcoord_i; prev_texcoord_i++) {
+ gltf_texcoord_key = vformat("TEXCOORD_%d", prev_texcoord_i);
+ if (!attributes.has(gltf_texcoord_key)) {
+ Vector empty;
+ empty.resize(vertex_num);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, empty, true);
+ }
+ }
+
+ LocalVector first_channel;
+ first_channel.resize(vertex_num);
+ LocalVector second_channel;
+ second_channel.resize(vertex_num);
+ for (int32_t vert_i = 0; vert_i < vertex_num; vert_i++) {
+ float u = a[vert_i * num_channels + 0];
+ float v = (num_channels == 1 ? 0.0f : a[vert_i * num_channels + 1]);
+ first_channel[vert_i] = Vector2(u, v);
+ u = 0;
+ v = 0;
+ if (num_channels >= 3) {
+ u = a[vert_i * num_channels + 2];
+ v = (num_channels == 3 ? 0.0f : a[vert_i * num_channels + 3]);
+ second_channel[vert_i] = Vector2(u, v);
+ }
+ }
+ gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, first_channel, true);
+ gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, second_channel, true);
+ }
+ }
{
Vector