Skip to content

Commit

Permalink
Merge pull request #79316 from aaronfranke/gltf-export
Browse files Browse the repository at this point in the history
Add export settings to the export dialog for GLTF
  • Loading branch information
akien-mga committed Jan 2, 2024
2 parents 13a0d6e + 8acef03 commit 0f0106c
Show file tree
Hide file tree
Showing 7 changed files with 430 additions and 163 deletions.
2 changes: 1 addition & 1 deletion modules/gltf/doc_classes/GLTFDocument.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
<param index="1" name="path" type="String" />
<description>
Takes a [GLTFState] object through the [param state] parameter and writes a glTF file to the filesystem.
[b]Note:[/b] The extension of the glTF file determines if it is a .glb binary file or a .gltf file.
[b]Note:[/b] The extension of the glTF file determines if it is a .glb binary file or a .gltf text file.
</description>
</method>
</methods>
Expand Down
81 changes: 48 additions & 33 deletions modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@

#ifdef TOOLS_ENABLED

#include "../gltf_document.h"
#include "editor_scene_exporter_gltf_settings.h"

#include "editor/editor_file_system.h"
#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/gui/editor_file_dialog.h"
#include "scene/gui/popup_menu.h"
#include "editor/import/scene_import_settings.h"

String SceneExporterGLTFPlugin::get_name() const {
return "ConvertGLTF2";
Expand All @@ -48,59 +50,72 @@ bool SceneExporterGLTFPlugin::has_main_screen() const {
}

SceneExporterGLTFPlugin::SceneExporterGLTFPlugin() {
file_export_lib = memnew(EditorFileDialog);
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_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 Scene to glTF 2.0 File"));

_gltf_document.instantiate();
// Set up the file dialog.
_file_dialog = memnew(EditorFileDialog);
_file_dialog->connect("file_selected", callable_mp(this, &SceneExporterGLTFPlugin::_export_scene_as_gltf));
_file_dialog->set_title(TTR("Export Library"));
_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
_file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
_file_dialog->clear_filters();
_file_dialog->add_filter("*.glb");
_file_dialog->add_filter("*.gltf");
_file_dialog->set_title(TTR("Export Scene to glTF 2.0 File"));
EditorNode::get_singleton()->get_gui_base()->add_child(_file_dialog);
// Set up the export settings menu.
_export_settings.instantiate();
_export_settings->generate_property_list(_gltf_document);
_settings_inspector = memnew(EditorInspector);
_settings_inspector->set_custom_minimum_size(Size2(350, 300) * EDSCALE);
_file_dialog->add_side_menu(_settings_inspector, TTR("Export Settings:"));
// Add a button to the Scene -> Export menu to pop up the settings dialog.
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));
menu->set_item_metadata(idx, callable_mp(this, &SceneExporterGLTFPlugin::_popup_gltf_export_dialog));
}

void SceneExporterGLTFPlugin::_popup_gltf_export_dialog() {
Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root();
if (!root) {
EditorNode::get_singleton()->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
return;
}
// Set the file dialog's file name to the scene name.
String filename = String(root->get_scene_file_path().get_file().get_basename());
if (filename.is_empty()) {
filename = root->get_name();
}
_file_dialog->set_current_file(filename + String(".gltf"));
// Generate and refresh the export settings.
_export_settings->generate_property_list(_gltf_document);
_settings_inspector->edit(nullptr);
_settings_inspector->edit(_export_settings.ptr());
// Show the file dialog.
_file_dialog->popup_centered_ratio();
}

void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) {
void SceneExporterGLTFPlugin::_export_scene_as_gltf(const String &p_file_path) {
Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root();
if (!root) {
EditorNode::get_singleton()->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
return;
}
List<String> deps;
Ref<GLTFDocument> doc;
doc.instantiate();
Ref<GLTFState> state;
state.instantiate();
state->set_copyright(_export_settings->get_copyright());
int32_t flags = 0;
flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
Error err = doc->append_from_scene(root, state, flags);
Error err = _gltf_document->append_from_scene(root, state, flags);
if (err != OK) {
ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
}
err = doc->write_to_filesystem(state, p_file);
err = _gltf_document->write_to_filesystem(state, p_file_path);
if (err != OK) {
ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
}
EditorFileSystem::get_singleton()->scan_changes();
}

void SceneExporterGLTFPlugin::convert_scene_to_gltf2() {
Node *root = EditorNode::get_singleton()->get_tree()->get_edited_scene_root();
if (!root) {
EditorNode::get_singleton()->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
return;
}
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 + String(".gltf"));
file_export_lib->popup_centered_ratio();
}

#endif // TOOLS_ENABLED
13 changes: 9 additions & 4 deletions modules/gltf/editor/editor_scene_exporter_gltf_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,23 @@

#ifdef TOOLS_ENABLED

#include "editor_scene_importer_gltf.h"
#include "../gltf_document.h"
#include "editor_scene_exporter_gltf_settings.h"

#include "editor/editor_plugin.h"

class EditorFileDialog;
class EditorInspector;

class SceneExporterGLTFPlugin : public EditorPlugin {
GDCLASS(SceneExporterGLTFPlugin, EditorPlugin);

EditorFileDialog *file_export_lib = nullptr;
void _gltf2_dialog_action(String p_file);
void convert_scene_to_gltf2();
Ref<GLTFDocument> _gltf_document;
Ref<EditorSceneExporterGLTFSettings> _export_settings;
EditorInspector *_settings_inspector = nullptr;
EditorFileDialog *_file_dialog = nullptr;
void _popup_gltf_export_dialog();
void _export_scene_as_gltf(const String &p_file_path);

public:
virtual String get_name() const override;
Expand Down
176 changes: 176 additions & 0 deletions modules/gltf/editor/editor_scene_exporter_gltf_settings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/**************************************************************************/
/* editor_scene_exporter_gltf_settings.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_exporter_gltf_settings.h"

const uint32_t PROP_EDITOR_SCRIPT_VAR = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE;

bool EditorSceneExporterGLTFSettings::_set(const StringName &p_name, const Variant &p_value) {
String name_str = String(p_name);
if (name_str.contains("/")) {
return _set_extension_setting(name_str, p_value);
}
if (p_name == StringName("image_format")) {
_document->set_image_format(p_value);
emit_signal("property_list_changed");
return true;
}
if (p_name == StringName("lossy_quality")) {
_document->set_lossy_quality(p_value);
return true;
}
if (p_name == StringName("root_node_mode")) {
_document->set_root_node_mode((GLTFDocument::RootNodeMode)(int64_t)p_value);
return true;
}
return false;
}

bool EditorSceneExporterGLTFSettings::_get(const StringName &p_name, Variant &r_ret) const {
String name_str = String(p_name);
if (name_str.contains("/")) {
return _get_extension_setting(name_str, r_ret);
}
if (p_name == StringName("image_format")) {
r_ret = _document->get_image_format();
return true;
}
if (p_name == StringName("lossy_quality")) {
r_ret = _document->get_lossy_quality();
return true;
}
if (p_name == StringName("root_node_mode")) {
r_ret = _document->get_root_node_mode();
return true;
}
return false;
}

void EditorSceneExporterGLTFSettings::_get_property_list(List<PropertyInfo> *p_list) const {
for (PropertyInfo prop : _property_list) {
if (prop.name == "lossy_quality") {
String image_format = get("image_format");
bool is_image_format_lossy = image_format == "JPEG" || image_format.findn("Lossy") != -1;
prop.usage = is_image_format_lossy ? PROPERTY_USAGE_DEFAULT : PROPERTY_USAGE_STORAGE;
}
p_list->push_back(prop);
}
}

bool EditorSceneExporterGLTFSettings::_set_extension_setting(const String &p_name_str, const Variant &p_value) {
PackedStringArray split = String(p_name_str).split("/", true, 1);
if (!_config_name_to_extension_map.has(split[0])) {
return false;
}
Ref<GLTFDocumentExtension> extension = _config_name_to_extension_map[split[0]];
bool valid;
extension->set(split[1], p_value, &valid);
return valid;
}

bool EditorSceneExporterGLTFSettings::_get_extension_setting(const String &p_name_str, Variant &r_ret) const {
PackedStringArray split = String(p_name_str).split("/", true, 1);
if (!_config_name_to_extension_map.has(split[0])) {
return false;
}
Ref<GLTFDocumentExtension> extension = _config_name_to_extension_map[split[0]];
bool valid;
r_ret = extension->get(split[1], &valid);
return valid;
}

String get_friendly_config_prefix(Ref<GLTFDocumentExtension> p_extension) {
String config_prefix = p_extension->get_name();
if (!config_prefix.is_empty()) {
return config_prefix;
}
const String class_name = p_extension->get_class_name();
config_prefix = class_name.trim_prefix("GLTFDocumentExtension").capitalize();
if (!config_prefix.is_empty()) {
return config_prefix;
}
PackedStringArray supported_extensions = p_extension->get_supported_extensions();
if (supported_extensions.size() > 0) {
return supported_extensions[0];
}
return "Unknown GLTFDocumentExtension";
}

// Run this before popping up the export settings, because the extensions may have changed.
void EditorSceneExporterGLTFSettings::generate_property_list(Ref<GLTFDocument> p_document) {
_property_list.clear();
_document = p_document;
String image_format_hint_string = "None,PNG,JPEG";
// Add properties from all document extensions.
for (Ref<GLTFDocumentExtension> &extension : GLTFDocument::get_all_gltf_document_extensions()) {
const String config_prefix = get_friendly_config_prefix(extension);
_config_name_to_extension_map[config_prefix] = extension;
// If the extension allows saving in different image formats, add to the enum.
PackedStringArray saveable_image_formats = extension->get_saveable_image_formats();
for (int i = 0; i < saveable_image_formats.size(); i++) {
image_format_hint_string += "," + saveable_image_formats[i];
}
// Look through the extension's properties and find the relevant ones.
List<PropertyInfo> ext_prop_list;
extension->get_property_list(&ext_prop_list);
for (const PropertyInfo &prop : ext_prop_list) {
// We only want properties that will show up in the exporter
// settings list. Exclude Resource's properties, as they are
// not relevant to the exporter. Include any user-defined script
// variables exposed to the editor (PROP_EDITOR_SCRIPT_VAR).
if ((prop.usage & PROP_EDITOR_SCRIPT_VAR) == PROP_EDITOR_SCRIPT_VAR) {
PropertyInfo ext_prop = prop;
ext_prop.name = config_prefix + "/" + prop.name;
_property_list.push_back(ext_prop);
}
}
}
// Add top-level properties (in addition to what _bind_methods registers).
PropertyInfo image_format_prop = PropertyInfo(Variant::STRING, "image_format", PROPERTY_HINT_ENUM, image_format_hint_string);
_property_list.push_back(image_format_prop);
PropertyInfo lossy_quality_prop = PropertyInfo(Variant::FLOAT, "lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01");
_property_list.push_back(lossy_quality_prop);
PropertyInfo root_node_mode_prop = PropertyInfo(Variant::INT, "root_node_mode", PROPERTY_HINT_ENUM, "Single Root,Keep Root,Multi Root");
_property_list.push_back(root_node_mode_prop);
}

String EditorSceneExporterGLTFSettings::get_copyright() const {
return _copyright;
}

void EditorSceneExporterGLTFSettings::set_copyright(const String &p_copyright) {
_copyright = p_copyright;
}

void EditorSceneExporterGLTFSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_copyright"), &EditorSceneExporterGLTFSettings::get_copyright);
ClassDB::bind_method(D_METHOD("set_copyright", "copyright"), &EditorSceneExporterGLTFSettings::set_copyright);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "copyright", PROPERTY_HINT_PLACEHOLDER_TEXT, "Example: 2014 Godette"), "set_copyright", "get_copyright");
}
64 changes: 64 additions & 0 deletions modules/gltf/editor/editor_scene_exporter_gltf_settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**************************************************************************/
/* editor_scene_exporter_gltf_settings.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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_EXPORTER_GLTF_SETTINGS_H
#define EDITOR_SCENE_EXPORTER_GLTF_SETTINGS_H

#ifdef TOOLS_ENABLED

#include "../gltf_document.h"

class EditorSceneExporterGLTFSettings : public RefCounted {
GDCLASS(EditorSceneExporterGLTFSettings, RefCounted);
List<PropertyInfo> _property_list;
Ref<GLTFDocument> _document;
HashMap<String, Ref<GLTFDocumentExtension>> _config_name_to_extension_map;

String _copyright;

protected:
static void _bind_methods();
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;

bool _set_extension_setting(const String &p_name_str, const Variant &p_value);
bool _get_extension_setting(const String &p_name_str, Variant &r_ret) const;

public:
void generate_property_list(Ref<GLTFDocument> p_document);

String get_copyright() const;
void set_copyright(const String &p_copyright);
};

#endif // TOOLS_ENABLED

#endif // EDITOR_SCENE_EXPORTER_GLTF_SETTINGS_H
Loading

0 comments on commit 0f0106c

Please sign in to comment.