2021-12-14 05:34:18 +00:00
/**************************************************************************/
/* editor_scene_importer_blend.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_importer_blend.h"
2022-03-31 20:00:17 +00:00
# ifdef TOOLS_ENABLED
2021-12-14 05:34:18 +00:00
2022-07-19 00:58:27 +00:00
# include "../gltf_defines.h"
2021-12-14 05:34:18 +00:00
# include "../gltf_document.h"
2022-11-28 20:03:48 +00:00
# include "editor_import_blend_runner.h"
2021-12-14 05:34:18 +00:00
# include "core/config/project_settings.h"
2022-03-31 20:00:17 +00:00
# include "editor/editor_node.h"
2021-12-14 05:34:18 +00:00
# include "editor/editor_settings.h"
2023-08-13 00:33:39 +00:00
# include "editor/editor_string_names.h"
2023-04-07 16:59:49 +00:00
# include "editor/gui/editor_file_dialog.h"
2024-01-15 12:14:55 +00:00
# include "editor/themes/editor_scale.h"
2022-03-31 20:00:17 +00:00
# include "main/main.h"
2022-11-19 11:45:49 +00:00
# include "scene/gui/line_edit.h"
2021-12-14 05:34:18 +00:00
2023-11-17 19:44:38 +00:00
# ifdef MINGW_ENABLED
# define near
# define far
# endif
2022-03-31 20:00:17 +00:00
# ifdef WINDOWS_ENABLED
# include <shlwapi.h>
# endif
2023-08-31 12:33:01 +00:00
static bool _get_blender_version ( const String & p_path , int & r_major , int & r_minor , String * r_err = nullptr ) {
2023-11-28 09:56:04 +00:00
if ( ! FileAccess : : exists ( p_path ) ) {
2023-08-31 12:33:01 +00:00
if ( r_err ) {
* r_err = TTR ( " Path does not contain a Blender installation. " ) ;
}
return false ;
}
List < String > args ;
args . push_back ( " --version " ) ;
String pipe ;
2023-11-28 09:56:04 +00:00
Error err = OS : : get_singleton ( ) - > execute ( p_path , args , & pipe ) ;
2023-08-31 12:33:01 +00:00
if ( err ! = OK ) {
if ( r_err ) {
* r_err = TTR ( " Can't execute Blender binary. " ) ;
}
return false ;
}
int bl = pipe . find ( " Blender " ) ;
if ( bl = = - 1 ) {
if ( r_err ) {
2023-11-28 09:56:04 +00:00
* r_err = vformat ( TTR ( " Unexpected --version output from Blender binary at: %s. " ) , p_path ) ;
2023-08-31 12:33:01 +00:00
}
return false ;
}
pipe = pipe . substr ( bl ) ;
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 ) ;
r_major = v . to_int ( ) ;
if ( r_major < 3 ) {
if ( r_err ) {
* r_err = TTR ( " This Blender installation is too old for this importer (not 3.0+). " ) ;
}
return false ;
}
int pp2 = pipe . find ( " . " , pp + 1 ) ;
r_minor = pp2 > pp ? pipe . substr ( pp + 1 , pp2 - pp - 1 ) . to_int ( ) : 0 ;
return true ;
}
2021-12-14 05:34:18 +00:00
uint32_t EditorSceneFormatImporterBlend : : get_import_flags ( ) const {
return ImportFlags : : IMPORT_SCENE | ImportFlags : : IMPORT_ANIMATION ;
}
void EditorSceneFormatImporterBlend : : get_extensions ( List < String > * r_extensions ) const {
r_extensions - > push_back ( " blend " ) ;
}
Node * EditorSceneFormatImporterBlend : : import_scene ( const String & p_path , uint32_t p_flags ,
2022-11-14 19:14:52 +00:00
const HashMap < StringName , Variant > & p_options ,
2021-12-14 05:34:18 +00:00
List < String > * r_missing_deps , Error * r_err ) {
2023-11-28 09:56:04 +00:00
String blender_path = EDITOR_GET ( " filesystem/import/blender/blender_path " ) ;
2021-12-14 05:34:18 +00:00
2023-08-31 12:33:01 +00:00
if ( blender_major_version = = - 1 | | blender_minor_version = = - 1 ) {
_get_blender_version ( blender_path , blender_major_version , blender_minor_version , nullptr ) ;
}
// Get global paths for source and sink.
2022-04-07 21:33:28 +00:00
// Escape paths to be valid Python strings to embed in the script.
2023-11-25 04:00:35 +00:00
String source_global = ProjectSettings : : get_singleton ( ) - > globalize_path ( p_path ) ;
# ifdef WINDOWS_ENABLED
// On Windows, when using a network share path, the above will return a path starting with "//"
// which once handed to Blender will be treated like a relative path. So we need to replace the
// first two characters with "\\" to make it absolute again.
if ( source_global . is_network_share_path ( ) ) {
source_global = " \\ \\ " + source_global . substr ( 2 ) ;
}
# endif
source_global = source_global . c_escape ( ) ;
2023-11-09 16:15:50 +00:00
const String blend_basename = p_path . get_file ( ) . get_basename ( ) ;
2022-08-30 00:34:01 +00:00
const String sink = ProjectSettings : : get_singleton ( ) - > get_imported_files_path ( ) . path_join (
2023-11-09 16:15:50 +00:00
vformat ( " %s-%s.gltf " , blend_basename , p_path . md5_text ( ) ) ) ;
2022-04-07 21:33:28 +00:00
const String sink_global = ProjectSettings : : get_singleton ( ) - > globalize_path ( sink ) . c_escape ( ) ;
2021-12-14 05:34:18 +00:00
// Handle configuration options.
2022-11-28 20:03:48 +00:00
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 ;
2021-12-14 05:34:18 +00:00
if ( p_options . has ( SNAME ( " blender/nodes/custom_properties " ) ) & & p_options [ SNAME ( " blender/nodes/custom_properties " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_extras " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_extras " ] = false ;
2021-12-14 05:34:18 +00:00
}
2022-10-03 18:30:20 +00:00
if ( p_options . has ( SNAME ( " blender/meshes/skins " ) ) ) {
2021-12-14 05:34:18 +00:00
int32_t skins = p_options [ " blender/meshes/skins " ] ;
if ( skins = = BLEND_BONE_INFLUENCES_NONE ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_skins " ] = false ;
2021-12-14 05:34:18 +00:00
} else if ( skins = = BLEND_BONE_INFLUENCES_COMPATIBLE ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_skins " ] = true ;
parameters_map [ " export_all_influences " ] = false ;
2021-12-14 05:34:18 +00:00
} else if ( skins = = BLEND_BONE_INFLUENCES_ALL ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_skins " ] = true ;
parameters_map [ " export_all_influences " ] = true ;
2021-12-14 05:34:18 +00:00
}
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_skins " ] = false ;
2021-12-14 05:34:18 +00:00
}
2022-10-03 18:30:20 +00:00
if ( p_options . has ( SNAME ( " blender/materials/export_materials " ) ) ) {
2022-03-31 17:05:46 +00:00
int32_t exports = p_options [ " blender/materials/export_materials " ] ;
if ( exports = = BLEND_MATERIAL_EXPORT_PLACEHOLDER ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_materials " ] = " PLACEHOLDER " ;
2022-03-31 17:05:46 +00:00
} else if ( exports = = BLEND_MATERIAL_EXPORT_EXPORT ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_materials " ] = " EXPORT " ;
2022-03-31 17:05:46 +00:00
}
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_materials " ] = " PLACEHOLDER " ;
2022-03-31 17:05:46 +00:00
}
2021-12-14 05:34:18 +00:00
if ( p_options . has ( SNAME ( " blender/nodes/cameras " ) ) & & p_options [ SNAME ( " blender/nodes/cameras " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_cameras " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_cameras " ] = false ;
2021-12-14 05:34:18 +00:00
}
2022-03-31 16:34:12 +00:00
if ( p_options . has ( SNAME ( " blender/nodes/punctual_lights " ) ) & & p_options [ SNAME ( " blender/nodes/punctual_lights " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_lights " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_lights " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/meshes/colors " ) ) & & p_options [ SNAME ( " blender/meshes/colors " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_colors " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_colors " ] = false ;
2021-12-14 05:34:18 +00:00
}
2022-10-03 18:30:20 +00:00
if ( p_options . has ( SNAME ( " blender/nodes/visible " ) ) ) {
2021-12-14 05:34:18 +00:00
int32_t visible = p_options [ " blender/nodes/visible " ] ;
if ( visible = = BLEND_VISIBLE_VISIBLE_ONLY ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " use_visible " ] = true ;
2021-12-14 05:34:18 +00:00
} else if ( visible = = BLEND_VISIBLE_RENDERABLE ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " use_renderable " ] = true ;
2021-12-14 05:34:18 +00:00
} else if ( visible = = BLEND_VISIBLE_ALL ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " use_renderable " ] = false ;
parameters_map [ " use_visible " ] = false ;
2021-12-14 05:34:18 +00:00
}
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " use_renderable " ] = false ;
parameters_map [ " use_visible " ] = false ;
2021-12-14 05:34:18 +00:00
}
2022-03-31 17:05:46 +00:00
2021-12-14 05:34:18 +00:00
if ( p_options . has ( SNAME ( " blender/meshes/uvs " ) ) & & p_options [ SNAME ( " blender/meshes/uvs " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_texcoords " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_texcoords " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/meshes/normals " ) ) & & p_options [ SNAME ( " blender/meshes/normals " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_normals " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_normals " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/meshes/tangents " ) ) & & p_options [ SNAME ( " blender/meshes/tangents " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_tangents " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_tangents " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/animation/group_tracks " ) ) & & p_options [ SNAME ( " blender/animation/group_tracks " ) ] ) {
2023-08-31 12:33:01 +00:00
if ( blender_major_version > 3 | | ( blender_major_version = = 3 & & blender_minor_version > = 6 ) ) {
parameters_map [ " export_animation_mode " ] = " ACTIONS " ;
} else {
parameters_map [ " export_nla_strips " ] = true ;
}
2021-12-14 05:34:18 +00:00
} else {
2023-08-31 12:33:01 +00:00
if ( blender_major_version > 3 | | ( blender_major_version = = 3 & & blender_minor_version > = 6 ) ) {
parameters_map [ " export_animation_mode " ] = " ACTIVE_ACTIONS " ;
} else {
parameters_map [ " export_nla_strips " ] = false ;
}
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/animation/limit_playback " ) ) & & p_options [ SNAME ( " blender/animation/limit_playback " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_frame_range " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_frame_range " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/animation/always_sample " ) ) & & p_options [ SNAME ( " blender/animation/always_sample " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_force_sampling " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_force_sampling " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/meshes/export_bones_deforming_mesh_only " ) ) & & p_options [ SNAME ( " blender/meshes/export_bones_deforming_mesh_only " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_def_bones " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_def_bones " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/nodes/modifiers " ) ) & & p_options [ SNAME ( " blender/nodes/modifiers " ) ] ) {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_apply " ] = true ;
2021-12-14 05:34:18 +00:00
} else {
2022-11-28 20:03:48 +00:00
parameters_map [ " export_apply " ] = false ;
2021-12-14 05:34:18 +00:00
}
if ( p_options . has ( SNAME ( " blender/materials/unpack_enabled " ) ) & & p_options [ SNAME ( " blender/materials/unpack_enabled " ) ] ) {
2022-11-28 20:03:48 +00:00
request_options [ " unpack_all " ] = true ;
} else {
request_options [ " unpack_all " ] = false ;
2021-12-14 05:34:18 +00:00
}
2022-11-28 20:03:48 +00:00
request_options [ " path " ] = source_global ;
request_options [ " gltf_options " ] = parameters_map ;
2021-12-14 05:34:18 +00:00
2022-11-28 20:03:48 +00:00
// Run Blender and export glTF.
Error err = EditorImportBlendRunner : : get_singleton ( ) - > do_import ( request_options ) ;
if ( err ! = OK ) {
2021-12-14 05:34:18 +00:00
if ( r_err ) {
* r_err = ERR_SCRIPT_FAILED ;
}
return nullptr ;
}
// Import the generated glTF.
2021-12-14 05:34:18 +00:00
// Use GLTFDocument instead of glTF importer to keep image references.
2021-12-14 05:34:18 +00:00
Ref < GLTFDocument > gltf ;
gltf . instantiate ( ) ;
Ref < GLTFState > state ;
state . instantiate ( ) ;
2022-07-19 00:58:27 +00:00
2021-12-14 05:34:18 +00:00
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 ( ) ;
}
2024-02-25 08:36:39 +00:00
if ( p_options . has ( SNAME ( " nodes/import_as_skeleton_bones " ) ) ? ( bool ) p_options [ SNAME ( " nodes/import_as_skeleton_bones " ) ] : false ) {
state - > set_import_as_skeleton_bones ( true ) ;
}
2023-11-09 16:15:50 +00:00
state - > set_scene_name ( blend_basename ) ;
2022-11-28 20:03:48 +00:00
err = gltf - > append_from_file ( sink . get_basename ( ) + " .gltf " , state , p_flags , base_dir ) ;
2021-12-14 05:34:18 +00:00
if ( err ! = OK ) {
if ( r_err ) {
* r_err = FAILED ;
}
return nullptr ;
}
2024-04-23 20:35:49 +00:00
ERR_FAIL_COND_V ( ! p_options . has ( " animation/fps " ) , nullptr ) ;
2023-02-15 08:14:22 +00:00
# ifndef DISABLE_DEPRECATED
bool trimming = p_options . has ( " animation/trimming " ) ? ( bool ) p_options [ " animation/trimming " ] : false ;
2024-03-31 06:45:22 +00:00
return gltf - > generate_scene ( state , ( float ) p_options [ " animation/fps " ] , trimming , false ) ;
2023-02-15 08:14:22 +00:00
# else
2024-03-31 06:45:22 +00:00
return gltf - > generate_scene ( state , ( float ) p_options [ " animation/fps " ] , ( bool ) p_options [ " animation/trimming " ] , false ) ;
2023-02-15 08:14:22 +00:00
# endif
2021-12-14 05:34:18 +00:00
}
2022-04-12 14:07:09 +00:00
Variant EditorSceneFormatImporterBlend : : get_option_visibility ( const String & p_path , bool p_for_animation , const String & p_option ,
2022-05-13 13:04:37 +00:00
const HashMap < StringName , Variant > & p_options ) {
2022-04-12 14:07:09 +00:00
if ( p_path . get_extension ( ) . to_lower ( ) ! = " blend " ) {
return true ;
}
2021-12-14 05:34:18 +00:00
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 < ResourceImporter : : ImportOption > * r_options ) {
2022-04-12 14:07:09 +00:00
if ( p_path . get_extension ( ) . to_lower ( ) ! = " blend " ) {
return ;
}
2021-12-14 05:34:18 +00:00
# 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 ) ) ;
2022-10-02 15:11:04 +00:00
ADD_OPTION_ENUM ( " blender/nodes/visible " , " All,Visible Only,Renderable " , BLEND_VISIBLE_ALL ) ;
2021-12-14 05:34:18 +00:00
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 ) ;
2022-03-31 17:05:46 +00:00
ADD_OPTION_ENUM ( " blender/materials/export_materials " , " Placeholder,Export " , BLEND_MATERIAL_EXPORT_EXPORT ) ;
2021-12-14 05:34:18 +00:00
ADD_OPTION_BOOL ( " blender/animation/limit_playback " , true ) ;
ADD_OPTION_BOOL ( " blender/animation/always_sample " , true ) ;
ADD_OPTION_BOOL ( " blender/animation/group_tracks " , true ) ;
}
2022-03-31 20:00:17 +00:00
///////////////////////////
static bool _test_blender_path ( const String & p_path , String * r_err = nullptr ) {
2023-08-31 12:33:01 +00:00
int major , minor ;
return _get_blender_version ( p_path , major , minor , r_err ) ;
2022-03-31 20:00:17 +00:00
}
bool EditorFileSystemImportFormatSupportQueryBlend : : is_active ( ) const {
bool blend_enabled = GLOBAL_GET ( " filesystem/import/blender/enabled " ) ;
2023-11-28 09:56:04 +00:00
if ( blend_enabled & & ! _test_blender_path ( EDITOR_GET ( " filesystem/import/blender/blender_path " ) . operator String ( ) ) ) {
2022-03-31 20:00:17 +00:00
// 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 ) {
2023-08-13 00:33:39 +00:00
path_status - > add_theme_color_override ( " font_color " , path_status - > get_theme_color ( SNAME ( " success_color " ) , EditorStringName ( Editor ) ) ) ;
2022-03-31 20:00:17 +00:00
configure_blender_dialog - > get_ok_button ( ) - > set_disabled ( false ) ;
} else {
2023-08-13 00:33:39 +00:00
path_status - > add_theme_color_override ( " font_color " , path_status - > get_theme_color ( SNAME ( " error_color " ) , EditorStringName ( Editor ) ) ) ;
2022-03-31 20:00:17 +00:00
configure_blender_dialog - > get_ok_button ( ) - > set_disabled ( true ) ;
}
}
2023-11-28 09:56:04 +00:00
bool EditorFileSystemImportFormatSupportQueryBlend : : _autodetect_path ( ) {
// Autodetect
auto_detected_path = " " ;
# if defined(MACOS_ENABLED)
Vector < String > find_paths = {
" /opt/homebrew/bin/blender " ,
" /opt/local/bin/blender " ,
" /usr/local/bin/blender " ,
" /usr/local/opt/blender " ,
" /Applications/Blender.app/Contents/MacOS/Blender " ,
} ;
{
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 ) {
for ( const String & find_path : output . split ( " \n " ) ) {
find_paths . push_back ( find_path . path_join ( " Contents/MacOS/Blender " ) ) ;
}
}
}
# elif defined(WINDOWS_ENABLED)
Vector < String > find_paths = {
" C: \\ Program Files \\ Blender Foundation \\ blender.exe " ,
" C: \\ Program Files (x86) \\ Blender Foundation \\ blender.exe " ,
} ;
{
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 ) {
find_paths . push_back ( String ( blender_opener_path ) . get_base_dir ( ) . path_join ( " blender.exe " ) ) ;
}
2022-03-31 20:00:17 +00:00
}
2023-11-28 09:56:04 +00:00
# elif defined(UNIX_ENABLED)
Vector < String > find_paths = {
" /usr/bin/blender " ,
" /usr/local/bin/blender " ,
" /opt/blender/bin/blender " ,
} ;
# endif
for ( const String & find_path : find_paths ) {
if ( _test_blender_path ( find_path ) ) {
auto_detected_path = find_path ;
return true ;
}
}
2022-03-31 20:00:17 +00:00
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 ( ) ) {
2023-11-28 09:56:04 +00:00
browse_dialog - > set_current_file ( blender_path - > get_text ( ) ) ;
2022-03-31 20:00:17 +00:00
}
browse_dialog - > popup_centered_ratio ( ) ;
}
2024-02-25 18:10:29 +00:00
void EditorFileSystemImportFormatSupportQueryBlend : : _update_icons ( ) {
blender_path_browse - > set_icon ( blender_path_browse - > get_editor_theme_icon ( SNAME ( " FolderBrowse " ) ) ) ;
}
2022-03-31 20:00:17 +00:00
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. \n Please 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 ) ;
2024-02-25 18:10:29 +00:00
2022-03-31 20:00:17 +00:00
blender_path_browse = memnew ( Button ) ;
blender_path_browse - > set_text ( TTR ( " Browse " ) ) ;
2024-05-14 07:40:21 +00:00
blender_path_browse - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorFileSystemImportFormatSupportQueryBlend : : _browse_install ) ) ;
2024-02-25 18:10:29 +00:00
hb - > add_child ( blender_path_browse ) ;
2022-03-31 20:00:17 +00:00
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 ) ;
2022-07-08 00:31:19 +00:00
configure_blender_dialog - > set_ok_button_text ( TTR ( " Confirm Path " ) ) ;
configure_blender_dialog - > set_cancel_button_text ( TTR ( " Disable '.blend' Import " ) ) ;
2022-08-25 10:42:17 +00:00
configure_blender_dialog - > get_cancel_button ( ) - > set_tooltip_text ( TTR ( " Disables Blender '.blend' files import for this project. Can be re-enabled in Project Settings. " ) ) ;
2022-03-31 20:00:17 +00:00
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 ) ;
2024-02-25 18:10:29 +00:00
// Update icons.
// This is a hack because we can't rely on notifications here as we don't receive them.
// Usually, we only have to wait for `NOTIFICATION_THEME_CHANGED` to update the icons.
callable_mp ( this , & EditorFileSystemImportFormatSupportQueryBlend : : _update_icons ) . call_deferred ( ) ;
2022-03-31 20:00:17 +00:00
}
2023-11-28 09:56:04 +00:00
String path = EDITOR_GET ( " filesystem/import/blender/blender_path " ) ;
2022-03-31 20:00:17 +00:00
2023-11-28 09:56:04 +00:00
if ( path . is_empty ( ) & & _autodetect_path ( ) ) {
path = auto_detected_path ;
2022-03-31 20:00:17 +00:00
}
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.
2023-11-28 09:56:04 +00:00
EditorSettings : : get_singleton ( ) - > set ( " filesystem/import/blender/blender_path " , blender_path - > get_text ( ) ) ;
2022-03-31 20:00:17 +00:00
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 ( ) {
}
2021-12-14 05:34:18 +00:00
# endif // TOOLS_ENABLED