Add Blender install autodetection and configuration.
This PR is a continuation to #54886 * Changed Blender path editor setting from binary to installation. * Add a class to query whether the format is supported. * This class allows to create proper editors to configure support. **NOTE**: This PR only provides autodetection on Linux. Code needs to be added for Windows and MacOS to autodetect the Blender installation. Co-authored-by: bruvzg <7645683+bruvzg@users.noreply.github.com> Co-authored-by: Pedro J. Estébanez <pedrojrulez@gmail.com>
This commit is contained in:
parent
155a94fabe
commit
e32215fbad
|
@ -61,6 +61,8 @@
|
|||
<member name="dialog_autowrap" type="bool" setter="set_autowrap" getter="has_autowrap" default="false">
|
||||
Sets autowrapping for the text in the dialog.
|
||||
</member>
|
||||
<member name="dialog_close_on_escape" type="bool" setter="set_close_on_escape" getter="get_close_on_escape" default="true">
|
||||
</member>
|
||||
<member name="dialog_hide_on_ok" type="bool" setter="set_hide_on_ok" getter="get_hide_on_ok" default="true">
|
||||
If [code]true[/code], the dialog is hidden when the OK button is pressed. You can set it to [code]false[/code] if you want to do e.g. input validation when receiving the [signal confirmed] signal, and handle hiding the dialog in your own logic.
|
||||
[b]Note:[/b] Some nodes derived from this class can have a different default value, and potentially their own built-in logic overriding this setting. For example [FileDialog] defaults to [code]false[/code], and has its own input validation code that is called when you press OK, which eventually hides the dialog if the input is valid. As such, this property can't be used in [FileDialog] to disable hiding the dialog when pressing OK.
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="EditorFileSystemImportFormatSupportQuery" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Used to query and configure import format support.
|
||||
</brief_description>
|
||||
<description>
|
||||
This class is used to query and configure a certain import format. It is used in conjuntion with asset format import plugins.
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="_get_file_extensions" qualifiers="virtual const">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
Return the file extensions supported.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_is_active" qualifiers="virtual const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Return whether this importer is active.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_query" qualifiers="virtual const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Query support. Return false if import must not continue.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
|
@ -553,9 +553,9 @@
|
|||
<member name="editor/script/templates_search_path" type="String" setter="" getter="" default=""res://script_templates"">
|
||||
Search path for project-specific script templates. Godot will search for script templates both in the editor-specific path and in this project-specific path.
|
||||
</member>
|
||||
<member name="filesystem/import/blend/enabled" type="bool" setter="" getter="" default="true">
|
||||
<member name="filesystem/import/blender/enabled" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], Blender 3D scene files with the [code].blend[/code] extension will be imported by converting them to glTF 2.0.
|
||||
This requires configuring a path to a Blender executable in the editor settings at [code]filesystem/import/blend/blender_path[/code]. Blender 3.0 or later is required.
|
||||
This requires configuring a path to a Blender executable in the editor settings at [code]filesystem/import/blender/blender3_path[/code]. Blender 3.0 or later is required.
|
||||
</member>
|
||||
<member name="filesystem/import/fbx/enabled" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], Autodesk FBX 3D scene files with the [code].fbx[/code] extension will be imported by converting them to glTF 2.0.
|
||||
|
|
|
@ -520,6 +520,45 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
|
|||
return false; //nothing changed
|
||||
}
|
||||
|
||||
bool EditorFileSystem::_scan_import_support(Vector<String> reimports) {
|
||||
if (import_support_queries.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
Map<String, int> import_support_test;
|
||||
Vector<bool> import_support_tested;
|
||||
import_support_tested.resize(import_support_queries.size());
|
||||
for (int i = 0; i < import_support_queries.size(); i++) {
|
||||
import_support_tested.write[i] = false;
|
||||
if (import_support_queries[i]->is_active()) {
|
||||
Vector<String> extensions = import_support_queries[i]->get_file_extensions();
|
||||
for (int j = 0; j < extensions.size(); j++) {
|
||||
import_support_test.insert(extensions[j], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (import_support_test.size() == 0) {
|
||||
return false; //well nothing to do
|
||||
}
|
||||
|
||||
for (int i = 0; i < reimports.size(); i++) {
|
||||
Map<String, int>::Element *E = import_support_test.find(reimports[i].get_extension());
|
||||
if (E) {
|
||||
import_support_tested.write[E->get()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < import_support_tested.size(); i++) {
|
||||
if (import_support_tested[i]) {
|
||||
if (import_support_queries.write[i]->query()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EditorFileSystem::_update_scan_actions() {
|
||||
sources_changed.clear();
|
||||
|
||||
|
@ -612,7 +651,7 @@ bool EditorFileSystem::_update_scan_actions() {
|
|||
if (_scan_extensions()) {
|
||||
//needs editor restart
|
||||
//extensions also may provide filetypes to be imported, so they must run before importing
|
||||
if (EditorNode::immediate_confirmation_dialog(TTR("Some extensions need the editor to restart to take effect."), first_scan ? TTR("Restart") : TTR("Save&Restart"), TTR("Continue"))) {
|
||||
if (EditorNode::immediate_confirmation_dialog(TTR("Some extensions need the editor to restart to take effect."), first_scan ? TTR("Restart") : TTR("Save & Restart"), TTR("Continue"))) {
|
||||
if (!first_scan) {
|
||||
EditorNode::get_singleton()->save_all_scenes();
|
||||
}
|
||||
|
@ -621,7 +660,12 @@ bool EditorFileSystem::_update_scan_actions() {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reimports.size()) {
|
||||
if (_scan_import_support(reimports)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
reimport_files(reimports);
|
||||
} else {
|
||||
//reimport files will update the uid cache file so if nothing was reimported, update it manually
|
||||
|
@ -2274,6 +2318,7 @@ static void _scan_extensions_dir(EditorFileSystemDirectory *d, Set<String> &exte
|
|||
bool EditorFileSystem::_scan_extensions() {
|
||||
EditorFileSystemDirectory *d = get_filesystem();
|
||||
Set<String> extensions;
|
||||
|
||||
_scan_extensions_dir(d, extensions);
|
||||
|
||||
//verify against loaded extensions
|
||||
|
@ -2374,6 +2419,14 @@ void EditorFileSystem::_update_extensions() {
|
|||
}
|
||||
}
|
||||
|
||||
void EditorFileSystem::add_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query) {
|
||||
ERR_FAIL_COND(import_support_queries.find(p_query) != -1);
|
||||
import_support_queries.push_back(p_query);
|
||||
}
|
||||
void EditorFileSystem::remove_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query) {
|
||||
import_support_queries.erase(p_query);
|
||||
}
|
||||
|
||||
EditorFileSystem::EditorFileSystem() {
|
||||
ResourceLoader::import = _resource_import;
|
||||
reimport_on_missing_imported_files = GLOBAL_DEF("editor/import/reimport_missing_imported_files", true);
|
||||
|
|
|
@ -109,6 +109,37 @@ public:
|
|||
~EditorFileSystemDirectory();
|
||||
};
|
||||
|
||||
class EditorFileSystemImportFormatSupportQuery : public RefCounted {
|
||||
GDCLASS(EditorFileSystemImportFormatSupportQuery, RefCounted);
|
||||
|
||||
protected:
|
||||
GDVIRTUAL0RC(bool, _is_active)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_file_extensions)
|
||||
GDVIRTUAL0RC(bool, _query)
|
||||
static void _bind_methods() {
|
||||
GDVIRTUAL_BIND(_is_active);
|
||||
GDVIRTUAL_BIND(_get_file_extensions);
|
||||
GDVIRTUAL_BIND(_query);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual bool is_active() const {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_REQUIRED_CALL(_is_active, ret);
|
||||
return ret;
|
||||
}
|
||||
virtual Vector<String> get_file_extensions() const {
|
||||
Vector<String> ret;
|
||||
GDVIRTUAL_REQUIRED_CALL(_get_file_extensions, ret);
|
||||
return ret;
|
||||
}
|
||||
virtual bool query() {
|
||||
bool ret = false;
|
||||
GDVIRTUAL_REQUIRED_CALL(_query, ret);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class EditorFileSystem : public Node {
|
||||
GDCLASS(EditorFileSystem, Node);
|
||||
|
||||
|
@ -257,6 +288,9 @@ class EditorFileSystem : public Node {
|
|||
static ResourceUID::ID _resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate);
|
||||
|
||||
bool _scan_extensions();
|
||||
bool _scan_import_support(Vector<String> reimports);
|
||||
|
||||
Vector<Ref<EditorFileSystemImportFormatSupportQuery>> import_support_queries;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
@ -289,6 +323,8 @@ public:
|
|||
|
||||
static bool _should_skip_directory(const String &p_path);
|
||||
|
||||
void add_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
|
||||
void remove_import_format_support_query(Ref<EditorFileSystemImportFormatSupportQuery> p_query);
|
||||
EditorFileSystem();
|
||||
~EditorFileSystem();
|
||||
};
|
||||
|
|
|
@ -3920,6 +3920,7 @@ void EditorNode::register_editor_types() {
|
|||
GDREGISTER_CLASS(EditorScriptPicker);
|
||||
|
||||
GDREGISTER_ABSTRACT_CLASS(FileSystemDock);
|
||||
GDREGISTER_VIRTUAL_CLASS(EditorFileSystemImportFormatSupportQuery);
|
||||
|
||||
GDREGISTER_CLASS(EditorScenePostImport);
|
||||
GDREGISTER_CLASS(EditorCommandPalette);
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
</brief_description>
|
||||
<description>
|
||||
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/blend/blender_path[/code] editor setting.
|
||||
This importer is only used if [member ProjectSettings.filesystem/import/blend/enabled] is enabled, otherwise [code].blend[/code] files present in the project folder are not imported.
|
||||
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.
|
||||
</description>
|
||||
|
|
|
@ -30,16 +30,25 @@
|
|||
|
||||
#include "editor_scene_importer_blend.h"
|
||||
|
||||
#if TOOLS_ENABLED
|
||||
#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 <shlwapi.h>
|
||||
#endif
|
||||
|
||||
uint32_t EditorSceneFormatImporterBlend::get_import_flags() const {
|
||||
return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
|
||||
}
|
||||
|
@ -169,7 +178,13 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
|
|||
|
||||
// Run script with configured Blender binary.
|
||||
|
||||
String blender_path = EDITOR_GET("filesystem/import/blend/blender_path");
|
||||
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<String> args;
|
||||
args.push_back("--background");
|
||||
|
@ -252,4 +267,294 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li
|
|||
#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(OSX_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<String> 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 excecute 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<String> EditorFileSystemImportFormatSupportQueryBlend::get_file_extensions() const {
|
||||
Vector<String> 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->get_ok_button()->set_text(TTR("Confirm Path"));
|
||||
configure_blender_dialog->get_cancel_button()->set_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(OSX_ENABLED)
|
||||
|
||||
{
|
||||
Vector<String> mdfind_paths;
|
||||
{
|
||||
List<String> 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
|
||||
|
|
|
@ -33,10 +33,12 @@
|
|||
|
||||
#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);
|
||||
|
@ -70,6 +72,39 @@ public:
|
|||
const Map<StringName, Variant> &p_options) override;
|
||||
};
|
||||
|
||||
class LineEdit;
|
||||
class Button;
|
||||
class EditorFileDialog;
|
||||
class Label;
|
||||
|
||||
class EditorFileSystemImportFormatSupportQueryBlend : public EditorFileSystemImportFormatSupportQuery {
|
||||
GDCLASS(EditorFileSystemImportFormatSupportQueryBlend, EditorFileSystemImportFormatSupportQuery);
|
||||
|
||||
ConfirmationDialog *configure_blender_dialog;
|
||||
LineEdit *blender_path;
|
||||
Button *blender_path_browse;
|
||||
EditorFileDialog *browse_dialog;
|
||||
Label *path_status;
|
||||
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<String> get_file_extensions() const override;
|
||||
virtual bool query() override;
|
||||
|
||||
EditorFileSystemImportFormatSupportQueryBlend();
|
||||
};
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#endif // EDITOR_SCENE_IMPORTER_BLEND_H
|
||||
|
|
|
@ -64,22 +64,19 @@ static void _editor_init() {
|
|||
|
||||
// Blend to glTF importer.
|
||||
|
||||
bool blend_enabled = GLOBAL_GET("filesystem/import/blend/enabled");
|
||||
bool blend_enabled = GLOBAL_GET("filesystem/import/blender/enabled");
|
||||
// Defined here because EditorSettings doesn't exist in `register_gltf_types` yet.
|
||||
String blender_path = EDITOR_DEF_RST("filesystem/import/blend/blender_path", "");
|
||||
EDITOR_DEF_RST("filesystem/import/blender/blender3_path", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING,
|
||||
"filesystem/import/blend/blender_path", PROPERTY_HINT_GLOBAL_FILE));
|
||||
"filesystem/import/blender/blender3_path", PROPERTY_HINT_GLOBAL_DIR));
|
||||
if (blend_enabled) {
|
||||
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
if (blender_path.is_empty()) {
|
||||
WARN_PRINT("Blend file import is enabled, but no Blender path is configured. Blend files will not be imported.");
|
||||
} else if (!da->file_exists(blender_path)) {
|
||||
WARN_PRINT("Blend file import is enabled, but the Blender path doesn't point to a valid Blender executable. Blend files will not be imported.");
|
||||
} else {
|
||||
Ref<EditorSceneFormatImporterBlend> importer;
|
||||
importer.instantiate();
|
||||
ResourceImporterScene::get_singleton()->add_importer(importer);
|
||||
}
|
||||
Ref<EditorSceneFormatImporterBlend> importer;
|
||||
importer.instantiate();
|
||||
ResourceImporterScene::get_singleton()->add_importer(importer);
|
||||
|
||||
Ref<EditorFileSystemImportFormatSupportQueryBlend> blend_import_query;
|
||||
blend_import_query.instantiate();
|
||||
EditorFileSystem::get_singleton()->add_import_format_support_query(blend_import_query);
|
||||
}
|
||||
|
||||
// FBX to glTF importer.
|
||||
|
@ -131,13 +128,14 @@ void register_gltf_types() {
|
|||
EditorPlugins::add_by_type<SceneExporterGLTFPlugin>();
|
||||
|
||||
// Project settings defined here so doctool finds them.
|
||||
GLOBAL_DEF_RST("filesystem/import/blend/enabled", true);
|
||||
GLOBAL_DEF_RST("filesystem/import/blender/enabled", true);
|
||||
GLOBAL_DEF_RST("filesystem/import/fbx/enabled", true);
|
||||
GDREGISTER_CLASS(EditorSceneFormatImporterBlend);
|
||||
GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
|
||||
|
||||
ClassDB::set_current_api(prev_api);
|
||||
EditorNode::add_init_callback(_editor_init);
|
||||
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
||||
|
|
|
@ -39,13 +39,13 @@
|
|||
|
||||
void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
|
||||
Ref<InputEventKey> key = p_event;
|
||||
if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
|
||||
if (close_on_escape && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
|
||||
_cancel_pressed();
|
||||
}
|
||||
}
|
||||
|
||||
void AcceptDialog::_parent_focused() {
|
||||
if (!is_exclusive()) {
|
||||
if (close_on_escape && !is_exclusive()) {
|
||||
_cancel_pressed();
|
||||
}
|
||||
}
|
||||
|
@ -145,6 +145,14 @@ bool AcceptDialog::get_hide_on_ok() const {
|
|||
return hide_on_ok;
|
||||
}
|
||||
|
||||
void AcceptDialog::set_close_on_escape(bool p_hide) {
|
||||
close_on_escape = p_hide;
|
||||
}
|
||||
|
||||
bool AcceptDialog::get_close_on_escape() const {
|
||||
return close_on_escape;
|
||||
}
|
||||
|
||||
void AcceptDialog::set_autowrap(bool p_autowrap) {
|
||||
label->set_autowrap_mode(p_autowrap ? Label::AUTOWRAP_WORD : Label::AUTOWRAP_OFF);
|
||||
}
|
||||
|
@ -288,6 +296,8 @@ void AcceptDialog::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_label"), &AcceptDialog::get_label);
|
||||
ClassDB::bind_method(D_METHOD("set_hide_on_ok", "enabled"), &AcceptDialog::set_hide_on_ok);
|
||||
ClassDB::bind_method(D_METHOD("get_hide_on_ok"), &AcceptDialog::get_hide_on_ok);
|
||||
ClassDB::bind_method(D_METHOD("set_close_on_escape", "enabled"), &AcceptDialog::set_close_on_escape);
|
||||
ClassDB::bind_method(D_METHOD("get_close_on_escape"), &AcceptDialog::get_close_on_escape);
|
||||
ClassDB::bind_method(D_METHOD("add_button", "text", "right", "action"), &AcceptDialog::add_button, DEFVAL(false), DEFVAL(""));
|
||||
ClassDB::bind_method(D_METHOD("add_cancel_button", "name"), &AcceptDialog::add_cancel_button);
|
||||
ClassDB::bind_method(D_METHOD("remove_button", "button"), &AcceptDialog::remove_button);
|
||||
|
@ -304,6 +314,7 @@ void AcceptDialog::_bind_methods() {
|
|||
ADD_GROUP("Dialog", "dialog");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "dialog_text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_hide_on_ok"), "set_hide_on_ok", "get_hide_on_ok");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_close_on_escape"), "set_close_on_escape", "get_close_on_escape");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ class AcceptDialog : public Window {
|
|||
Label *label;
|
||||
Button *ok;
|
||||
bool hide_on_ok = true;
|
||||
bool close_on_escape = true;
|
||||
|
||||
void _custom_action(const String &p_action);
|
||||
void _update_child_rects();
|
||||
|
@ -87,6 +88,9 @@ public:
|
|||
void set_hide_on_ok(bool p_hide);
|
||||
bool get_hide_on_ok() const;
|
||||
|
||||
void set_close_on_escape(bool p_enable);
|
||||
bool get_close_on_escape() const;
|
||||
|
||||
void set_text(String p_text);
|
||||
String get_text() const;
|
||||
|
||||
|
|
Loading…
Reference in New Issue