diff options
Diffstat (limited to 'modules/gltf')
-rw-r--r-- | modules/gltf/doc_classes/GLTFDocumentExtension.xml | 16 | ||||
-rw-r--r-- | modules/gltf/editor/editor_import_blend_runner.cpp | 314 | ||||
-rw-r--r-- | modules/gltf/editor/editor_import_blend_runner.h | 69 | ||||
-rw-r--r-- | modules/gltf/editor/editor_scene_importer_blend.cpp | 131 | ||||
-rw-r--r-- | modules/gltf/extensions/gltf_document_extension.cpp | 32 | ||||
-rw-r--r-- | modules/gltf/extensions/gltf_document_extension.h | 16 | ||||
-rw-r--r-- | modules/gltf/register_types.cpp | 11 |
7 files changed, 482 insertions, 107 deletions
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 6004de32f1..6e8340f618 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -22,7 +22,7 @@ </description> </method> <method name="_export_node" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="json" type="Dictionary" /> @@ -33,7 +33,7 @@ </description> </method> <method name="_export_post" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <description> Part of the export process. This method is run last, after all other parts of the export process. @@ -41,7 +41,7 @@ </description> </method> <method name="_export_preflight" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="root" type="Node" /> <description> @@ -67,7 +67,7 @@ </description> </method> <method name="_import_node" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="json" type="Dictionary" /> @@ -78,7 +78,7 @@ </description> </method> <method name="_import_post" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="root" type="Node" /> <description> @@ -87,7 +87,7 @@ </description> </method> <method name="_import_post_parse" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <description> Part of the import process. This method is run after [method _generate_scene_node] and before [method _import_node]. @@ -95,7 +95,7 @@ </description> </method> <method name="_import_preflight" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="extensions" type="PackedStringArray" /> <description> @@ -104,7 +104,7 @@ </description> </method> <method name="_parse_node_extensions" qualifiers="virtual"> - <return type="int" /> + <return type="int" enum="Error" /> <param index="0" name="state" type="GLTFState" /> <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="extensions" type="Dictionary" /> diff --git a/modules/gltf/editor/editor_import_blend_runner.cpp b/modules/gltf/editor/editor_import_blend_runner.cpp new file mode 100644 index 0000000000..c203a91834 --- /dev/null +++ b/modules/gltf/editor/editor_import_blend_runner.cpp @@ -0,0 +1,314 @@ +/**************************************************************************/ +/* editor_import_blend_runner.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_import_blend_runner.h" + +#ifdef TOOLS_ENABLED + +#include "core/io/http_client.h" +#include "editor/editor_file_system.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" + +static constexpr char PYTHON_SCRIPT_RPC[] = R"( +import bpy, sys, threading +from xmlrpc.server import SimpleXMLRPCServer +req = threading.Condition() +res = threading.Condition() +info = None +def xmlrpc_server(): + server = SimpleXMLRPCServer(('127.0.0.1', %d)) + server.register_function(export_gltf) + server.serve_forever() +def export_gltf(opts): + with req: + global info + info = ('export_gltf', opts) + req.notify() + with res: + res.wait() +if bpy.app.version < (3, 0, 0): + print('Blender 3.0 or higher is required.', file=sys.stderr) +threading.Thread(target=xmlrpc_server).start() +while True: + with req: + while info is None: + req.wait() + method, opts = info + if method == 'export_gltf': + try: + bpy.ops.wm.open_mainfile(filepath=opts['path']) + if opts['unpack_all']: + bpy.ops.file.unpack_all(method='USE_LOCAL') + bpy.ops.export_scene.gltf(**opts['gltf_options']) + except: + pass + info = None + with res: + res.notify() +)"; + +static constexpr char PYTHON_SCRIPT_DIRECT[] = R"( +import bpy, sys +opts = %s +if bpy.app.version < (3, 0, 0): + print('Blender 3.0 or higher is required.', file=sys.stderr) +bpy.ops.wm.open_mainfile(filepath=opts['path']) +if opts['unpack_all']: + bpy.ops.file.unpack_all(method='USE_LOCAL') +bpy.ops.export_scene.gltf(**opts['gltf_options']) +)"; + +String dict_to_python(const Dictionary &p_dict) { + String entries; + Array dict_keys = p_dict.keys(); + for (int i = 0; i < dict_keys.size(); i++) { + const String key = dict_keys[i]; + String value; + Variant raw_value = p_dict[key]; + + switch (raw_value.get_type()) { + case Variant::Type::BOOL: { + value = raw_value ? "True" : "False"; + break; + } + case Variant::Type::STRING: + case Variant::Type::STRING_NAME: { + value = raw_value; + value = vformat("'%s'", value.c_escape()); + break; + } + case Variant::Type::DICTIONARY: { + value = dict_to_python(raw_value); + break; + } + default: { + ERR_FAIL_V_MSG("", vformat("Unhandled Variant type %s for python dictionary", Variant::get_type_name(raw_value.get_type()))); + } + } + + entries += vformat("'%s': %s,", key, value); + } + return vformat("{%s}", entries); +} + +String dict_to_xmlrpc(const Dictionary &p_dict) { + String members; + Array dict_keys = p_dict.keys(); + for (int i = 0; i < dict_keys.size(); i++) { + const String key = dict_keys[i]; + String value; + Variant raw_value = p_dict[key]; + + switch (raw_value.get_type()) { + case Variant::Type::BOOL: { + value = vformat("<boolean>%d</boolean>", raw_value ? 1 : 0); + break; + } + case Variant::Type::STRING: + case Variant::Type::STRING_NAME: { + value = raw_value; + value = vformat("<string>%s</string>", value.xml_escape()); + break; + } + case Variant::Type::DICTIONARY: { + value = dict_to_xmlrpc(raw_value); + break; + } + default: { + ERR_FAIL_V_MSG("", vformat("Unhandled Variant type %s for XMLRPC", Variant::get_type_name(raw_value.get_type()))); + } + } + + members += vformat("<member><name>%s</name><value>%s</value></member>", key, value); + } + return vformat("<struct>%s</struct>", members); +} + +Error EditorImportBlendRunner::start_blender(const String &p_python_script, bool p_blocking) { + String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); + +#ifdef WINDOWS_ENABLED + blender_path = blender_path.path_join("blender.exe"); +#else + blender_path = blender_path.path_join("blender"); +#endif + + List<String> args; + args.push_back("--background"); + args.push_back("--python-expr"); + args.push_back(p_python_script); + + Error err; + if (p_blocking) { + int exitcode = 0; + err = OS::get_singleton()->execute(blender_path, args, nullptr, &exitcode); + if (exitcode != 0) { + return FAILED; + } + } else { + err = OS::get_singleton()->create_process(blender_path, args, &blender_pid); + } + return err; +} + +Error EditorImportBlendRunner::do_import(const Dictionary &p_options) { + if (is_using_rpc()) { + return do_import_rpc(p_options); + } else { + return do_import_direct(p_options); + } +} + +Error EditorImportBlendRunner::do_import_rpc(const Dictionary &p_options) { + kill_timer->stop(); + + // Start Blender if not already running. + if (!is_running()) { + // Start an XML RPC server on the given port. + String python = vformat(PYTHON_SCRIPT_RPC, rpc_port); + Error err = start_blender(python, false); + if (err != OK || blender_pid == 0) { + return FAILED; + } + } + + // Convert options to XML body. + String xml_options = dict_to_xmlrpc(p_options); + String xml_body = vformat("<?xml version=\"1.0\"?><methodCall><methodName>export_gltf</methodName><params><param><value>%s</value></param></params></methodCall>", xml_options); + + // Connect to RPC server. + Ref<HTTPClient> client = HTTPClient::create(); + client->connect_to_host("127.0.0.1", rpc_port); + + bool done = false; + while (!done) { + HTTPClient::Status status = client->get_status(); + switch (status) { + case HTTPClient::STATUS_RESOLVING: + case HTTPClient::STATUS_CONNECTING: { + client->poll(); + break; + } + case HTTPClient::STATUS_CONNECTED: { + done = true; + break; + } + default: { + ERR_FAIL_V_MSG(ERR_CONNECTION_ERROR, vformat("Unexpected status during RPC connection: %d", status)); + } + } + } + + // Send XML request. + PackedByteArray xml_buffer = xml_body.to_utf8_buffer(); + Error err = client->request(HTTPClient::METHOD_POST, "/", Vector<String>(), xml_buffer.ptr(), xml_buffer.size()); + if (err != OK) { + ERR_FAIL_V_MSG(err, vformat("Unable to send RPC request: %d", err)); + } + + // Wait for response. + done = false; + while (!done) { + HTTPClient::Status status = client->get_status(); + switch (status) { + case HTTPClient::STATUS_REQUESTING: { + client->poll(); + break; + } + case HTTPClient::STATUS_BODY: { + client->poll(); + // Parse response here if needed. For now we can just ignore it. + done = true; + break; + } + default: { + ERR_FAIL_V_MSG(ERR_CONNECTION_ERROR, vformat("Unexpected status during RPC response: %d", status)); + } + } + } + + return OK; +} + +Error EditorImportBlendRunner::do_import_direct(const Dictionary &p_options) { + // Export glTF directly. + String python = vformat(PYTHON_SCRIPT_DIRECT, dict_to_python(p_options)); + Error err = start_blender(python, true); + if (err != OK) { + return err; + } + + return OK; +} + +void EditorImportBlendRunner::_resources_reimported(const PackedStringArray &p_files) { + if (is_running()) { + // After a batch of imports is done, wait a few seconds before trying to kill blender, + // in case of having multiple imports trigger in quick succession. + kill_timer->start(); + } +} + +void EditorImportBlendRunner::_kill_blender() { + kill_timer->stop(); + if (is_running()) { + OS::get_singleton()->kill(blender_pid); + } + blender_pid = 0; +} + +void EditorImportBlendRunner::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_PREDELETE: { + _kill_blender(); + break; + } + } +} + +EditorImportBlendRunner *EditorImportBlendRunner::singleton = nullptr; + +EditorImportBlendRunner::EditorImportBlendRunner() { + ERR_FAIL_COND_MSG(singleton != nullptr, "EditorImportBlendRunner already created."); + singleton = this; + + rpc_port = EDITOR_GET("filesystem/import/blender/rpc_port"); + + kill_timer = memnew(Timer); + add_child(kill_timer); + kill_timer->set_one_shot(true); + kill_timer->set_wait_time(EDITOR_GET("filesystem/import/blender/rpc_server_uptime")); + kill_timer->connect("timeout", callable_mp(this, &EditorImportBlendRunner::_kill_blender)); + + EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &EditorImportBlendRunner::_resources_reimported)); +} + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor/editor_import_blend_runner.h b/modules/gltf/editor/editor_import_blend_runner.h new file mode 100644 index 0000000000..b2b82394e1 --- /dev/null +++ b/modules/gltf/editor/editor_import_blend_runner.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/* editor_import_blend_runner.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_IMPORT_BLEND_RUNNER_H +#define EDITOR_IMPORT_BLEND_RUNNER_H + +#ifdef TOOLS_ENABLED + +#include "core/os/os.h" +#include "scene/main/node.h" +#include "scene/main/timer.h" + +class EditorImportBlendRunner : public Node { + GDCLASS(EditorImportBlendRunner, Node); + + static EditorImportBlendRunner *singleton; + + Timer *kill_timer; + void _resources_reimported(const PackedStringArray &p_files); + void _kill_blender(); + void _notification(int p_what); + +protected: + int rpc_port = 0; + OS::ProcessID blender_pid = 0; + Error start_blender(const String &p_python_script, bool p_blocking); + Error do_import_direct(const Dictionary &p_options); + Error do_import_rpc(const Dictionary &p_options); + +public: + static EditorImportBlendRunner *get_singleton() { return singleton; } + + bool is_running() { return blender_pid != 0 && OS::get_singleton()->is_process_running(blender_pid); } + bool is_using_rpc() { return rpc_port != 0; } + Error do_import(const Dictionary &p_options); + + EditorImportBlendRunner(); +}; + +#endif // TOOLS_ENABLED + +#endif // EDITOR_IMPORT_BLEND_RUNNER_H diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 5415c5818f..520f33261a 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -34,6 +34,7 @@ #include "../gltf_defines.h" #include "../gltf_document.h" +#include "editor_import_blend_runner.h" #include "core/config/project_settings.h" #include "editor/editor_file_dialog.h" @@ -68,149 +69,129 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ // Handle configuration options. - String parameters_arg; + Dictionary request_options; + Dictionary parameters_map; + + parameters_map["filepath"] = sink_global; + parameters_map["export_keep_originals"] = true; + parameters_map["export_format"] = "GLTF_SEPARATE"; + parameters_map["export_yup"] = true; if (p_options.has(SNAME("blender/nodes/custom_properties")) && p_options[SNAME("blender/nodes/custom_properties")]) { - parameters_arg += "export_extras=True,"; + parameters_map["export_extras"] = true; } else { - parameters_arg += "export_extras=False,"; + parameters_map["export_extras"] = false; } if (p_options.has(SNAME("blender/meshes/skins"))) { int32_t skins = p_options["blender/meshes/skins"]; if (skins == BLEND_BONE_INFLUENCES_NONE) { - parameters_arg += "export_skins=False,"; + parameters_map["export_skins"] = false; } else if (skins == BLEND_BONE_INFLUENCES_COMPATIBLE) { - parameters_arg += "export_all_influences=False,export_skins=True,"; + parameters_map["export_skins"] = true; + parameters_map["export_all_influences"] = false; } else if (skins == BLEND_BONE_INFLUENCES_ALL) { - parameters_arg += "export_all_influences=True,export_skins=True,"; + parameters_map["export_skins"] = true; + parameters_map["export_all_influences"] = true; } } else { - parameters_arg += "export_skins=False,"; + parameters_map["export_skins"] = false; } if (p_options.has(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',"; + parameters_map["export_materials"] = "PLACEHOLDER"; } else if (exports == BLEND_MATERIAL_EXPORT_EXPORT) { - parameters_arg += "export_materials='EXPORT',"; + parameters_map["export_materials"] = "EXPORT"; } } else { - parameters_arg += "export_materials='PLACEHOLDER',"; + parameters_map["export_materials"] = "PLACEHOLDER"; } if (p_options.has(SNAME("blender/nodes/cameras")) && p_options[SNAME("blender/nodes/cameras")]) { - parameters_arg += "export_cameras=True,"; + parameters_map["export_cameras"] = true; } else { - parameters_arg += "export_cameras=False,"; + parameters_map["export_cameras"] = false; } if (p_options.has(SNAME("blender/nodes/punctual_lights")) && p_options[SNAME("blender/nodes/punctual_lights")]) { - parameters_arg += "export_lights=True,"; + parameters_map["export_lights"] = true; } else { - parameters_arg += "export_lights=False,"; + parameters_map["export_lights"] = false; } if (p_options.has(SNAME("blender/meshes/colors")) && p_options[SNAME("blender/meshes/colors")]) { - parameters_arg += "export_colors=True,"; + parameters_map["export_colors"] = true; } else { - parameters_arg += "export_colors=False,"; + parameters_map["export_colors"] = false; } if (p_options.has(SNAME("blender/nodes/visible"))) { int32_t visible = p_options["blender/nodes/visible"]; if (visible == BLEND_VISIBLE_VISIBLE_ONLY) { - parameters_arg += "use_visible=True,"; + parameters_map["use_visible"] = true; } else if (visible == BLEND_VISIBLE_RENDERABLE) { - parameters_arg += "use_renderable=True,"; + parameters_map["use_renderable"] = true; } else if (visible == BLEND_VISIBLE_ALL) { - parameters_arg += "use_visible=False,use_renderable=False,"; + parameters_map["use_renderable"] = false; + parameters_map["use_visible"] = false; } } else { - parameters_arg += "use_visible=False,use_renderable=False,"; + parameters_map["use_renderable"] = false; + parameters_map["use_visible"] = false; } if (p_options.has(SNAME("blender/meshes/uvs")) && p_options[SNAME("blender/meshes/uvs")]) { - parameters_arg += "export_texcoords=True,"; + parameters_map["export_texcoords"] = true; } else { - parameters_arg += "export_texcoords=False,"; + parameters_map["export_texcoords"] = false; } if (p_options.has(SNAME("blender/meshes/normals")) && p_options[SNAME("blender/meshes/normals")]) { - parameters_arg += "export_normals=True,"; + parameters_map["export_normals"] = true; } else { - parameters_arg += "export_normals=False,"; + parameters_map["export_normals"] = false; } if (p_options.has(SNAME("blender/meshes/tangents")) && p_options[SNAME("blender/meshes/tangents")]) { - parameters_arg += "export_tangents=True,"; + parameters_map["export_tangents"] = true; } else { - parameters_arg += "export_tangents=False,"; + parameters_map["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,"; + parameters_map["export_nla_strips"] = true; } else { - parameters_arg += "export_nla_strips=False,"; + parameters_map["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,"; + parameters_map["export_frame_range"] = true; } else { - parameters_arg += "export_frame_range=False,"; + parameters_map["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,"; + parameters_map["export_force_sampling"] = true; } else { - parameters_arg += "export_force_sampling=False,"; + parameters_map["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,"; + parameters_map["export_def_bones"] = true; } else { - parameters_arg += "export_def_bones=False,"; + parameters_map["export_def_bones"] = false; } if (p_options.has(SNAME("blender/nodes/modifiers")) && p_options[SNAME("blender/nodes/modifiers")]) { - parameters_arg += "export_apply=True"; + parameters_map["export_apply"] = true; } else { - parameters_arg += "export_apply=False"; + parameters_map["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');"; + request_options["unpack_all"] = true; + } else { + request_options["unpack_all"] = false; } - // Prepare Blender export script. - - String common_args = vformat("filepath='%s',", sink_global) + - "export_format='GLTF_SEPARATE'," - "export_yup=True," + - parameters_arg; - String export_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(export_script); - - // Run script with configured Blender binary. + request_options["path"] = source_global; + request_options["gltf_options"] = parameters_map; - String blender_path = EDITOR_GET("filesystem/import/blender/blender3_path"); - -#ifdef WINDOWS_ENABLED - blender_path = blender_path.path_join("blender.exe"); -#else - blender_path = blender_path.path_join("blender"); -#endif - - List<String> args; - args.push_back("--background"); - args.push_back("--python-expr"); - args.push_back(export_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) { + // Run Blender and export glTF. + Error err = EditorImportBlendRunner::get_singleton()->do_import(request_options); + if (err != OK) { if (r_err) { *r_err = ERR_SCRIPT_FAILED; } - ERR_PRINT(vformat("Blend export to glTF failed with error: %d.", ret)); return nullptr; } @@ -226,7 +207,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ 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, base_dir); + err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir); if (err != OK) { if (r_err) { *r_err = FAILED; diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 496a8f6cb8..bedb42eb32 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -49,9 +49,9 @@ void GLTFDocumentExtension::_bind_methods() { // Import process. Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err); - return Error(err); + return err; } Vector<String> GLTFDocumentExtension::get_supported_extensions() { @@ -63,9 +63,9 @@ Vector<String> GLTFDocumentExtension::get_supported_extensions() { Error GLTFDocumentExtension::parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_parse_node_extensions, p_state, p_gltf_node, p_extensions, err); - return Error(err); + return err; } Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) { @@ -79,34 +79,34 @@ Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<G Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_post_parse, p_state, err); - return Error(err); + return err; } Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err); - return Error(err); + return err; } Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_import_post, p_state, p_root, err); - return Error(err); + return err; } // Export process. Error GLTFDocumentExtension::export_preflight(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_export_preflight, p_state, p_root, err); - return Error(err); + return err; } void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node) { @@ -120,14 +120,14 @@ Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err); - return Error(err); + return err; } Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - int err = OK; + Error err = OK; GDVIRTUAL_CALL(_export_post, p_state, err); - return Error(err); + return err; } diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index 97f5045a1c..3531f81f6f 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -55,18 +55,18 @@ public: virtual Error export_post(Ref<GLTFState> p_state); // Import process. - GDVIRTUAL2R(int, _import_preflight, Ref<GLTFState>, Vector<String>); + GDVIRTUAL2R(Error, _import_preflight, Ref<GLTFState>, Vector<String>); GDVIRTUAL0R(Vector<String>, _get_supported_extensions); - GDVIRTUAL3R(int, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary); + GDVIRTUAL3R(Error, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary); GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); - GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>); - GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); - GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *); + GDVIRTUAL1R(Error, _import_post_parse, Ref<GLTFState>); + GDVIRTUAL4R(Error, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); + GDVIRTUAL2R(Error, _import_post, Ref<GLTFState>, Node *); // Export process. - GDVIRTUAL2R(int, _export_preflight, Ref<GLTFState>, Node *); + GDVIRTUAL2R(Error, _export_preflight, Ref<GLTFState>, Node *); GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); - GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); - GDVIRTUAL1R(int, _export_post, Ref<GLTFState>); + GDVIRTUAL4R(Error, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); + GDVIRTUAL1R(Error, _export_post, Ref<GLTFState>); }; #endif // GLTF_DOCUMENT_EXTENSION_H diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index f80e12bbae..78589090db 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -36,6 +36,7 @@ #ifdef TOOLS_ENABLED #include "core/config/project_settings.h" +#include "editor/editor_import_blend_runner.h" #include "editor/editor_node.h" #include "editor/editor_scene_exporter_gltf_plugin.h" #include "editor/editor_scene_importer_blend.h" @@ -52,6 +53,14 @@ static void _editor_init() { bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled"); // Defined here because EditorSettings doesn't exist in `register_gltf_types` yet. + EDITOR_DEF_RST("filesystem/import/blender/rpc_port", 6011); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, + "filesystem/import/blender/rpc_port", PROPERTY_HINT_RANGE, "0,65535,1")); + + EDITOR_DEF_RST("filesystem/import/blender/rpc_server_uptime", 5); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, + "filesystem/import/blender/rpc_server_uptime", PROPERTY_HINT_RANGE, "0,300,1,or_greater,suffix:s")); + String blender3_path = EDITOR_DEF_RST("filesystem/import/blender/blender3_path", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "filesystem/import/blender/blender3_path", PROPERTY_HINT_GLOBAL_DIR)); @@ -71,6 +80,8 @@ static void _editor_init() { EditorFileSystem::get_singleton()->add_import_format_support_query(blend_import_query); } } + memnew(EditorImportBlendRunner); + EditorNode::get_singleton()->add_child(EditorImportBlendRunner::get_singleton()); // FBX to glTF importer. |