From 9484ee7a9ef3a4bd7ca3731eda954f0ad425db3d Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Mon, 13 Dec 2021 21:34:18 -0800 Subject: Add support for importing .blend files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets you drag or place .blend files in the project folder and it will import the files. Checks for Blender 3.0's gltf2 `export_keep_originals` option. Add basepath support to GLTFDocument append_from_file. Co-authored-by: RĂ©mi Verschelde --- .../gltf/editor/editor_scene_importer_blend.cpp | 268 +++++++++++++++++++++ modules/gltf/editor/editor_scene_importer_blend.h | 75 ++++++ modules/gltf/editor/editor_scene_importer_gltf.cpp | 20 +- modules/gltf/editor/editor_scene_importer_gltf.h | 14 +- 4 files changed, 358 insertions(+), 19 deletions(-) create mode 100644 modules/gltf/editor/editor_scene_importer_blend.cpp create mode 100644 modules/gltf/editor/editor_scene_importer_blend.h (limited to 'modules/gltf/editor') 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 0000000000..931b2bc73b --- /dev/null +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -0,0 +1,268 @@ +/*************************************************************************/ +/* 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" + +#if TOOLS_ENABLED + +#include "../gltf_document.h" +#include "../gltf_state.h" + +#include "core/config/project_settings.h" +#include "core/io/json.h" +#include "editor/editor_settings.h" +#include "scene/main/node.h" +#include "scene/resources/animation.h" + +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 Map &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err) { + // Parse JSON config. + + 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(); + const String json_parameters = vformat("{\"source\": \"%s\", \"sink\": \"%s\"}", source_global, sink_global); + + Ref json; + json.instantiate(); + Error err = json->parse(json_parameters); + if (err != OK) { + if (r_err) { + *r_err = err; + } + ERR_PRINT(vformat("Blend config can't be read at line %s with error: %s", + json->get_error_line(), json->get_error_message())); + return nullptr; + } + Dictionary parameters = json->get_data(); + + // 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/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/lights")) && p_options[SNAME("blender/nodes/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',", parameters["sink"]) + + "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');", parameters["source"]) + + 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/blend/blender_path"); + + List args; + args.push_back("--background"); + args.push_back("--python-expr"); + args.push_back(script); + + String standard_out; + int32_t ret = OS::get_singleton()->execute(blender_path, args, &standard_out, &ret, true); + print_verbose(standard_out); + + if (ret != OK) { + if (r_err) { + *r_err = ERR_SCRIPT_FAILED; + } + ERR_PRINT(vformat("Blend import 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(); + } + 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); +} + +Ref EditorSceneFormatImporterBlend::import_animation(const String &p_path, uint32_t p_flags, + const Map &p_options, int p_bake_fps) { + return Ref(); +} + +Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, const String &p_option, + const Map &p_options) { + 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) { +#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_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 +} + +#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 0000000000..4bdf4c93a2 --- /dev/null +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* 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/import/resource_importer_scene.h" + +class Animation; +class Node; + +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_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 Map &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err = nullptr) override; + virtual Ref import_animation(const String &p_path, uint32_t p_flags, + const Map &p_options, int p_bake_fps) override; + virtual void get_import_options(const String &p_path, + List *r_options) override; + virtual Variant get_option_visibility(const String &p_path, const String &p_option, + const Map &p_options) override; +}; + +#endif // TOOLS_ENABLED + +#endif // EDITOR_SCENE_IMPORTER_BLEND_H diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp index f9193c2a42..1d6a82e58a 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp @@ -35,8 +35,7 @@ #include "../gltf_document.h" #include "../gltf_state.h" -#include "scene/3d/node_3d.h" -#include "scene/animation/animation_player.h" +#include "scene/main/node.h" #include "scene/resources/animation.h" uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const { @@ -48,26 +47,25 @@ void EditorSceneFormatImporterGLTF::get_extensions(List *r_extensions) c r_extensions->push_back("glb"); } -Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, - uint32_t p_flags, const Map &p_options, int p_bake_fps, - List *r_missing_deps, - Error *r_err) { +Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, + const Map &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) { - *r_err = err; + if (r_err) { + *r_err = err; + } return nullptr; } - Node *root = doc->generate_scene(state, p_bake_fps); - return root; + return doc->generate_scene(state, p_bake_fps); } Ref EditorSceneFormatImporterGLTF::import_animation(const String &p_path, - uint32_t p_flags, const Map &p_options, - int p_bake_fps) { + uint32_t p_flags, const Map &p_options, int p_bake_fps) { return Ref(); } diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h index 206fe63426..1f62ca9537 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_gltf.h @@ -33,14 +33,10 @@ #ifdef TOOLS_ENABLED -#include "../gltf_document_extension.h" -#include "../gltf_state.h" - #include "editor/import/resource_importer_scene.h" -#include "scene/main/node.h" -#include "scene/resources/packed_scene.h" class Animation; +class Node; class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterGLTF, EditorSceneFormatImporter); @@ -48,9 +44,11 @@ class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter { public: 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 Map &p_options, int p_bake_fps, List *r_missing_deps, Error *r_err = nullptr) override; - virtual Ref import_animation(const String &p_path, - uint32_t p_flags, const Map &p_options, int p_bake_fps) override; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, + const Map &p_options, int p_bake_fps, + List *r_missing_deps, Error *r_err = nullptr) override; + virtual Ref import_animation(const String &p_path, uint32_t p_flags, + const Map &p_options, int p_bake_fps) override; }; #endif // TOOLS_ENABLED -- cgit v1.2.3