summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp9
-rw-r--r--core/core_bind.cpp10
-rw-r--r--core/core_bind.h2
-rw-r--r--core/input/godotcontrollerdb.txt2
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--core/object/object.cpp2
-rw-r--r--core/register_core_types.cpp1
-rw-r--r--core/string/translation.compat.inc5
-rw-r--r--core/string/translation.cpp917
-rw-r--r--core/string/translation.h128
-rw-r--r--core/string/translation_server.compat.inc38
-rw-r--r--core/string/translation_server.cpp947
-rw-r--r--core/string/translation_server.h164
-rw-r--r--core/string/ustring.cpp2
-rw-r--r--doc/classes/ClassDB.xml16
-rw-r--r--doc/classes/Compositor.xml1
-rw-r--r--doc/classes/CompositorEffect.xml1
-rw-r--r--doc/classes/EditorSettings.xml9
-rw-r--r--doc/classes/Geometry2D.xml30
-rw-r--r--doc/classes/NavigationLink2D.xml13
-rw-r--r--doc/classes/NavigationLink3D.xml13
-rw-r--r--doc/classes/Polygon2D.xml3
-rw-r--r--doc/classes/ProjectSettings.xml2
-rw-r--r--doc/classes/ResourceImporterDynamicFont.xml3
-rw-r--r--doc/classes/TextServer.xml7
-rw-r--r--doc/classes/TextServerExtension.xml8
-rw-r--r--doc/classes/TileMap.xml27
-rw-r--r--doc/classes/TileMapLayer.xml21
-rw-r--r--doc/classes/Timer.xml2
-rw-r--r--drivers/gles3/shaders/scene.glsl2
-rw-r--r--editor/animation_track_editor_plugins.cpp6
-rw-r--r--editor/animation_track_editor_plugins.h6
-rw-r--r--editor/dependency_editor.cpp9
-rw-r--r--editor/dependency_editor.h5
-rw-r--r--editor/doc_tools.cpp2
-rw-r--r--editor/editor_asset_installer.cpp3
-rw-r--r--editor/editor_asset_installer.h1
-rw-r--r--editor/editor_file_system.cpp4
-rw-r--r--editor/editor_locale_dialog.cpp1
-rw-r--r--editor/editor_locale_dialog.h1
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_properties.cpp75
-rw-r--r--editor/editor_properties.h29
-rw-r--r--editor/editor_properties_array_dict.cpp9
-rw-r--r--editor/editor_properties_array_dict.h3
-rw-r--r--editor/editor_property_name_processor.cpp2
-rw-r--r--editor/editor_settings.cpp3
-rw-r--r--editor/editor_translation.cpp1
-rw-r--r--editor/export/export_template_manager.cpp3
-rw-r--r--editor/export/export_template_manager.h1
-rw-r--r--editor/filesystem_dock.cpp3
-rw-r--r--editor/gui/editor_object_selector.cpp3
-rw-r--r--editor/gui/editor_object_selector.h1
-rw-r--r--editor/gui/editor_spin_slider.cpp6
-rw-r--r--editor/import/3d/scene_import_settings.cpp9
-rw-r--r--editor/import/dynamic_font_import_settings.cpp1
-rw-r--r--editor/import/resource_importer_csv_translation.cpp2
-rw-r--r--editor/import/resource_importer_dynamic_font.cpp37
-rw-r--r--editor/localization_editor.cpp2
-rw-r--r--editor/node_dock.cpp3
-rw-r--r--editor/node_dock.h1
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp3
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h1
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp3
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h1
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp3
-rw-r--r--editor/plugins/bone_map_editor_plugin.h2
-rw-r--r--editor/plugins/camera_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/camera_3d_editor_plugin.h1
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.h1
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.h1
-rw-r--r--editor/plugins/editor_plugin_settings.cpp3
-rw-r--r--editor/plugins/editor_plugin_settings.h2
-rw-r--r--editor/plugins/font_config_plugin.cpp4
-rw-r--r--editor/plugins/font_config_plugin.h1
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h1
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.h3
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp3
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h1
-rw-r--r--editor/plugins/input_event_editor_plugin.cpp3
-rw-r--r--editor/plugins/input_event_editor_plugin.h1
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp3
-rw-r--r--editor/plugins/mesh_library_editor_plugin.h3
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp3
-rw-r--r--editor/plugins/multimesh_editor_plugin.h1
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp26
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.cpp5
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.h3
-rw-r--r--editor/plugins/root_motion_editor_plugin.cpp3
-rw-r--r--editor/plugins/root_motion_editor_plugin.h1
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp3
-rw-r--r--editor/plugins/shader_file_editor_plugin.h1
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h1
-rw-r--r--editor/plugins/skeleton_ik_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/skeleton_ik_3d_editor_plugin.h3
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp39
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp2
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp6
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h4
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp3
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.h1
-rw-r--r--editor/project_manager/quick_settings_dialog.cpp2
-rw-r--r--editor/property_selector.cpp179
-rw-r--r--editor/property_selector.h4
-rw-r--r--editor/themes/editor_fonts.cpp11
-rw-r--r--main/main.cpp39
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp211
-rw-r--r--modules/etcpak/image_compress_etcpak.h2
-rw-r--r--modules/fbx/fbx_document.cpp3
-rw-r--r--modules/fbx/fbx_document.h3
-rw-r--r--modules/gdscript/gdscript.cpp3
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gltf/README.md12
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml12
-rw-r--r--modules/gltf/doc_classes/GLTFAnimation.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFBufferView.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFCamera.xml18
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml10
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml20
-rw-r--r--modules/gltf/doc_classes/GLTFLight.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml32
-rw-r--r--modules/gltf/doc_classes/GLTFPhysicsBody.xml10
-rw-r--r--modules/gltf/doc_classes/GLTFPhysicsShape.xml10
-rw-r--r--modules/gltf/doc_classes/GLTFSkeleton.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFSpecGloss.xml6
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml48
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFTextureSampler.xml4
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp3
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h1
-rw-r--r--modules/gltf/extensions/gltf_light.cpp8
-rw-r--r--modules/gltf/extensions/physics/gltf_document_extension_physics.cpp20
-rw-r--r--modules/gltf/extensions/physics/gltf_physics_body.cpp16
-rw-r--r--modules/gltf/extensions/physics/gltf_physics_shape.cpp2
-rw-r--r--modules/gltf/gltf_document.cpp26
-rw-r--r--modules/gltf/structures/gltf_camera.cpp12
-rw-r--r--modules/gltf/structures/gltf_camera.h4
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp1
-rw-r--r--modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs3
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml4
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp3
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.h1
-rw-r--r--modules/openxr/editor/openxr_select_runtime.cpp3
-rw-r--r--modules/openxr/editor/openxr_select_runtime.h1
-rw-r--r--modules/text_server_adv/SCsub10
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp33
-rw-r--r--modules/text_server_adv/text_server_adv.h1
-rw-r--r--modules/text_server_fb/text_server_fb.cpp33
-rw-r--r--modules/text_server_fb/text_server_fb.h1
-rw-r--r--platform/macos/export/export_plugin.cpp8
-rw-r--r--platform/web/audio_driver_web.cpp5
-rw-r--r--platform/web/audio_driver_web.h1
-rw-r--r--platform/web/display_server_web.cpp6
-rw-r--r--platform/web/emscripten_helpers.py3
-rw-r--r--platform/web/export/export_plugin.cpp2
-rw-r--r--platform/web/godot_audio.h1
-rw-r--r--platform/web/js/engine/config.js2
-rw-r--r--platform/web/js/libs/audio.position.worklet.js50
-rw-r--r--platform/web/js/libs/library_godot_audio.js109
-rw-r--r--platform/windows/display_server_windows.cpp55
-rw-r--r--platform/windows/display_server_windows.h8
-rw-r--r--scene/2d/audio_stream_player_2d.cpp4
-rw-r--r--scene/2d/navigation_link_2d.cpp133
-rw-r--r--scene/2d/navigation_link_2d.h13
-rw-r--r--scene/2d/polygon_2d.cpp2
-rw-r--r--scene/2d/tile_map.cpp16
-rw-r--r--scene/2d/tile_map.h4
-rw-r--r--scene/2d/tile_map_layer.cpp16
-rw-r--r--scene/2d/tile_map_layer.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp4
-rw-r--r--scene/3d/navigation_link_3d.cpp106
-rw-r--r--scene/3d/navigation_link_3d.h9
-rw-r--r--scene/3d/occluder_instance_3d.cpp3
-rw-r--r--scene/3d/occluder_instance_3d.h1
-rw-r--r--scene/3d/soft_body_3d.cpp8
-rw-r--r--scene/animation/animation_blend_tree.cpp21
-rw-r--r--scene/animation/animation_blend_tree.h21
-rw-r--r--scene/animation/tween.cpp17
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/audio/audio_stream_player.cpp4
-rw-r--r--scene/audio/audio_stream_player_internal.cpp13
-rw-r--r--scene/audio/audio_stream_player_internal.h5
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/rich_text_label.cpp10
-rw-r--r--scene/gui/text_edit.cpp2
-rw-r--r--scene/main/window.cpp2
-rw-r--r--scene/resources/2d/tile_set.cpp17
-rw-r--r--scene/resources/2d/tile_set.h1
-rw-r--r--scene/resources/font.cpp9
-rw-r--r--scene/resources/visual_shader.cpp1
-rw-r--r--servers/audio/audio_stream.cpp6
-rw-r--r--servers/audio/audio_stream.h5
-rw-r--r--servers/audio_server.cpp11
-rw-r--r--servers/audio_server.h2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl2
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_data_rd.h3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h2
-rw-r--r--servers/rendering/shader_language.cpp23
-rw-r--r--servers/rendering/shader_language.h1
-rw-r--r--servers/text/text_server_dummy.h1
-rw-r--r--servers/text/text_server_extension.cpp7
-rw-r--r--servers/text/text_server_extension.h2
-rw-r--r--servers/text_server.cpp7
-rw-r--r--servers/text_server.h1
-rw-r--r--tests/core/string/test_translation.h1
-rw-r--r--tests/core/string/test_translation_server.h2
220 files changed, 2539 insertions, 1920 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 37a2608c10..f231e4010f 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1489,15 +1489,6 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
GLOBAL_DEF("audio/general/ios/mix_with_others", false);
- PackedStringArray extensions;
- extensions.push_back("gd");
- if (ClassDB::class_exists("CSharpScript")) {
- extensions.push_back("cs");
- }
- extensions.push_back("gdshader");
-
- GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions);
-
_add_builtin_input_map();
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a1b7b81111..a15085bcde 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1440,6 +1440,14 @@ TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_clas
return ret;
}
+StringName ClassDB::class_get_property_getter(const StringName &p_class, const StringName &p_property) {
+ return ::ClassDB::get_property_getter(p_class, p_property);
+}
+
+StringName ClassDB::class_get_property_setter(const StringName &p_class, const StringName &p_property) {
+ return ::ClassDB::get_property_setter(p_class, p_property);
+}
+
Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const {
Variant ret;
::ClassDB::get_property(p_object, p_property, ret);
@@ -1601,6 +1609,8 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_property_getter", "class", "property"), &ClassDB::class_get_property_getter);
+ ::ClassDB::bind_method(D_METHOD("class_get_property_setter", "class", "property"), &ClassDB::class_get_property_setter);
::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property);
::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property);
diff --git a/core/core_bind.h b/core/core_bind.h
index b142a2fbbd..69a359e4ed 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -447,6 +447,8 @@ public:
TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
+ StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
Variant class_get_property(Object *p_object, const StringName &p_property) const;
Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt
index f5158bfabb..8e8ec4c718 100644
--- a/core/input/godotcontrollerdb.txt
+++ b/core/input/godotcontrollerdb.txt
@@ -8,7 +8,7 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back
Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android,
# Web
-standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
+standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index e727dfa56d..4988e73624 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -38,7 +38,7 @@
#include "core/os/os.h"
#include "core/os/safe_binary_mutex.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/variant/variant_parser.h"
#include "servers/rendering_server.h"
diff --git a/core/object/object.cpp b/core/object/object.cpp
index e4d1a8fc9a..ac23bf831f 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -38,7 +38,7 @@
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/templates/local_vector.h"
#include "core/variant/typed_array.h"
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index c0a86e9fb7..c866ff0415 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -79,6 +79,7 @@
#include "core/os/time.h"
#include "core/string/optimized_translation.h"
#include "core/string/translation.h"
+#include "core/string/translation_server.h"
static Ref<ResourceFormatSaverBinary> resource_saver_binary;
static Ref<ResourceFormatLoaderBinary> resource_loader_binary;
diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc
index d792d4a6fc..68bd1831e4 100644
--- a/core/string/translation.compat.inc
+++ b/core/string/translation.compat.inc
@@ -38,9 +38,4 @@ void Translation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
}
-void TranslationServer::_bind_compatibility_methods() {
- ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
-}
-
#endif
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 432016284a..33d4a1bcde 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -31,14 +31,9 @@
#include "translation.h"
#include "translation.compat.inc"
-#include "core/config/project_settings.h"
-#include "core/io/resource_loader.h"
#include "core/os/os.h"
-#include "core/string/locales.h"
-
-#ifdef TOOLS_ENABLED
-#include "main/main.h"
-#endif
+#include "core/os/thread.h"
+#include "core/string/translation_server.h"
Dictionary Translation::_get_messages() const {
Dictionary d;
@@ -173,911 +168,3 @@ void Translation::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");
}
-
-///////////////////////////////////////////////
-
-struct _character_accent_pair {
- const char32_t character;
- const char32_t *accented_character;
-};
-
-static _character_accent_pair _character_to_accented[] = {
- { 'A', U"Å" },
- { 'B', U"ß" },
- { 'C', U"Ç" },
- { 'D', U"Ð" },
- { 'E', U"É" },
- { 'F', U"F́" },
- { 'G', U"Ĝ" },
- { 'H', U"Ĥ" },
- { 'I', U"Ĩ" },
- { 'J', U"Ĵ" },
- { 'K', U"ĸ" },
- { 'L', U"Ł" },
- { 'M', U"Ḿ" },
- { 'N', U"й" },
- { 'O', U"Ö" },
- { 'P', U"Ṕ" },
- { 'Q', U"Q́" },
- { 'R', U"Ř" },
- { 'S', U"Ŝ" },
- { 'T', U"Ŧ" },
- { 'U', U"Ũ" },
- { 'V', U"Ṽ" },
- { 'W', U"Ŵ" },
- { 'X', U"X́" },
- { 'Y', U"Ÿ" },
- { 'Z', U"Ž" },
- { 'a', U"á" },
- { 'b', U"ḅ" },
- { 'c', U"ć" },
- { 'd', U"d́" },
- { 'e', U"é" },
- { 'f', U"f́" },
- { 'g', U"ǵ" },
- { 'h', U"h̀" },
- { 'i', U"í" },
- { 'j', U"ǰ" },
- { 'k', U"ḱ" },
- { 'l', U"ł" },
- { 'm', U"m̀" },
- { 'n', U"ή" },
- { 'o', U"ô" },
- { 'p', U"ṕ" },
- { 'q', U"q́" },
- { 'r', U"ŕ" },
- { 's', U"š" },
- { 't', U"ŧ" },
- { 'u', U"ü" },
- { 'v', U"ṽ" },
- { 'w', U"ŵ" },
- { 'x', U"x́" },
- { 'y', U"ý" },
- { 'z', U"ź" },
-};
-
-Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
-
-HashMap<String, String> TranslationServer::language_map;
-HashMap<String, String> TranslationServer::script_map;
-HashMap<String, String> TranslationServer::locale_rename_map;
-HashMap<String, String> TranslationServer::country_name_map;
-HashMap<String, String> TranslationServer::variant_map;
-HashMap<String, String> TranslationServer::country_rename_map;
-
-void TranslationServer::init_locale_info() {
- // Init locale info.
- language_map.clear();
- int idx = 0;
- while (language_list[idx][0] != nullptr) {
- language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]);
- idx++;
- }
-
- // Init locale-script map.
- locale_script_info.clear();
- idx = 0;
- while (locale_scripts[idx][0] != nullptr) {
- LocaleScriptInfo info;
- info.name = locale_scripts[idx][0];
- info.script = locale_scripts[idx][1];
- info.default_country = locale_scripts[idx][2];
- Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false);
- for (int i = 0; i < supported_countries.size(); i++) {
- info.supported_countries.insert(supported_countries[i]);
- }
- locale_script_info.push_back(info);
- idx++;
- }
-
- // Init supported script list.
- script_map.clear();
- idx = 0;
- while (script_list[idx][0] != nullptr) {
- script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]);
- idx++;
- }
-
- // Init regional variant map.
- variant_map.clear();
- idx = 0;
- while (locale_variants[idx][0] != nullptr) {
- variant_map[locale_variants[idx][0]] = locale_variants[idx][1];
- idx++;
- }
-
- // Init locale renames.
- locale_rename_map.clear();
- idx = 0;
- while (locale_renames[idx][0] != nullptr) {
- if (!String(locale_renames[idx][1]).is_empty()) {
- locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1];
- }
- idx++;
- }
-
- // Init country names.
- country_name_map.clear();
- idx = 0;
- while (country_names[idx][0] != nullptr) {
- country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]);
- idx++;
- }
-
- // Init country renames.
- country_rename_map.clear();
- idx = 0;
- while (country_renames[idx][0] != nullptr) {
- if (!String(country_renames[idx][1]).is_empty()) {
- country_rename_map[country_renames[idx][0]] = country_renames[idx][1];
- }
- idx++;
- }
-}
-
-String TranslationServer::standardize_locale(const String &p_locale) const {
- return _standardize_locale(p_locale, false);
-}
-
-String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const {
- // Replaces '-' with '_' for macOS style locales.
- String univ_locale = p_locale.replace("-", "_");
-
- // Extract locale elements.
- String lang_name, script_name, country_name, variant_name;
- Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
- lang_name = locale_elements[0];
- if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
- script_name = locale_elements[1];
- }
- if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
- country_name = locale_elements[1];
- }
- }
- if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
- country_name = locale_elements[2];
- } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) {
- variant_name = locale_elements[2].to_lower();
- }
- }
- if (locale_elements.size() >= 4) {
- if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) {
- variant_name = locale_elements[3].to_lower();
- }
- }
-
- // Try extract script and variant from the extra part.
- Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";");
- for (int i = 0; i < script_extra.size(); i++) {
- if (script_extra[i].to_lower() == "cyrillic") {
- script_name = "Cyrl";
- break;
- } else if (script_extra[i].to_lower() == "latin") {
- script_name = "Latn";
- break;
- } else if (script_extra[i].to_lower() == "devanagari") {
- script_name = "Deva";
- break;
- } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) {
- variant_name = script_extra[i].to_lower();
- }
- }
-
- // Handles known non-ISO language names used e.g. on Windows.
- if (locale_rename_map.has(lang_name)) {
- lang_name = locale_rename_map[lang_name];
- }
-
- // Handle country renames.
- if (country_rename_map.has(country_name)) {
- country_name = country_rename_map[country_name];
- }
-
- // Remove unsupported script codes.
- if (!script_map.has(script_name)) {
- script_name = "";
- }
-
- // Add script code base on language and country codes for some ambiguous cases.
- if (p_add_defaults) {
- if (script_name.is_empty()) {
- for (int i = 0; i < locale_script_info.size(); i++) {
- const LocaleScriptInfo &info = locale_script_info[i];
- if (info.name == lang_name) {
- if (country_name.is_empty() || info.supported_countries.has(country_name)) {
- script_name = info.script;
- break;
- }
- }
- }
- }
- if (!script_name.is_empty() && country_name.is_empty()) {
- // Add conntry code based on script for some ambiguous cases.
- for (int i = 0; i < locale_script_info.size(); i++) {
- const LocaleScriptInfo &info = locale_script_info[i];
- if (info.name == lang_name && info.script == script_name) {
- country_name = info.default_country;
- break;
- }
- }
- }
- }
-
- // Combine results.
- String out = lang_name;
- if (!script_name.is_empty()) {
- out = out + "_" + script_name;
- }
- if (!country_name.is_empty()) {
- out = out + "_" + country_name;
- }
- if (!variant_name.is_empty()) {
- out = out + "_" + variant_name;
- }
- return out;
-}
-
-int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
- String locale_a = _standardize_locale(p_locale_a, true);
- String locale_b = _standardize_locale(p_locale_b, true);
-
- if (locale_a == locale_b) {
- // Exact match.
- return 10;
- }
-
- Vector<String> locale_a_elements = locale_a.split("_");
- Vector<String> locale_b_elements = locale_b.split("_");
- if (locale_a_elements[0] == locale_b_elements[0]) {
- // Matching language, both locales have extra parts.
- // Return number of matching elements.
- int matching_elements = 1;
- for (int i = 1; i < locale_a_elements.size(); i++) {
- for (int j = 1; j < locale_b_elements.size(); j++) {
- if (locale_a_elements[i] == locale_b_elements[j]) {
- matching_elements++;
- }
- }
- }
- return matching_elements;
- } else {
- // No match.
- return 0;
- }
-}
-
-String TranslationServer::get_locale_name(const String &p_locale) const {
- String lang_name, script_name, country_name;
- Vector<String> locale_elements = standardize_locale(p_locale).split("_");
- lang_name = locale_elements[0];
- if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
- script_name = locale_elements[1];
- }
- if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
- country_name = locale_elements[1];
- }
- }
- if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
- country_name = locale_elements[2];
- }
- }
-
- String name = language_map[lang_name];
- if (!script_name.is_empty()) {
- name = name + " (" + script_map[script_name] + ")";
- }
- if (!country_name.is_empty()) {
- name = name + ", " + country_name_map[country_name];
- }
- return name;
-}
-
-Vector<String> TranslationServer::get_all_languages() const {
- Vector<String> languages;
-
- for (const KeyValue<String, String> &E : language_map) {
- languages.push_back(E.key);
- }
-
- return languages;
-}
-
-String TranslationServer::get_language_name(const String &p_language) const {
- return language_map[p_language];
-}
-
-Vector<String> TranslationServer::get_all_scripts() const {
- Vector<String> scripts;
-
- for (const KeyValue<String, String> &E : script_map) {
- scripts.push_back(E.key);
- }
-
- return scripts;
-}
-
-String TranslationServer::get_script_name(const String &p_script) const {
- return script_map[p_script];
-}
-
-Vector<String> TranslationServer::get_all_countries() const {
- Vector<String> countries;
-
- for (const KeyValue<String, String> &E : country_name_map) {
- countries.push_back(E.key);
- }
-
- return countries;
-}
-
-String TranslationServer::get_country_name(const String &p_country) const {
- return country_name_map[p_country];
-}
-
-void TranslationServer::set_locale(const String &p_locale) {
- String new_locale = standardize_locale(p_locale);
- if (locale == new_locale) {
- return;
- }
-
- locale = new_locale;
- ResourceLoader::reload_translation_remaps();
-
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
- }
-}
-
-String TranslationServer::get_locale() const {
- return locale;
-}
-
-PackedStringArray TranslationServer::get_loaded_locales() const {
- PackedStringArray locales;
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), PackedStringArray());
- String l = t->get_locale();
-
- locales.push_back(l);
- }
-
- return locales;
-}
-
-void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
- translations.insert(p_translation);
-}
-
-void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
- translations.erase(p_translation);
-}
-
-Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
- Ref<Translation> res;
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), nullptr);
- String l = t->get_locale();
-
- int score = compare_locales(p_locale, l);
- if (score > 0 && score >= best_score) {
- res = t;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
- return res;
-}
-
-void TranslationServer::clear() {
- translations.clear();
-}
-
-StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
- // Match given message against the translation catalog for the project locale.
-
- if (!enabled) {
- return p_message;
- }
-
- StringName res = _get_message_from_translations(p_message, p_context, locale, false);
-
- if (!res && fallback.length() >= 2) {
- res = _get_message_from_translations(p_message, p_context, fallback, false);
- }
-
- if (!res) {
- return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
- }
-
- return pseudolocalization_enabled ? pseudolocalize(res) : res;
-}
-
-StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (!enabled) {
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
- }
-
- StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
-
- if (!res && fallback.length() >= 2) {
- res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
- }
-
- if (!res) {
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
- }
-
- return res;
-}
-
-StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
- StringName res;
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), p_message);
- String l = t->get_locale();
-
- int score = compare_locales(p_locale, l);
- if (score > 0 && score >= best_score) {
- StringName r;
- if (!plural) {
- r = t->get_message(p_message, p_context);
- } else {
- r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
- }
- if (!r) {
- continue;
- }
- res = r;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
-
- return res;
-}
-
-TranslationServer *TranslationServer::singleton = nullptr;
-
-bool TranslationServer::_load_translations(const String &p_from) {
- if (ProjectSettings::get_singleton()->has_setting(p_from)) {
- const Vector<String> &translation_names = GLOBAL_GET(p_from);
-
- int tcount = translation_names.size();
-
- if (tcount) {
- const String *r = translation_names.ptr();
-
- for (int i = 0; i < tcount; i++) {
- Ref<Translation> tr = ResourceLoader::load(r[i]);
- if (tr.is_valid()) {
- add_translation(tr);
- }
- }
- }
- return true;
- }
-
- return false;
-}
-
-void TranslationServer::setup() {
- String test = GLOBAL_DEF("internationalization/locale/test", "");
- test = test.strip_edges();
- if (!test.is_empty()) {
- set_locale(test);
- } else {
- set_locale(OS::get_singleton()->get_locale());
- }
-
- fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
- pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
- pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
- pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
- pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
- pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
- expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
- pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
- pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
-
-#ifdef TOOLS_ENABLED
- ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
-#endif
-}
-
-void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
- tool_translation = p_translation;
-}
-
-Ref<Translation> TranslationServer::get_tool_translation() const {
- return tool_translation;
-}
-
-String TranslationServer::get_tool_locale() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
- if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) {
- return tool_translation->get_locale();
- } else {
- return "en";
- }
- } else {
-#else
- {
-#endif
- // Look for best matching loaded translation.
- String best_locale = "en";
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), best_locale);
- String l = t->get_locale();
-
- int score = compare_locales(locale, l);
- if (score > 0 && score >= best_score) {
- best_locale = l;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
- return best_locale;
- }
-}
-
-StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
- if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
- property_translation = p_translation;
-}
-
-StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
- if (property_translation.is_valid()) {
- StringName r = property_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
- doc_translation = p_translation;
-}
-
-StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
- if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
- extractable_translation = p_translation;
-}
-
-StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const {
- if (extractable_translation.is_valid()) {
- StringName r = extractable_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (extractable_translation.is_valid()) {
- StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-bool TranslationServer::is_pseudolocalization_enabled() const {
- return pseudolocalization_enabled;
-}
-
-void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
- pseudolocalization_enabled = p_enabled;
-
- ResourceLoader::reload_translation_remaps();
-
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
- }
-}
-
-void TranslationServer::reload_pseudolocalization() {
- pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
- pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
- pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
- pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
- expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
- pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
- pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
-
- ResourceLoader::reload_translation_remaps();
-
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
- }
-}
-
-StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- int length = message.length();
- if (pseudolocalization_override_enabled) {
- message = get_override_string(message);
- }
-
- if (pseudolocalization_double_vowels_enabled) {
- message = double_vowels(message);
- }
-
- if (pseudolocalization_accents_enabled) {
- message = replace_with_accented_string(message);
- }
-
- if (pseudolocalization_fake_bidi_enabled) {
- message = wrap_with_fakebidi_characters(message);
- }
-
- StringName res = add_padding(message, length);
- return res;
-}
-
-StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- message = double_vowels(message);
- message = replace_with_accented_string(message);
- StringName res = "[!!! " + message + " !!!]";
- return res;
-}
-
-String TranslationServer::get_override_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += '*';
- }
- return res;
-}
-
-String TranslationServer::double_vowels(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += p_message[i];
- if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
- p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
- res += p_message[i];
- }
- }
- return res;
-};
-
-String TranslationServer::replace_with_accented_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- const char32_t *accented = get_accented_version(p_message[i]);
- if (accented) {
- res += accented;
- } else {
- res += p_message[i];
- }
- }
- return res;
-}
-
-String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
- String res;
- char32_t fakebidiprefix = U'\u202e';
- char32_t fakebidisuffix = U'\u202c';
- res += fakebidiprefix;
- // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
- for (int i = 0; i < p_message.length(); i++) {
- if (p_message[i] == '\n') {
- res += fakebidisuffix;
- res += p_message[i];
- res += fakebidiprefix;
- } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += fakebidisuffix;
- res += p_message[i];
- res += p_message[i + 1];
- res += fakebidiprefix;
- i++;
- } else {
- res += p_message[i];
- }
- }
- res += fakebidisuffix;
- return res;
-}
-
-String TranslationServer::add_padding(const String &p_message, int p_length) const {
- String underscores = String("_").repeat(p_length * expansion_ratio / 2);
- String prefix = pseudolocalization_prefix + underscores;
- String suffix = underscores + pseudolocalization_suffix;
-
- return prefix + p_message + suffix;
-}
-
-const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
- if (!is_ascii_alphabet_char(p_character)) {
- return nullptr;
- }
-
- for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
- if (_character_to_accented[i].character == p_character) {
- return _character_to_accented[i].accented_character;
- }
- }
-
- return nullptr;
-}
-
-bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
- return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
- (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
- p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
-}
-
-#ifdef TOOLS_ENABLED
-void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
- const String pf = p_function;
- if (p_idx == 0) {
- HashMap<String, String> *target_hash_map = nullptr;
- if (pf == "get_language_name") {
- target_hash_map = &language_map;
- } else if (pf == "get_script_name") {
- target_hash_map = &script_map;
- } else if (pf == "get_country_name") {
- target_hash_map = &country_name_map;
- }
-
- if (target_hash_map) {
- for (const KeyValue<String, String> &E : *target_hash_map) {
- r_options->push_back(E.key.quote());
- }
- }
- }
- Object::get_argument_options(p_function, p_idx, r_options);
-}
-#endif // TOOLS_ENABLED
-
-void TranslationServer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
- ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
- ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale);
-
- ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales);
- ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale);
-
- ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages);
- ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name);
-
- ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts);
- ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name);
-
- ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries);
- ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name);
-
- ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
-
- ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName()));
- ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName()));
-
- ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
- ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
- ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
-
- ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
-
- ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
-
- ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled);
- ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled);
- ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization);
- ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize);
- ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
-}
-
-void TranslationServer::load_translations() {
- _load_translations("internationalization/locale/translations"); //all
- _load_translations("internationalization/locale/translations_" + locale.substr(0, 2));
-
- if (locale.substr(0, 2) != locale) {
- _load_translations("internationalization/locale/translations_" + locale);
- }
-}
-
-TranslationServer::TranslationServer() {
- singleton = this;
- init_locale_info();
-}
diff --git a/core/string/translation.h b/core/string/translation.h
index 0a7eacc45f..2c5baae8b7 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -74,132 +74,4 @@ public:
Translation() {}
};
-class TranslationServer : public Object {
- GDCLASS(TranslationServer, Object);
-
- String locale = "en";
- String fallback;
-
- HashSet<Ref<Translation>> translations;
- Ref<Translation> tool_translation;
- Ref<Translation> property_translation;
- Ref<Translation> doc_translation;
- Ref<Translation> extractable_translation;
-
- bool enabled = true;
-
- bool pseudolocalization_enabled = false;
- bool pseudolocalization_accents_enabled = false;
- bool pseudolocalization_double_vowels_enabled = false;
- bool pseudolocalization_fake_bidi_enabled = false;
- bool pseudolocalization_override_enabled = false;
- bool pseudolocalization_skip_placeholders_enabled = false;
- float expansion_ratio = 0.0;
- String pseudolocalization_prefix;
- String pseudolocalization_suffix;
-
- StringName tool_pseudolocalize(const StringName &p_message) const;
- String get_override_string(String &p_message) const;
- String double_vowels(String &p_message) const;
- String replace_with_accented_string(String &p_message) const;
- String wrap_with_fakebidi_characters(String &p_message) const;
- String add_padding(const String &p_message, int p_length) const;
- const char32_t *get_accented_version(char32_t p_character) const;
- bool is_placeholder(String &p_message, int p_index) const;
-
- static TranslationServer *singleton;
- bool _load_translations(const String &p_from);
- String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
-
- StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
-
- static void _bind_methods();
-
-#ifndef DISABLE_DEPRECATED
- static void _bind_compatibility_methods();
-#endif
-
- struct LocaleScriptInfo {
- String name;
- String script;
- String default_country;
- HashSet<String> supported_countries;
- };
- static Vector<LocaleScriptInfo> locale_script_info;
-
- static HashMap<String, String> language_map;
- static HashMap<String, String> script_map;
- static HashMap<String, String> locale_rename_map;
- static HashMap<String, String> country_name_map;
- static HashMap<String, String> country_rename_map;
- static HashMap<String, String> variant_map;
-
- void init_locale_info();
-
-public:
- _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
-
- void set_enabled(bool p_enabled) { enabled = p_enabled; }
- _FORCE_INLINE_ bool is_enabled() const { return enabled; }
-
- void set_locale(const String &p_locale);
- String get_locale() const;
- Ref<Translation> get_translation_object(const String &p_locale);
-
- Vector<String> get_all_languages() const;
- String get_language_name(const String &p_language) const;
-
- Vector<String> get_all_scripts() const;
- String get_script_name(const String &p_script) const;
-
- Vector<String> get_all_countries() const;
- String get_country_name(const String &p_country) const;
-
- String get_locale_name(const String &p_locale) const;
-
- PackedStringArray get_loaded_locales() const;
-
- void add_translation(const Ref<Translation> &p_translation);
- void remove_translation(const Ref<Translation> &p_translation);
-
- StringName translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
-
- StringName pseudolocalize(const StringName &p_message) const;
-
- bool is_pseudolocalization_enabled() const;
- void set_pseudolocalization_enabled(bool p_enabled);
- void reload_pseudolocalization();
-
- String standardize_locale(const String &p_locale) const;
-
- int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
-
- String get_tool_locale();
- void set_tool_translation(const Ref<Translation> &p_translation);
- Ref<Translation> get_tool_translation() const;
- StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
- void set_property_translation(const Ref<Translation> &p_translation);
- StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
- void set_doc_translation(const Ref<Translation> &p_translation);
- StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
- void set_extractable_translation(const Ref<Translation> &p_translation);
- StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
-
- void setup();
-
- void clear();
-
- void load_translations();
-
-#ifdef TOOLS_ENABLED
- virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
-#endif // TOOLS_ENABLED
-
- TranslationServer();
-};
-
#endif // TRANSLATION_H
diff --git a/core/string/translation_server.compat.inc b/core/string/translation_server.compat.inc
new file mode 100644
index 0000000000..9d1ee8b9df
--- /dev/null
+++ b/core/string/translation_server.compat.inc
@@ -0,0 +1,38 @@
+/**************************************************************************/
+/* translation_server.compat.inc */
+/**************************************************************************/
+/* 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 DISABLE_DEPRECATED
+
+void TranslationServer::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
+ ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
+}
+
+#endif
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
new file mode 100644
index 0000000000..6e784881d0
--- /dev/null
+++ b/core/string/translation_server.cpp
@@ -0,0 +1,947 @@
+/**************************************************************************/
+/* translation_server.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 "translation_server.h"
+#include "translation_server.compat.inc"
+
+#include "core/config/project_settings.h"
+#include "core/io/resource_loader.h"
+#include "core/os/os.h"
+#include "core/string/locales.h"
+
+#ifdef TOOLS_ENABLED
+#include "main/main.h"
+#endif
+
+struct _character_accent_pair {
+ const char32_t character;
+ const char32_t *accented_character;
+};
+
+static _character_accent_pair _character_to_accented[] = {
+ { 'A', U"Å" },
+ { 'B', U"ß" },
+ { 'C', U"Ç" },
+ { 'D', U"Ð" },
+ { 'E', U"É" },
+ { 'F', U"F́" },
+ { 'G', U"Ĝ" },
+ { 'H', U"Ĥ" },
+ { 'I', U"Ĩ" },
+ { 'J', U"Ĵ" },
+ { 'K', U"ĸ" },
+ { 'L', U"Ł" },
+ { 'M', U"Ḿ" },
+ { 'N', U"й" },
+ { 'O', U"Ö" },
+ { 'P', U"Ṕ" },
+ { 'Q', U"Q́" },
+ { 'R', U"Ř" },
+ { 'S', U"Ŝ" },
+ { 'T', U"Ŧ" },
+ { 'U', U"Ũ" },
+ { 'V', U"Ṽ" },
+ { 'W', U"Ŵ" },
+ { 'X', U"X́" },
+ { 'Y', U"Ÿ" },
+ { 'Z', U"Ž" },
+ { 'a', U"á" },
+ { 'b', U"ḅ" },
+ { 'c', U"ć" },
+ { 'd', U"d́" },
+ { 'e', U"é" },
+ { 'f', U"f́" },
+ { 'g', U"ǵ" },
+ { 'h', U"h̀" },
+ { 'i', U"í" },
+ { 'j', U"ǰ" },
+ { 'k', U"ḱ" },
+ { 'l', U"ł" },
+ { 'm', U"m̀" },
+ { 'n', U"ή" },
+ { 'o', U"ô" },
+ { 'p', U"ṕ" },
+ { 'q', U"q́" },
+ { 'r', U"ŕ" },
+ { 's', U"š" },
+ { 't', U"ŧ" },
+ { 'u', U"ü" },
+ { 'v', U"ṽ" },
+ { 'w', U"ŵ" },
+ { 'x', U"x́" },
+ { 'y', U"ý" },
+ { 'z', U"ź" },
+};
+
+Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
+
+HashMap<String, String> TranslationServer::language_map;
+HashMap<String, String> TranslationServer::script_map;
+HashMap<String, String> TranslationServer::locale_rename_map;
+HashMap<String, String> TranslationServer::country_name_map;
+HashMap<String, String> TranslationServer::variant_map;
+HashMap<String, String> TranslationServer::country_rename_map;
+
+void TranslationServer::init_locale_info() {
+ // Init locale info.
+ language_map.clear();
+ int idx = 0;
+ while (language_list[idx][0] != nullptr) {
+ language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]);
+ idx++;
+ }
+
+ // Init locale-script map.
+ locale_script_info.clear();
+ idx = 0;
+ while (locale_scripts[idx][0] != nullptr) {
+ LocaleScriptInfo info;
+ info.name = locale_scripts[idx][0];
+ info.script = locale_scripts[idx][1];
+ info.default_country = locale_scripts[idx][2];
+ Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false);
+ for (int i = 0; i < supported_countries.size(); i++) {
+ info.supported_countries.insert(supported_countries[i]);
+ }
+ locale_script_info.push_back(info);
+ idx++;
+ }
+
+ // Init supported script list.
+ script_map.clear();
+ idx = 0;
+ while (script_list[idx][0] != nullptr) {
+ script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]);
+ idx++;
+ }
+
+ // Init regional variant map.
+ variant_map.clear();
+ idx = 0;
+ while (locale_variants[idx][0] != nullptr) {
+ variant_map[locale_variants[idx][0]] = locale_variants[idx][1];
+ idx++;
+ }
+
+ // Init locale renames.
+ locale_rename_map.clear();
+ idx = 0;
+ while (locale_renames[idx][0] != nullptr) {
+ if (!String(locale_renames[idx][1]).is_empty()) {
+ locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1];
+ }
+ idx++;
+ }
+
+ // Init country names.
+ country_name_map.clear();
+ idx = 0;
+ while (country_names[idx][0] != nullptr) {
+ country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]);
+ idx++;
+ }
+
+ // Init country renames.
+ country_rename_map.clear();
+ idx = 0;
+ while (country_renames[idx][0] != nullptr) {
+ if (!String(country_renames[idx][1]).is_empty()) {
+ country_rename_map[country_renames[idx][0]] = country_renames[idx][1];
+ }
+ idx++;
+ }
+}
+
+String TranslationServer::standardize_locale(const String &p_locale) const {
+ return _standardize_locale(p_locale, false);
+}
+
+String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const {
+ // Replaces '-' with '_' for macOS style locales.
+ String univ_locale = p_locale.replace("-", "_");
+
+ // Extract locale elements.
+ String lang_name, script_name, country_name, variant_name;
+ Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
+ lang_name = locale_elements[0];
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
+ script_name = locale_elements[1];
+ }
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
+ country_name = locale_elements[1];
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
+ country_name = locale_elements[2];
+ } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) {
+ variant_name = locale_elements[2].to_lower();
+ }
+ }
+ if (locale_elements.size() >= 4) {
+ if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) {
+ variant_name = locale_elements[3].to_lower();
+ }
+ }
+
+ // Try extract script and variant from the extra part.
+ Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";");
+ for (int i = 0; i < script_extra.size(); i++) {
+ if (script_extra[i].to_lower() == "cyrillic") {
+ script_name = "Cyrl";
+ break;
+ } else if (script_extra[i].to_lower() == "latin") {
+ script_name = "Latn";
+ break;
+ } else if (script_extra[i].to_lower() == "devanagari") {
+ script_name = "Deva";
+ break;
+ } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) {
+ variant_name = script_extra[i].to_lower();
+ }
+ }
+
+ // Handles known non-ISO language names used e.g. on Windows.
+ if (locale_rename_map.has(lang_name)) {
+ lang_name = locale_rename_map[lang_name];
+ }
+
+ // Handle country renames.
+ if (country_rename_map.has(country_name)) {
+ country_name = country_rename_map[country_name];
+ }
+
+ // Remove unsupported script codes.
+ if (!script_map.has(script_name)) {
+ script_name = "";
+ }
+
+ // Add script code base on language and country codes for some ambiguous cases.
+ if (p_add_defaults) {
+ if (script_name.is_empty()) {
+ for (int i = 0; i < locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = locale_script_info[i];
+ if (info.name == lang_name) {
+ if (country_name.is_empty() || info.supported_countries.has(country_name)) {
+ script_name = info.script;
+ break;
+ }
+ }
+ }
+ }
+ if (!script_name.is_empty() && country_name.is_empty()) {
+ // Add conntry code based on script for some ambiguous cases.
+ for (int i = 0; i < locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = locale_script_info[i];
+ if (info.name == lang_name && info.script == script_name) {
+ country_name = info.default_country;
+ break;
+ }
+ }
+ }
+ }
+
+ // Combine results.
+ String out = lang_name;
+ if (!script_name.is_empty()) {
+ out = out + "_" + script_name;
+ }
+ if (!country_name.is_empty()) {
+ out = out + "_" + country_name;
+ }
+ if (!variant_name.is_empty()) {
+ out = out + "_" + variant_name;
+ }
+ return out;
+}
+
+int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
+ String locale_a = _standardize_locale(p_locale_a, true);
+ String locale_b = _standardize_locale(p_locale_b, true);
+
+ if (locale_a == locale_b) {
+ // Exact match.
+ return 10;
+ }
+
+ Vector<String> locale_a_elements = locale_a.split("_");
+ Vector<String> locale_b_elements = locale_b.split("_");
+ if (locale_a_elements[0] == locale_b_elements[0]) {
+ // Matching language, both locales have extra parts.
+ // Return number of matching elements.
+ int matching_elements = 1;
+ for (int i = 1; i < locale_a_elements.size(); i++) {
+ for (int j = 1; j < locale_b_elements.size(); j++) {
+ if (locale_a_elements[i] == locale_b_elements[j]) {
+ matching_elements++;
+ }
+ }
+ }
+ return matching_elements;
+ } else {
+ // No match.
+ return 0;
+ }
+}
+
+String TranslationServer::get_locale_name(const String &p_locale) const {
+ String lang_name, script_name, country_name;
+ Vector<String> locale_elements = standardize_locale(p_locale).split("_");
+ lang_name = locale_elements[0];
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
+ script_name = locale_elements[1];
+ }
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
+ country_name = locale_elements[1];
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
+ country_name = locale_elements[2];
+ }
+ }
+
+ String name = language_map[lang_name];
+ if (!script_name.is_empty()) {
+ name = name + " (" + script_map[script_name] + ")";
+ }
+ if (!country_name.is_empty()) {
+ name = name + ", " + country_name_map[country_name];
+ }
+ return name;
+}
+
+Vector<String> TranslationServer::get_all_languages() const {
+ Vector<String> languages;
+
+ for (const KeyValue<String, String> &E : language_map) {
+ languages.push_back(E.key);
+ }
+
+ return languages;
+}
+
+String TranslationServer::get_language_name(const String &p_language) const {
+ return language_map[p_language];
+}
+
+Vector<String> TranslationServer::get_all_scripts() const {
+ Vector<String> scripts;
+
+ for (const KeyValue<String, String> &E : script_map) {
+ scripts.push_back(E.key);
+ }
+
+ return scripts;
+}
+
+String TranslationServer::get_script_name(const String &p_script) const {
+ return script_map[p_script];
+}
+
+Vector<String> TranslationServer::get_all_countries() const {
+ Vector<String> countries;
+
+ for (const KeyValue<String, String> &E : country_name_map) {
+ countries.push_back(E.key);
+ }
+
+ return countries;
+}
+
+String TranslationServer::get_country_name(const String &p_country) const {
+ return country_name_map[p_country];
+}
+
+void TranslationServer::set_locale(const String &p_locale) {
+ String new_locale = standardize_locale(p_locale);
+ if (locale == new_locale) {
+ return;
+ }
+
+ locale = new_locale;
+ ResourceLoader::reload_translation_remaps();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+}
+
+String TranslationServer::get_locale() const {
+ return locale;
+}
+
+PackedStringArray TranslationServer::get_loaded_locales() const {
+ PackedStringArray locales;
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), PackedStringArray());
+ String l = t->get_locale();
+
+ locales.push_back(l);
+ }
+
+ return locales;
+}
+
+void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
+ translations.insert(p_translation);
+}
+
+void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
+ translations.erase(p_translation);
+}
+
+Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
+ Ref<Translation> res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), nullptr);
+ String l = t->get_locale();
+
+ int score = compare_locales(p_locale, l);
+ if (score > 0 && score >= best_score) {
+ res = t;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+ return res;
+}
+
+void TranslationServer::clear() {
+ translations.clear();
+}
+
+StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
+ // Match given message against the translation catalog for the project locale.
+
+ if (!enabled) {
+ return p_message;
+ }
+
+ StringName res = _get_message_from_translations(p_message, p_context, locale, false);
+
+ if (!res && fallback.length() >= 2) {
+ res = _get_message_from_translations(p_message, p_context, fallback, false);
+ }
+
+ if (!res) {
+ return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
+ }
+
+ return pseudolocalization_enabled ? pseudolocalize(res) : res;
+}
+
+StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (!enabled) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+
+ StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
+
+ if (!res && fallback.length() >= 2) {
+ res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
+ }
+
+ if (!res) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+
+ return res;
+}
+
+StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
+ StringName res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), p_message);
+ String l = t->get_locale();
+
+ int score = compare_locales(p_locale, l);
+ if (score > 0 && score >= best_score) {
+ StringName r;
+ if (!plural) {
+ r = t->get_message(p_message, p_context);
+ } else {
+ r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ }
+ if (!r) {
+ continue;
+ }
+ res = r;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+
+ return res;
+}
+
+TranslationServer *TranslationServer::singleton = nullptr;
+
+bool TranslationServer::_load_translations(const String &p_from) {
+ if (ProjectSettings::get_singleton()->has_setting(p_from)) {
+ const Vector<String> &translation_names = GLOBAL_GET(p_from);
+
+ int tcount = translation_names.size();
+
+ if (tcount) {
+ const String *r = translation_names.ptr();
+
+ for (int i = 0; i < tcount; i++) {
+ Ref<Translation> tr = ResourceLoader::load(r[i]);
+ if (tr.is_valid()) {
+ add_translation(tr);
+ }
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void TranslationServer::setup() {
+ String test = GLOBAL_DEF("internationalization/locale/test", "");
+ test = test.strip_edges();
+ if (!test.is_empty()) {
+ set_locale(test);
+ } else {
+ set_locale(OS::get_singleton()->get_locale());
+ }
+
+ fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
+ pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
+ pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
+ pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
+ pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
+ pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
+ expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
+ pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
+ pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
+ pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
+
+#ifdef TOOLS_ENABLED
+ ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
+#endif
+}
+
+void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
+ tool_translation = p_translation;
+}
+
+Ref<Translation> TranslationServer::get_tool_translation() const {
+ return tool_translation;
+}
+
+String TranslationServer::get_tool_locale() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
+ if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) {
+ return tool_translation->get_locale();
+ } else {
+ return "en";
+ }
+ } else {
+#else
+ {
+#endif
+ // Look for best matching loaded translation.
+ String best_locale = "en";
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), best_locale);
+ String l = t->get_locale();
+
+ int score = compare_locales(locale, l);
+ if (score > 0 && score >= best_score) {
+ best_locale = l;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+ return best_locale;
+ }
+}
+
+StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
+ if (tool_translation.is_valid()) {
+ StringName r = tool_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (tool_translation.is_valid()) {
+ StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
+void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
+ property_translation = p_translation;
+}
+
+StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
+ if (property_translation.is_valid()) {
+ StringName r = property_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
+ doc_translation = p_translation;
+}
+
+StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
+ if (doc_translation.is_valid()) {
+ StringName r = doc_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (doc_translation.is_valid()) {
+ StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
+void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
+ extractable_translation = p_translation;
+}
+
+StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const {
+ if (extractable_translation.is_valid()) {
+ StringName r = extractable_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (extractable_translation.is_valid()) {
+ StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
+bool TranslationServer::is_pseudolocalization_enabled() const {
+ return pseudolocalization_enabled;
+}
+
+void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
+ pseudolocalization_enabled = p_enabled;
+
+ ResourceLoader::reload_translation_remaps();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+}
+
+void TranslationServer::reload_pseudolocalization() {
+ pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
+ pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
+ pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
+ pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
+ expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
+ pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
+ pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
+ pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+
+ ResourceLoader::reload_translation_remaps();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+}
+
+StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ int length = message.length();
+ if (pseudolocalization_override_enabled) {
+ message = get_override_string(message);
+ }
+
+ if (pseudolocalization_double_vowels_enabled) {
+ message = double_vowels(message);
+ }
+
+ if (pseudolocalization_accents_enabled) {
+ message = replace_with_accented_string(message);
+ }
+
+ if (pseudolocalization_fake_bidi_enabled) {
+ message = wrap_with_fakebidi_characters(message);
+ }
+
+ StringName res = add_padding(message, length);
+ return res;
+}
+
+StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ message = double_vowels(message);
+ message = replace_with_accented_string(message);
+ StringName res = "[!!! " + message + " !!!]";
+ return res;
+}
+
+String TranslationServer::get_override_string(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += '*';
+ }
+ return res;
+}
+
+String TranslationServer::double_vowels(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += p_message[i];
+ if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
+ p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
+ res += p_message[i];
+ }
+ }
+ return res;
+};
+
+String TranslationServer::replace_with_accented_string(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ const char32_t *accented = get_accented_version(p_message[i]);
+ if (accented) {
+ res += accented;
+ } else {
+ res += p_message[i];
+ }
+ }
+ return res;
+}
+
+String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
+ String res;
+ char32_t fakebidiprefix = U'\u202e';
+ char32_t fakebidisuffix = U'\u202c';
+ res += fakebidiprefix;
+ // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
+ for (int i = 0; i < p_message.length(); i++) {
+ if (p_message[i] == '\n') {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += fakebidiprefix;
+ } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += p_message[i + 1];
+ res += fakebidiprefix;
+ i++;
+ } else {
+ res += p_message[i];
+ }
+ }
+ res += fakebidisuffix;
+ return res;
+}
+
+String TranslationServer::add_padding(const String &p_message, int p_length) const {
+ String underscores = String("_").repeat(p_length * expansion_ratio / 2);
+ String prefix = pseudolocalization_prefix + underscores;
+ String suffix = underscores + pseudolocalization_suffix;
+
+ return prefix + p_message + suffix;
+}
+
+const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
+ if (!is_ascii_alphabet_char(p_character)) {
+ return nullptr;
+ }
+
+ for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
+ if (_character_to_accented[i].character == p_character) {
+ return _character_to_accented[i].accented_character;
+ }
+ }
+
+ return nullptr;
+}
+
+bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
+ return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
+ (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
+ p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
+}
+
+#ifdef TOOLS_ENABLED
+void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ const String pf = p_function;
+ if (p_idx == 0) {
+ HashMap<String, String> *target_hash_map = nullptr;
+ if (pf == "get_language_name") {
+ target_hash_map = &language_map;
+ } else if (pf == "get_script_name") {
+ target_hash_map = &script_map;
+ } else if (pf == "get_country_name") {
+ target_hash_map = &country_name_map;
+ }
+
+ if (target_hash_map) {
+ for (const KeyValue<String, String> &E : *target_hash_map) {
+ r_options->push_back(E.key.quote());
+ }
+ }
+ }
+ Object::get_argument_options(p_function, p_idx, r_options);
+}
+#endif // TOOLS_ENABLED
+
+void TranslationServer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
+ ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
+ ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale);
+
+ ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales);
+ ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale);
+
+ ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages);
+ ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name);
+
+ ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts);
+ ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name);
+
+ ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries);
+ ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name);
+
+ ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
+
+ ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName()));
+
+ ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
+ ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
+ ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
+
+ ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
+
+ ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
+
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization);
+ ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize);
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
+}
+
+void TranslationServer::load_translations() {
+ _load_translations("internationalization/locale/translations"); //all
+ _load_translations("internationalization/locale/translations_" + locale.substr(0, 2));
+
+ if (locale.substr(0, 2) != locale) {
+ _load_translations("internationalization/locale/translations_" + locale);
+ }
+}
+
+TranslationServer::TranslationServer() {
+ singleton = this;
+ init_locale_info();
+}
diff --git a/core/string/translation_server.h b/core/string/translation_server.h
new file mode 100644
index 0000000000..ebe81d9712
--- /dev/null
+++ b/core/string/translation_server.h
@@ -0,0 +1,164 @@
+/**************************************************************************/
+/* translation_server.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 TRANSLATION_SERVER_H
+#define TRANSLATION_SERVER_H
+
+#include "core/string/translation.h"
+
+class TranslationServer : public Object {
+ GDCLASS(TranslationServer, Object);
+
+ String locale = "en";
+ String fallback;
+
+ HashSet<Ref<Translation>> translations;
+ Ref<Translation> tool_translation;
+ Ref<Translation> property_translation;
+ Ref<Translation> doc_translation;
+ Ref<Translation> extractable_translation;
+
+ bool enabled = true;
+
+ bool pseudolocalization_enabled = false;
+ bool pseudolocalization_accents_enabled = false;
+ bool pseudolocalization_double_vowels_enabled = false;
+ bool pseudolocalization_fake_bidi_enabled = false;
+ bool pseudolocalization_override_enabled = false;
+ bool pseudolocalization_skip_placeholders_enabled = false;
+ float expansion_ratio = 0.0;
+ String pseudolocalization_prefix;
+ String pseudolocalization_suffix;
+
+ StringName tool_pseudolocalize(const StringName &p_message) const;
+ String get_override_string(String &p_message) const;
+ String double_vowels(String &p_message) const;
+ String replace_with_accented_string(String &p_message) const;
+ String wrap_with_fakebidi_characters(String &p_message) const;
+ String add_padding(const String &p_message, int p_length) const;
+ const char32_t *get_accented_version(char32_t p_character) const;
+ bool is_placeholder(String &p_message, int p_index) const;
+
+ static TranslationServer *singleton;
+ bool _load_translations(const String &p_from);
+ String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
+
+ StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
+
+ static void _bind_methods();
+
+#ifndef DISABLE_DEPRECATED
+ static void _bind_compatibility_methods();
+#endif
+
+ struct LocaleScriptInfo {
+ String name;
+ String script;
+ String default_country;
+ HashSet<String> supported_countries;
+ };
+ static Vector<LocaleScriptInfo> locale_script_info;
+
+ static HashMap<String, String> language_map;
+ static HashMap<String, String> script_map;
+ static HashMap<String, String> locale_rename_map;
+ static HashMap<String, String> country_name_map;
+ static HashMap<String, String> country_rename_map;
+ static HashMap<String, String> variant_map;
+
+ void init_locale_info();
+
+public:
+ _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
+
+ void set_enabled(bool p_enabled) { enabled = p_enabled; }
+ _FORCE_INLINE_ bool is_enabled() const { return enabled; }
+
+ void set_locale(const String &p_locale);
+ String get_locale() const;
+ Ref<Translation> get_translation_object(const String &p_locale);
+
+ Vector<String> get_all_languages() const;
+ String get_language_name(const String &p_language) const;
+
+ Vector<String> get_all_scripts() const;
+ String get_script_name(const String &p_script) const;
+
+ Vector<String> get_all_countries() const;
+ String get_country_name(const String &p_country) const;
+
+ String get_locale_name(const String &p_locale) const;
+
+ PackedStringArray get_loaded_locales() const;
+
+ void add_translation(const Ref<Translation> &p_translation);
+ void remove_translation(const Ref<Translation> &p_translation);
+
+ StringName translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+
+ StringName pseudolocalize(const StringName &p_message) const;
+
+ bool is_pseudolocalization_enabled() const;
+ void set_pseudolocalization_enabled(bool p_enabled);
+ void reload_pseudolocalization();
+
+ String standardize_locale(const String &p_locale) const;
+
+ int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
+
+ String get_tool_locale();
+ void set_tool_translation(const Ref<Translation> &p_translation);
+ Ref<Translation> get_tool_translation() const;
+ StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+ void set_property_translation(const Ref<Translation> &p_translation);
+ StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
+ void set_doc_translation(const Ref<Translation> &p_translation);
+ StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+ void set_extractable_translation(const Ref<Translation> &p_translation);
+ StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+
+ void setup();
+
+ void clear();
+
+ void load_translations();
+
+#ifdef TOOLS_ENABLED
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+#endif // TOOLS_ENABLED
+
+ TranslationServer();
+};
+
+#endif // TRANSLATION_SERVER_H
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 36f1b6b65e..5a1f6925aa 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -36,7 +36,7 @@
#include "core/os/memory.h"
#include "core/string/print_string.h"
#include "core/string/string_name.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/string/ucaps.h"
#include "core/variant/variant.h"
#include "core/version_generated.gen.h"
diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml
index 59ed0b8fe7..66b67d1a59 100644
--- a/doc/classes/ClassDB.xml
+++ b/doc/classes/ClassDB.xml
@@ -99,6 +99,14 @@
Returns the default value of [param property] of [param class] or its ancestor classes.
</description>
</method>
+ <method name="class_get_property_getter">
+ <return type="StringName" />
+ <param index="0" name="class" type="StringName" />
+ <param index="1" name="property" type="StringName" />
+ <description>
+ Returns the getter method name of [param property] of [param class].
+ </description>
+ </method>
<method name="class_get_property_list" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="class" type="StringName" />
@@ -107,6 +115,14 @@
Returns an array with all the properties of [param class] or its ancestry if [param no_inheritance] is [code]false[/code].
</description>
</method>
+ <method name="class_get_property_setter">
+ <return type="StringName" />
+ <param index="0" name="class" type="StringName" />
+ <param index="1" name="property" type="StringName" />
+ <description>
+ Returns the setter method name of [param property] of [param class].
+ </description>
+ </method>
<method name="class_get_signal" qualifiers="const">
<return type="Dictionary" />
<param index="0" name="class" type="StringName" />
diff --git a/doc/classes/Compositor.xml b/doc/classes/Compositor.xml
index 7605083319..0732526957 100644
--- a/doc/classes/Compositor.xml
+++ b/doc/classes/Compositor.xml
@@ -7,6 +7,7 @@
The compositor resource stores attributes used to customize how a [Viewport] is rendered.
</description>
<tutorials>
+ <link title="The Compositor">$DOCS_URL/tutorials/rendering/compositor.html</link>
</tutorials>
<members>
<member name="compositor_effects" type="CompositorEffect[]" setter="set_compositor_effects" getter="get_compositor_effects" default="[]">
diff --git a/doc/classes/CompositorEffect.xml b/doc/classes/CompositorEffect.xml
index cf3aa9fca4..76a3887918 100644
--- a/doc/classes/CompositorEffect.xml
+++ b/doc/classes/CompositorEffect.xml
@@ -7,6 +7,7 @@
This resource defines a custom rendering effect that can be applied to [Viewport]s through the viewports' [Environment]. You can implement a callback that is called during rendering at a given stage of the rendering pipeline and allows you to insert additional passes. Note that this callback happens on the rendering thread. CompositorEffect is an abstract base class and must be extended to implement specific rendering logic.
</description>
<tutorials>
+ <link title="The Compositor">$DOCS_URL/tutorials/rendering/compositor.html</link>
</tutorials>
<methods>
<method name="_render_callback" qualifiers="virtual">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 4d82a32dae..320b119b6a 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -311,13 +311,13 @@
The grid size in units. Higher values prevent the grid from appearing "cut off" at certain angles, but make the grid more demanding to render. Depending on the camera's position, the grid may not be fully visible since a shader is used to fade it progressively.
</member>
<member name="editors/3d/grid_xy_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on an XY plane. This can be useful for 3D side-scrolling games.
+ If [code]true[/code], renders the grid on the XY plane in perspective view. This can be useful for 3D side-scrolling games.
</member>
<member name="editors/3d/grid_xz_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on an XZ plane.
+ If [code]true[/code], renders the grid on the XZ plane in perspective view.
</member>
<member name="editors/3d/grid_yz_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on a YZ plane. This can be useful for 3D side-scrolling games.
+ If [code]true[/code], renders the grid on the YZ plane in perspective view. This can be useful for 3D side-scrolling games.
</member>
<member name="editors/3d/navigation/emulate_3_button_mouse" type="bool" setter="" getter="">
If [code]true[/code], enables 3-button mouse emulation mode. This is useful on laptops when using a trackpad.
@@ -656,6 +656,9 @@
Expanding main editor window content to the title, if supported by [DisplayServer]. See [constant DisplayServer.WINDOW_FLAG_EXTEND_TO_TITLE].
Specific to the macOS platform.
</member>
+ <member name="interface/editor/font_allow_msdf" type="bool" setter="" getter="">
+ If set to [code]true[/code], MSDF font rendering will be used for the visual shader graph editor. You may need to set this to [code]false[/code] when using a custom main font, as some fonts will look broken due to the use of self-intersecting outlines in their font data. Downloading the font from the font maker's official website as opposed to a service like Google Fonts can help resolve this issue.
+ </member>
<member name="interface/editor/font_antialiasing" type="int" setter="" getter="">
FreeType's font anti-aliasing mode used to render the editor fonts. Most fonts are not designed to look good with anti-aliasing disabled, so it's recommended to leave this enabled unless you're using a pixel art font.
</member>
diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml
index f21696d02c..2dd76ad989 100644
--- a/doc/classes/Geometry2D.xml
+++ b/doc/classes/Geometry2D.xml
@@ -126,8 +126,34 @@
<param index="2" name="from_b" type="Vector2" />
<param index="3" name="dir_b" type="Vector2" />
<description>
- Checks if the two lines ([param from_a], [param dir_a]) and ([param from_b], [param dir_b]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns [code]null[/code].
- [b]Note:[/b] The lines are specified using direction vectors, not end points.
+ Returns the point of intersection between the two lines ([param from_a], [param dir_a]) and ([param from_b], [param dir_b]). Returns a [Vector2], or [code]null[/code] if the lines are parallel.
+ [code]from[/code] and [code]dir[/code] are [i]not[/i] endpoints of a line segment or ray but the slope ([code]dir[/code]) and a known point ([code]from[/code]) on that line.
+ [codeblocks]
+ [gdscript]
+ var from_a = Vector2.ZERO
+ var dir_a = Vector2.RIGHT
+ var from_b = Vector2.DOWN
+
+ # Returns Vector2(1, 0)
+ Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2(1, -1))
+ # Returns Vector2(-1, 0)
+ Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2(-1, -1))
+ # Returns null
+ Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2.RIGHT)
+ [/gdscript]
+ [csharp]
+ var fromA = Vector2.Zero;
+ var dirA = Vector2.Right;
+ var fromB = Vector2.Down;
+
+ // Returns new Vector2(1, 0)
+ Geometry2D.LineIntersectsLine(fromA, dirA, fromB, new Vector2(1, -1));
+ // Returns new Vector2(-1, 0)
+ Geometry2D.LineIntersectsLine(fromA, dirA, fromB, new Vector2(-1, -1));
+ // Returns null
+ Geometry2D.LineIntersectsLine(fromA, dirA, fromB, Vector2.Right);
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="make_atlas">
diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml
index 0892c9ec44..2e1c962dd1 100644
--- a/doc/classes/NavigationLink2D.xml
+++ b/doc/classes/NavigationLink2D.xml
@@ -29,6 +29,12 @@
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the current navigation map [RID] used by this link.
+ </description>
+ </method>
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
@@ -57,6 +63,13 @@
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World2D] default navigation map so this function is only required to override the default map.
+ </description>
+ </method>
</methods>
<members>
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml
index 0fcc106beb..174228ea5b 100644
--- a/doc/classes/NavigationLink3D.xml
+++ b/doc/classes/NavigationLink3D.xml
@@ -29,6 +29,12 @@
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the current navigation map [RID] used by this link.
+ </description>
+ </method>
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
@@ -57,6 +63,13 @@
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World3D] default navigation map so this function is only required to override the default map.
+ </description>
+ </method>
</methods>
<members>
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml
index 90d3522002..4e9208d9bb 100644
--- a/doc/classes/Polygon2D.xml
+++ b/doc/classes/Polygon2D.xml
@@ -71,9 +71,6 @@
<member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false">
If [code]true[/code], polygon edges will be anti-aliased.
</member>
- <member name="bones" type="Array" setter="_set_bones" getter="_get_bones" default="[]">
- Internal list of [Bone2D] nodes used by the assigned [member skeleton]. Edited using the Polygon2D editor ("UV" button on the top toolbar).
- </member>
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)" keywords="colour">
The polygon's fill color. If [member texture] is set, it will be multiplied by this color. It will also be the default color for vertices not set in [member vertex_colors].
</member>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index ff2c0bbfd9..8d567f347a 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1004,7 +1004,7 @@
prime-run %command%
[/codeblock]
</member>
- <member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray(&quot;gd&quot;, &quot;gdshader&quot;)">
+ <member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="">
Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files.
</member>
<member name="editor/script/templates_search_path" type="String" setter="" getter="" default="&quot;res://script_templates&quot;">
diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml
index f100670e08..b678a04e34 100644
--- a/doc/classes/ResourceImporterDynamicFont.xml
+++ b/doc/classes/ResourceImporterDynamicFont.xml
@@ -69,12 +69,13 @@
<member name="script_support" type="Dictionary" setter="" getter="" default="{}">
Override the list of language scripts supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member language_support].
</member>
- <member name="subpixel_positioning" type="int" setter="" getter="" default="1">
+ <member name="subpixel_positioning" type="int" setter="" getter="" default="4">
Subpixel positioning improves font rendering appearance, especially at smaller font sizes. The downside is that it takes more time to initially render the font, which can cause stuttering during gameplay, especially if used with large font sizes. This should be set to [b]Disabled[/b] for fonts with a pixel art appearance.
[b]Disabled:[/b] No subpixel positioning. Lowest quality, fastest rendering.
[b]Auto:[/b] Use subpixel positioning at small font sizes (the chosen quality varies depending on font size). Large fonts will not use subpixel positioning. This is a good tradeoff between performance and quality.
[b]One Half of a Pixel:[/b] Always perform intermediate subpixel positioning regardless of font size. High quality, slow rendering.
[b]One Quarter of a Pixel:[/b] Always perform precise subpixel positioning regardless of font size. Highest quality, slowest rendering.
+ [b]Auto (Except Pixel Fonts):[/b] [b]Disabled[/b] for the pixel style fonts (each glyph contours contain only straight horizontal and vertical lines), [b]Auto[/b] for the other fonts.
</member>
</members>
</class>
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 4fa9700f9c..9d476691bf 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -459,6 +459,13 @@
Returns a string containing all the characters available in the font.
</description>
</method>
+ <method name="font_get_supported_glyphs" qualifiers="const">
+ <return type="PackedInt32Array" />
+ <param index="0" name="font_rid" type="RID" />
+ <description>
+ Returns an array containing all glyph indices in the font.
+ </description>
+ </method>
<method name="font_get_texture_count" qualifiers="const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index c148cdad52..3c27404f8e 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -496,6 +496,14 @@
Returns a string containing all the characters available in the font.
</description>
</method>
+ <method name="_font_get_supported_glyphs" qualifiers="virtual const">
+ <return type="PackedInt32Array" />
+ <param index="0" name="font_rid" type="RID" />
+ <description>
+ [b]Required.[/b]
+ Returns an array containing all glyph indices in the font.
+ </description>
+ </method>
<method name="_font_get_texture_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index d3197efc6b..0995a5a672 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -256,6 +256,33 @@
Returns a rectangle enclosing the used (non-empty) tiles of the map, including all layers.
</description>
</method>
+ <method name="is_cell_flipped_h" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="coords" type="Vector2i" />
+ <param index="2" name="use_proxies" type="bool" default="false" />
+ <description>
+ Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_flipped_v" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="coords" type="Vector2i" />
+ <param index="2" name="use_proxies" type="bool" default="false" />
+ <description>
+ Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_transposed" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="coords" type="Vector2i" />
+ <param index="2" name="use_proxies" type="bool" default="false" />
+ <description>
+ Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is transposed. The result is valid only for atlas sources.
+ </description>
+ </method>
<method name="is_layer_enabled" qualifiers="const">
<return type="bool" />
<param index="0" name="layer" type="int" />
diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml
index b9acef2095..bead1c32c0 100644
--- a/doc/classes/TileMapLayer.xml
+++ b/doc/classes/TileMapLayer.xml
@@ -153,6 +153,27 @@
Returns whether the provided [param body] [RID] belongs to one of this [TileMapLayer]'s cells.
</description>
</method>
+ <method name="is_cell_flipped_h" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="coords" type="Vector2i" />
+ <description>
+ Returns [code]true[/code] if the cell at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_flipped_v" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="coords" type="Vector2i" />
+ <description>
+ Returns [code]true[/code] if the cell at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_transposed" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="coords" type="Vector2i" />
+ <description>
+ Returns [code]true[/code] if the cell at coordinates [param coords] is transposed. The result is valid only for atlas sources.
+ </description>
+ </method>
<method name="local_to_map" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="local_position" type="Vector2" />
diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml
index 9de1e09273..f8f9393847 100644
--- a/doc/classes/Timer.xml
+++ b/doc/classes/Timer.xml
@@ -28,7 +28,7 @@
<return type="void" />
<param index="0" name="time_sec" type="float" default="-1" />
<description>
- Starts the timer, if it was not started already. Fails if the timer is not inside the tree. If [param time_sec] is greater than [code]0[/code], this value is used for the [member wait_time].
+ Starts the timer, or resets the timer if it was started already. Fails if the timer is not inside the tree. If [param time_sec] is greater than [code]0[/code], this value is used for the [member wait_time].
[b]Note:[/b] This method does not resume a paused timer. See [member paused].
</description>
</method>
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 6dd04af6b6..ba70de9e34 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1871,7 +1871,7 @@ void main() {
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // !ALPHA_SCISSOR_USED
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 0297aaaf7f..d36e152ed9 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -353,9 +353,6 @@ void AnimationTrackEditAudio::set_node(Object *p_object) {
id = p_object->get_instance_id();
}
-void AnimationTrackEditAudio::_bind_methods() {
-}
-
AnimationTrackEditAudio::AnimationTrackEditAudio() {
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditAudio::_preview_changed));
}
@@ -952,9 +949,6 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int
}
}
-void AnimationTrackEditTypeAudio::_bind_methods() {
-}
-
AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() {
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditTypeAudio::_preview_changed));
}
diff --git a/editor/animation_track_editor_plugins.h b/editor/animation_track_editor_plugins.h
index 0e9ad44229..7ddbae1bbc 100644
--- a/editor/animation_track_editor_plugins.h
+++ b/editor/animation_track_editor_plugins.h
@@ -63,9 +63,6 @@ class AnimationTrackEditAudio : public AnimationTrackEdit {
void _preview_changed(ObjectID p_which);
-protected:
- static void _bind_methods();
-
public:
virtual int get_key_height() const override;
virtual Rect2 get_key_rect(int p_index, float p_pixels_sec) override;
@@ -121,9 +118,6 @@ class AnimationTrackEditTypeAudio : public AnimationTrackEdit {
float len_resizing_rel = 0.0f;
bool over_drag_position = false;
-protected:
- static void _bind_methods();
-
public:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index c83e677b37..02b0eca6aa 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -237,9 +237,6 @@ void DependencyEditor::edit(const String &p_path) {
}
}
-void DependencyEditor::_bind_methods() {
-}
-
DependencyEditor::DependencyEditor() {
VBoxContainer *vb = memnew(VBoxContainer);
vb->set_name(TTR("Dependencies"));
@@ -353,9 +350,6 @@ void DependencyEditorOwners::_file_option(int p_option) {
}
}
-void DependencyEditorOwners::_bind_methods() {
-}
-
void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) {
if (!efsd) {
return;
@@ -865,9 +859,6 @@ void OrphanResourcesDialog::_button_pressed(Object *p_item, int p_column, int p_
dep_edit->edit(path);
}
-void OrphanResourcesDialog::_bind_methods() {
-}
-
OrphanResourcesDialog::OrphanResourcesDialog() {
set_title(TTR("Orphan Resource Explorer"));
delete_confirm = memnew(ConfirmationDialog);
diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h
index d43b12f1d2..0256f39979 100644
--- a/editor/dependency_editor.h
+++ b/editor/dependency_editor.h
@@ -60,9 +60,6 @@ class DependencyEditor : public AcceptDialog {
void _update_file();
-protected:
- static void _bind_methods();
-
public:
void edit(const String &p_path);
DependencyEditor();
@@ -81,7 +78,6 @@ class DependencyEditorOwners : public AcceptDialog {
void _fill_owners(EditorFileSystemDirectory *efsd);
- static void _bind_methods();
void _list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _select_file(int p_idx);
void _empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
@@ -175,7 +171,6 @@ class OrphanResourcesDialog : public ConfirmationDialog {
void _button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void refresh();
- static void _bind_methods();
public:
void show();
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 331dacf6ad..bf5b717c19 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -38,7 +38,7 @@
#include "core/io/marshalls.h"
#include "core/io/resource_importer.h"
#include "core/object/script_language.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
#include "scene/resources/theme.h"
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index 33049b6bc2..b415c72c15 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -659,9 +659,6 @@ void EditorAssetInstaller::_notification(int p_what) {
}
}
-void EditorAssetInstaller::_bind_methods() {
-}
-
EditorAssetInstaller::EditorAssetInstaller() {
VBoxContainer *vb = memnew(VBoxContainer);
add_child(vb);
diff --git a/editor/editor_asset_installer.h b/editor/editor_asset_installer.h
index bc63bc9916..41f092f304 100644
--- a/editor/editor_asset_installer.h
+++ b/editor/editor_asset_installer.h
@@ -96,7 +96,6 @@ class EditorAssetInstaller : public ConfirmationDialog {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
void open_asset(const String &p_path, bool p_autoskip_toplevel = false);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index feca12b409..02a95fd836 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1779,7 +1779,9 @@ String EditorFileSystem::_get_global_script_class(const String &p_type, const St
void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) {
String icon_path;
- if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
+ if (file_info->resource_script_class != StringName()) {
+ icon_path = EditorNode::get_editor_data().script_class_get_icon_path(file_info->resource_script_class);
+ } else if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
const String &script_dep = file_info->deps[0]; // Assuming the first dependency is a script.
const String &script_path = script_dep.contains("::") ? script_dep.get_slice("::", 2) : script_dep;
if (!script_path.is_empty()) {
diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp
index f8fd05bf1e..83f1c70c69 100644
--- a/editor/editor_locale_dialog.cpp
+++ b/editor/editor_locale_dialog.cpp
@@ -31,6 +31,7 @@
#include "editor_locale_dialog.h"
#include "core/config/project_settings.h"
+#include "core/string/translation_server.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/check_button.h"
diff --git a/editor/editor_locale_dialog.h b/editor/editor_locale_dialog.h
index 467861c36b..bc75e1df59 100644
--- a/editor/editor_locale_dialog.h
+++ b/editor/editor_locale_dialog.h
@@ -31,7 +31,6 @@
#ifndef EDITOR_LOCALE_DIALOG_H
#define EDITOR_LOCALE_DIALOG_H
-#include "core/string/translation.h"
#include "scene/gui/dialogs.h"
class Button;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f24fa344ae..d1dffba2ab 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -42,7 +42,7 @@
#include "core/os/os.h"
#include "core/os/time.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/version.h"
#include "editor/editor_string_names.h"
#include "main/main.h"
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index fdb4ec170b..19a4165041 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -129,9 +129,6 @@ void EditorPropertyText::set_placeholder(const String &p_string) {
text->set_placeholder(p_string);
}
-void EditorPropertyText::_bind_methods() {
-}
-
EditorPropertyText::EditorPropertyText() {
HBoxContainer *hb = memnew(HBoxContainer);
add_child(hb);
@@ -219,9 +216,6 @@ void EditorPropertyMultilineText::_notification(int p_what) {
}
}
-void EditorPropertyMultilineText::_bind_methods() {
-}
-
EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override("separation", 0);
@@ -343,9 +337,6 @@ void EditorPropertyTextEnum::setup(const Vector<String> &p_options, bool p_strin
}
}
-void EditorPropertyTextEnum::_bind_methods() {
-}
-
void EditorPropertyTextEnum::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
@@ -447,9 +438,6 @@ void EditorPropertyLocale::_locale_focus_exited() {
_locale_selected(locale->get_text());
}
-void EditorPropertyLocale::_bind_methods() {
-}
-
EditorPropertyLocale::EditorPropertyLocale() {
HBoxContainer *locale_hb = memnew(HBoxContainer);
add_child(locale_hb);
@@ -586,9 +574,6 @@ bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant
return false;
}
-void EditorPropertyPath::_bind_methods() {
-}
-
EditorPropertyPath::EditorPropertyPath() {
HBoxContainer *path_hb = memnew(HBoxContainer);
add_child(path_hb);
@@ -637,9 +622,6 @@ void EditorPropertyClassName::_dialog_created() {
update_property();
}
-void EditorPropertyClassName::_bind_methods() {
-}
-
EditorPropertyClassName::EditorPropertyClassName() {
property = memnew(Button);
property->set_clip_text(true);
@@ -669,9 +651,6 @@ void EditorPropertyCheck::update_property() {
checkbox->set_disabled(is_read_only());
}
-void EditorPropertyCheck::_bind_methods() {
-}
-
EditorPropertyCheck::EditorPropertyCheck() {
checkbox = memnew(CheckBox);
checkbox->set_text(TTR("On"));
@@ -725,9 +704,6 @@ void EditorPropertyEnum::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
-void EditorPropertyEnum::_bind_methods() {
-}
-
EditorPropertyEnum::EditorPropertyEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
@@ -804,9 +780,6 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) {
}
}
-void EditorPropertyFlags::_bind_methods() {
-}
-
EditorPropertyFlags::EditorPropertyFlags() {
vbox = memnew(VBoxContainer);
add_child(vbox);
@@ -1280,9 +1253,6 @@ void EditorPropertyLayers::_refresh_names() {
setup(layer_type);
}
-void EditorPropertyLayers::_bind_methods() {
-}
-
EditorPropertyLayers::EditorPropertyLayers() {
HBoxContainer *hb = memnew(HBoxContainer);
hb->set_clip_contents(true);
@@ -1330,9 +1300,6 @@ void EditorPropertyInteger::update_property() {
#endif
}
-void EditorPropertyInteger::_bind_methods() {
-}
-
void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
spin->set_min(p_min);
spin->set_max(p_max);
@@ -1385,9 +1352,6 @@ void EditorPropertyObjectID::setup(const String &p_base_type) {
base_type = p_base_type;
}
-void EditorPropertyObjectID::_bind_methods() {
-}
-
EditorPropertyObjectID::EditorPropertyObjectID() {
edit = memnew(Button);
add_child(edit);
@@ -1413,9 +1377,6 @@ void EditorPropertySignal::update_property() {
edit->set_icon(get_editor_theme_icon(SNAME("Signals")));
}
-void EditorPropertySignal::_bind_methods() {
-}
-
EditorPropertySignal::EditorPropertySignal() {
edit = memnew(Button);
add_child(edit);
@@ -1435,9 +1396,6 @@ void EditorPropertyCallable::update_property() {
edit->set_icon(get_editor_theme_icon(SNAME("Callable")));
}
-void EditorPropertyCallable::_bind_methods() {
-}
-
EditorPropertyCallable::EditorPropertyCallable() {
edit = memnew(Button);
add_child(edit);
@@ -1465,9 +1423,6 @@ void EditorPropertyFloat::update_property() {
spin->set_value_no_signal(val);
}
-void EditorPropertyFloat::_bind_methods() {
-}
-
void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_exp_range, bool p_greater, bool p_lesser, const String &p_suffix, bool p_radians_as_degrees) {
radians_as_degrees = p_radians_as_degrees;
spin->set_min(p_min);
@@ -1675,9 +1630,6 @@ void EditorPropertyEasing::_notification(int p_what) {
}
}
-void EditorPropertyEasing::_bind_methods() {
-}
-
EditorPropertyEasing::EditorPropertyEasing() {
easing_draw = memnew(Control);
easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing));
@@ -1740,9 +1692,6 @@ void EditorPropertyRect2::_notification(int p_what) {
}
}
-void EditorPropertyRect2::_bind_methods() {
-}
-
void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -1837,9 +1786,6 @@ void EditorPropertyRect2i::_notification(int p_what) {
}
}
-void EditorPropertyRect2i::_bind_methods() {
-}
-
void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -1933,9 +1879,6 @@ void EditorPropertyPlane::_notification(int p_what) {
}
}
-void EditorPropertyPlane::_bind_methods() {
-}
-
void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -2085,9 +2028,6 @@ void EditorPropertyQuaternion::_notification(int p_what) {
}
}
-void EditorPropertyQuaternion::_bind_methods() {
-}
-
void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix, bool p_hide_editor) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -2232,9 +2172,6 @@ void EditorPropertyAABB::_notification(int p_what) {
}
}
-void EditorPropertyAABB::_bind_methods() {
-}
-
void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 6; i++) {
spin[i]->set_min(p_min);
@@ -2313,9 +2250,6 @@ void EditorPropertyTransform2D::_notification(int p_what) {
}
}
-void EditorPropertyTransform2D::_bind_methods() {
-}
-
void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 6; i++) {
spin[i]->set_min(p_min);
@@ -2398,9 +2332,6 @@ void EditorPropertyBasis::_notification(int p_what) {
}
}
-void EditorPropertyBasis::_bind_methods() {
-}
-
void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 9; i++) {
spin[i]->set_min(p_min);
@@ -2490,9 +2421,6 @@ void EditorPropertyTransform3D::_notification(int p_what) {
}
}
-void EditorPropertyTransform3D::_bind_methods() {
-}
-
void EditorPropertyTransform3D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 12; i++) {
spin[i]->set_min(p_min);
@@ -2590,9 +2518,6 @@ void EditorPropertyProjection::_notification(int p_what) {
}
}
-void EditorPropertyProjection::_bind_methods() {
-}
-
void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 16; i++) {
spin[i]->set_min(p_min);
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index e9e788ab7b..2ec78cdb44 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -66,7 +66,6 @@ class EditorPropertyText : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void set_string_name(bool p_enabled);
@@ -92,7 +91,6 @@ class EditorPropertyMultilineText : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -126,7 +124,6 @@ class EditorPropertyTextEnum : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -153,7 +150,6 @@ class EditorPropertyPath : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -174,7 +170,6 @@ class EditorPropertyLocale : public EditorProperty {
void _locale_focus_exited();
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -196,7 +191,6 @@ private:
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(const String &p_base_type, const String &p_selected_type);
@@ -212,7 +206,6 @@ class EditorPropertyCheck : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -227,7 +220,6 @@ class EditorPropertyEnum : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(const Vector<String> &p_options);
@@ -246,7 +238,6 @@ class EditorPropertyFlags : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(const Vector<String> &p_options);
@@ -327,7 +318,6 @@ private:
protected:
void _notification(int p_what);
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(LayerType p_layer_type);
@@ -344,7 +334,6 @@ class EditorPropertyInteger : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -360,7 +349,6 @@ class EditorPropertyObjectID : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -374,9 +362,6 @@ class EditorPropertySignal : public EditorProperty {
String base_type;
void _edit_pressed();
-protected:
- static void _bind_methods();
-
public:
virtual void update_property() override;
EditorPropertySignal();
@@ -387,9 +372,6 @@ class EditorPropertyCallable : public EditorProperty {
Button *edit = nullptr;
String base_type;
-protected:
- static void _bind_methods();
-
public:
virtual void update_property() override;
EditorPropertyCallable();
@@ -403,7 +385,6 @@ class EditorPropertyFloat : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -445,7 +426,6 @@ class EditorPropertyEasing : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -461,7 +441,6 @@ class EditorPropertyRect2 : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -477,7 +456,6 @@ class EditorPropertyRect2i : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -493,7 +471,6 @@ class EditorPropertyPlane : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -526,7 +503,6 @@ class EditorPropertyQuaternion : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -542,7 +518,6 @@ class EditorPropertyAABB : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -558,7 +533,6 @@ class EditorPropertyTransform2D : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -574,7 +548,6 @@ class EditorPropertyBasis : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -590,7 +563,6 @@ class EditorPropertyTransform3D : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -607,7 +579,6 @@ class EditorPropertyProjection : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 9aa2390230..d58d0520cc 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -836,9 +836,6 @@ bool EditorPropertyArray::is_colored(ColorationMode p_mode) {
return p_mode == COLORATION_CONTAINER_RESOURCE;
}
-void EditorPropertyArray::_bind_methods() {
-}
-
EditorPropertyArray::EditorPropertyArray() {
object.instantiate();
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
@@ -1166,9 +1163,6 @@ void EditorPropertyDictionary::_page_changed(int p_page) {
update_property();
}
-void EditorPropertyDictionary::_bind_methods() {
-}
-
bool EditorPropertyDictionary::is_colored(ColorationMode p_mode) {
return p_mode == COLORATION_CONTAINER_RESOURCE;
}
@@ -1385,9 +1379,6 @@ void EditorPropertyLocalizableString::_page_changed(int p_page) {
update_property();
}
-void EditorPropertyLocalizableString::_bind_methods() {
-}
-
EditorPropertyLocalizableString::EditorPropertyLocalizableString() {
object.instantiate();
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index acde766754..024c04956f 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -150,7 +150,6 @@ protected:
bool updating = false;
bool dropping = false;
- static void _bind_methods();
void _notification(int p_what);
virtual void _add_element();
@@ -234,7 +233,6 @@ class EditorPropertyDictionary : public EditorProperty {
void _object_id_selected(const StringName &p_property, ObjectID p_id);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -271,7 +269,6 @@ class EditorPropertyLocalizableString : public EditorProperty {
void _object_id_selected(const StringName &p_property, ObjectID p_id);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index f23cab676c..7b45ba6a9f 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -30,7 +30,7 @@
#include "editor_property_name_processor.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor_settings.h"
EditorPropertyNameProcessor *EditorPropertyNameProcessor::singleton = nullptr;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 0956d12236..c3a939c007 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -43,7 +43,7 @@
#include "core/object/class_db.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/version.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
@@ -440,6 +440,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
#endif
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_subpixel_positioning", 1, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/font_disable_embedded_bitmaps", true, "");
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/font_allow_msdf", true, "")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font_bold", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp
index 77154ec344..4654d41082 100644
--- a/editor/editor_translation.cpp
+++ b/editor/editor_translation.cpp
@@ -33,6 +33,7 @@
#include "core/io/compression.h"
#include "core/io/file_access_memory.h"
#include "core/io/translation_loader_po.h"
+#include "core/string/translation_server.h"
#include "editor/doc_translations.gen.h"
#include "editor/editor_translations.gen.h"
#include "editor/extractable_translations.gen.h"
diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp
index a6f6c87ee1..0caf0ee066 100644
--- a/editor/export/export_template_manager.cpp
+++ b/editor/export/export_template_manager.cpp
@@ -859,9 +859,6 @@ void ExportTemplateManager::_notification(int p_what) {
}
}
-void ExportTemplateManager::_bind_methods() {
-}
-
ExportTemplateManager::ExportTemplateManager() {
set_title(TTR("Export Template Manager"));
set_hide_on_ok(false);
diff --git a/editor/export/export_template_manager.h b/editor/export/export_template_manager.h
index a00d874580..b1c5855878 100644
--- a/editor/export/export_template_manager.h
+++ b/editor/export/export_template_manager.h
@@ -119,7 +119,6 @@ class ExportTemplateManager : public AcceptDialog {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
static String get_android_build_directory(const Ref<EditorExportPreset> &p_preset);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index d4bd97a393..b780d42119 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -953,7 +953,8 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->set_max_columns(1);
files->set_max_text_lines(1);
files->set_fixed_column_width(0);
- files->set_fixed_icon_size(Size2());
+ const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
+ files->set_fixed_icon_size(Size2(icon_size, icon_size));
}
Ref<Texture2D> folder_icon = (use_thumbnails) ? folder_thumbnail : get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
diff --git a/editor/gui/editor_object_selector.cpp b/editor/gui/editor_object_selector.cpp
index 5b303760b0..bfd7e18de1 100644
--- a/editor/gui/editor_object_selector.cpp
+++ b/editor/gui/editor_object_selector.cpp
@@ -216,9 +216,6 @@ void EditorObjectSelector::_notification(int p_what) {
}
}
-void EditorObjectSelector::_bind_methods() {
-}
-
EditorObjectSelector::EditorObjectSelector(EditorSelectionHistory *p_history) {
history = p_history;
diff --git a/editor/gui/editor_object_selector.h b/editor/gui/editor_object_selector.h
index 72ff285cf6..60d6df488b 100644
--- a/editor/gui/editor_object_selector.h
+++ b/editor/gui/editor_object_selector.h
@@ -58,7 +58,6 @@ class EditorObjectSelector : public Button {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual Size2 get_minimum_size() const override;
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 4e8d6d63bf..9f9bdb37b3 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -614,13 +614,13 @@ void EditorSpinSlider::_value_focus_exited() {
// -> TAB was pressed
// -> modal_close was not called
// -> need to close/hide manually
- if (value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
+ if (!is_visible_in_tree() || value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
+ // Hidden or something else took focus.
if (value_input_popup) {
value_input_popup->hide();
}
- //tab was pressed
} else {
- //enter, click, esc
+ // Enter or Esc was pressed.
grab_focus();
}
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 6b741b7dd7..ed3eaa94c1 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -117,7 +117,9 @@ class SceneImportSettingsData : public Object {
ERR_FAIL_NULL(settings);
if (r_option.name == "rest_pose/load_pose") {
if (!settings->has("rest_pose/load_pose") || int((*settings)["rest_pose/load_pose"]) != 2) {
- (*settings)["rest_pose/external_animation_library"] = Variant();
+ if (settings->has("rest_pose/external_animation_library")) {
+ (*settings)["rest_pose/external_animation_library"] = Variant();
+ }
}
}
if (r_option.name == "rest_pose/selected_animation") {
@@ -134,7 +136,10 @@ class SceneImportSettingsData : public Object {
}
} break;
case 2: {
- Object *res = (*settings)["rest_pose/external_animation_library"];
+ Object *res = nullptr;
+ if (settings->has("rest_pose/external_animation_library")) {
+ res = (*settings)["rest_pose/external_animation_library"];
+ }
Ref<Animation> anim(res);
Ref<AnimationLibrary> library(res);
if (anim.is_valid()) {
diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp
index 00ce3d6a7a..590e3a9ede 100644
--- a/editor/import/dynamic_font_import_settings.cpp
+++ b/editor/import/dynamic_font_import_settings.cpp
@@ -31,6 +31,7 @@
#include "dynamic_font_import_settings.h"
#include "core/config/project_settings.h"
+#include "core/string/translation_server.h"
#include "editor/editor_file_system.h"
#include "editor/editor_inspector.h"
#include "editor/editor_locale_dialog.h"
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
index d2705ac98a..c181011402 100644
--- a/editor/import/resource_importer_csv_translation.cpp
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -33,7 +33,7 @@
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
#include "core/string/optimized_translation.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
String ResourceImporterCSVTranslation::get_importer_name() const {
return "csv_translation";
diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp
index c52f53146e..fa222b2790 100644
--- a/editor/import/resource_importer_dynamic_font.cpp
+++ b/editor/import/resource_importer_dynamic_font.cpp
@@ -118,7 +118,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel,Auto (Except Pixel Fonts)"), 4));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Fallbacks", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
@@ -176,11 +176,44 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
font->set_fixed_size(0);
font->set_force_autohinter(autohinter);
font->set_allow_system_fallback(allow_system_fallback);
- font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
font->set_hinting((TextServer::Hinting)hinting);
font->set_oversampling(oversampling);
font->set_fallbacks(fallbacks);
+ if (subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) {
+ PackedInt32Array glyphs = TS->font_get_supported_glyphs(font->get_rids()[0]);
+ bool is_pixel = true;
+ for (int32_t gl : glyphs) {
+ Dictionary ct = TS->font_get_glyph_contours(font->get_rids()[0], 16, gl);
+ PackedInt32Array contours = ct["contours"];
+ PackedVector3Array points = ct["points"];
+ int prev_start = 0;
+ for (int i = 0; i < contours.size(); i++) {
+ for (int j = prev_start; j <= contours[i]; j++) {
+ int next_point = (j < contours[i]) ? (j + 1) : prev_start;
+ if ((points[j].z != TextServer::CONTOUR_CURVE_TAG_ON) || (!Math::is_equal_approx(points[j].x, points[next_point].x) && !Math::is_equal_approx(points[j].y, points[next_point].y))) {
+ is_pixel = false;
+ break;
+ }
+ }
+ prev_start = contours[i] + 1;
+ if (!is_pixel) {
+ break;
+ }
+ }
+ if (!is_pixel) {
+ break;
+ }
+ }
+ if (is_pixel && !glyphs.is_empty()) {
+ print_line(vformat("%s: Pixel font detected, disabling subpixel positioning.", p_source_file));
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
+ } else {
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ }
+ }
+ font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
+
Dictionary langs = p_options["language_support"];
for (int i = 0; i < langs.size(); i++) {
String key = langs.get_key_at_index(i);
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index 11d1b3e089..6bc3a27a95 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -31,7 +31,7 @@
#include "localization_editor.h"
#include "core/config/project_settings.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h"
diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp
index 0c79e5d747..0da8d8291f 100644
--- a/editor/node_dock.cpp
+++ b/editor/node_dock.cpp
@@ -48,9 +48,6 @@ void NodeDock::show_connections() {
connections->show();
}
-void NodeDock::_bind_methods() {
-}
-
void NodeDock::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
diff --git a/editor/node_dock.h b/editor/node_dock.h
index e9dcc41d48..adf378bd06 100644
--- a/editor/node_dock.h
+++ b/editor/node_dock.h
@@ -55,7 +55,6 @@ public:
static NodeDock *get_singleton() { return singleton; }
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index f73c494b25..60d808952e 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -620,9 +620,6 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) {
canvas_item_editor->update_viewport();
}
-void AbstractPolygon2DEditor::_bind_methods() {
-}
-
void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Vector<Vector2> vertices = _get_polygon(p_vertex.polygon);
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 42170d9ffd..66d4e1b7ef 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -109,7 +109,6 @@ protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
- static void _bind_methods();
void remove_point(const Vertex &p_vertex);
Vertex get_active_point() const;
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index 757d410b78..7906b50c2c 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -199,9 +199,6 @@ void AnimationTreeEditor::_notification(int p_what) {
}
}
-void AnimationTreeEditor::_bind_methods() {
-}
-
AnimationTreeEditor *AnimationTreeEditor::singleton = nullptr;
void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) {
diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h
index 8dc820695a..a234af6874 100644
--- a/editor/plugins/animation_tree_editor_plugin.h
+++ b/editor/plugins/animation_tree_editor_plugin.h
@@ -72,7 +72,6 @@ class AnimationTreeEditor : public VBoxContainer {
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
- static void _bind_methods();
static AnimationTreeEditor *singleton;
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index c00436b01f..015dfdbca5 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -252,9 +252,6 @@ StringName BonePicker::get_selected_bone() {
return selected->get_text(0);
}
-void BonePicker::_bind_methods() {
-}
-
void BonePicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h
index f3aa2fc84d..988ad40ec4 100644
--- a/editor/plugins/bone_map_editor_plugin.h
+++ b/editor/plugins/bone_map_editor_plugin.h
@@ -122,8 +122,6 @@ public:
protected:
void _notification(int p_what);
- static void _bind_methods();
-
void _confirm();
private:
diff --git a/editor/plugins/camera_3d_editor_plugin.cpp b/editor/plugins/camera_3d_editor_plugin.cpp
index 62b40043c1..f4116ed364 100644
--- a/editor/plugins/camera_3d_editor_plugin.cpp
+++ b/editor/plugins/camera_3d_editor_plugin.cpp
@@ -46,9 +46,6 @@ void Camera3DEditor::_pressed() {
Node3DEditor::get_singleton()->set_custom_camera(sn);
}
-void Camera3DEditor::_bind_methods() {
-}
-
void Camera3DEditor::edit(Node *p_camera) {
node = p_camera;
diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h
index 2e4d8a1ee3..1c6838aa02 100644
--- a/editor/plugins/camera_3d_editor_plugin.h
+++ b/editor/plugins/camera_3d_editor_plugin.h
@@ -45,7 +45,6 @@ class Camera3DEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(Node *p_camera);
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index 92c0fd847b..4869a202d7 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -258,9 +258,6 @@ void CPUParticles2DEditorPlugin::_notification(int p_what) {
}
}
-void CPUParticles2DEditorPlugin::_bind_methods() {
-}
-
CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() {
particles = nullptr;
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h
index 4d59c9981e..645e6d8345 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.h
@@ -79,7 +79,6 @@ class CPUParticles2DEditorPlugin : public EditorPlugin {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual String get_name() const override { return "CPUParticles2D"; }
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
index be2847d1d7..69f287c134 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
@@ -157,9 +157,6 @@ void CPUParticles3DEditor::_generate_emission_points() {
}
}
-void CPUParticles3DEditor::_bind_methods() {
-}
-
CPUParticles3DEditor::CPUParticles3DEditor() {
particles_editor_hb = memnew(HBoxContainer);
Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb);
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h
index 99178b7fde..2a1ea93ac8 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.h
@@ -60,7 +60,6 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase {
protected:
void _notification(int p_notification);
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(CPUParticles3D *p_particles);
diff --git a/editor/plugins/editor_plugin_settings.cpp b/editor/plugins/editor_plugin_settings.cpp
index afc60b09d6..5949bc141f 100644
--- a/editor/plugins/editor_plugin_settings.cpp
+++ b/editor/plugins/editor_plugin_settings.cpp
@@ -199,9 +199,6 @@ Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) {
return plugins;
}
-void EditorPluginSettings::_bind_methods() {
-}
-
EditorPluginSettings::EditorPluginSettings() {
ProjectSettings::get_singleton()->add_hidden_prefix("editor_plugins/");
diff --git a/editor/plugins/editor_plugin_settings.h b/editor/plugins/editor_plugin_settings.h
index 5b470b3e58..ea7d0ea8b3 100644
--- a/editor/plugins/editor_plugin_settings.h
+++ b/editor/plugins/editor_plugin_settings.h
@@ -67,8 +67,6 @@ class EditorPluginSettings : public VBoxContainer {
protected:
void _notification(int p_what);
- static void _bind_methods();
-
public:
void update_plugins();
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index e6ce63fe36..d712c14861 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -30,6 +30,7 @@
#include "font_config_plugin.h"
+#include "core/string/translation_server.h"
#include "editor/editor_settings.h"
#include "editor/import/dynamic_font_import_settings.h"
#include "editor/themes/editor_scale.h"
@@ -63,9 +64,6 @@ bool EditorPropertyFontMetaObject::_get(const StringName &p_name, Variant &r_ret
return false;
}
-void EditorPropertyFontMetaObject::_bind_methods() {
-}
-
void EditorPropertyFontMetaObject::set_dict(const Dictionary &p_dict) {
dict = p_dict;
}
diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h
index 4e798fc3e8..e83f29a77b 100644
--- a/editor/plugins/font_config_plugin.h
+++ b/editor/plugins/font_config_plugin.h
@@ -46,7 +46,6 @@ class EditorPropertyFontMetaObject : public RefCounted {
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
- static void _bind_methods();
public:
void set_dict(const Dictionary &p_dict);
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index b70c34b785..1b68b55ff6 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -360,9 +360,6 @@ void GPUParticles2DEditorPlugin::_notification(int p_what) {
}
}
-void GPUParticles2DEditorPlugin::_bind_methods() {
-}
-
GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() {
particles = nullptr;
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index bb0ca5de3a..658e4d87e5 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -86,7 +86,6 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual String get_name() const override { return "GPUParticles2D"; }
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 00c5332464..4e9be0aa53 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -198,9 +198,6 @@ void GPUParticles3DEditorBase::_node_selected(const NodePath &p_path) {
emission_dialog->popup_centered(Size2(300, 130));
}
-void GPUParticles3DEditorBase::_bind_methods() {
-}
-
GPUParticles3DEditorBase::GPUParticles3DEditorBase() {
emission_dialog = memnew(ConfirmationDialog);
emission_dialog->set_title(TTR("Create Emitter"));
@@ -402,9 +399,6 @@ void GPUParticles3DEditor::_generate_emission_points() {
}
}
-void GPUParticles3DEditor::_bind_methods() {
-}
-
GPUParticles3DEditor::GPUParticles3DEditor() {
node = nullptr;
particles_editor_hb = memnew(HBoxContainer);
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h
index 3b2ab2f8ca..5f59f6dca7 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.h
@@ -62,8 +62,6 @@ protected:
virtual void _generate_emission_points(){};
void _node_selected(const NodePath &p_path);
- static void _bind_methods();
-
public:
GPUParticles3DEditorBase();
};
@@ -95,7 +93,6 @@ class GPUParticles3DEditor : public GPUParticles3DEditorBase {
protected:
void _notification(int p_notification);
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(GPUParticles3D *p_particles);
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
index 25b076d0e5..c21a1b5dd6 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -175,9 +175,6 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake(const Strin
}
}
-void GPUParticlesCollisionSDF3DEditorPlugin::_bind_methods() {
-}
-
GPUParticlesCollisionSDF3DEditorPlugin::GPUParticlesCollisionSDF3DEditorPlugin() {
bake_hb = memnew(HBoxContainer);
bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
index bba8bd2584..3e1dae77ea 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -58,7 +58,6 @@ class GPUParticlesCollisionSDF3DEditorPlugin : public EditorPlugin {
void _sdf_save_path_and_bake(const String &p_path);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp
index 5a90d2de61..30debfc14f 100644
--- a/editor/plugins/input_event_editor_plugin.cpp
+++ b/editor/plugins/input_event_editor_plugin.cpp
@@ -33,9 +33,6 @@
#include "editor/event_listener_line_edit.h"
#include "editor/input_event_configuration_dialog.h"
-void InputEventConfigContainer::_bind_methods() {
-}
-
void InputEventConfigContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
diff --git a/editor/plugins/input_event_editor_plugin.h b/editor/plugins/input_event_editor_plugin.h
index 48c16a4807..276bb74f6b 100644
--- a/editor/plugins/input_event_editor_plugin.h
+++ b/editor/plugins/input_event_editor_plugin.h
@@ -51,7 +51,6 @@ class InputEventConfigContainer : public VBoxContainer {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
void set_event(const Ref<InputEvent> &p_event);
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index 0b2549986c..58cc670475 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -242,9 +242,6 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
}
}
-void MeshLibraryEditor::_bind_methods() {
-}
-
MeshLibraryEditor::MeshLibraryEditor() {
file = memnew(EditorFileDialog);
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h
index 94f46beea1..5a9e32178d 100644
--- a/editor/plugins/mesh_library_editor_plugin.h
+++ b/editor/plugins/mesh_library_editor_plugin.h
@@ -68,9 +68,6 @@ class MeshLibraryEditor : public Control {
static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
static void _import_scene_parse_node(Ref<MeshLibrary> p_library, HashMap<int, MeshInstance3D *> &p_mesh_instances, Node *p_node, bool p_merge, bool p_apply_xforms);
-protected:
- static void _bind_methods();
-
public:
MenuButton *get_menu_button() const { return menu; }
diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp
index 3980f23c8a..76ffdb02e1 100644
--- a/editor/plugins/multimesh_editor_plugin.cpp
+++ b/editor/plugins/multimesh_editor_plugin.cpp
@@ -266,9 +266,6 @@ void MultiMeshEditor::_browse(bool p_source) {
std->popup_scenetree_dialog(browsed_node);
}
-void MultiMeshEditor::_bind_methods() {
-}
-
MultiMeshEditor::MultiMeshEditor() {
options = memnew(MenuButton);
options->set_switch_on_hover(true);
diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h
index 5051926c64..3fe63bfa92 100644
--- a/editor/plugins/multimesh_editor_plugin.h
+++ b/editor/plugins/multimesh_editor_plugin.h
@@ -79,7 +79,6 @@ class MultiMeshEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(MultiMeshInstance3D *p_multimesh);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 80d2e85830..1df9dae767 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1064,21 +1064,23 @@ void Node3DEditorViewport::_select_region() {
if (found_nodes.has(sp)) {
continue;
}
-
found_nodes.insert(sp);
Node *node = Object::cast_to<Node>(sp);
+
+ // Selection requires that the node is the edited scene or its descendant, and has an owner.
if (node != edited_scene) {
+ if (!node->get_owner() || !edited_scene->is_ancestor_of(node)) {
+ continue;
+ }
node = edited_scene->get_deepest_editable_node(node);
- }
-
- // Prevent selection of nodes not owned by the edited scene.
- while (node && node != edited_scene->get_parent()) {
- Node *node_owner = node->get_owner();
- if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
- break;
+ while (node != edited_scene) {
+ Node *node_owner = node->get_owner();
+ if (node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ break;
+ }
+ node = node->get_parent();
}
- node = node->get_parent();
}
// Replace the node by the group if grouped
@@ -7315,9 +7317,9 @@ void Node3DEditor::_init_grid() {
int primary_grid_steps = EDITOR_GET("editors/3d/primary_grid_steps");
// Which grid planes are enabled? Which should we generate?
- grid_enable[0] = grid_visible[0] = EDITOR_GET("editors/3d/grid_xy_plane");
- grid_enable[1] = grid_visible[1] = EDITOR_GET("editors/3d/grid_yz_plane");
- grid_enable[2] = grid_visible[2] = EDITOR_GET("editors/3d/grid_xz_plane");
+ grid_enable[0] = grid_visible[0] = orthogonal || EDITOR_GET("editors/3d/grid_xy_plane");
+ grid_enable[1] = grid_visible[1] = orthogonal || EDITOR_GET("editors/3d/grid_yz_plane");
+ grid_enable[2] = grid_visible[2] = orthogonal || EDITOR_GET("editors/3d/grid_xz_plane");
// Offsets division_level for bigger or smaller grids.
// Default value is -0.2. -1.0 gives Blender-like behavior, 0.5 gives huge grids.
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp
index e9ddaeb3fe..b38965753e 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.cpp
+++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp
@@ -83,9 +83,8 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
int idx_last = atr_owners.size() - 1;
if (idx_last > 0 && !parent_path.begins_with(atr_owners[idx_last].first)) {
- // Switch to the previous auto translation owner this was nested in, if that was the case.
+ // Exit from the current owner nesting into the previous one.
atr_owners.remove_at(idx_last);
- idx_last -= 1;
}
if (property == "auto_translate_mode") {
@@ -106,7 +105,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
// If `auto_translate_mode` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`).
if (!auto_translate_mode_found) {
int idx_last = atr_owners.size() - 1;
- if (idx_last > 0 && atr_owners[idx_last].first == parent_path) {
+ if (idx_last > 0 && parent_path.begins_with(atr_owners[idx_last].first)) {
auto_translating = atr_owners[idx_last].second;
} else {
atr_owners.push_back(Pair(state->get_node_path(i), true));
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp
index b7c12ab5c0..d7e9701452 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.cpp
+++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp
@@ -36,9 +36,6 @@
#include "scene/3d/physics/physical_bone_3d.h"
#include "scene/gui/separator.h"
-void PhysicalBone3DEditor::_bind_methods() {
-}
-
void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed) {
_set_move_joint();
}
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.h b/editor/plugins/physical_bone_3d_editor_plugin.h
index fb6f30cc57..b057644bb1 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.h
+++ b/editor/plugins/physical_bone_3d_editor_plugin.h
@@ -45,9 +45,6 @@ class PhysicalBone3DEditor : public Object {
PhysicalBone3D *selected = nullptr;
-protected:
- static void _bind_methods();
-
private:
void _on_toggle_button_transform_joint(bool p_is_pressed);
void _set_move_joint();
diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp
index 8b31aa92b3..cd422fc291 100644
--- a/editor/plugins/root_motion_editor_plugin.cpp
+++ b/editor/plugins/root_motion_editor_plugin.cpp
@@ -193,9 +193,6 @@ void EditorPropertyRootMotion::_notification(int p_what) {
}
}
-void EditorPropertyRootMotion::_bind_methods() {
-}
-
EditorPropertyRootMotion::EditorPropertyRootMotion() {
HBoxContainer *hbc = memnew(HBoxContainer);
add_child(hbc);
diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h
index 5befdb6006..a3e90360ae 100644
--- a/editor/plugins/root_motion_editor_plugin.h
+++ b/editor/plugins/root_motion_editor_plugin.h
@@ -52,7 +52,6 @@ class EditorPropertyRootMotion : public EditorProperty {
void _node_clear();
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp
index 05919fb0f6..d732d51f69 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -218,9 +218,6 @@ void ShaderFileEditor::_editor_settings_changed() {
}
}
-void ShaderFileEditor::_bind_methods() {
-}
-
void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) {
if (p_shader.is_null()) {
if (shader_file.is_valid()) {
diff --git a/editor/plugins/shader_file_editor_plugin.h b/editor/plugins/shader_file_editor_plugin.h
index 9a915513ef..fea770b7e0 100644
--- a/editor/plugins/shader_file_editor_plugin.h
+++ b/editor/plugins/shader_file_editor_plugin.h
@@ -62,7 +62,6 @@ class ShaderFileEditor : public PanelContainer {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
static ShaderFileEditor *singleton;
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index 8308fe6d6e..97c5c0c7dd 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -90,9 +90,6 @@ void Skeleton2DEditor::_menu_option(int p_option) {
}
}
-void Skeleton2DEditor::_bind_methods() {
-}
-
Skeleton2DEditor::Skeleton2DEditor() {
options = memnew(MenuButton);
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
index 74fd59f1c4..89e2e5d0f2 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.h
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -57,7 +57,6 @@ class Skeleton2DEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(Skeleton2D *p_sprite);
diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp
index 1753df5cb7..9b98b6ffa2 100644
--- a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp
@@ -73,9 +73,6 @@ void SkeletonIK3DEditorPlugin::make_visible(bool p_visible) {
}
}
-void SkeletonIK3DEditorPlugin::_bind_methods() {
-}
-
SkeletonIK3DEditorPlugin::SkeletonIK3DEditorPlugin() {
play_btn = memnew(Button);
play_btn->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Play"), EditorStringName(EditorIcons)));
diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h
index 2ef5467263..95ab3696be 100644
--- a/editor/plugins/skeleton_ik_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_ik_3d_editor_plugin.h
@@ -45,9 +45,6 @@ class SkeletonIK3DEditorPlugin : public EditorPlugin {
void _play();
-protected:
- static void _bind_methods();
-
public:
virtual String get_name() const override { return "SkeletonIK3D"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 3942baed8a..b806d1e042 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -372,7 +372,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
- if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) {
+ if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Color color = grid_color;
if (frame > 0) {
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index f985bbc629..af52243c41 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -165,10 +165,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
base_control->draw_set_transform_matrix(xform);
// Draw fill rect under texture region.
- Rect2 texture_rect(-background_region.size / 2, background_region.size);
+ Rect2 texture_rect(Vector2(), background_region.size);
if (tile_data) {
texture_rect.position -= tile_data->get_texture_origin();
+ if (tile_data->get_transpose()) {
+ texture_rect.size = Size2(texture_rect.size.y, texture_rect.size.x);
+ }
}
+ texture_rect.position -= texture_rect.size / 2; // Half-size offset must be applied after transposing.
base_control->draw_rect(texture_rect, Color(1, 1, 1, 0.3));
// Draw the background.
@@ -180,18 +184,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
if (tile_data->get_flip_v()) {
region_size.y = -region_size.y;
}
- base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(-background_region.size / 2 - tile_data->get_texture_origin(), region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose());
+ // Destination rect position must account for transposing, size must not.
+ base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(texture_rect.position, region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose());
}
// Compute and draw the grid area.
Rect2 grid_area = Rect2(-base_tile_size / 2, base_tile_size);
- if (tile_data) {
- grid_area.expand_to(-background_region.get_size() / 2 - tile_data->get_texture_origin());
- grid_area.expand_to(background_region.get_size() / 2 - tile_data->get_texture_origin());
- } else {
- grid_area.expand_to(-background_region.get_size() / 2);
- grid_area.expand_to(background_region.get_size() / 2);
- }
+ grid_area.expand_to(texture_rect.position);
+ grid_area.expand_to(texture_rect.get_end());
base_control->draw_rect(grid_area, Color(1, 1, 1, 0.3), false);
// Draw grid.
@@ -523,6 +523,21 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
xform.set_origin(base_control->get_size() / 2 + panning);
xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom()));
+ Ref<InputEventPanGesture> pan_gesture = p_event;
+ if (pan_gesture.is_valid()) {
+ panning += pan_gesture->get_delta() * 8;
+ drag_last_pos = Vector2();
+ button_center_view->set_disabled(panning.is_zero_approx());
+ accept_event();
+ }
+
+ Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
+ if (magnify_gesture.is_valid()) {
+ editor_zoom_widget->set_zoom(editor_zoom_widget->get_zoom() * magnify_gesture->get_factor());
+ _zoom_changed();
+ accept_event();
+ }
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (drag_type == DRAG_TYPE_DRAG_POINT) {
@@ -1385,10 +1400,8 @@ void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran
TileSetSource *source = *(tile_set->get_source(p_cell.source_id));
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, -tile_set_tile_size / 2) && atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, tile_set_tile_size / 2 - Vector2(1, 1))) {
- Transform2D tile_xform;
- tile_xform.set_scale(tile_set_tile_size);
- tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color);
+ if (atlas_source->is_rect_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Rect2(Vector2(-tile_set_tile_size) / 2, tile_set_tile_size))) {
+ tile_set->draw_tile_shape(p_canvas_item, p_transform.scaled_local(tile_set_tile_size), color);
}
if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) {
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index b76e673414..63a54372b5 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -2368,7 +2368,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
tiles_bottom_panel->set_name(TTR("Tiles"));
missing_source_label = memnew(Label);
- missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Go to the TileSet bottom panel to add one."));
+ missing_source_label->set_text(TTR("This TileMap's TileSet has no Tile Source configured. Go to the TileSet bottom panel to add one."));
missing_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
missing_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
missing_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 263e9cfa3b..7e5336ce06 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -818,7 +818,7 @@ TileSetEditor::TileSetEditor() {
tabs_bar = memnew(TabBar);
tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
tabs_bar->set_clip_tabs(false);
- tabs_bar->add_tab(TTR("Tiles"));
+ tabs_bar->add_tab(TTR("Tile Sources"));
tabs_bar->add_tab(TTR("Patterns"));
tabs_bar->connect("tab_changed", callable_mp(this, &TileSetEditor::_tab_changed));
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index f4ce35f8f2..b9b9a51735 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -7652,9 +7652,6 @@ void EditorPropertyVisualShaderMode::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
-void EditorPropertyVisualShaderMode::_bind_methods() {
-}
-
EditorPropertyVisualShaderMode::EditorPropertyVisualShaderMode() {
options = memnew(OptionButton);
options->set_clip_text(true);
@@ -7786,9 +7783,6 @@ void VisualShaderNodePortPreview::_notification(int p_what) {
}
}
-void VisualShaderNodePortPreview::_bind_methods() {
-}
-
//////////////////////////////////
String VisualShaderConversionPlugin::converts_to() const {
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index a9826fd617..75c9d97b7c 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -630,9 +630,6 @@ class EditorPropertyVisualShaderMode : public EditorProperty {
void _option_selected(int p_which);
-protected:
- static void _bind_methods();
-
public:
void setup(const Vector<String> &p_options);
virtual void update_property() override;
@@ -658,7 +655,6 @@ class VisualShaderNodePortPreview : public Control {
void _shader_changed(); //must regen
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual Size2 get_minimum_size() const override;
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index e7b2435567..3e835d5cb6 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -177,9 +177,6 @@ void VoxelGIEditorPlugin::_voxel_gi_save_path_and_bake(const String &p_path) {
}
}
-void VoxelGIEditorPlugin::_bind_methods() {
-}
-
VoxelGIEditorPlugin::VoxelGIEditorPlugin() {
bake_hb = memnew(HBoxContainer);
bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h
index 58ef22ddc6..d09822dda6 100644
--- a/editor/plugins/voxel_gi_editor_plugin.h
+++ b/editor/plugins/voxel_gi_editor_plugin.h
@@ -58,7 +58,6 @@ class VoxelGIEditorPlugin : public EditorPlugin {
void _voxel_gi_save_path_and_bake(const String &p_path);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/project_manager/quick_settings_dialog.cpp b/editor/project_manager/quick_settings_dialog.cpp
index a98d9073b0..4502b42fe6 100644
--- a/editor/project_manager/quick_settings_dialog.cpp
+++ b/editor/project_manager/quick_settings_dialog.cpp
@@ -31,7 +31,7 @@
#include "quick_settings_dialog.h"
#include "core/config/project_settings.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index 0394c9e249..d47270841d 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -123,50 +123,6 @@ void PropertySelector::_update_search() {
TreeItem *category = nullptr;
bool found = false;
-
- Ref<Texture2D> type_icons[] = {
- search_options->get_editor_theme_icon(SNAME("Variant")),
- search_options->get_editor_theme_icon(SNAME("bool")),
- search_options->get_editor_theme_icon(SNAME("int")),
- search_options->get_editor_theme_icon(SNAME("float")),
- search_options->get_editor_theme_icon(SNAME("String")),
- search_options->get_editor_theme_icon(SNAME("Vector2")),
- search_options->get_editor_theme_icon(SNAME("Vector2i")),
- search_options->get_editor_theme_icon(SNAME("Rect2")),
- search_options->get_editor_theme_icon(SNAME("Rect2i")),
- search_options->get_editor_theme_icon(SNAME("Vector3")),
- search_options->get_editor_theme_icon(SNAME("Vector3i")),
- search_options->get_editor_theme_icon(SNAME("Transform2D")),
- search_options->get_editor_theme_icon(SNAME("Vector4")),
- search_options->get_editor_theme_icon(SNAME("Vector4i")),
- search_options->get_editor_theme_icon(SNAME("Plane")),
- search_options->get_editor_theme_icon(SNAME("Quaternion")),
- search_options->get_editor_theme_icon(SNAME("AABB")),
- search_options->get_editor_theme_icon(SNAME("Basis")),
- search_options->get_editor_theme_icon(SNAME("Transform3D")),
- search_options->get_editor_theme_icon(SNAME("Projection")),
- search_options->get_editor_theme_icon(SNAME("Color")),
- search_options->get_editor_theme_icon(SNAME("StringName")),
- search_options->get_editor_theme_icon(SNAME("NodePath")),
- search_options->get_editor_theme_icon(SNAME("RID")),
- search_options->get_editor_theme_icon(SNAME("MiniObject")),
- search_options->get_editor_theme_icon(SNAME("Callable")),
- search_options->get_editor_theme_icon(SNAME("Signal")),
- search_options->get_editor_theme_icon(SNAME("Dictionary")),
- search_options->get_editor_theme_icon(SNAME("Array")),
- search_options->get_editor_theme_icon(SNAME("PackedByteArray")),
- search_options->get_editor_theme_icon(SNAME("PackedInt32Array")),
- search_options->get_editor_theme_icon(SNAME("PackedInt64Array")),
- search_options->get_editor_theme_icon(SNAME("PackedFloat32Array")),
- search_options->get_editor_theme_icon(SNAME("PackedFloat64Array")),
- search_options->get_editor_theme_icon(SNAME("PackedStringArray")),
- search_options->get_editor_theme_icon(SNAME("PackedVector2Array")),
- search_options->get_editor_theme_icon(SNAME("PackedVector3Array")),
- search_options->get_editor_theme_icon(SNAME("PackedColorArray")),
- search_options->get_editor_theme_icon(SNAME("PackedVector4Array")),
- };
- static_assert((sizeof(type_icons) / sizeof(type_icons[0])) == Variant::VARIANT_MAX, "Number of type icons doesn't match the number of Variant types.");
-
for (const PropertyInfo &E : props) {
if (E.usage == PROPERTY_USAGE_CATEGORY) {
if (category && category->get_first_child() == nullptr) {
@@ -194,14 +150,14 @@ void PropertySelector::_update_search() {
continue;
}
- if (type_filter.size() && !type_filter.has(E.type)) {
+ if (!type_filter.is_empty() && !type_filter.has(E.type)) {
continue;
}
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, E.name);
item->set_metadata(0, E.name);
- item->set_icon(0, type_icons[E.type]);
+ item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(E.type)));
if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) {
item->select(0);
@@ -209,6 +165,9 @@ void PropertySelector::_update_search() {
}
item->set_selectable(0, true);
+
+ _create_subproperties(item, E.type);
+ item->set_collapsed(true);
}
if (category && category->get_first_child() == nullptr) {
@@ -407,6 +366,133 @@ void PropertySelector::_hide_requested() {
_cancel_pressed(); // From AcceptDialog.
}
+void PropertySelector::_create_subproperties(TreeItem *p_parent_item, Variant::Type p_type) {
+ switch (p_type) {
+ case Variant::VECTOR2: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ } break;
+
+ case Variant::VECTOR2I: {
+ _create_subproperty(p_parent_item, "x", Variant::INT);
+ _create_subproperty(p_parent_item, "y", Variant::INT);
+ } break;
+
+ case Variant::RECT2: {
+ _create_subproperty(p_parent_item, "position", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "size", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "end", Variant::VECTOR2);
+ } break;
+
+ case Variant::RECT2I: {
+ _create_subproperty(p_parent_item, "position", Variant::VECTOR2I);
+ _create_subproperty(p_parent_item, "size", Variant::VECTOR2I);
+ _create_subproperty(p_parent_item, "end", Variant::VECTOR2I);
+ } break;
+
+ case Variant::VECTOR3: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ } break;
+
+ case Variant::VECTOR3I: {
+ _create_subproperty(p_parent_item, "x", Variant::INT);
+ _create_subproperty(p_parent_item, "y", Variant::INT);
+ _create_subproperty(p_parent_item, "z", Variant::INT);
+ } break;
+
+ case Variant::TRANSFORM2D: {
+ _create_subproperty(p_parent_item, "origin", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "x", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "y", Variant::VECTOR2);
+ } break;
+
+ case Variant::VECTOR4: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "w", Variant::FLOAT);
+ } break;
+
+ case Variant::VECTOR4I: {
+ _create_subproperty(p_parent_item, "x", Variant::INT);
+ _create_subproperty(p_parent_item, "y", Variant::INT);
+ _create_subproperty(p_parent_item, "z", Variant::INT);
+ _create_subproperty(p_parent_item, "w", Variant::INT);
+ } break;
+
+ case Variant::PLANE: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "normal", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "d", Variant::FLOAT);
+ } break;
+
+ case Variant::QUATERNION: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "w", Variant::FLOAT);
+ } break;
+
+ case Variant::AABB: {
+ _create_subproperty(p_parent_item, "position", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "size", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "end", Variant::VECTOR3);
+ } break;
+
+ case Variant::BASIS: {
+ _create_subproperty(p_parent_item, "x", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "y", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "z", Variant::VECTOR3);
+ } break;
+
+ case Variant::TRANSFORM3D: {
+ _create_subproperty(p_parent_item, "basis", Variant::BASIS);
+ _create_subproperty(p_parent_item, "origin", Variant::VECTOR3);
+ } break;
+
+ case Variant::PROJECTION: {
+ _create_subproperty(p_parent_item, "x", Variant::VECTOR4);
+ _create_subproperty(p_parent_item, "y", Variant::VECTOR4);
+ _create_subproperty(p_parent_item, "z", Variant::VECTOR4);
+ _create_subproperty(p_parent_item, "w", Variant::VECTOR4);
+ } break;
+
+ case Variant::COLOR: {
+ _create_subproperty(p_parent_item, "r", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "g", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "b", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "a", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "r8", Variant::INT);
+ _create_subproperty(p_parent_item, "g8", Variant::INT);
+ _create_subproperty(p_parent_item, "b8", Variant::INT);
+ _create_subproperty(p_parent_item, "a8", Variant::INT);
+ _create_subproperty(p_parent_item, "h", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "s", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "v", Variant::FLOAT);
+ } break;
+
+ default: {
+ }
+ }
+}
+
+void PropertySelector::_create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type) {
+ if (!type_filter.is_empty() && !type_filter.has(p_type)) {
+ return;
+ }
+
+ TreeItem *item = search_options->create_item(p_parent_item);
+ item->set_text(0, p_name);
+ item->set_metadata(0, String(p_parent_item->get_metadata(0)) + ":" + p_name);
+ item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(p_type)));
+
+ _create_subproperties(item, p_type);
+}
+
void PropertySelector::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -576,7 +662,6 @@ PropertySelector::PropertySelector() {
search_options->connect("item_activated", callable_mp(this, &PropertySelector::_confirmed));
search_options->connect("cell_selected", callable_mp(this, &PropertySelector::_item_selected));
search_options->set_hide_root(true);
- search_options->set_hide_folding(true);
help_bit = memnew(EditorHelpBit);
help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
diff --git a/editor/property_selector.h b/editor/property_selector.h
index b162ee6cfd..34cade9267 100644
--- a/editor/property_selector.h
+++ b/editor/property_selector.h
@@ -36,6 +36,7 @@
class EditorHelpBit;
class LineEdit;
class Tree;
+class TreeItem;
class PropertySelector : public ConfirmationDialog {
GDCLASS(PropertySelector, ConfirmationDialog);
@@ -62,6 +63,9 @@ class PropertySelector : public ConfirmationDialog {
Vector<Variant::Type> type_filter;
+ void _create_subproperties(TreeItem *p_parent_item, Variant::Type p_type);
+ void _create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp
index fc994a17d1..c50d1237e0 100644
--- a/editor/themes/editor_fonts.cpp
+++ b/editor/themes/editor_fonts.cpp
@@ -117,6 +117,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
int font_hinting_setting = (int)EDITOR_GET("interface/editor/font_hinting");
TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EDITOR_GET("interface/editor/font_subpixel_positioning");
bool font_disable_embedded_bitmaps = (bool)EDITOR_GET("interface/editor/font_disable_embedded_bitmaps");
+ bool font_allow_msdf = (bool)EDITOR_GET("interface/editor/font_allow_msdf");
TextServer::Hinting font_hinting;
TextServer::Hinting font_mono_hinting;
@@ -153,7 +154,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
const float embolden_strength = 0.6;
Ref<Font> default_font = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false);
- Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, true);
+ Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
TypedArray<Font> fallbacks;
Ref<FontFile> arabic_font = load_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks);
@@ -173,7 +174,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
default_font_msdf->set_fallbacks(fallbacks);
Ref<FontFile> default_font_bold = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false);
- Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, true);
+ Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
TypedArray<Font> fallbacks_bold;
Ref<FontFile> arabic_font_bold = load_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks_bold);
@@ -234,7 +235,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
Ref<FontVariation> default_fc_msdf;
default_fc_msdf.instantiate();
if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
- Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps);
+ Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
{
TypedArray<Font> fallback_custom;
fallback_custom.push_back(default_font_msdf);
@@ -277,7 +278,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
Ref<FontVariation> bold_fc_msdf;
bold_fc_msdf.instantiate();
if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
- Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps);
+ Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
{
TypedArray<Font> fallback_custom;
fallback_custom.push_back(default_font_bold_msdf);
@@ -285,7 +286,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
}
bold_fc_msdf->set_base_font(custom_font);
} else if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
- Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps);
+ Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
{
TypedArray<Font> fallback_custom;
fallback_custom.push_back(default_font_bold_msdf);
diff --git a/main/main.cpp b/main/main.cpp
index d0b58d9abd..20968d3971 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -49,7 +49,7 @@
#include "core/os/os.h"
#include "core/os/time.h"
#include "core/register_core_types.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/version.h"
#include "drivers/register_driver_types.h"
#include "main/app_icon.gen.h"
@@ -2537,7 +2537,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
if (p_second_phase) {
- return setup2();
+ exit_err = setup2();
+ if (exit_err != OK) {
+ goto error;
+ }
}
return OK;
@@ -2835,6 +2838,30 @@ Error Main::setup2(bool p_show_boot_logo) {
if (err != OK || display_server == nullptr) {
ERR_PRINT("Unable to create DisplayServer, all display drivers failed.\nUse \"--headless\" command line argument to run the engine in headless mode if this is desired (e.g. for continuous integration).");
+
+ if (display_server) {
+ memdelete(display_server);
+ }
+
+ GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
+ uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
+ unregister_server_types();
+
+ if (input) {
+ memdelete(input);
+ }
+ if (tsman) {
+ memdelete(tsman);
+ }
+#ifndef _3D_DISABLED
+ if (physics_server_3d_manager) {
+ memdelete(physics_server_3d_manager);
+ }
+#endif // _3D_DISABLED
+ if (physics_server_2d_manager) {
+ memdelete(physics_server_2d_manager);
+ }
+
return err;
}
@@ -3134,6 +3161,14 @@ Error Main::setup2(bool p_show_boot_logo) {
OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions");
}
+ PackedStringArray extensions;
+ extensions.push_back("gd");
+ if (ClassDB::class_exists("CSharpScript")) {
+ extensions.push_back("cs");
+ }
+ extensions.push_back("gdshader");
+ GLOBAL_DEF_NOVAL(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions); // Note: should be defined after Scene level modules init to see .NET.
+
OS::get_singleton()->benchmark_end_measure("Startup", "Scene");
#ifdef TOOLS_ENABLED
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 95ed462070..14887ce469 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -50,6 +50,7 @@ EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
return EtcpakType::ETCPAK_TYPE_ETC2;
case Image::USED_CHANNELS_RGBA:
return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
+
default:
return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
}
@@ -69,6 +70,7 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
return EtcpakType::ETCPAK_TYPE_DXT1;
case Image::USED_CHANNELS_RGBA:
return EtcpakType::ETCPAK_TYPE_DXT5;
+
default:
return EtcpakType::ETCPAK_TYPE_DXT5;
}
@@ -79,71 +81,86 @@ void _compress_etc1(Image *r_img) {
}
void _compress_etc2(Image *r_img, Image::UsedChannels p_channels) {
- EtcpakType type = _determine_etc_type(p_channels);
- _compress_etcpak(type, r_img);
+ _compress_etcpak(_determine_etc_type(p_channels), r_img);
}
void _compress_bc(Image *r_img, Image::UsedChannels p_channels) {
- EtcpakType type = _determine_dxt_type(p_channels);
- _compress_etcpak(type, r_img);
+ _compress_etcpak(_determine_dxt_type(p_channels), r_img);
}
-void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
+void _compress_etcpak(EtcpakType p_compress_type, Image *r_img) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
- Image::Format img_format = r_img->get_format();
- if (Image::is_format_compressed(img_format)) {
- return; // Do not compress, already compressed.
- }
- if (img_format > Image::FORMAT_RGBA8) {
- // TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually
+ // The image is already compressed, return.
+ if (r_img->is_compressed()) {
return;
}
- // Use RGBA8 to convert.
- if (img_format != Image::FORMAT_RGBA8) {
- r_img->convert(Image::FORMAT_RGBA8);
- }
+ // Convert to RGBA8 for compression.
+ r_img->convert(Image::FORMAT_RGBA8);
// Determine output format based on Etcpak type.
Image::Format target_format = Image::FORMAT_RGBA8;
- if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
- target_format = Image::FORMAT_ETC;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
- target_format = Image::FORMAT_ETC2_RGB8;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_R) {
- target_format = Image::FORMAT_ETC2_R11;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RG) {
- target_format = Image::FORMAT_ETC2_RG11;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
- target_format = Image::FORMAT_ETC2_RA_AS_RG;
- r_img->convert_rg_to_ra_rgba8();
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
- target_format = Image::FORMAT_ETC2_RGBA8;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
- target_format = Image::FORMAT_DXT1;
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
- target_format = Image::FORMAT_DXT5_RA_AS_RG;
- r_img->convert_rg_to_ra_rgba8();
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) {
- target_format = Image::FORMAT_DXT5;
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_R) {
- target_format = Image::FORMAT_RGTC_R;
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_RG) {
- target_format = Image::FORMAT_RGTC_RG;
- } else {
- ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT.");
+
+ switch (p_compress_type) {
+ case EtcpakType::ETCPAK_TYPE_ETC1:
+ target_format = Image::FORMAT_ETC;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2:
+ target_format = Image::FORMAT_ETC2_RGB8;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA:
+ target_format = Image::FORMAT_ETC2_RGBA8;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_R:
+ target_format = Image::FORMAT_ETC2_R11;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_RG:
+ target_format = Image::FORMAT_ETC2_RG11;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG:
+ target_format = Image::FORMAT_ETC2_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_DXT1:
+ target_format = Image::FORMAT_DXT1;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_DXT5:
+ target_format = Image::FORMAT_DXT5;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_RGTC_R:
+ target_format = Image::FORMAT_RGTC_R;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_RGTC_RG:
+ target_format = Image::FORMAT_RGTC_RG;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG:
+ target_format = Image::FORMAT_DXT5_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
+ break;
+
+ default:
+ ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT.");
+ break;
}
- // Compress image data and (if required) mipmaps.
+ // It's badly documented but ETCPAK seems to expect BGRA8 for ETC formats.
+ if (p_compress_type < EtcpakType::ETCPAK_TYPE_DXT1) {
+ r_img->convert_rgba8_to_bgra8();
+ }
- const bool mipmaps = r_img->has_mipmaps();
+ // Compress image data and (if required) mipmaps.
+ const bool has_mipmaps = r_img->has_mipmaps();
int width = r_img->get_width();
int height = r_img->get_height();
@@ -164,109 +181,115 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
are used for a 2x2 map, and texel 'a' is used for 1x1. Note that this is similar to, but distinct from,
the surface pitch, which can encompass additional padding beyond the physical surface size.
*/
- int next_width = width <= 2 ? width : (width + 3) & ~3;
- int next_height = height <= 2 ? height : (height + 3) & ~3;
- if (next_width != width || next_height != height) {
- r_img->resize(next_width, next_height, Image::INTERPOLATE_LANCZOS);
- width = r_img->get_width();
- height = r_img->get_height();
+
+ if (width % 4 != 0 || height % 4 != 0) {
+ width = width <= 2 ? width : (width + 3) & ~3;
+ height = height <= 2 ? height : (height + 3) & ~3;
}
- // ERR_FAIL_COND(width % 4 != 0 || height % 4 != 0); // FIXME: No longer guaranteed.
+
// Multiple-of-4 should be guaranteed by above.
// However, power-of-two 3d textures will create Nx2 and Nx1 mipmap levels,
// which are individually compressed Image objects that violate the above rule.
// Hence, we allow Nx1 and Nx2 images through without forcing to multiple-of-4.
- const uint8_t *src_read = r_img->get_data().ptr();
-
- print_verbose(vformat("etcpak: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : ""));
-
- int64_t dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
+ // Create the buffer for compressed image data.
Vector<uint8_t> dest_data;
- dest_data.resize(dest_size);
+ dest_data.resize(Image::get_image_data_size(width, height, target_format, has_mipmaps));
uint8_t *dest_write = dest_data.ptrw();
- int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+ const uint8_t *src_read = r_img->get_data().ptr();
+
+ const int mip_count = has_mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
Vector<uint32_t> padded_src;
for (int i = 0; i < mip_count + 1; i++) {
// Get write mip metrics for target image.
- int orig_mip_w, orig_mip_h;
- int64_t mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, orig_mip_w, orig_mip_h);
+ int dest_mip_w, dest_mip_h;
+ int64_t dest_mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dest_mip_w, dest_mip_h);
+
// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
- ERR_FAIL_COND(mip_ofs % 8 != 0);
- uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs];
+ ERR_FAIL_COND(dest_mip_ofs % 8 != 0);
+ uint64_t *dest_mip_write = reinterpret_cast<uint64_t *>(dest_write + dest_mip_ofs);
- // Block size. Align stride to multiple of 4 (RGBA8).
- int mip_w = (orig_mip_w + 3) & ~3;
- int mip_h = (orig_mip_h + 3) & ~3;
- const uint32_t blocks = mip_w * mip_h / 16;
+ // Block size.
+ dest_mip_w = (dest_mip_w + 3) & ~3;
+ dest_mip_h = (dest_mip_h + 3) & ~3;
+ const uint32_t blocks = dest_mip_w * dest_mip_h / 16;
// Get mip data from source image for reading.
- int64_t src_mip_ofs = r_img->get_mipmap_offset(i);
- const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs];
+ int64_t src_mip_ofs, src_mip_size;
+ int src_mip_w, src_mip_h;
+
+ r_img->get_mipmap_offset_size_and_dimensions(i, src_mip_ofs, src_mip_size, src_mip_w, src_mip_h);
+
+ const uint32_t *src_mip_read = reinterpret_cast<const uint32_t *>(src_read + src_mip_ofs);
// Pad textures to nearest block by smearing.
- if (mip_w != orig_mip_w || mip_h != orig_mip_h) {
- padded_src.resize(mip_w * mip_h);
+ if (dest_mip_w != src_mip_w || dest_mip_h != src_mip_h) {
+ // Reserve the buffer for padded image data.
+ padded_src.resize(dest_mip_w * dest_mip_h);
uint32_t *ptrw = padded_src.ptrw();
+
int x = 0, y = 0;
- for (y = 0; y < orig_mip_h; y++) {
- for (x = 0; x < orig_mip_w; x++) {
- ptrw[mip_w * y + x] = src_mip_read[orig_mip_w * y + x];
+ for (y = 0; y < src_mip_h; y++) {
+ for (x = 0; x < src_mip_w; x++) {
+ ptrw[dest_mip_w * y + x] = src_mip_read[src_mip_w * y + x];
}
+
// First, smear in x.
- for (; x < mip_w; x++) {
- ptrw[mip_w * y + x] = ptrw[mip_w * y + x - 1];
+ for (; x < dest_mip_w; x++) {
+ ptrw[dest_mip_w * y + x] = ptrw[dest_mip_w * y + x - 1];
}
}
+
// Then, smear in y.
- for (; y < mip_h; y++) {
- for (x = 0; x < mip_w; x++) {
- ptrw[mip_w * y + x] = ptrw[mip_w * y + x - mip_w];
+ for (; y < dest_mip_h; y++) {
+ for (x = 0; x < dest_mip_w; x++) {
+ ptrw[dest_mip_w * y + x] = ptrw[dest_mip_w * y + x - dest_mip_w];
}
}
+
// Override the src_mip_read pointer to our temporary Vector.
src_mip_read = padded_src.ptr();
}
- switch (p_compresstype) {
+ switch (p_compress_type) {
case EtcpakType::ETCPAK_TYPE_ETC1:
- CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_ETC2:
- CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
+ CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, dest_mip_w, true);
break;
case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA:
case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG:
- CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
+ CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, dest_mip_w, true);
break;
case EtcpakType::ETCPAK_TYPE_ETC2_R:
- CompressEacR(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEacR(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_ETC2_RG:
- CompressEacRg(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEacRg(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_DXT1:
- CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_DXT5:
case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG:
- CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressDxt5(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_RGTC_R:
- CompressBc4(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressBc4(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_RGTC_RG:
- CompressBc5(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressBc5(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
default:
@@ -276,7 +299,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
}
// Replace original image with compressed one.
- r_img->set_data(width, height, mipmaps, target_format, dest_data);
+ r_img->set_data(width, height, has_mipmaps, target_format, dest_data);
print_verbose(vformat("etcpak: Encoding took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time));
}
diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h
index 9d5343740b..d50b322fe4 100644
--- a/modules/etcpak/image_compress_etcpak.h
+++ b/modules/etcpak/image_compress_etcpak.h
@@ -51,6 +51,6 @@ void _compress_etc1(Image *r_img);
void _compress_etc2(Image *r_img, Image::UsedChannels p_channels);
void _compress_bc(Image *r_img, Image::UsedChannels p_channels);
-void _compress_etcpak(EtcpakType p_compresstype, Image *r_img);
+void _compress_etcpak(EtcpakType p_compress_type, Image *r_img);
#endif // IMAGE_COMPRESS_ETCPAK_H
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index b9d9ec7b6c..4d3f7554c0 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -2114,9 +2114,6 @@ Error FBXDocument::_parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess>
return OK;
}
-void FBXDocument::_bind_methods() {
-}
-
Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) {
Ref<FBXState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), nullptr);
diff --git a/modules/fbx/fbx_document.h b/modules/fbx/fbx_document.h
index 4a3bb176c2..96f1905881 100644
--- a/modules/fbx/fbx_document.h
+++ b/modules/fbx/fbx_document.h
@@ -61,9 +61,6 @@ public:
PackedByteArray generate_buffer(Ref<GLTFState> p_state) override;
Error write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) override;
-protected:
- static void _bind_methods();
-
private:
String _get_texture_path(const String &p_base_directory, const String &p_source_file_path) const;
void _process_uv_set(PackedVector2Array &uv_array);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index eaf2565e69..7bf5e946fb 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2857,8 +2857,11 @@ GDScriptLanguage::GDScriptLanguage() {
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
+#ifdef DEBUG_ENABLED
profiling = false;
+ profile_native_calls = false;
script_frame_time = 0;
+#endif
int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 4e78fbe302..6527a0ea4d 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -459,9 +459,11 @@ class GDScriptLanguage : public ScriptLanguage {
friend class GDScriptFunction;
SelfList<GDScriptFunction>::List function_list;
+#ifdef DEBUG_ENABLED
bool profiling;
bool profile_native_calls;
uint64_t script_frame_time;
+#endif
HashMap<String, ObjectID> orphan_subclasses;
diff --git a/modules/gltf/README.md b/modules/gltf/README.md
index 5d8966b201..2d1e92e070 100644
--- a/modules/gltf/README.md
+++ b/modules/gltf/README.md
@@ -1,11 +1,11 @@
-# Godot GLTF import and export module
+# Godot glTF import and export module
-In a nutshell, the GLTF module works like this:
+In a nutshell, the glTF module works like this:
-* The [`structures/`](structures/) folder contains GLTF structures, the
- small pieces that make up a GLTF file, represented as C++ classes.
-* The [`extensions/`](extensions/) folder contains GLTF extensions, which
- are optional features that build on top of the base GLTF spec.
+* The [`structures/`](structures/) folder contains glTF structures, the
+ small pieces that make up a glTF file, represented as C++ classes.
+* The [`extensions/`](extensions/) folder contains glTF extensions, which
+ are optional features that build on top of the base glTF spec.
* [`GLTFState`](gltf_state.h) holds collections of structures and extensions.
* [`GLTFDocument`](gltf_document.h) operates on GLTFState and its elements.
* The [`editor/`](editor/) folder uses GLTFDocument to import and export 3D models.
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index dd059e6b79..bc142797a3 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFAccessor" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF accessor.
+ Represents a glTF accessor.
</brief_description>
<description>
- GLTFAccessor is a data structure representing GLTF a [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view.
- Most custom data stored in GLTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU.
+ GLTFAccessor is a data structure representing a glTF [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view.
+ Most custom data stored in glTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU.
</description>
<tutorials>
<link title="Buffers, BufferViews, and Accessors in Khronos glTF specification">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md</link>
@@ -13,7 +13,7 @@
</tutorials>
<members>
<member name="accessor_type" type="int" setter="set_accessor_type" getter="get_accessor_type" enum="GLTFAccessor.GLTFAccessorType" default="0">
- The GLTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4".
+ The glTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4".
</member>
<member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1">
The index of the buffer view this accessor is referencing. If [code]-1[/code], this accessor is not referencing any buffer view.
@@ -22,7 +22,7 @@
The offset relative to the start of the buffer view in bytes.
</member>
<member name="component_type" type="int" setter="set_component_type" getter="get_component_type" default="0">
- The GLTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices.
+ The glTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices.
</member>
<member name="count" type="int" setter="set_count" getter="get_count" default="0">
The number of elements referenced by this accessor.
@@ -55,7 +55,7 @@
The offset relative to the start of the bufferView in bytes.
</member>
<member name="type" type="int" setter="set_type" getter="get_type" deprecated="Use [member accessor_type] instead.">
- The GLTF accessor type as an enum. Use [member accessor_type] instead.
+ The glTF accessor type as an enum. Use [member accessor_type] instead.
</member>
</members>
<constants>
diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml
index 2c70f4461f..d269145bbd 100644
--- a/modules/gltf/doc_classes/GLTFAnimation.xml
+++ b/modules/gltf/doc_classes/GLTFAnimation.xml
@@ -13,7 +13,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
</description>
</method>
<method name="set_additional_data">
@@ -22,7 +22,7 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
</methods>
diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml
index e191935fc9..b7f499ad72 100644
--- a/modules/gltf/doc_classes/GLTFBufferView.xml
+++ b/modules/gltf/doc_classes/GLTFBufferView.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFBufferView" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF buffer view.
+ Represents a glTF buffer view.
</brief_description>
<description>
- GLTFBufferView is a data structure representing GLTF a [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer.
+ GLTFBufferView is a data structure representing a glTF [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer.
Most custom uses of buffers only need to use the [member buffer], [member byte_length], and [member byte_offset]. The [member byte_stride] and [member indices] properties are for more advanced use cases such as interleaved mesh data encoded for the GPU.
</description>
<tutorials>
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
index b334bf2867..9fce21659c 100644
--- a/modules/gltf/doc_classes/GLTFCamera.xml
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFCamera" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF camera.
+ Represents a glTF camera.
</brief_description>
<description>
- Represents a camera as defined by the base GLTF spec.
+ Represents a camera as defined by the base glTF spec.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="GLTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link>
- <link title="GLTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link>
+ <link title="glTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link>
+ <link title="glTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
@@ -41,19 +41,19 @@
</methods>
<members>
<member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0">
- The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]zfar[/code] property.
+ The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]zfar[/code] property.
</member>
<member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
- The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]znear[/code] property.
+ The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]znear[/code] property.
</member>
<member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309">
- The FOV of the camera. This class and GLTF define the camera FOV in radians, while Godot uses degrees. This maps to GLTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
+ The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
</member>
<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
- Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to GLTF's camera [code]type[/code] property. See [member Camera3D.projection] and the GLTF spec for more information.
+ Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to glTF's camera [code]type[/code] property. See [member Camera3D.projection] and the glTF spec for more information.
</member>
<member name="size_mag" type="float" setter="set_size_mag" getter="get_size_mag" default="0.5">
- The size of the camera. This class and GLTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to GLTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
+ The size of the camera. This class and glTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to glTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 1b51def28e..ebeed015e9 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene.
- All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects.
+ All of the data in a glTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects.
GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported.
</description>
<tutorials>
@@ -21,7 +21,7 @@
<param index="2" name="state" type="GLTFState" />
<param index="3" name="flags" type="int" default="0" />
<description>
- Takes a [PackedByteArray] defining a GLTF and imports the data to the given [GLTFState] object through the [param state] parameter.
+ Takes a [PackedByteArray] defining a glTF and imports the data to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty.
</description>
</method>
@@ -32,7 +32,7 @@
<param index="2" name="flags" type="int" default="0" />
<param index="3" name="base_path" type="String" default="&quot;&quot;" />
<description>
- Takes a path to a GLTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
+ Takes a path to a glTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty.
</description>
</method>
@@ -49,7 +49,7 @@
<return type="PackedByteArray" />
<param index="0" name="state" type="GLTFState" />
<description>
- Takes a [GLTFState] object through the [param state] parameter and returns a GLTF [PackedByteArray].
+ Takes a [GLTFState] object through the [param state] parameter and returns a glTF [PackedByteArray].
</description>
</method>
<method name="generate_scene">
@@ -91,7 +91,7 @@
</methods>
<members>
<member name="image_format" type="String" setter="set_image_format" getter="get_image_format" default="&quot;PNG&quot;">
- The user-friendly name of the export image format. This is used when exporting the GLTF file, including writing to a file and writing to a byte array.
+ The user-friendly name of the export image format. This is used when exporting the glTF file, including writing to a file and writing to a byte array.
By default, Godot allows the following options: "None", "PNG", "JPEG", "Lossless WebP", and "Lossy WebP". Support for more image formats can be added in [GLTFDocumentExtension] classes.
</member>
<member name="lossy_quality" type="float" setter="set_lossy_quality" getter="get_lossy_quality" default="0.75">
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 0eabcb5022..5c548c472f 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -4,7 +4,7 @@
[GLTFDocument] extension class.
</brief_description>
<description>
- Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export.
+ Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of glTF import or export.
To use, make a new class extending GLTFDocumentExtension, override any methods you need, make an instance of your class, and register it using [method GLTFDocument.register_gltf_document_extension].
[b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
</description>
@@ -30,7 +30,7 @@
<param index="3" name="node" type="Node" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _export_post]. If this [GLTFDocumentExtension] is used for exporting images, this runs after [method _serialize_texture_json].
- This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting GLTF data not generated from a Godot scene.
+ This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting glTF data not generated from a Godot scene.
</description>
</method>
<method name="_export_post" qualifiers="virtual">
@@ -38,7 +38,7 @@
<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.
- This method can be used to modify the final JSON of the generated GLTF file.
+ This method can be used to modify the final JSON of the generated glTF file.
</description>
</method>
<method name="_export_preflight" qualifiers="virtual">
@@ -47,7 +47,7 @@
<param index="1" name="root" type="Node" />
<description>
Part of the export process. This method is run first, before all other parts of the export process.
- The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
+ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given glTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
</description>
</method>
<method name="_export_preserialize" qualifiers="virtual">
@@ -86,7 +86,7 @@
<return type="PackedStringArray" />
<description>
Part of the import process. This method is run after [method _import_preflight] and before [method _parse_node_extensions].
- Returns an array of the GLTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a GLTF file with required extensions can be loaded.
+ Returns an array of the glTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a glTF file with required extensions can be loaded.
</description>
</method>
<method name="_import_node" qualifiers="virtual">
@@ -123,7 +123,7 @@
<param index="1" name="extensions" type="PackedStringArray" />
<description>
Part of the import process. This method is run first, before all other parts of the import process.
- The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given GLTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
+ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given glTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
</description>
</method>
<method name="_parse_image_data" qualifiers="virtual">
@@ -134,7 +134,7 @@
<param index="3" name="ret_image" type="Image" />
<description>
Part of the import process. This method is run after [method _parse_node_extensions] and before [method _parse_texture_json].
- Runs when parsing image data from a GLTF file. The data could be sourced from a separate file, a URI, or a buffer, and then is passed as a byte array.
+ Runs when parsing image data from a glTF file. The data could be sourced from a separate file, a URI, or a buffer, and then is passed as a byte array.
</description>
</method>
<method name="_parse_node_extensions" qualifiers="virtual">
@@ -154,7 +154,7 @@
<param index="2" name="ret_gltf_texture" type="GLTFTexture" />
<description>
Part of the import process. This method is run after [method _parse_image_data] and before [method _generate_scene_node].
- Runs when parsing the texture JSON from the GLTF textures array. This can be used to set the source image index to use as the texture.
+ Runs when parsing the texture JSON from the glTF textures array. This can be used to set the source image index to use as the texture.
</description>
</method>
<method name="_save_image_at_path" qualifiers="virtual">
@@ -166,7 +166,7 @@
<param index="4" name="lossy_quality" type="float" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json].
- This method is run when saving images separately from the GLTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
+ This method is run when saving images separately from the glTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
</description>
</method>
<method name="_serialize_image_to_bytes" qualifiers="virtual">
@@ -178,7 +178,7 @@
<param index="4" name="lossy_quality" type="float" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json].
- This method is run when embedding images in the GLTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
+ This method is run when embedding images in the glTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
This method must set the image MIME type in the [param image_dict] with the [code]"mimeType"[/code] key. For example, for a PNG image, it would be set to [code]"image/png"[/code]. The return value must be a [PackedByteArray] containing the image data.
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml
index 87ea159e7c..e07d24a144 100644
--- a/modules/gltf/doc_classes/GLTFLight.xml
+++ b/modules/gltf/doc_classes/GLTFLight.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFLight" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF light.
+ Represents a glTF light.
</brief_description>
<description>
- Represents a light as defined by the [code]KHR_lights_punctual[/code] GLTF extension.
+ Represents a light as defined by the [code]KHR_lights_punctual[/code] glTF extension.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="KHR_lights_punctual GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link>
+ <link title="KHR_lights_punctual glTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
@@ -70,7 +70,7 @@
At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight.
</member>
<member name="range" type="float" setter="set_range" getter="get_range" default="inf">
- The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096.
+ The range of the light, beyond which the light has no effect. glTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index b4c3db7618..da73c20c1d 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFMesh" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFMesh represents a GLTF mesh.
+ GLTFMesh represents a glTF mesh.
</brief_description>
<description>
- GLTFMesh handles 3D mesh data imported from GLTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself.
+ GLTFMesh handles 3D mesh data imported from glTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
@@ -15,7 +15,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
</description>
</method>
<method name="set_additional_data">
@@ -24,7 +24,7 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
</methods>
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index 4a7570e4bc..2786c25e9a 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTF node class.
+ glTF node class.
</brief_description>
<description>
- Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes).
- GLTF nodes generally exist inside of [GLTFState] which represents all data of a GLTF file. Most of GLTFNode's properties are indices of other data in the GLTF file. You can extend a GLTF node with additional properties by using [method get_additional_data] and [method set_additional_data].
+ Represents a glTF node. glTF nodes may have names, transforms, children (other glTF nodes), and more specialized properties (represented by their own classes).
+ glTF nodes generally exist inside of [GLTFState] which represents all data of a glTF file. Most of GLTFNode's properties are indices of other data in the glTF file. You can extend a glTF node with additional properties by using [method get_additional_data] and [method set_additional_data].
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link>
+ <link title="glTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link>
</tutorials>
<methods>
<method name="get_additional_data">
@@ -17,7 +17,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
</description>
</method>
<method name="set_additional_data">
@@ -26,25 +26,25 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
</methods>
<members>
<member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1">
- If this GLTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera.
+ If this glTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera.
</member>
<member name="children" type="PackedInt32Array" setter="set_children" getter="get_children" default="PackedInt32Array()">
- The indices of the child nodes in the [GLTFState]. If this GLTF node has no children, this will be an empty array.
+ The indices of the child nodes in the [GLTFState]. If this glTF node has no children, this will be an empty array.
</member>
<member name="height" type="int" setter="set_height" getter="get_height" default="-1">
How deep into the node hierarchy this node is. A root node will have a height of 0, its children will have a height of 1, and so on. If -1, the height has not been calculated.
</member>
<member name="light" type="int" setter="set_light" getter="get_light" default="-1">
- If this GLTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light.
+ If this glTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light.
</member>
<member name="mesh" type="int" setter="set_mesh" getter="get_mesh" default="-1">
- If this GLTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh.
+ If this glTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh.
</member>
<member name="original_name" type="String" setter="set_original_name" getter="get_original_name" default="&quot;&quot;">
The original name of the node.
@@ -53,22 +53,22 @@
The index of the parent node in the [GLTFState]. If -1, this node is a root node.
</member>
<member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)">
- The position of the GLTF node relative to its parent.
+ The position of the glTF node relative to its parent.
</member>
<member name="rotation" type="Quaternion" setter="set_rotation" getter="get_rotation" default="Quaternion(0, 0, 0, 1)">
- The rotation of the GLTF node relative to its parent.
+ The rotation of the glTF node relative to its parent.
</member>
<member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)">
- The scale of the GLTF node relative to its parent.
+ The scale of the glTF node relative to its parent.
</member>
<member name="skeleton" type="int" setter="set_skeleton" getter="get_skeleton" default="-1">
- If this GLTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton.
+ If this glTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton.
</member>
<member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1">
- If this GLTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin.
+ If this glTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin.
</member>
<member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
- The transform of the GLTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred.
+ The transform of the glTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml
index cd701e2f2f..1a76b190ba 100644
--- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml
+++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFPhysicsBody" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF physics body.
+ Represents a glTF physics body.
</brief_description>
<description>
- Represents a physics body as an intermediary between the [code]OMI_physics_body[/code] GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future.
+ Represents a physics body as an intermediary between the [code]OMI_physics_body[/code] glTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different glTF physics extensions in the future.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="OMI_physics_body GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link>
+ <link title="OMI_physics_body glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
<return type="GLTFPhysicsBody" />
<param index="0" name="dictionary" type="Dictionary" />
<description>
- Creates a new GLTFPhysicsBody instance by parsing the given [Dictionary] in the [code]OMI_physics_body[/code] GLTF extension format.
+ Creates a new GLTFPhysicsBody instance by parsing the given [Dictionary] in the [code]OMI_physics_body[/code] glTF extension format.
</description>
</method>
<method name="from_node" qualifiers="static">
@@ -28,7 +28,7 @@
<method name="to_dictionary" qualifiers="const">
<return type="Dictionary" />
<description>
- Serializes this GLTFPhysicsBody instance into a [Dictionary]. It will be in the format expected by the [code]OMI_physics_body[/code] GLTF extension.
+ Serializes this GLTFPhysicsBody instance into a [Dictionary]. It will be in the format expected by the [code]OMI_physics_body[/code] glTF extension.
</description>
</method>
<method name="to_node" qualifiers="const">
diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml
index a4aaf9415c..53872a942f 100644
--- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml
+++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFPhysicsShape" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF physics shape.
+ Represents a glTF physics shape.
</brief_description>
<description>
- Represents a physics shape as defined by the [code]OMI_physics_shape[/code] or [code]OMI_collider[/code] GLTF extensions. This class is an intermediary between the GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future.
+ Represents a physics shape as defined by the [code]OMI_physics_shape[/code] or [code]OMI_collider[/code] glTF extensions. This class is an intermediary between the glTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different glTF physics extensions in the future.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="OMI_physics_shape GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape</link>
- <link title="OMI_collider GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider</link>
+ <link title="OMI_physics_shape glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape</link>
+ <link title="OMI_collider glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
@@ -66,7 +66,7 @@
This is the only variable not used in the [method to_node] method, it's intended to be used alongside when deciding where to add the generated node as a child.
</member>
<member name="mesh_index" type="int" setter="set_mesh_index" getter="get_mesh_index" default="-1">
- The index of the shape's mesh in the GLTF file. This is only used when the shape type is "hull" (convex hull) or "trimesh" (concave trimesh).
+ The index of the shape's mesh in the glTF file. This is only used when the shape type is "hull" (convex hull) or "trimesh" (concave trimesh).
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The radius of the shape, in meters. This is only used when the shape type is "capsule", "cylinder", or "sphere". This value should not be negative.
diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml
index ac03a6ee9e..2dd3a37413 100644
--- a/modules/gltf/doc_classes/GLTFSkeleton.xml
+++ b/modules/gltf/doc_classes/GLTFSkeleton.xml
@@ -22,7 +22,7 @@
<method name="get_godot_bone_node">
<return type="Dictionary" />
<description>
- Returns a [Dictionary] that maps skeleton bone indices to the indices of GLTF nodes. This property is unused during import, and only set during export. In a GLTF file, a bone is a node, so Godot converts skeleton bones to GLTF nodes.
+ Returns a [Dictionary] that maps skeleton bone indices to the indices of glTF nodes. This property is unused during import, and only set during export. In a glTF file, a bone is a node, so Godot converts skeleton bones to glTF nodes.
</description>
</method>
<method name="get_godot_skeleton">
@@ -39,7 +39,7 @@
<return type="void" />
<param index="0" name="godot_bone_node" type="Dictionary" />
<description>
- Sets a [Dictionary] that maps skeleton bone indices to the indices of GLTF nodes. This property is unused during import, and only set during export. In a GLTF file, a bone is a node, so Godot converts skeleton bones to GLTF nodes.
+ Sets a [Dictionary] that maps skeleton bone indices to the indices of glTF nodes. This property is unused during import, and only set during export. In a glTF file, a bone is a node, so Godot converts skeleton bones to glTF nodes.
</description>
</method>
<method name="set_unique_names">
diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml
index 722fa5e9ae..11151f53d0 100644
--- a/modules/gltf/doc_classes/GLTFSpecGloss.xml
+++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFSpecGloss" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Archived GLTF extension for specular/glossy materials.
+ Archived glTF extension for specular/glossy materials.
</brief_description>
<description>
- KHR_materials_pbrSpecularGlossiness is an archived GLTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files.
+ KHR_materials_pbrSpecularGlossiness is an archived glTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="KHR_materials_pbrSpecularGlossiness GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link>
+ <link title="KHR_materials_pbrSpecularGlossiness glTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link>
</tutorials>
<members>
<member name="diffuse_factor" type="Color" setter="set_diffuse_factor" getter="get_diffuse_factor" default="Color(1, 1, 1, 1)">
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 21a0527813..c049acf557 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFState" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents all data of a GLTF file.
+ Represents all data of a glTF file.
</brief_description>
<description>
- Contains all nodes and resources of a GLTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless.
- GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a GLTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the GLTF file or for custom data to be converted to/from Godot nodes.
+ Contains all nodes and resources of a glTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless.
+ GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a glTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the glTF file or for custom data to be converted to/from Godot nodes.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="GLTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link>
+ <link title="glTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link>
</tutorials>
<methods>
<method name="add_used_extension">
@@ -17,7 +17,7 @@
<param index="0" name="extension_name" type="String" />
<param index="1" name="required" type="bool" />
<description>
- Appends an extension to the list of extensions used by this GLTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
+ Appends an extension to the list of extensions used by this glTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
</description>
</method>
<method name="append_data_to_buffers">
@@ -38,27 +38,27 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null.
+ The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
</description>
</method>
<method name="get_animation_player">
<return type="AnimationPlayer" />
<param index="0" name="idx" type="int" />
<description>
- Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations.
+ Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to glTF animations.
</description>
</method>
<method name="get_animation_players_count">
<return type="int" />
<param index="0" name="idx" type="int" />
<description>
- Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations.
+ Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to glTF animations.
</description>
</method>
<method name="get_animations">
<return type="GLTFAnimation[]" />
<description>
- Returns an array of all [GLTFAnimation]s in the GLTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes.
+ Returns an array of all [GLTFAnimation]s in the glTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes.
</description>
</method>
<method name="get_buffer_views">
@@ -69,7 +69,7 @@
<method name="get_cameras">
<return type="GLTFCamera[]" />
<description>
- Returns an array of all [GLTFCamera]s in the GLTF file. These are the cameras that the [member GLTFNode.camera] index refers to.
+ Returns an array of all [GLTFCamera]s in the glTF file. These are the cameras that the [member GLTFNode.camera] index refers to.
</description>
</method>
<method name="get_handle_binary_image">
@@ -80,13 +80,13 @@
<method name="get_images">
<return type="Texture2D[]" />
<description>
- Gets the images of the GLTF file as an array of [Texture2D]s. These are the images that the [member GLTFTexture.src_image] index refers to.
+ Gets the images of the glTF file as an array of [Texture2D]s. These are the images that the [member GLTFTexture.src_image] index refers to.
</description>
</method>
<method name="get_lights">
<return type="GLTFLight[]" />
<description>
- Returns an array of all [GLTFLight]s in the GLTF file. These are the lights that the [member GLTFNode.light] index refers to.
+ Returns an array of all [GLTFLight]s in the glTF file. These are the lights that the [member GLTFNode.light] index refers to.
</description>
</method>
<method name="get_materials">
@@ -97,7 +97,7 @@
<method name="get_meshes">
<return type="GLTFMesh[]" />
<description>
- Returns an array of all [GLTFMesh]es in the GLTF file. These are the meshes that the [member GLTFNode.mesh] index refers to.
+ Returns an array of all [GLTFMesh]es in the glTF file. These are the meshes that the [member GLTFNode.mesh] index refers to.
</description>
</method>
<method name="get_node_index">
@@ -111,7 +111,7 @@
<method name="get_nodes">
<return type="GLTFNode[]" />
<description>
- Returns an array of all [GLTFNode]s in the GLTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes.
+ Returns an array of all [GLTFNode]s in the glTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes.
</description>
</method>
<method name="get_scene_node">
@@ -125,19 +125,19 @@
<method name="get_skeletons">
<return type="GLTFSkeleton[]" />
<description>
- Returns an array of all [GLTFSkeleton]s in the GLTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to.
+ Returns an array of all [GLTFSkeleton]s in the glTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to.
</description>
</method>
<method name="get_skins">
<return type="GLTFSkin[]" />
<description>
- Returns an array of all [GLTFSkin]s in the GLTF file. These are the skins that the [member GLTFNode.skin] index refers to.
+ Returns an array of all [GLTFSkin]s in the glTF file. These are the skins that the [member GLTFNode.skin] index refers to.
</description>
</method>
<method name="get_texture_samplers">
<return type="GLTFTextureSampler[]" />
<description>
- Retrieves the array of texture samplers that are used by the textures contained in the GLTF.
+ Retrieves the array of texture samplers that are used by the textures contained in the glTF.
</description>
</method>
<method name="get_textures">
@@ -169,7 +169,7 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
<method name="set_animations">
@@ -250,7 +250,7 @@
<return type="void" />
<param index="0" name="texture_samplers" type="GLTFTextureSampler[]" />
<description>
- Sets the array of texture samplers that are used by the textures contained in the GLTF.
+ Sets the array of texture samplers that are used by the textures contained in the glTF.
</description>
</method>
<method name="set_textures">
@@ -279,17 +279,17 @@
The baking fps of the animation for either import or export.
</member>
<member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
- The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
+ The folder path associated with this glTF data. This is used to find other files the glTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
</member>
<member name="buffers" type="PackedByteArray[]" setter="set_buffers" getter="get_buffers" default="[]">
</member>
<member name="copyright" type="String" setter="set_copyright" getter="get_copyright" default="&quot;&quot;">
- The copyright string in the asset header of the GLTF file. This is set during import if present and export if non-empty. See the GLTF asset header documentation for more information.
+ The copyright string in the asset header of the glTF file. This is set during import if present and export if non-empty. See the glTF asset header documentation for more information.
</member>
<member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true">
</member>
<member name="filename" type="String" setter="set_filename" getter="get_filename" default="&quot;&quot;">
- The file name associated with this GLTF data. If it ends with [code].gltf[/code], this is text-based GLTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string.
+ The file name associated with this glTF data. If it ends with [code].gltf[/code], this is text-based glTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string.
</member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
The binary buffer attached to a .glb file.
@@ -305,10 +305,10 @@
<member name="minor_version" type="int" setter="set_minor_version" getter="get_minor_version" default="0">
</member>
<member name="root_nodes" type="PackedInt32Array" setter="set_root_nodes" getter="get_root_nodes" default="PackedInt32Array()">
- The root nodes of the GLTF file. Typically, a GLTF file will only have one scene, and therefore one root node. However, a GLTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene.
+ The root nodes of the glTF file. Typically, a glTF file will only have one scene, and therefore one root node. However, a glTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene.
</member>
<member name="scene_name" type="String" setter="set_scene_name" getter="get_scene_name" default="&quot;&quot;">
- The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the GLTF file.
+ The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the glTF file.
</member>
<member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false">
</member>
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index 9ad7c0f4c6..2a868a8ba3 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFTexture" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFTexture represents a texture in a GLTF file.
+ GLTFTexture represents a texture in a glTF file.
</brief_description>
<description>
</description>
diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml
index 2b5bad6724..d00ab463c2 100644
--- a/modules/gltf/doc_classes/GLTFTextureSampler.xml
+++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFTextureSampler" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF texture sampler
+ Represents a glTF texture sampler
</brief_description>
<description>
- Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object.
+ Represents a texture sampler as defined by the base glTF spec. Texture samplers in glTF specify how to sample data from the texture's base image, when rendering the texture on an object.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
index 07faee3dfc..676f764c11 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
@@ -34,9 +34,6 @@
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/3d/importer_mesh.h"
-void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() {
-}
-
void GLTFDocumentExtensionConvertImporterMesh::_copy_meta(Object *p_src_object, Object *p_dst_object) {
List<StringName> meta_list;
p_src_object->get_meta_list(&meta_list);
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
index ca10444eb5..b216a47a7f 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
@@ -37,7 +37,6 @@ class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension {
GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension);
protected:
- static void _bind_methods();
static void _copy_meta(Object *p_src_object, Object *p_dst_object);
public:
diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp
index c1d2fea98b..f6e91c1635 100644
--- a/modules/gltf/extensions/gltf_light.cpp
+++ b/modules/gltf/extensions/gltf_light.cpp
@@ -170,7 +170,7 @@ Light3D *GLTFLight::to_node() const {
}
Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
- ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse GLTF light, missing required field 'type'.");
+ ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse glTF light, missing required field 'type'.");
Ref<GLTFLight> light;
light.instantiate();
const String &type = p_dictionary["type"];
@@ -181,7 +181,7 @@ Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
if (arr.size() == 3) {
light->color = Color(arr[0], arr[1], arr[2]).linear_to_srgb();
} else {
- ERR_PRINT("Error parsing GLTF light: The color must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF light: The color must have exactly 3 numbers.");
}
}
if (p_dictionary.has("intensity")) {
@@ -195,10 +195,10 @@ Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
light->inner_cone_angle = spot["innerConeAngle"];
light->outer_cone_angle = spot["outerConeAngle"];
if (light->inner_cone_angle >= light->outer_cone_angle) {
- ERR_PRINT("Error parsing GLTF light: The inner angle must be smaller than the outer angle.");
+ ERR_PRINT("Error parsing glTF light: The inner angle must be smaller than the outer angle.");
}
} else if (type != "point" && type != "directional") {
- ERR_PRINT("Error parsing GLTF light: Light type '" + type + "' is unknown.");
+ ERR_PRINT("Error parsing glTF light: Light type '" + type + "' is unknown.");
}
return light;
}
diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
index 7e52cde059..5c26a1686b 100644
--- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
+++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
@@ -88,7 +88,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
// "collider" is the index of the collider in the state colliders array.
int node_collider_index = node_collider_ext["collider"];
Array state_colliders = p_state->get_additional_data(StringName("GLTFPhysicsShapes"));
- ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the collider index " + itos(node_collider_index) + " is not in the state colliders (size: " + itos(state_colliders.size()) + ").");
+ ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the collider index " + itos(node_collider_index) + " is not in the state colliders (size: " + itos(state_colliders.size()) + ").");
p_gltf_node->set_additional_data(StringName("GLTFPhysicsShape"), state_colliders[node_collider_index]);
} else {
p_gltf_node->set_additional_data(StringName("GLTFPhysicsShape"), GLTFPhysicsShape::from_dictionary(node_collider_ext));
@@ -103,7 +103,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
int node_shape_index = node_collider.get("shape", -1);
if (node_shape_index != -1) {
Array state_shapes = p_state->get_additional_data(StringName("GLTFPhysicsShapes"));
- ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ").");
+ ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ").");
p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShape"), state_shapes[node_shape_index]);
} else {
// If this node is a collider but does not have a collider
@@ -117,7 +117,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
int node_shape_index = node_trigger.get("shape", -1);
if (node_shape_index != -1) {
Array state_shapes = p_state->get_additional_data(StringName("GLTFPhysicsShapes"));
- ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ").");
+ ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ").");
p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShape"), state_shapes[node_shape_index]);
} else {
// If this node is a trigger but does not have a trigger shape,
@@ -150,7 +150,7 @@ void _setup_shape_mesh_resource_from_index_if_needed(Ref<GLTFState> p_state, Ref
return; // The mesh resource is already set up.
}
TypedArray<GLTFMesh> state_meshes = p_state->get_meshes();
- ERR_FAIL_INDEX_MSG(shape_mesh_index, state_meshes.size(), "GLTF Physics: When importing '" + p_state->get_scene_name() + "', the shape mesh index " + itos(shape_mesh_index) + " is not in the state meshes (size: " + itos(state_meshes.size()) + ").");
+ ERR_FAIL_INDEX_MSG(shape_mesh_index, state_meshes.size(), "glTF Physics: When importing '" + p_state->get_scene_name() + "', the shape mesh index " + itos(shape_mesh_index) + " is not in the state meshes (size: " + itos(state_meshes.size()) + ").");
Ref<GLTFMesh> gltf_mesh = state_meshes[shape_mesh_index];
ERR_FAIL_COND(gltf_mesh.is_null());
importer_mesh = gltf_mesh->get_mesh();
@@ -164,12 +164,12 @@ CollisionObject3D *_generate_shape_with_body(Ref<GLTFState> p_state, Ref<GLTFNod
bool is_trigger = p_physics_shape->get_is_trigger();
// This method is used for the case where we must generate a parent body.
// This is can happen for multiple reasons. One possibility is that this
- // GLTF file is using OMI_collider but not OMI_physics_body, or at least
+ // glTF file is using OMI_collider but not OMI_physics_body, or at least
// this particular node is not using it. Another possibility is that the
- // physics body information is set up on the same GLTF node, not a parent.
+ // physics body information is set up on the same glTF node, not a parent.
CollisionObject3D *body;
if (p_physics_body.is_valid()) {
- // This code is run when the physics body is on the same GLTF node.
+ // This code is run when the physics body is on the same glTF node.
body = p_physics_body->to_node();
if (is_trigger && (p_physics_body->get_body_type() != "trigger")) {
// Edge case: If the body's trigger and the collider's trigger
@@ -266,7 +266,7 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state
Ref<GLTFPhysicsShape> gltf_physics_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsShape"));
if (gltf_physics_shape.is_valid()) {
_setup_shape_mesh_resource_from_index_if_needed(p_state, gltf_physics_shape);
- // If this GLTF node specifies both a shape and a body, generate both.
+ // If this glTF node specifies both a shape and a body, generate both.
if (gltf_physics_body.is_valid()) {
return _generate_shape_with_body(p_state, p_gltf_node, gltf_physics_shape, gltf_physics_body);
}
@@ -309,7 +309,7 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state
}
} else if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) {
if (p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundCollider"))) {
- // If the GLTF file wants this node to group solid shapes together,
+ // If the glTF file wants this node to group solid shapes together,
// and there is no parent body, we need to create a static body.
ancestor_col_obj = memnew(StaticBody3D);
ret = ancestor_col_obj;
@@ -386,7 +386,7 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re
if (cast_to<CollisionShape3D>(p_scene_node)) {
CollisionShape3D *godot_shape = Object::cast_to<CollisionShape3D>(p_scene_node);
Ref<GLTFPhysicsShape> gltf_shape = GLTFPhysicsShape::from_node(godot_shape);
- ERR_FAIL_COND_MSG(gltf_shape.is_null(), "GLTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?");
+ ERR_FAIL_COND_MSG(gltf_shape.is_null(), "glTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?");
{
Ref<ImporterMesh> importer_mesh = gltf_shape->get_importer_mesh();
if (importer_mesh.is_valid()) {
diff --git a/modules/gltf/extensions/physics/gltf_physics_body.cpp b/modules/gltf/extensions/physics/gltf_physics_body.cpp
index 7929b46542..c11aa5d2ff 100644
--- a/modules/gltf/extensions/physics/gltf_physics_body.cpp
+++ b/modules/gltf/extensions/physics/gltf_physics_body.cpp
@@ -108,7 +108,7 @@ void GLTFPhysicsBody::set_body_type(String p_body_type) {
} else if (p_body_type == "trigger") {
body_type = PhysicsBodyType::TRIGGER;
} else {
- ERR_PRINT("Error setting GLTF physics body type: The body type must be one of \"static\", \"animatable\", \"character\", \"rigid\", \"vehicle\", or \"trigger\".");
+ ERR_PRINT("Error setting glTF physics body type: The body type must be one of \"static\", \"animatable\", \"character\", \"rigid\", \"vehicle\", or \"trigger\".");
}
}
@@ -194,7 +194,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_node(const CollisionObject3D *p_body_
physics_body->center_of_mass = body->get_center_of_mass();
physics_body->inertia_diagonal = body->get_inertia();
if (body->get_center_of_mass() != Vector3()) {
- WARN_PRINT("GLTFPhysicsBody: This rigid body has a center of mass offset from the origin, which will be ignored when exporting to GLTF.");
+ WARN_PRINT("GLTFPhysicsBody: This rigid body has a center of mass offset from the origin, which will be ignored when exporting to glTF.");
}
if (cast_to<VehicleBody3D>(p_body_node)) {
physics_body->body_type = PhysicsBodyType::VEHICLE;
@@ -289,7 +289,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
physics_body->body_type = PhysicsBodyType::TRIGGER;
#endif // DISABLE_DEPRECATED
} else {
- ERR_PRINT("Error parsing GLTF physics body: The body type in the GLTF file \"" + body_type_string + "\" was not recognized.");
+ ERR_PRINT("Error parsing glTF physics body: The body type in the glTF file \"" + body_type_string + "\" was not recognized.");
}
}
if (motion.has("mass")) {
@@ -300,7 +300,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_linear_velocity(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The linear velocity vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The linear velocity vector must have exactly 3 numbers.");
}
}
if (motion.has("angularVelocity")) {
@@ -308,7 +308,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_angular_velocity(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The angular velocity vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The angular velocity vector must have exactly 3 numbers.");
}
}
if (motion.has("centerOfMass")) {
@@ -316,7 +316,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_center_of_mass(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The center of mass vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The center of mass vector must have exactly 3 numbers.");
}
}
if (motion.has("inertiaDiagonal")) {
@@ -324,7 +324,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_inertia_diagonal(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The inertia diagonal vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The inertia diagonal vector must have exactly 3 numbers.");
}
}
if (motion.has("inertiaOrientation")) {
@@ -332,7 +332,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 4) {
physics_body->set_inertia_orientation(Quaternion(arr[0], arr[1], arr[2], arr[3]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The inertia orientation quaternion must have exactly 4 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The inertia orientation quaternion must have exactly 4 numbers.");
}
}
return physics_body;
diff --git a/modules/gltf/extensions/physics/gltf_physics_shape.cpp b/modules/gltf/extensions/physics/gltf_physics_shape.cpp
index 6897bdbd3a..0340eb11b5 100644
--- a/modules/gltf/extensions/physics/gltf_physics_shape.cpp
+++ b/modules/gltf/extensions/physics/gltf_physics_shape.cpp
@@ -134,7 +134,7 @@ void GLTFPhysicsShape::set_importer_mesh(Ref<ImporterMesh> p_importer_mesh) {
Ref<ImporterMesh> _convert_hull_points_to_mesh(const Vector<Vector3> &p_hull_points) {
Ref<ImporterMesh> importer_mesh;
- ERR_FAIL_COND_V_MSG(p_hull_points.size() < 3, importer_mesh, "GLTFPhysicsShape: Convex hull has fewer points (" + itos(p_hull_points.size()) + ") than the minimum of 3. At least 3 points are required in order to save to GLTF, since it uses a mesh to represent convex hulls.");
+ ERR_FAIL_COND_V_MSG(p_hull_points.size() < 3, importer_mesh, "GLTFPhysicsShape: Convex hull has fewer points (" + itos(p_hull_points.size()) + ") than the minimum of 3. At least 3 points are required in order to save to glTF, since it uses a mesh to represent convex hulls.");
if (p_hull_points.size() > 255) {
WARN_PRINT("GLTFPhysicsShape: Convex hull has more points (" + itos(p_hull_points.size()) + ") than the recommended maximum of 255. This may not load correctly in other engines.");
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index dff1e62e82..d98b250538 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -620,7 +620,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) {
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_node_extensions(p_state, node, extensions);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing.");
+ ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing.");
}
}
@@ -3353,7 +3353,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
ERR_CONTINUE(image.is_null());
if (image->is_compressed()) {
image->decompress();
- ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "GLTF: Image was compressed, but could not be decompressed.");
+ ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "glTF: Image was compressed, but could not be decompressed.");
}
if (p_state->filename.to_lower().ends_with("gltf")) {
@@ -3374,7 +3374,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
if (_image_save_extension.is_valid()) {
img_name = img_name + _image_save_extension->get_image_file_extension();
Error err = _image_save_extension->save_image_at_path(p_state, image, full_texture_dir.path_join(img_name), _image_format, _lossy_quality);
- ERR_FAIL_COND_V_MSG(err != OK, err, "GLTF: Failed to save image in '" + _image_format + "' format as a separate file.");
+ ERR_FAIL_COND_V_MSG(err != OK, err, "glTF: Failed to save image in '" + _image_format + "' format as a separate file.");
} else if (_image_format == "PNG") {
img_name = img_name + ".png";
image->save_png(full_texture_dir.path_join(img_name));
@@ -3382,7 +3382,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
img_name = img_name + ".jpg";
image->save_jpg(full_texture_dir.path_join(img_name), _lossy_quality);
} else {
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Unknown image format '" + _image_format + "'.");
}
image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode();
} else {
@@ -3412,9 +3412,9 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
buffer = image->save_jpg_to_buffer(_lossy_quality);
image_dict["mimeType"] = "image/jpeg";
} else {
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Unknown image format '" + _image_format + "'.");
}
- ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "GLTF: Failed to save image in '" + _image_format + "' format.");
+ ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "glTF: Failed to save image in '" + _image_format + "' format.");
bv->byte_length = buffer.size();
p_state->buffers.write[bi].resize(p_state->buffers[bi].size() + bv->byte_length);
@@ -3445,7 +3445,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing.");
+ ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing.");
if (!r_image->is_empty()) {
r_file_extension = ext->get_image_file_extension();
return r_image;
@@ -3736,13 +3736,13 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) {
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_texture_json(p_state, texture_dict, gltf_texture);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing.");
+ ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing.");
if (gltf_texture->get_src_image() != -1) {
break;
}
}
if (gltf_texture->get_src_image() == -1) {
- // No extensions handled it, so use the base GLTF source.
+ // No extensions handled it, so use the base glTF source.
// This may be the fallback, or the only option anyway.
ERR_FAIL_COND_V(!texture_dict.has("source"), ERR_PARSE_ERROR);
gltf_texture->set_src_image(texture_dict["source"]);
@@ -5631,7 +5631,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
// If none of our GLTFDocumentExtension classes generated us a node, we generate one.
if (!current_node) {
if (gltf_node->skin >= 0 && gltf_node->mesh >= 0 && !gltf_node->children.is_empty()) {
- // GLTF specifies that skinned meshes should ignore their node transforms,
+ // glTF specifies that skinned meshes should ignore their node transforms,
// only being controlled by the skeleton, so Godot will reparent a skinned
// mesh to its skeleton. However, we still need to ensure any child nodes
// keep their place in the tree, so if there are any child nodes, the skinned
@@ -7138,9 +7138,9 @@ Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) {
HashMap<ObjectID, SkinSkeletonIndex> skeleton_map;
Error err = SkinTool::_create_skeletons(p_state->unique_names, p_state->skins, p_state->nodes,
skeleton_map, p_state->skeletons, p_state->scene_nodes);
- ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skeletons.");
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "glTF: Failed to create skeletons.");
err = _create_skins(p_state);
- ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skins.");
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "glTF: Failed to create skins.");
// Generate the node tree.
Node *single_root;
if (p_state->extensions_used.has("GODOT_single_root")) {
@@ -7459,7 +7459,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) {
Error ret = OK;
for (int i = 0; i < p_state->extensions_required.size(); i++) {
if (!supported_extensions.has(p_state->extensions_required[i])) {
- ERR_PRINT("GLTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?");
+ ERR_PRINT("glTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?");
ret = ERR_UNAVAILABLE;
}
}
diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp
index d56f67a092..863e1df967 100644
--- a/modules/gltf/structures/gltf_camera.cpp
+++ b/modules/gltf/structures/gltf_camera.cpp
@@ -62,9 +62,9 @@ Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) {
c.instantiate();
ERR_FAIL_NULL_V_MSG(p_camera, c, "Tried to create a GLTFCamera from a Camera3D node, but the given node was null.");
c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
- // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
+ // glTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
c->set_fov(Math::deg_to_rad(p_camera->get_fov()));
- // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
+ // glTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
c->set_size_mag(p_camera->get_size() * 0.5f);
c->set_depth_far(p_camera->get_far());
c->set_depth_near(p_camera->get_near());
@@ -74,9 +74,9 @@ Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) {
Camera3D *GLTFCamera::to_node() const {
Camera3D *camera = memnew(Camera3D);
camera->set_projection(perspective ? Camera3D::PROJECTION_PERSPECTIVE : Camera3D::PROJECTION_ORTHOGONAL);
- // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
+ // glTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
camera->set_fov(Math::rad_to_deg(fov));
- // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
+ // glTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
camera->set_size(size_mag * 2.0f);
camera->set_near(depth_near);
camera->set_far(depth_far);
@@ -84,7 +84,7 @@ Camera3D *GLTFCamera::to_node() const {
}
Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) {
- ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse GLTF camera, missing required field 'type'.");
+ ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse glTF camera, missing required field 'type'.");
Ref<GLTFCamera> camera;
camera.instantiate();
const String &type = p_dictionary["type"];
@@ -107,7 +107,7 @@ Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) {
camera->set_depth_near(ortho["znear"]);
}
} else {
- ERR_PRINT("Error parsing GLTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic.");
+ ERR_PRINT("Error parsing glTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic.");
}
return camera;
}
diff --git a/modules/gltf/structures/gltf_camera.h b/modules/gltf/structures/gltf_camera.h
index ef55b06a76..1a583c82cc 100644
--- a/modules/gltf/structures/gltf_camera.h
+++ b/modules/gltf/structures/gltf_camera.h
@@ -42,8 +42,8 @@ class GLTFCamera : public Resource {
GDCLASS(GLTFCamera, Resource);
private:
- // GLTF has no default camera values, they should always be specified in
- // the GLTF file. Here we default to Godot's default camera settings.
+ // glTF has no default camera values, they should always be specified in
+ // the glTF file. Here we default to Godot's default camera settings.
bool perspective = true;
real_t fov = Math::deg_to_rad(75.0);
real_t size_mag = 0.5;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index f402e2a583..ea63e07104 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -643,6 +643,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
_do_paste();
input_action = INPUT_NONE;
_update_paste_indicator();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (mb->is_shift_pressed() && can_edit) {
input_action = INPUT_SELECT;
last_selection = selection;
diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
index cd335934db..ece1ab44a2 100644
--- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
+++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
@@ -3,6 +3,8 @@
using _BINDINGS_NAMESPACE_;
using System;
+[Tool]
+[GlobalClass]
public partial class VisualShaderNode_CLASS_ : _BASE_
{
public override string _GetName()
@@ -20,37 +22,37 @@ public partial class VisualShaderNode_CLASS_ : _BASE_
return "";
}
- public override long _GetReturnIconType()
+ public override VisualShaderNode.PortType _GetReturnIconType()
{
return 0;
}
- public override long _GetInputPortCount()
+ public override int _GetInputPortCount()
{
return 0;
}
- public override string _GetInputPortName(long port)
+ public override string _GetInputPortName(int port)
{
return "";
}
- public override long _GetInputPortType(long port)
+ public override VisualShaderNode.PortType _GetInputPortType(int port)
{
return 0;
}
- public override long _GetOutputPortCount()
+ public override int _GetOutputPortCount()
{
return 1;
}
- public override string _GetOutputPortName(long port)
+ public override string _GetOutputPortName(int port)
{
return "result";
}
- public override long _GetOutputPortType(long port)
+ public override VisualShaderNode.PortType _GetOutputPortType(int port)
{
return 0;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs
index 563a6abe9b..1fc6e54e09 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using Godot.NativeInterop;
#nullable enable
@@ -51,7 +52,7 @@ namespace Godot
/// </summary>
/// <param name="instance">The instance to check.</param>
/// <returns>If the instance is a valid object.</returns>
- public static bool IsInstanceValid(GodotObject? instance)
+ public static bool IsInstanceValid([NotNullWhen(true)] GodotObject? instance)
{
return instance != null && instance.NativeInstance != IntPtr.Zero;
}
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
index 3da245f806..b620292519 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -48,11 +48,11 @@
</methods>
<members>
<member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function">
- Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
+ Method called on all peers when a custom [method spawn] is requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
[b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
</member>
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
- Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
+ Maximum number of nodes allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
When set to [code]0[/code] (the default), there is no limit.
</member>
<member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath(&quot;&quot;)">
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index d07d3cdff5..f37ed9b168 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -126,9 +126,6 @@ void NavigationMeshEditor::edit(NavigationRegion3D *p_nav_region) {
node = p_nav_region;
}
-void NavigationMeshEditor::_bind_methods() {
-}
-
NavigationMeshEditor::NavigationMeshEditor() {
bake_hbox = memnew(HBoxContainer);
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h
index 6114c62ebf..f5a471d531 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h
@@ -60,7 +60,6 @@ class NavigationMeshEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/modules/openxr/editor/openxr_select_runtime.cpp b/modules/openxr/editor/openxr_select_runtime.cpp
index 026797c6e0..4d95b079e2 100644
--- a/modules/openxr/editor/openxr_select_runtime.cpp
+++ b/modules/openxr/editor/openxr_select_runtime.cpp
@@ -35,9 +35,6 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
-void OpenXRSelectRuntime::_bind_methods() {
-}
-
void OpenXRSelectRuntime::_update_items() {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
OS *os = OS::get_singleton();
diff --git a/modules/openxr/editor/openxr_select_runtime.h b/modules/openxr/editor/openxr_select_runtime.h
index 60b5137f67..9a3487439c 100644
--- a/modules/openxr/editor/openxr_select_runtime.h
+++ b/modules/openxr/editor/openxr_select_runtime.h
@@ -40,7 +40,6 @@ public:
OpenXRSelectRuntime();
protected:
- static void _bind_methods();
void _notification(int p_notification);
private:
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 68a5d499d4..4112b81622 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -142,7 +142,14 @@ if env["builtin_harfbuzz"]:
env_harfbuzz.Append(CCFLAGS=["-DHAVE_ICU"])
if env["builtin_icu4c"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
- env_harfbuzz.Append(CCFLAGS=["-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN"])
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
+ "-DU_HAVE_LIB_SUFFIX=1",
+ "-DU_LIB_SUFFIX_C_NAME=_godot",
+ "-DHAVE_ICU_BUILTIN",
+ ]
+ )
if freetype_enabled:
env_harfbuzz.Append(
@@ -499,6 +506,7 @@ if env["builtin_icu4c"]:
)
env_text_server_adv.Append(
CXXFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
"-DU_HAVE_LIB_SUFFIX=1",
"-DU_LIB_SUFFIX_C_NAME=_godot",
"-DICU_DATA_NAME=" + icu_data_name,
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index d0d13fec3f..effed1e772 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -415,6 +415,7 @@ if env["platform"] == "android" or env["platform"] == "linuxbsd":
env_harfbuzz.Append(
CCFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
"-DU_HAVE_LIB_SUFFIX=1",
"-DU_LIB_SUFFIX_C_NAME=_godot",
"-DHAVE_ICU_BUILTIN",
@@ -746,6 +747,7 @@ env_icu.Append(
)
env.Append(
CXXFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
"-DU_HAVE_LIB_SUFFIX=1",
"-DU_LIB_SUFFIX_C_NAME=_godot",
"-DICU_DATA_NAME=" + icu_data_name,
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 499ddb703b..d0c22e9e4d 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -51,7 +51,7 @@ using namespace godot;
#include "core/error/error_macros.h"
#include "core/object/worker_thread_pool.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -3528,6 +3528,37 @@ String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) cons
return chars;
}
+PackedInt32Array TextServerAdvanced::_font_get_supported_glyphs(const RID &p_font_rid) const {
+ FontAdvanced *fd = _get_font_data(p_font_rid);
+ ERR_FAIL_NULL_V(fd, PackedInt32Array());
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array());
+ }
+ FontForSizeAdvanced *at_size = fd->cache.begin()->value;
+
+ PackedInt32Array glyphs;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ glyphs.push_back(gindex);
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return glyphs;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ glyphs.push_back(E.key);
+ }
+ }
+ return glyphs;
+}
+
void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 92bdb93bcf..fdebb8e4cd 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -871,6 +871,7 @@ public:
MODBIND2RC(bool, font_has_char, const RID &, int64_t);
MODBIND1RC(String, font_get_supported_chars, const RID &);
+ MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &);
MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t);
MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t);
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index b45c004011..a7ddfc719e 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -52,7 +52,7 @@ using namespace godot;
#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -2477,6 +2477,37 @@ String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) cons
return chars;
}
+PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_font_rid) const {
+ FontFallback *fd = _get_font_data(p_font_rid);
+ ERR_FAIL_NULL_V(fd, PackedInt32Array());
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array());
+ }
+ FontForSizeFallback *at_size = fd->cache.begin()->value;
+
+ PackedInt32Array glyphs;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ glyphs.push_back(gindex);
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return glyphs;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ glyphs.push_back(E.key);
+ }
+ }
+ return glyphs;
+}
+
void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 2235247b31..1b76c6fa0f 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -739,6 +739,7 @@ public:
MODBIND2RC(bool, font_has_char, const RID &, int64_t);
MODBIND1RC(String, font_get_supported_chars, const RID &);
+ MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &);
MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t);
MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t);
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 2ff02d2e74..057fb4ec16 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1092,7 +1092,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
List<String> args;
args.push_back("sign");
- if (p_path.get_extension() != "dmg") {
+ if (!p_ent_path.is_empty()) {
args.push_back("--entitlements-xml-path");
args.push_back(p_ent_path);
}
@@ -1153,7 +1153,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
args.push_back("runtime");
}
- if (p_path.get_extension() != "dmg") {
+ if (!p_ent_path.is_empty()) {
args.push_back("--entitlements");
args.push_back(p_ent_path);
}
@@ -1237,7 +1237,7 @@ void EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPrese
}
if (extensions_to_sign.has(current_file.get_extension())) {
- String ent_path = p_ent_path;
+ String ent_path;
bool set_bundle_id = false;
if (sandbox && FileAccess::exists(current_file_path)) {
int ftype = MachO::get_filetype(current_file_path);
@@ -1357,7 +1357,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
_code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code_sign);
} else {
if (extensions_to_sign.has(p_in_app_path.get_extension())) {
- String ent_path = p_ent_path;
+ String ent_path;
bool set_bundle_id = false;
if (p_sandbox && FileAccess::exists(p_in_app_path)) {
int ftype = MachO::get_filetype(p_in_app_path);
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index 22487d2756..5e046d7050 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -312,6 +312,11 @@ bool AudioDriverWeb::is_sample_playback_active(const Ref<AudioSamplePlayback> &p
return godot_audio_sample_is_active(itos(p_playback->get_instance_id()).utf8().get_data()) != 0;
}
+double AudioDriverWeb::get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) {
+ ERR_FAIL_COND_V_MSG(p_playback.is_null(), false, "Parameter p_playback is null.");
+ return godot_audio_get_sample_playback_position(itos(p_playback->get_instance_id()).utf8().get_data());
+}
+
void AudioDriverWeb::update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
godot_audio_sample_update_pitch_scale(
diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h
index 46c5ce4de1..d352fa4692 100644
--- a/platform/web/audio_driver_web.h
+++ b/platform/web/audio_driver_web.h
@@ -96,6 +96,7 @@ public:
virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override;
virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) override;
virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) override;
+ virtual double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) override;
virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) override;
virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) override;
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 40de4e523b..4e55cc137a 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -902,8 +902,10 @@ void DisplayServerWeb::process_joypads() {
for (int b = 0; b < s_btns_num; b++) {
// Buttons 6 and 7 in the standard mapping need to be
// axis to be handled as JoyAxis::TRIGGER by Godot.
- if (s_standard && (b == 6 || b == 7)) {
- input->joy_axis(idx, (JoyAxis)b, s_btns[b]);
+ if (s_standard && (b == 6)) {
+ input->joy_axis(idx, JoyAxis::TRIGGER_LEFT, s_btns[b]);
+ } else if (s_standard && (b == 7)) {
+ input->joy_axis(idx, JoyAxis::TRIGGER_RIGHT, s_btns[b]);
} else {
input->joy_button(idx, (JoyButton)b, s_btns[b]);
}
diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py
index 2cee3e8110..8fcabb21c7 100644
--- a/platform/web/emscripten_helpers.py
+++ b/platform/web/emscripten_helpers.py
@@ -51,11 +51,13 @@ def create_template_zip(env, js, wasm, worker, side):
js,
wasm,
"#platform/web/js/libs/audio.worklet.js",
+ "#platform/web/js/libs/audio.position.worklet.js",
]
out_files = [
zip_dir.File(binary_name + ".js"),
zip_dir.File(binary_name + ".wasm"),
zip_dir.File(binary_name + ".audio.worklet.js"),
+ zip_dir.File(binary_name + ".audio.position.worklet.js"),
]
if env["threads"]:
in_files.append(worker)
@@ -74,6 +76,7 @@ def create_template_zip(env, js, wasm, worker, side):
"offline.html",
"godot.editor.js",
"godot.editor.audio.worklet.js",
+ "godot.editor.audio.position.worklet.js",
"logo.svg",
"favicon.png",
]
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index d83e465e8e..d8c1b6033d 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -242,6 +242,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
}
cache_files.push_back(name + ".worker.js");
cache_files.push_back(name + ".audio.worklet.js");
+ cache_files.push_back(name + ".audio.position.worklet.js");
replaces["___GODOT_CACHE___"] = Variant(cache_files).to_json_string();
// Heavy files that are cached on demand.
@@ -835,6 +836,7 @@ Error EditorExportPlatformWeb::_export_project(const Ref<EditorExportPreset> &p_
DirAccess::remove_file_or_error(basepath + ".js");
DirAccess::remove_file_or_error(basepath + ".worker.js");
DirAccess::remove_file_or_error(basepath + ".audio.worklet.js");
+ DirAccess::remove_file_or_error(basepath + ".audio.position.worklet.js");
DirAccess::remove_file_or_error(basepath + ".service.worker.js");
DirAccess::remove_file_or_error(basepath + ".pck");
DirAccess::remove_file_or_error(basepath + ".png");
diff --git a/platform/web/godot_audio.h b/platform/web/godot_audio.h
index dd5bec00cf..4961ebd2bb 100644
--- a/platform/web/godot_audio.h
+++ b/platform/web/godot_audio.h
@@ -55,6 +55,7 @@ extern void godot_audio_sample_start(const char *p_playback_object_id, const cha
extern void godot_audio_sample_stop(const char *p_playback_object_id);
extern void godot_audio_sample_set_pause(const char *p_playback_object_id, bool p_pause);
extern int godot_audio_sample_is_active(const char *p_playback_object_id);
+extern double godot_audio_get_sample_playback_position(const char *p_playback_object_id);
extern void godot_audio_sample_update_pitch_scale(const char *p_playback_object_id, float p_pitch_scale);
extern void godot_audio_sample_set_volumes_linear(const char *p_playback_object_id, int *p_buses_buf, int p_buses_size, float *p_volumes_buf, int p_volumes_size);
extern void godot_audio_sample_set_finished_callback(void (*p_callback)(const char *));
diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js
index 8c4e1b1b24..61b488cf81 100644
--- a/platform/web/js/engine/config.js
+++ b/platform/web/js/engine/config.js
@@ -299,6 +299,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
return `${loadPath}.worker.js`;
} else if (path.endsWith('.audio.worklet.js')) {
return `${loadPath}.audio.worklet.js`;
+ } else if (path.endsWith('.audio.position.worklet.js')) {
+ return `${loadPath}.audio.position.worklet.js`;
} else if (path.endsWith('.js')) {
return `${loadPath}.js`;
} else if (path in gdext) {
diff --git a/platform/web/js/libs/audio.position.worklet.js b/platform/web/js/libs/audio.position.worklet.js
new file mode 100644
index 0000000000..bf3ac4ae2d
--- /dev/null
+++ b/platform/web/js/libs/audio.position.worklet.js
@@ -0,0 +1,50 @@
+/**************************************************************************/
+/* godot.audio.position.worklet.js */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+class GodotPositionReportingProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.position = 0;
+ }
+
+ process(inputs, _outputs, _parameters) {
+ if (inputs.length > 0) {
+ const input = inputs[0];
+ if (input.length > 0) {
+ this.position += input[0].length;
+ this.port.postMessage({ 'type': 'position', 'data': this.position });
+ return true;
+ }
+ }
+ return true;
+ }
+}
+
+registerProcessor('godot-position-reporting-processor', GodotPositionReportingProcessor);
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 8b7c572196..0ba6eed464 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -330,6 +330,7 @@ class SampleNodeBus {
* startTime?: number
* loopMode?: LoopMode
* volume?: Float32Array
+ * start?: boolean
* }} SampleNodeOptions
*/
@@ -421,9 +422,15 @@ class SampleNode {
/** @type {number} */
this.offset = options.offset ?? 0;
/** @type {number} */
+ this._playbackPosition = options.offset;
+ /** @type {number} */
this.startTime = options.startTime ?? 0;
/** @type {boolean} */
this.isPaused = false;
+ /** @type {boolean} */
+ this.isStarted = false;
+ /** @type {boolean} */
+ this.isCanceled = false;
/** @type {number} */
this.pauseTime = 0;
/** @type {number} */
@@ -440,6 +447,8 @@ class SampleNode {
this._source = GodotAudio.ctx.createBufferSource();
this._onended = null;
+ /** @type {AudioWorkletNode | null} */
+ this._positionWorklet = null;
this.setPlaybackRate(options.playbackRate ?? 44100);
this._source.buffer = this.getSample().getAudioBuffer();
@@ -449,6 +458,8 @@ class SampleNode {
const bus = GodotAudio.Bus.getBus(params.busIndex);
const sampleNodeBus = this.getSampleNodeBus(bus);
sampleNodeBus.setVolume(options.volume);
+
+ this.connectPositionWorklet(options.start);
}
/**
@@ -460,6 +471,14 @@ class SampleNode {
}
/**
+ * Gets the playback position.
+ * @returns {number}
+ */
+ getPlaybackPosition() {
+ return this._playbackPosition;
+ }
+
+ /**
* Sets the playback rate.
* @param {number} val Value to set.
* @returns {void}
@@ -508,8 +527,12 @@ class SampleNode {
* @returns {void}
*/
start() {
+ if (this.isStarted) {
+ return;
+ }
this._resetSourceStartTime();
this._source.start(this.startTime, this.offset);
+ this.isStarted = true;
}
/**
@@ -585,17 +608,73 @@ class SampleNode {
}
/**
+ * Sets up and connects the source to the GodotPositionReportingProcessor
+ * If the worklet module is not loaded in, it will be added
+ */
+ connectPositionWorklet(start) {
+ try {
+ this._positionWorklet = this.createPositionWorklet();
+ this._source.connect(this._positionWorklet);
+ if (start) {
+ this.start();
+ }
+ } catch (error) {
+ if (error?.name !== 'InvalidStateError') {
+ throw error;
+ }
+ const path = GodotConfig.locate_file('godot.audio.position.worklet.js');
+ GodotAudio.ctx.audioWorklet
+ .addModule(path)
+ .then(() => {
+ if (!this.isCanceled) {
+ this._positionWorklet = this.createPositionWorklet();
+ this._source.connect(this._positionWorklet);
+ if (start) {
+ this.start();
+ }
+ }
+ }).catch((addModuleError) => {
+ GodotRuntime.error('Failed to create PositionWorklet.', addModuleError);
+ });
+ }
+ }
+
+ /**
+ * Creates the AudioWorkletProcessor used to track playback position.
+ * @returns {AudioWorkletNode}
+ */
+ createPositionWorklet() {
+ const worklet = new AudioWorkletNode(
+ GodotAudio.ctx,
+ 'godot-position-reporting-processor'
+ );
+ worklet.port.onmessage = (event) => {
+ switch (event.data['type']) {
+ case 'position':
+ this._playbackPosition = (parseInt(event.data.data, 10) / this.getSample().sampleRate) + this.offset;
+ break;
+ default:
+ // Do nothing.
+ }
+ };
+ return worklet;
+ }
+
+ /**
* Clears the `SampleNode`.
* @returns {void}
*/
clear() {
+ this.isCanceled = true;
this.isPaused = false;
this.pauseTime = 0;
if (this._source != null) {
this._source.removeEventListener('ended', this._onended);
this._onended = null;
- this._source.stop();
+ if (this.isStarted) {
+ this._source.stop();
+ }
this._source.disconnect();
this._source = null;
}
@@ -605,6 +684,12 @@ class SampleNode {
}
this._sampleNodeBuses.clear();
+ if (this._positionWorklet) {
+ this._positionWorklet.disconnect();
+ this._positionWorklet.port.onmessage = null;
+ this._positionWorklet = null;
+ }
+
GodotAudio.SampleNode.delete(this.id);
}
@@ -645,7 +730,9 @@ class SampleNode {
const pauseTime = this.isPaused
? this.pauseTime
: 0;
+ this.connectPositionWorklet();
this._source.start(this.startTime, this.offset + pauseTime);
+ this.isStarted = true;
}
/**
@@ -1262,7 +1349,7 @@ const _GodotAudio = {
startOptions
) {
GodotAudio.SampleNode.stopSampleNode(playbackObjectId);
- const sampleNode = GodotAudio.SampleNode.create(
+ GodotAudio.SampleNode.create(
{
busIndex,
id: playbackObjectId,
@@ -1270,7 +1357,6 @@ const _GodotAudio = {
},
startOptions
);
- sampleNode.start();
},
/**
@@ -1590,6 +1676,7 @@ const _GodotAudio = {
offset,
volume,
playbackRate: 1,
+ start: true,
};
GodotAudio.start_sample(
playbackObjectId,
@@ -1635,6 +1722,22 @@ const _GodotAudio = {
return Number(GodotAudio.sampleNodes.has(playbackObjectId));
},
+ godot_audio_get_sample_playback_position__proxy: 'sync',
+ godot_audio_get_sample_playback_position__sig: 'di',
+ /**
+ * Returns the position of the playback position.
+ * @param {number} playbackObjectIdStrPtr Playback object id pointer
+ * @returns {number}
+ */
+ godot_audio_get_sample_playback_position: function (playbackObjectIdStrPtr) {
+ const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
+ const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);
+ if (sampleNode == null) {
+ return 0;
+ }
+ return sampleNode.getPlaybackPosition();
+ },
+
godot_audio_sample_update_pitch_scale__proxy: 'sync',
godot_audio_sample_update_pitch_scale__sig: 'vii',
/**
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 88ab9a4af0..f0fe56a9c8 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -5612,6 +5612,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
return id;
}
+BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0;
+
// WinTab API.
bool DisplayServerWindows::wintab_available = false;
WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;
@@ -5774,6 +5776,8 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
KeyMappingWindows::initialize();
+ tested_drivers.clear();
+
drop_events = false;
key_event_pos = 0;
@@ -5942,7 +5946,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
wc.lpszClassName = L"Engine";
if (!RegisterClassExW(&wc)) {
- MessageBoxW(nullptr, L"Failed To Register The Window Class.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
r_error = ERR_UNAVAILABLE;
return;
}
@@ -5953,11 +5956,13 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
rendering_context = memnew(RenderingContextDriverVulkanWindows);
+ tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);
}
#endif
#if defined(D3D12_ENABLED)
if (rendering_driver == "d3d12") {
rendering_context = memnew(RenderingContextDriverD3D12);
+ tested_drivers.set_flag(DRIVER_ID_RD_D3D12);
}
#endif
@@ -5969,6 +5974,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (failed && fallback_to_vulkan && rendering_driver != "vulkan") {
memdelete(rendering_context);
rendering_context = memnew(RenderingContextDriverVulkanWindows);
+ tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);
if (rendering_context->initialize() == OK) {
WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan.");
rendering_driver = "vulkan";
@@ -5981,6 +5987,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (failed && fallback_to_d3d12 && rendering_driver != "d3d12") {
memdelete(rendering_context);
rendering_context = memnew(RenderingContextDriverD3D12);
+ tested_drivers.set_flag(DRIVER_ID_RD_D3D12);
if (rendering_context->initialize() == OK) {
WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12.");
rendering_driver = "d3d12";
@@ -6051,6 +6058,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
if (force_angle || (gl_info["version"].operator int() < 30003)) {
+ tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
if (show_warning) {
WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");
}
@@ -6060,6 +6068,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (rendering_driver == "opengl3") {
gl_manager_native = memnew(GLManagerNative_Windows);
+ tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
if (gl_manager_native->initialize() != OK) {
memdelete(gl_manager_native);
@@ -6072,6 +6081,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
if (rendering_driver == "opengl3_angle") {
gl_manager_angle = memnew(GLManagerANGLE_Windows);
+ tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11);
if (gl_manager_angle->initialize() != OK) {
memdelete(gl_manager_angle);
@@ -6205,32 +6215,41 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
if (r_error != OK) {
- if (p_rendering_driver == "vulkan") {
- String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert(
- vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
- "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
- "If you have recently updated your video card drivers, try rebooting.",
- executable_name),
- "Unable to initialize Vulkan video driver");
- } else if (p_rendering_driver == "d3d12") {
+ if (tested_drivers == 0) {
+ OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");
+ } else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {
+ Vector<String> drivers;
+ if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) {
+ drivers.push_back("Vulkan");
+ }
+ if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {
+ drivers.push_back("Direct3D 12");
+ }
String executable_name = OS::get_singleton()->get_executable_path().get_file();
OS::get_singleton()->alert(
- vformat("Your video card drivers seem not to support the required DirectX 12 version.\n\n"
+ vformat("Your video card drivers seem not to support the required %s version.\n\n"
"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
"You can enable the OpenGL 3 driver by starting the engine from the\n"
"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
"If you have recently updated your video card drivers, try rebooting.",
+ String(" or ").join(drivers),
executable_name),
- "Unable to initialize DirectX 12 video driver");
+ "Unable to initialize video driver");
} else {
+ Vector<String> drivers;
+ if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) {
+ drivers.push_back("OpenGL 3.3");
+ }
+ if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) {
+ drivers.push_back("Direct3D 11");
+ }
OS::get_singleton()->alert(
- "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
- "If possible, consider updating your video card drivers.\n\n"
- "If you have recently updated your video card drivers, try rebooting.",
- "Unable to initialize OpenGL video driver");
+ vformat(
+ "Your video card drivers seem not to support the required %s version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ String(" or ").join(drivers)),
+ "Unable to initialize video driver");
}
}
return ds;
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index de5b813953..26328ba876 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -388,6 +388,14 @@ class DisplayServerWindows : public DisplayServer {
String tablet_driver;
Vector<String> tablet_drivers;
+ enum DriverID {
+ DRIVER_ID_COMPAT_OPENGL3 = 1 << 0,
+ DRIVER_ID_COMPAT_ANGLE_D3D11 = 1 << 1,
+ DRIVER_ID_RD_VULKAN = 1 << 2,
+ DRIVER_ID_RD_D3D12 = 1 << 3,
+ };
+ static BitField<DriverID> tested_drivers;
+
enum TimerID {
TIMER_ID_MOVE_REDRAW = 1,
TIMER_ID_WINDOW_ACTIVATION = 2,
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 8e91dce425..89a0479de3 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -242,7 +242,7 @@ void AudioStreamPlayer2D::seek(float p_seconds) {
void AudioStreamPlayer2D::stop() {
setplay.set(-1);
- internal->stop();
+ internal->stop_basic();
}
bool AudioStreamPlayer2D::is_playing() const {
@@ -430,7 +430,7 @@ void AudioStreamPlayer2D::_bind_methods() {
}
AudioStreamPlayer2D::AudioStreamPlayer2D() {
- internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), true));
+ internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), callable_mp(this, &AudioStreamPlayer2D::stop), true));
cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength");
set_hide_clip_children(true);
}
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index 04ba550888..111f5a7b78 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -41,6 +41,9 @@ void NavigationLink2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink2D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink2D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional);
ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional);
@@ -106,12 +109,7 @@ bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const {
void NavigationLink2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (enabled) {
- NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
- }
- current_global_transform = get_global_transform();
- NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ _link_enter_navigation_map();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -120,36 +118,15 @@ void NavigationLink2D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
- if (is_inside_tree()) {
- Transform2D new_global_transform = get_global_transform();
- if (current_global_transform != new_global_transform) {
- current_global_transform = new_global_transform;
- NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
- queue_redraw();
- }
- }
+ _link_update_transform();
} break;
case NOTIFICATION_EXIT_TREE: {
- NavigationServer2D::get_singleton()->link_set_map(link, RID());
+ _link_exit_navigation_map();
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) {
- Color color;
- if (enabled) {
- color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
- } else {
- color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
- }
-
- real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
-
- draw_line(get_start_position(), get_end_position(), color);
- draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color);
- draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color);
- }
+ _update_debug_mesh();
#endif // DEBUG_ENABLED
} break;
}
@@ -188,15 +165,32 @@ void NavigationLink2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
+ NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
- queue_redraw();
- }
+ queue_redraw();
#endif // DEBUG_ENABLED
}
+void NavigationLink2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
+ map_override = p_navigation_map;
+
+ NavigationServer2D::get_singleton()->link_set_map(link, map_override);
+}
+
+RID NavigationLink2D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_2d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationLink2D::set_bidirectional(bool p_bidirectional) {
if (bidirectional == p_bidirectional) {
return;
@@ -255,9 +249,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) {
update_configuration_warnings();
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
- queue_redraw();
- }
+ queue_redraw();
#endif // DEBUG_ENABLED
}
@@ -277,9 +269,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) {
update_configuration_warnings();
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
- queue_redraw();
- }
+ queue_redraw();
#endif // DEBUG_ENABLED
}
@@ -347,6 +337,69 @@ PackedStringArray NavigationLink2D::get_configuration_warnings() const {
return warnings;
}
+void NavigationLink2D::_link_enter_navigation_map() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (map_override.is_valid()) {
+ NavigationServer2D::get_singleton()->link_set_map(link, map_override);
+ } else {
+ NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
+ }
+
+ current_global_transform = get_global_transform();
+
+ NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
+
+ queue_redraw();
+}
+
+void NavigationLink2D::_link_exit_navigation_map() {
+ NavigationServer2D::get_singleton()->link_set_map(link, RID());
+}
+
+void NavigationLink2D::_link_update_transform() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Transform2D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ queue_redraw();
+ }
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationLink2D::_update_debug_mesh() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (!Engine::get_singleton()->is_editor_hint() && !NavigationServer2D::get_singleton()->get_debug_enabled()) {
+ return;
+ }
+
+ Color color;
+ if (enabled) {
+ color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
+ } else {
+ color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
+ }
+
+ real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
+
+ draw_line(get_start_position(), get_end_position(), color);
+ draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color);
+ draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color);
+}
+#endif // DEBUG_ENABLED
+
NavigationLink2D::NavigationLink2D() {
link = NavigationServer2D::get_singleton()->link_create();
diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h
index 2929691c04..c724096607 100644
--- a/scene/2d/navigation_link_2d.h
+++ b/scene/2d/navigation_link_2d.h
@@ -38,6 +38,7 @@ class NavigationLink2D : public Node2D {
bool enabled = true;
RID link;
+ RID map_override;
bool bidirectional = true;
uint32_t navigation_layers = 1;
Vector2 end_position;
@@ -47,6 +48,10 @@ class NavigationLink2D : public Node2D {
Transform2D current_global_transform;
+#ifdef DEBUG_ENABLED
+ void _update_debug_mesh();
+#endif // DEBUG_ENABLED
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -66,6 +71,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const { return enabled; }
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_bidirectional(bool p_bidirectional);
bool is_bidirectional() const { return bidirectional; }
@@ -97,6 +105,11 @@ public:
NavigationLink2D();
~NavigationLink2D();
+
+private:
+ void _link_enter_navigation_map();
+ void _link_exit_navigation_map();
+ void _link_update_transform();
};
#endif // NAVIGATION_LINK_2D_H
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 4266060466..42f7a75c0a 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -682,7 +682,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_bones", "_get_bones");
ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count");
}
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index c12b95314e..b10f2097da 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -532,6 +532,18 @@ TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, boo
}
}
+bool TileMap::is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_H;
+}
+
+bool TileMap::is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_V;
+}
+
+bool TileMap::is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
+}
+
Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) {
TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref<TileMapPattern>(), get_pattern, p_coords_array);
}
@@ -926,6 +938,10 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_h, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_v, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_cell_transposed", "layer", "coords", "use_proxies"), &TileMap::is_cell_transposed, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid);
ClassDB::bind_method(D_METHOD("get_layer_for_body_rid", "body"), &TileMap::get_layer_for_body_rid);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 690102f730..142dc1193f 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -167,6 +167,10 @@ public:
// Helper method to make accessing the data easier.
TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ bool is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ bool is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ bool is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+
// Patterns.
Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index 437790bb99..7b125a6895 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -1773,6 +1773,10 @@ void TileMapLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapLayer::get_cell_alternative_tile);
ClassDB::bind_method(D_METHOD("get_cell_tile_data", "coords"), &TileMapLayer::get_cell_tile_data);
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "coords"), &TileMapLayer::is_cell_flipped_h);
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "coords"), &TileMapLayer::is_cell_flipped_v);
+ ClassDB::bind_method(D_METHOD("is_cell_transposed", "coords"), &TileMapLayer::is_cell_transposed);
+
ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapLayer::get_used_cells);
ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "source_id", "atlas_coords", "alternative_tile"), &TileMapLayer::get_used_cells_by_id, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMapLayer::get_used_rect);
@@ -2490,6 +2494,18 @@ Rect2i TileMapLayer::get_used_rect() const {
return used_rect_cache;
}
+bool TileMapLayer::is_cell_flipped_h(const Vector2i &p_coords) const {
+ return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_H;
+}
+
+bool TileMapLayer::is_cell_flipped_v(const Vector2i &p_coords) const {
+ return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_V;
+}
+
+bool TileMapLayer::is_cell_transposed(const Vector2i &p_coords) const {
+ return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
+}
+
Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) {
ERR_FAIL_COND_V(tile_set.is_null(), nullptr);
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index c71f13d7be..1a6d182094 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -438,6 +438,10 @@ public:
TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
Rect2i get_used_rect() const;
+ bool is_cell_flipped_h(const Vector2i &p_coords) const;
+ bool is_cell_flipped_v(const Vector2i &p_coords) const;
+ bool is_cell_transposed(const Vector2i &p_coords) const;
+
// Patterns.
Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 6888462876..4d3f494ccf 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -562,7 +562,7 @@ void AudioStreamPlayer3D::seek(float p_seconds) {
void AudioStreamPlayer3D::stop() {
setplay.set(-1);
- internal->stop();
+ internal->stop_basic();
}
bool AudioStreamPlayer3D::is_playing() const {
@@ -862,7 +862,7 @@ void AudioStreamPlayer3D::_bind_methods() {
}
AudioStreamPlayer3D::AudioStreamPlayer3D() {
- internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), true));
+ internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), callable_mp(this, &AudioStreamPlayer3D::stop), true));
velocity_tracker.instantiate();
set_disable_scale(true);
cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength");
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index dc776ebea2..bebba9a6c0 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -152,6 +152,9 @@ void NavigationLink3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink3D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink3D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional);
ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional);
@@ -217,16 +220,7 @@ bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const {
void NavigationLink3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (enabled) {
- NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map());
- }
- current_global_transform = get_global_transform();
- NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
-
-#ifdef DEBUG_ENABLED
- _update_debug_mesh();
-#endif // DEBUG_ENABLED
+ _link_enter_navigation_map();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -235,30 +229,11 @@ void NavigationLink3D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
- if (is_inside_tree()) {
- Transform3D new_global_transform = get_global_transform();
- if (current_global_transform != new_global_transform) {
- current_global_transform = new_global_transform;
- NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
-#ifdef DEBUG_ENABLED
- if (debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
- }
-#endif // DEBUG_ENABLED
- }
- }
+ _link_update_transform();
} break;
case NOTIFICATION_EXIT_TREE: {
- NavigationServer3D::get_singleton()->link_set_map(link, RID());
-
-#ifdef DEBUG_ENABLED
- if (debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_scenario(debug_instance, RID());
- RS::get_singleton()->instance_set_visible(debug_instance, false);
- }
-#endif // DEBUG_ENABLED
+ _link_exit_navigation_map();
} break;
}
}
@@ -320,6 +295,25 @@ void NavigationLink3D::set_enabled(bool p_enabled) {
update_gizmos();
}
+void NavigationLink3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
+ map_override = p_navigation_map;
+
+ NavigationServer3D::get_singleton()->link_set_map(link, map_override);
+}
+
+RID NavigationLink3D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_3d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationLink3D::set_bidirectional(bool p_bidirectional) {
if (bidirectional == p_bidirectional) {
return;
@@ -467,3 +461,53 @@ PackedStringArray NavigationLink3D::get_configuration_warnings() const {
return warnings;
}
+
+void NavigationLink3D::_link_enter_navigation_map() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->link_set_map(link, map_override);
+ } else {
+ NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map());
+ }
+
+ current_global_transform = get_global_transform();
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
+
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ _update_debug_mesh();
+ }
+#endif // DEBUG_ENABLED
+}
+
+void NavigationLink3D::_link_exit_navigation_map() {
+ NavigationServer3D::get_singleton()->link_set_map(link, RID());
+#ifdef DEBUG_ENABLED
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+#endif // DEBUG_ENABLED
+}
+
+void NavigationLink3D::_link_update_transform() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Transform3D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ _update_debug_mesh();
+ }
+#endif // DEBUG_ENABLED
+ }
+}
diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h
index 1867082811..e894761f40 100644
--- a/scene/3d/navigation_link_3d.h
+++ b/scene/3d/navigation_link_3d.h
@@ -38,6 +38,7 @@ class NavigationLink3D : public Node3D {
bool enabled = true;
RID link;
+ RID map_override;
bool bidirectional = true;
uint32_t navigation_layers = 1;
Vector3 end_position;
@@ -72,6 +73,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const { return enabled; }
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_bidirectional(bool p_bidirectional);
bool is_bidirectional() const { return bidirectional; }
@@ -100,6 +104,11 @@ public:
real_t get_travel_cost() const { return travel_cost; }
PackedStringArray get_configuration_warnings() const override;
+
+private:
+ void _link_enter_navigation_map();
+ void _link_exit_navigation_map();
+ void _link_update_transform();
};
#endif // NAVIGATION_LINK_3D_H
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 150771545b..6982df12f6 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -129,9 +129,6 @@ void Occluder3D::_notification(int p_what) {
}
}
-void Occluder3D::_bind_methods() {
-}
-
Occluder3D::Occluder3D() {
occluder = RS::get_singleton()->occluder_create();
}
diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h
index 91445710b3..62e9478527 100644
--- a/scene/3d/occluder_instance_3d.h
+++ b/scene/3d/occluder_instance_3d.h
@@ -49,7 +49,6 @@ protected:
void _update();
virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0;
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index f02cd9b700..4fe5dd2385 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -218,7 +218,13 @@ bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String
if ("spatial_attachment_path" == p_what) {
PinnedPoint *w = pinned_points.ptrw();
- callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value);
+
+ if (is_inside_tree()) {
+ callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value);
+ } else {
+ pin_point(w[p_item].point_index, true, p_value);
+ _make_cache_dirty();
+ }
} else if ("offset" == p_what) {
PinnedPoint *w = pinned_points.ptrw();
w[p_item].offset = p_value;
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index e4baae1afb..cdc85d2b2d 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -754,9 +754,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::Pl
return nti;
}
-void AnimationNodeAdd2::_bind_methods() {
-}
-
AnimationNodeAdd2::AnimationNodeAdd2() {
add_input("in");
add_input("add");
@@ -800,9 +797,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::Pl
return nti;
}
-void AnimationNodeAdd3::_bind_methods() {
-}
-
AnimationNodeAdd3::AnimationNodeAdd3() {
add_input("-add");
add_input("in");
@@ -845,9 +839,6 @@ bool AnimationNodeBlend2::has_filter() const {
return true;
}
-void AnimationNodeBlend2::_bind_methods() {
-}
-
AnimationNodeBlend2::AnimationNodeBlend2() {
add_input("in");
add_input("blend");
@@ -887,9 +878,6 @@ AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer::
return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough.
}
-void AnimationNodeBlend3::_bind_methods() {
-}
-
AnimationNodeBlend3::AnimationNodeBlend3() {
add_input("-blend");
add_input("in");
@@ -932,9 +920,6 @@ AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::Pl
return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
}
-void AnimationNodeSub2::_bind_methods() {
-}
-
AnimationNodeSub2::AnimationNodeSub2() {
add_input("in");
add_input("sub");
@@ -972,9 +957,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixe
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
}
-void AnimationNodeTimeScale::_bind_methods() {
-}
-
AnimationNodeTimeScale::AnimationNodeTimeScale() {
add_input("in");
}
@@ -1014,9 +996,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
}
-void AnimationNodeTimeSeek::_bind_methods() {
-}
-
AnimationNodeTimeSeek::AnimationNodeTimeSeek() {
add_input("in");
}
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index c7ef7ed624..2add35d009 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -200,9 +200,6 @@ class AnimationNodeAdd2 : public AnimationNodeSync {
StringName add_amount = PNAME("add_amount");
-protected:
- static void _bind_methods();
-
public:
void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -220,9 +217,6 @@ class AnimationNodeAdd3 : public AnimationNodeSync {
StringName add_amount = PNAME("add_amount");
-protected:
- static void _bind_methods();
-
public:
void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -240,9 +234,6 @@ class AnimationNodeBlend2 : public AnimationNodeSync {
StringName blend_amount = PNAME("blend_amount");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -259,9 +250,6 @@ class AnimationNodeBlend3 : public AnimationNodeSync {
StringName blend_amount = PNAME("blend_amount");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -277,9 +265,6 @@ class AnimationNodeSub2 : public AnimationNodeSync {
StringName sub_amount = PNAME("sub_amount");
-protected:
- static void _bind_methods();
-
public:
void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -297,9 +282,6 @@ class AnimationNodeTimeScale : public AnimationNode {
StringName scale = PNAME("scale");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -316,9 +298,6 @@ class AnimationNodeTimeSeek : public AnimationNode {
StringName seek_pos_request = PNAME("seek_request");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 6a61e8693d..f8bbd704f4 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -61,6 +61,11 @@ Ref<Tween> Tweener::_get_tween() {
return Ref<Tween>(ObjectDB::get_instance(tween_id));
}
+void Tweener::_finish() {
+ finished = true;
+ emit_signal(SceneStringName(finished));
+}
+
void Tweener::_bind_methods() {
ADD_SIGNAL(MethodInfo("finished"));
}
@@ -612,9 +617,8 @@ bool PropertyTweener::step(double &r_delta) {
return true;
} else {
target_instance->set_indexed(property, final_val);
- finished = true;
r_delta = elapsed_time - delay - duration;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
}
@@ -672,9 +676,8 @@ bool IntervalTweener::step(double &r_delta) {
r_delta = 0;
return true;
} else {
- finished = true;
r_delta = elapsed_time - duration;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
}
@@ -715,9 +718,8 @@ bool CallbackTweener::step(double &r_delta) {
ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
}
- finished = true;
r_delta = elapsed_time - delay;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
@@ -801,9 +803,8 @@ bool MethodTweener::step(double &r_delta) {
r_delta = 0;
return true;
} else {
- finished = true;
r_delta = elapsed_time - delay - duration;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index f5ae5e9776..40e1da0ad3 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -50,6 +50,7 @@ protected:
static void _bind_methods();
Ref<Tween> _get_tween();
+ void _finish();
double elapsed_time = 0;
bool finished = false;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index e90c1aa245..183c4af950 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -112,7 +112,7 @@ void AudioStreamPlayer::seek(float p_seconds) {
}
void AudioStreamPlayer::stop() {
- internal->stop();
+ internal->stop_basic();
}
bool AudioStreamPlayer::is_playing() const {
@@ -283,7 +283,7 @@ void AudioStreamPlayer::_bind_methods() {
}
AudioStreamPlayer::AudioStreamPlayer() {
- internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), false));
+ internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), callable_mp(this, &AudioStreamPlayer::stop), false));
}
AudioStreamPlayer::~AudioStreamPlayer() {
diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp
index 36c14e03d5..206408e3a7 100644
--- a/scene/audio/audio_stream_player_internal.cpp
+++ b/scene/audio/audio_stream_player_internal.cpp
@@ -132,7 +132,7 @@ Ref<AudioStreamPlayback> AudioStreamPlayerInternal::play_basic() {
}
ERR_FAIL_COND_V_MSG(!node->is_inside_tree(), stream_playback, "Playback can only happen when a node is inside the scene tree");
if (stream->is_monophonic() && is_playing()) {
- stop();
+ stop_callable.call();
}
stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_V_MSG(stream_playback.is_null(), stream_playback, "Failed to instantiate playback.");
@@ -242,7 +242,7 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) {
if (stream.is_valid()) {
stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayerInternal::_update_stream_parameters));
}
- stop();
+ stop_callable.call();
stream = p_stream;
_update_stream_parameters();
if (stream.is_valid()) {
@@ -253,12 +253,12 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) {
void AudioStreamPlayerInternal::seek(float p_seconds) {
if (is_playing()) {
- stop();
+ stop_callable.call();
play_callable.call(p_seconds);
}
}
-void AudioStreamPlayerInternal::stop() {
+void AudioStreamPlayerInternal::stop_basic() {
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
AudioServer::get_singleton()->stop_playback_stream(playback);
}
@@ -289,7 +289,7 @@ void AudioStreamPlayerInternal::set_playing(bool p_enable) {
if (p_enable) {
play_callable.call(0.0);
} else {
- stop();
+ stop_callable.call();
}
}
@@ -339,9 +339,10 @@ StringName AudioStreamPlayerInternal::get_bus() const {
return SceneStringName(Master);
}
-AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical) {
+AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical) {
node = p_node;
play_callable = p_play_callable;
+ stop_callable = p_stop_callable;
physical = p_physical;
bus = SceneStringName(Master);
diff --git a/scene/audio/audio_stream_player_internal.h b/scene/audio/audio_stream_player_internal.h
index ec4489067e..7d8faeba06 100644
--- a/scene/audio/audio_stream_player_internal.h
+++ b/scene/audio/audio_stream_player_internal.h
@@ -53,6 +53,7 @@ private:
Node *node = nullptr;
Callable play_callable;
+ Callable stop_callable;
bool physical = false;
AudioServer::PlaybackType playback_type = AudioServer::PlaybackType::PLAYBACK_TYPE_DEFAULT;
@@ -94,7 +95,7 @@ public:
Ref<AudioStreamPlayback> play_basic();
void seek(float p_seconds);
- void stop();
+ void stop_basic();
bool is_playing() const;
float get_playback_position();
@@ -110,7 +111,7 @@ public:
void set_playback_type(AudioServer::PlaybackType p_playback_type);
AudioServer::PlaybackType get_playback_type() const;
- AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical);
+ AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical);
};
#endif // AUDIO_STREAM_PLAYER_INTERNAL_H
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index d169e82e5d..2d425490d1 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -37,7 +37,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/main/canvas_layer.h"
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 1da3668ebe..e9fe78e162 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1448,6 +1448,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
bool line_clicked = false;
float text_rect_begin = 0.0;
int char_pos = -1;
+ bool char_clicked = false;
Line &l = p_frame->lines[p_line];
MutexLock lock(l.text_buf->get_mutex());
@@ -1578,6 +1579,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
+ if (!p_meta) {
+ char_pos = rtl ? TS->shaped_text_get_range(rid).y : TS->shaped_text_get_range(rid).x;
+ }
if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) {
if (p_meta) {
int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x);
@@ -1592,6 +1596,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
obj_rect.position.y += baseline_y;
if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
break;
}
@@ -1602,18 +1607,21 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size);
if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
} else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) {
// Hex code box.
Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index);
if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
}
}
} else {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
char_pos = TS->shaped_text_closest_character_pos(rid, char_pos);
+ char_clicked = true;
}
}
line_clicked = true;
@@ -1621,7 +1629,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
// If table hit was detected, and line hit is in the table bounds use table hit.
- if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) {
+ if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || !char_clicked)) {
if (r_click_frame != nullptr) {
*r_click_frame = table_click_frame;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index ac6ebd5cc1..5cdb0c3d44 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -38,7 +38,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/string/string_builder.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "scene/gui/label.h"
#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 23843938a4..c2704a3848 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -34,7 +34,7 @@
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/shortcut.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/variant/variant_parser.h"
#include "scene/gui/control.h"
#include "scene/theme/theme_db.h"
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index d124577d25..6a799e90ce 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -5275,11 +5275,26 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int
bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const {
Size2 size = get_tile_texture_region(p_atlas_coords).size;
- Rect2 rect = Rect2(-size / 2 - get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_origin(), size);
+ TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile);
+ if (tile_data->get_transpose()) {
+ size = Size2(size.y, size.x);
+ }
+ Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size);
return rect.has_point(p_position);
}
+bool TileSetAtlasSource::is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const {
+ Size2 size = get_tile_texture_region(p_atlas_coords).size;
+ TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile);
+ if (tile_data->get_transpose()) {
+ size = Size2(size.y, size.x);
+ }
+ Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size);
+
+ return p_rect.intersection(rect) == p_rect;
+}
+
int TileSetAtlasSource::alternative_no_transform(int p_alternative_id) {
return p_alternative_id & ~(TRANSFORM_FLIP_H | TRANSFORM_FLIP_V | TRANSFORM_TRANSPOSE);
}
diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h
index e083fa45b9..51df972c8d 100644
--- a/scene/resources/2d/tile_set.h
+++ b/scene/resources/2d/tile_set.h
@@ -763,6 +763,7 @@ public:
Vector2i get_atlas_grid_size() const;
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
+ bool is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const;
static int alternative_no_transform(int p_alternative_id);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 104187775d..6b65ea4cfb 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -1482,8 +1482,8 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
switch (block_type) {
case 1: /* info */ {
ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, "Invalid BMFont info block size.");
- base_size = f->get_16();
- if (base_size <= 0) {
+ base_size = ABS(static_cast<int16_t>(f->get_16()));
+ if (base_size == 0) {
base_size = 16;
}
uint8_t flags = f->get_8();
@@ -1776,7 +1776,10 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
if (type == "info") {
if (keys.has("size")) {
- base_size = keys["size"].to_int();
+ base_size = ABS(keys["size"].to_int());
+ if (base_size == 0) {
+ base_size = 16;
+ }
}
if (keys.has("outline")) {
outline = keys["outline"].to_int();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index fac3a2f663..1fa52b9c73 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -412,7 +412,6 @@ String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_t
}
VisualShaderNode::Category VisualShaderNode::get_category() const {
- WARN_PRINT(get_caption() + " is missing a category.");
return CATEGORY_NONE;
}
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 7ab150c141..e2c8686911 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -342,12 +342,6 @@ bool AudioStreamMicrophone::is_monophonic() const {
return true;
}
-void AudioStreamMicrophone::_bind_methods() {
-}
-
-AudioStreamMicrophone::AudioStreamMicrophone() {
-}
-
int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
AudioDriver::get_singleton()->lock();
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 0ca4777d5c..d02dc6aae7 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -219,9 +219,6 @@ class AudioStreamMicrophone : public AudioStream {
HashSet<AudioStreamPlaybackMicrophone *> playbacks;
-protected:
- static void _bind_methods();
-
public:
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
@@ -229,8 +226,6 @@ public:
virtual double get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
-
- AudioStreamMicrophone();
};
class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled {
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 54840adcae..f0f894d03b 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1379,6 +1379,12 @@ bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) {
float AudioServer::get_playback_position(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), 0);
+ // Samples.
+ if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) {
+ Ref<AudioSamplePlayback> sample_playback = p_playback->get_sample_playback();
+ return AudioServer::get_singleton()->get_sample_playback_position(sample_playback);
+ }
+
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return 0;
@@ -1847,6 +1853,11 @@ bool AudioServer::is_sample_playback_active(const Ref<AudioSamplePlayback> &p_pl
return AudioDriver::get_singleton()->is_sample_playback_active(p_playback);
}
+double AudioServer::get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) {
+ ERR_FAIL_COND_V_MSG(p_playback.is_null(), false, "Parameter p_playback is null.");
+ return AudioDriver::get_singleton()->get_sample_playback_position(p_playback);
+}
+
void AudioServer::update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
return AudioDriver::get_singleton()->update_sample_playback_pitch_scale(p_playback, p_pitch_scale);
diff --git a/servers/audio_server.h b/servers/audio_server.h
index fd6cdb451e..2d6fc60860 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -141,6 +141,7 @@ public:
virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {}
virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) {}
virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) { return false; }
+ virtual double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) { return false; }
virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) {}
virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) {}
@@ -484,6 +485,7 @@ public:
void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback);
void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused);
bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback);
+ double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback);
void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f);
AudioServer();
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index 4a630b0b0a..67ee84b01b 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -2242,7 +2242,7 @@ void fragment_shader(in SceneData scene_data) {
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // ALPHA_SCISSOR_USED
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 530a7a37db..24568d7e94 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -1765,7 +1765,7 @@ void main() {
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // !ALPHA_SCISSOR_USED
diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp
index ac4fbba75b..ddc4d09279 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp
@@ -30,9 +30,6 @@
#include "render_data_rd.h"
-void RenderDataRD::_bind_methods() {
-}
-
Ref<RenderSceneBuffers> RenderDataRD::get_render_scene_buffers() const {
return render_buffers;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h
index 3cd397b8ed..6ebb85442b 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h
@@ -38,9 +38,6 @@
class RenderDataRD : public RenderData {
GDCLASS(RenderDataRD, RenderData);
-protected:
- static void _bind_methods();
-
public:
// Access methods to expose data externally
virtual Ref<RenderSceneBuffers> get_render_scene_buffers() const override;
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
index ba8aafda6d..148a556b46 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
@@ -34,9 +34,6 @@
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_default.h"
-void RenderSceneDataRD::_bind_methods() {
-}
-
Transform3D RenderSceneDataRD::get_cam_transform() const {
return cam_transform;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
index 5579a97792..b2c93acd44 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
@@ -95,8 +95,6 @@ public:
virtual RID get_uniform_buffer() const override;
private:
- static void _bind_methods();
-
RID uniform_buffer; // loaded into this uniform buffer (supplied externally)
// This struct is loaded into Set 1 - Binding 0, populated at start of rendering a frame, must match with shader code
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 431dca0ad4..66fcefe228 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -898,6 +898,13 @@ bool ShaderLanguage::_lookup_next(Token &r_tk) {
return false;
}
+ShaderLanguage::Token ShaderLanguage::_peek() {
+ TkPos pre_pos = _get_tkpos();
+ Token tk = _get_token();
+ _set_tkpos(pre_pos);
+ return tk;
+}
+
String ShaderLanguage::token_debug(const String &p_code) {
clear();
@@ -8061,7 +8068,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!expr) {
return ERR_PARSE_ERROR;
}
- is_condition = expr->type == Node::NODE_TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL;
+ is_condition = expr->get_datatype() == TYPE_BOOL;
if (expr->type == Node::NODE_TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(expr);
@@ -8077,7 +8084,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
if (tk.type == TK_COMMA) {
if (!is_condition) {
- _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ _set_error(RTR("The middle expression is expected to have a boolean data type."));
+ return ERR_PARSE_ERROR;
+ }
+ tk = _peek();
+ if (tk.type == TK_SEMICOLON) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
return ERR_PARSE_ERROR;
}
continue;
@@ -8088,6 +8100,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
} else if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION) {
if (tk.type == TK_COMMA) {
+ tk = _peek();
+ if (tk.type == TK_PARENTHESIS_CLOSE) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
+ return ERR_PARSE_ERROR;
+ }
continue;
}
if (tk.type != TK_PARENTHESIS_CLOSE) {
@@ -8106,7 +8123,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION && !is_condition) {
- _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ _set_error(RTR("The middle expression is expected to have a boolean data type."));
return ERR_PARSE_ERROR;
}
}
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 4a67c8f2d2..ad3b78546d 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -1037,6 +1037,7 @@ private:
Token _make_token(TokenType p_type, const StringName &p_text = StringName());
Token _get_token();
bool _lookup_next(Token &r_tk);
+ Token _peek();
ShaderNode *shader = nullptr;
diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h
index a5ab444f55..1a945ac221 100644
--- a/servers/text/text_server_dummy.h
+++ b/servers/text/text_server_dummy.h
@@ -88,6 +88,7 @@ public:
virtual int64_t font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const override { return 0; }
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override { return false; }
virtual String font_get_supported_chars(const RID &p_font_rid) const override { return String(); }
+ virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override { return PackedInt32Array(); };
virtual void font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {}
virtual void font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {}
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 509d49a1e4..d387c8ff7e 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -196,6 +196,7 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_font_has_char, "font_rid", "char");
GDVIRTUAL_BIND(_font_get_supported_chars, "font_rid");
+ GDVIRTUAL_BIND(_font_get_supported_glyphs, "font_rid");
GDVIRTUAL_BIND(_font_render_range, "font_rid", "size", "start", "end");
GDVIRTUAL_BIND(_font_render_glyph, "font_rid", "size", "index");
@@ -927,6 +928,12 @@ String TextServerExtension::font_get_supported_chars(const RID &p_font_rid) cons
return ret;
}
+PackedInt32Array TextServerExtension::font_get_supported_glyphs(const RID &p_font_rid) const {
+ PackedInt32Array ret;
+ GDVIRTUAL_REQUIRED_CALL(_font_get_supported_glyphs, p_font_rid, ret);
+ return ret;
+}
+
void TextServerExtension::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
GDVIRTUAL_CALL(_font_render_range, p_font_rid, p_size, p_start, p_end);
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 16a03b6592..52654c010c 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -323,8 +323,10 @@ public:
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override;
virtual String font_get_supported_chars(const RID &p_font_rid) const override;
+ virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override;
GDVIRTUAL2RC(bool, _font_has_char, RID, int64_t);
GDVIRTUAL1RC(String, _font_get_supported_chars, RID);
+ GDVIRTUAL1RC(PackedInt32Array, _font_get_supported_glyphs, RID);
virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override;
virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index e7a1511064..f391c79514 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -352,6 +352,7 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char);
ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars);
+ ClassDB::bind_method(D_METHOD("font_get_supported_glyphs", "font_rid"), &TextServer::font_get_supported_glyphs);
ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range);
ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph);
@@ -1563,7 +1564,7 @@ int64_t TextServer::shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p
int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const {
const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
- int64_t prev = 0;
+ int64_t prev = shaped_text_get_range(p_shaped).x;
for (const int32_t &E : chars) {
if (E >= p_pos) {
return prev;
@@ -1575,7 +1576,7 @@ int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t
int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const {
const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
- int64_t prev = 0;
+ int64_t prev = shaped_text_get_range(p_shaped).x;
for (const int32_t &E : chars) {
if (E > p_pos) {
return E;
@@ -1587,7 +1588,7 @@ int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t
int64_t TextServer::shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const {
const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
- int64_t prev = 0;
+ int64_t prev = shaped_text_get_range(p_shaped).x;
for (const int32_t &E : chars) {
if (E == p_pos) {
return E;
diff --git a/servers/text_server.h b/servers/text_server.h
index a77953e6f2..ba3fdaa35e 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -396,6 +396,7 @@ public:
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const = 0;
virtual String font_get_supported_chars(const RID &p_font_rid) const = 0;
+ virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const = 0;
virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) = 0;
virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) = 0;
diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h
index acdd851b29..7c389191e3 100644
--- a/tests/core/string/test_translation.h
+++ b/tests/core/string/test_translation.h
@@ -34,6 +34,7 @@
#include "core/string/optimized_translation.h"
#include "core/string/translation.h"
#include "core/string/translation_po.h"
+#include "core/string/translation_server.h"
#ifdef TOOLS_ENABLED
#include "editor/import/resource_importer_csv_translation.h"
diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h
index 2c20574309..ac1599f2e8 100644
--- a/tests/core/string/test_translation_server.h
+++ b/tests/core/string/test_translation_server.h
@@ -31,7 +31,7 @@
#ifndef TEST_TRANSLATION_SERVER_H
#define TEST_TRANSLATION_SERVER_H
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "tests/test_macros.h"