summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp3
-rw-r--r--core/extension/gdextension.cpp4
-rw-r--r--doc/classes/Animation.xml5
-rw-r--r--doc/classes/AnimationMixer.xml14
-rw-r--r--doc/classes/AnimationTree.xml1
-rw-r--r--doc/classes/CPUParticles3D.xml4
-rw-r--r--doc/classes/Control.xml3
-rw-r--r--doc/classes/EditorSettings.xml56
-rw-r--r--doc/classes/GDExtension.xml24
-rw-r--r--doc/classes/MultiMesh.xml4
-rw-r--r--doc/classes/Node.xml46
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/PropertyTweener.xml19
-rw-r--r--doc/classes/RenderingServer.xml15
-rw-r--r--doc/classes/VisibleOnScreenEnabler2D.xml2
-rw-r--r--doc/classes/VisibleOnScreenEnabler3D.xml2
-rw-r--r--doc/classes/Window.xml2
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp31
-rw-r--r--drivers/gles3/storage/mesh_storage.h3
-rw-r--r--drivers/gles3/storage/utilities.cpp5
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp29
-rw-r--r--editor/animation_track_editor.cpp82
-rw-r--r--editor/connections_dialog.cpp13
-rw-r--r--editor/connections_dialog.h2
-rw-r--r--editor/create_dialog.cpp2
-rw-r--r--editor/dependency_editor.cpp2
-rw-r--r--editor/editor_about.cpp2
-rw-r--r--editor/editor_feature_profile.cpp2
-rw-r--r--editor/editor_layouts_dialog.cpp2
-rw-r--r--editor/editor_node.cpp131
-rw-r--r--editor/editor_node.h5
-rw-r--r--editor/editor_properties.cpp6
-rw-r--r--editor/editor_property_name_processor.cpp1
-rw-r--r--editor/editor_resource_picker.cpp2
-rw-r--r--editor/editor_resource_preview.cpp1
-rw-r--r--editor/editor_settings.cpp50
-rw-r--r--editor/export/editor_export.cpp14
-rw-r--r--editor/export/editor_export.h5
-rw-r--r--editor/export/editor_export_platform_pc.cpp31
-rw-r--r--editor/export/editor_export_preset.cpp1
-rw-r--r--editor/export/export_template_manager.cpp72
-rw-r--r--editor/export/export_template_manager.h12
-rw-r--r--editor/export/project_export.cpp2
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/gui/editor_file_dialog.cpp9
-rw-r--r--editor/gui/editor_object_selector.cpp4
-rw-r--r--editor/gui/editor_scene_tabs.cpp2
-rw-r--r--editor/gui/editor_spin_slider.cpp78
-rw-r--r--editor/gui/editor_spin_slider.h3
-rw-r--r--editor/gui/scene_tree_editor.cpp2
-rw-r--r--editor/history_dock.cpp2
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp2
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.cpp68
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.h5
-rw-r--r--editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp47
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.cpp48
-rw-r--r--editor/plugins/script_editor_plugin.cpp6
-rw-r--r--editor/plugins/shader_editor_plugin.cpp2
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp2
-rw-r--r--editor/plugins/theme_editor_plugin.cpp4
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp8
-rw-r--r--editor/plugins/tiles/tile_proxies_manager_dialog.cpp6
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp8
-rw-r--r--editor/project_manager/project_list.cpp2
-rw-r--r--editor/project_manager/project_tag.cpp2
-rw-r--r--editor/project_settings_editor.cpp1
-rw-r--r--editor/scene_create_dialog.cpp2
-rw-r--r--misc/extension_api_validation/4.2-stable.expected20
-rw-r--r--modules/basis_universal/register_types.cpp4
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp2
-rw-r--r--modules/ktx/texture_loader_ktx.cpp2
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml16
-rw-r--r--platform/android/export/export_plugin.cpp171
-rw-r--r--platform/android/export/export_plugin.h17
-rw-r--r--platform/android/export/gradle_export_util.cpp8
-rw-r--r--platform/android/export/gradle_export_util.h3
-rw-r--r--platform/android/java/app/build.gradle8
-rw-r--r--platform/android/java/app/config.gradle23
-rw-r--r--platform/android/java/lib/build.gradle3
-rw-r--r--platform/ios/export/export_plugin.cpp34
-rw-r--r--platform/ios/export/export_plugin.h4
-rw-r--r--platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml14
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp8
-rw-r--r--platform/linuxbsd/os_linuxbsd.h2
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp31
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.h2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp116
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.h4
-rw-r--r--platform/windows/display_server_windows.cpp51
-rw-r--r--platform/windows/doc_classes/EditorExportPlatformWindows.xml14
-rw-r--r--scene/2d/tile_map.cpp8
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.cpp9
-rw-r--r--scene/3d/cpu_particles_3d.cpp20
-rw-r--r--scene/3d/cpu_particles_3d.h5
-rw-r--r--scene/3d/soft_body_3d.cpp5
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp9
-rw-r--r--scene/animation/animation_mixer.cpp99
-rw-r--r--scene/animation/animation_mixer.h15
-rw-r--r--scene/animation/animation_tree.cpp1
-rw-r--r--scene/animation/tween.cpp28
-rw-r--r--scene/animation/tween.h2
-rw-r--r--scene/gui/control.cpp33
-rw-r--r--scene/gui/control.h6
-rw-r--r--scene/gui/menu_button.cpp4
-rw-r--r--scene/gui/option_button.cpp5
-rw-r--r--scene/main/node.cpp87
-rw-r--r--scene/main/node.h24
-rw-r--r--scene/main/scene_tree.cpp1
-rw-r--r--scene/main/viewport.cpp1
-rw-r--r--scene/main/window.cpp32
-rw-r--r--scene/main/window.h12
-rw-r--r--scene/resources/animation.compat.inc61
-rw-r--r--scene/resources/animation.cpp49
-rw-r--r--scene/resources/animation.h27
-rw-r--r--scene/resources/compressed_texture.cpp1
-rw-r--r--scene/resources/multimesh.cpp13
-rw-r--r--scene/resources/multimesh.h4
-rw-r--r--servers/rendering/dummy/rasterizer_dummy.h2
-rw-r--r--servers/rendering/dummy/storage/mesh_storage.h9
-rw-r--r--servers/rendering/dummy/storage/utilities.h2
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp32
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/utilities.cpp2
-rw-r--r--servers/rendering/rendering_device_graph.cpp12
-rw-r--r--servers/rendering/rendering_device_graph.h2
-rw-r--r--servers/rendering/rendering_server_default.h3
-rw-r--r--servers/rendering/storage/mesh_storage.h3
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--servers/rendering_server.h3
141 files changed, 1692 insertions, 565 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 4fd6ab9028..d3cbae2f29 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1430,6 +1430,9 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true);
GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false);
+ GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true);
+ GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true);
+
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"), "res://default_bus_layout.tres");
GLOBAL_DEF_RST("audio/general/text_to_speech", false);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index fa4e7eb2e2..9b3282ecba 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -807,12 +807,8 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) {
}
void GDExtension::_bind_methods() {
- ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library);
- ClassDB::bind_method(D_METHOD("close_library"), &GDExtension::close_library);
ClassDB::bind_method(D_METHOD("is_library_open"), &GDExtension::is_library_open);
-
ClassDB::bind_method(D_METHOD("get_minimum_library_initialization_level"), &GDExtension::get_minimum_library_initialization_level);
- ClassDB::bind_method(D_METHOD("initialize_library", "level"), &GDExtension::initialize_library);
BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_CORE);
BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SERVERS);
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index e04320c30d..ebdca643ce 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -234,6 +234,7 @@
<return type="float" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
+ <param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated blend shape value at the given time (in seconds). The [param track_idx] must be the index of a blend shape track.
</description>
@@ -305,6 +306,7 @@
<return type="Vector3" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
+ <param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated position value at the given time (in seconds). The [param track_idx] must be the index of a 3D position track.
</description>
@@ -329,6 +331,7 @@
<return type="Quaternion" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
+ <param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated rotation value at the given time (in seconds). The [param track_idx] must be the index of a 3D rotation track.
</description>
@@ -346,6 +349,7 @@
<return type="Vector3" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
+ <param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated scale value at the given time (in seconds). The [param track_idx] must be the index of a 3D scale track.
</description>
@@ -574,6 +578,7 @@
<return type="Variant" />
<param index="0" name="track_idx" type="int" />
<param index="1" name="time_sec" type="float" />
+ <param index="2" name="backward" type="bool" default="false" />
<description>
Returns the interpolated value at the given time (in seconds). The [param track_idx] must be the index of a value track.
</description>
diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml
index d17e9377da..e521baffda 100644
--- a/doc/classes/AnimationMixer.xml
+++ b/doc/classes/AnimationMixer.xml
@@ -273,6 +273,11 @@
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers.
For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each.
</member>
+ <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="1">
+ Ordinarily, tracks can be set to [constant Animation.UPDATE_DISCRETE] to update infrequently, usually when using nearest interpolation.
+ However, when blending with [constant Animation.UPDATE_CONTINUOUS] several results are considered. The [member callback_mode_discrete] specify it explicitly. See also [enum AnimationCallbackModeDiscrete].
+ To make the blended results look good, it is recommended to set this to [constant ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to update every frame during blending. Other values exist for compatibility and they are fine if there is no blending, but not so, may produce artifacts.
+ </member>
<member name="callback_mode_method" type="int" setter="set_callback_mode_method" getter="get_callback_mode_method" enum="AnimationMixer.AnimationCallbackModeMethod" default="0">
The call mode to use for Call Method tracks.
</member>
@@ -350,5 +355,14 @@
<constant name="ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE" value="1" enum="AnimationCallbackModeMethod">
Make method calls immediately when reached in the animation.
</constant>
+ <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT" value="0" enum="AnimationCallbackModeDiscrete">
+ An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values.
+ </constant>
+ <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE" value="1" enum="AnimationCallbackModeDiscrete">
+ An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer].
+ </constant>
+ <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete">
+ Always treat the [constant Animation.UPDATE_DISCRETE] track value as [constant Animation.UPDATE_CONTINUOUS] with [constant Animation.INTERPOLATION_NEAREST]. This is the default behavior for [AnimationTree].
+ </constant>
</constants>
</class>
diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml
index 3e0c088b8a..778b5cf513 100644
--- a/doc/classes/AnimationTree.xml
+++ b/doc/classes/AnimationTree.xml
@@ -31,6 +31,7 @@
<member name="anim_player" type="NodePath" setter="set_animation_player" getter="get_animation_player" default="NodePath(&quot;&quot;)">
The path to the [AnimationPlayer] used for animating.
</member>
+ <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" overrides="AnimationMixer" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="2" />
<member name="deterministic" type="bool" setter="set_deterministic" getter="is_deterministic" overrides="AnimationMixer" default="true" />
<member name="tree_root" type="AnimationRootNode" setter="set_tree_root" getter="get_tree_root">
The root animation node of this [AnimationTree]. See [AnimationRootNode].
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index 27404b68bb..a6f85e7fe5 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -309,6 +309,10 @@
<member name="tangential_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0">
Minimum tangent acceleration.
</member>
+ <member name="visibility_aabb" type="AABB" setter="set_visibility_aabb" getter="get_visibility_aabb" default="AABB(-4, -4, -4, 8, 8, 8)">
+ The [AABB] that determines the node's region which needs to be visible on screen for the particle system to be active.
+ Grow the box if particles suddenly appear/disappear when the node enters/exits the screen. The [AABB] can be grown via code or with the [b]Particles → Generate AABB[/b] editor tool.
+ </member>
</members>
<signals>
<signal name="finished">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index c3ec2b9e3c..07f41192f7 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -937,9 +937,8 @@
<member name="anchor_top" type="float" setter="_set_anchor" getter="get_anchor" default="0.0">
Anchors the top edge of the node to the origin, the center or the end of its parent control. It changes how the top offset updates when the node moves or changes size. You can use one of the [enum Anchor] constants for convenience.
</member>
- <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true">
+ <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true" deprecated="Use [member Node.auto_translate_mode] instead.">
Toggles if any text should automatically change to its translated version depending on the current locale.
- Also decides if the node's strings should be parsed for POT generation.
</member>
<member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" default="false">
Enables whether rendering of [CanvasItem] based children should be clipped to this control's rectangle. If [code]true[/code], parts of a child which would be visibly outside of this control's rectangle will not be rendered and won't receive input.
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 32123d59bf..5dcfdb21b1 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -590,8 +590,13 @@
<member name="interface/editor/font_subpixel_positioning" type="int" setter="" getter="">
The subpixel positioning mode to use when rendering editor font glyphs. This affects both the main and code fonts. [b]Disabled[/b] is the fastest to render and uses the least memory. [b]Auto[/b] only uses subpixel positioning for small font sizes (where the benefit is the most noticeable). [b]One Half of a Pixel[/b] and [b]One Quarter of a Pixel[/b] force the same subpixel positioning mode for all editor fonts, regardless of their size (with [b]One Quarter of a Pixel[/b] being the highest-quality option).
</member>
- <member name="interface/editor/low_processor_mode_sleep_usec" type="float" setter="" getter="">
+ <member name="interface/editor/localize_settings" type="bool" setter="" getter="">
+ If [code]true[/code], setting names in the Editor Settings are localized when possible. This is disabled by default, since localization can make it difficult to look up setting names online.
+ [b]Note:[/b] This setting does not control property name localization in the inspector. To show translated property names in the inspector, set [member interface/inspector/default_property_name_style] to [b]Localized[/b].
+ </member>
+ <member name="interface/editor/low_processor_mode_sleep_usec" type="int" setter="" getter="">
The amount of sleeping between frames when the low-processor usage mode is enabled (in microseconds). Higher values will result in lower CPU/GPU usage, which can improve battery life on laptops. However, higher values will result in a less responsive editor. The default value is set to allow for maximum smoothness on monitors up to 144 Hz. See also [member interface/editor/unfocused_low_processor_mode_sleep_usec].
+ [b]Note:[/b] This setting is ignored if [member interface/editor/update_continuously] is [code]true[/code], as enabling that setting disables low-processor mode.
</member>
<member name="interface/editor/main_font" type="String" setter="" getter="">
The font to use for the editor interface. Must be a resource of a [Font] type such as a [code].ttf[/code] or [code].otf[/code] font file.
@@ -611,6 +616,9 @@
<member name="interface/editor/save_each_scene_on_quit" type="bool" setter="" getter="">
If [code]false[/code], the editor will save all scenes when confirming the [b]Save[/b] action when quitting the editor or quitting to the project list. If [code]true[/code], the editor will ask to save each scene individually.
</member>
+ <member name="interface/editor/save_on_focus_loss" type="bool" setter="" getter="">
+ If [code]true[/code], scenes and scripts are saved when the editor loses focus. Depending on the work flow, this behavior can be less intrusive than [member text_editor/behavior/files/autosave_interval_secs] or remembering to save manually.
+ </member>
<member name="interface/editor/separate_distraction_mode" type="bool" setter="" getter="">
If [code]true[/code], the editor's Script tab will have a separate distraction mode setting from the 2D/3D/AssetLib tabs. If [code]false[/code], the distraction-free mode toggle is shared between all tabs.
</member>
@@ -622,6 +630,7 @@
If enabled, displays an icon in the top-right corner of the editor that spins when the editor redraws a frame. This can be used to diagnose situations where the engine is constantly redrawing, which should be avoided as this increases CPU and GPU utilization for no good reason. To further troubleshoot these situations, start the editor with the [code]--debug-canvas-item-redraw[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url].
Consider enabling this if you are developing editor plugins to ensure they only make the editor redraw when required.
The default [b]Auto[/b] value will only enable this if the editor was compiled with the [code]dev_build=yes[/code] SCons option (the default is [code]dev_build=no[/code]).
+ [b]Note:[/b] If [member interface/editor/update_continuously] is [code]true[/code], the spinner icon displays in red.
</member>
<member name="interface/editor/single_window_mode" type="bool" setter="" getter="">
If [code]true[/code], embed modal windows such as docks inside the main editor window. When single-window mode is enabled, tooltips will also be embedded inside the main editor window, which means they can't be displayed outside of the editor window.
@@ -630,8 +639,13 @@
<member name="interface/editor/ui_layout_direction" type="int" setter="" getter="">
Editor UI default layout direction.
</member>
- <member name="interface/editor/unfocused_low_processor_mode_sleep_usec" type="float" setter="" getter="">
+ <member name="interface/editor/unfocused_low_processor_mode_sleep_usec" type="int" setter="" getter="">
When the editor window is unfocused, the amount of sleeping between frames when the low-processor usage mode is enabled (in microseconds). Higher values will result in lower CPU/GPU usage, which can improve battery life on laptops (in addition to improving the running project's performance if the editor has to redraw continuously). However, higher values will result in a less responsive editor. The default value is set to limit the editor to 20 FPS when the editor window is unfocused. See also [member interface/editor/low_processor_mode_sleep_usec].
+ [b]Note:[/b] This setting is ignored if [member interface/editor/update_continuously] is [code]true[/code], as enabling that setting disables low-processor mode.
+ </member>
+ <member name="interface/editor/update_continuously" type="bool" setter="" getter="">
+ If [code]true[/code], redraws the editor every frame even if nothing has changed on screen. When this setting is enabled, the update spinner displays in red (see [member interface/editor/show_update_spinner]).
+ [b]Warning:[/b] This greatly increases CPU and GPU utilization, leading to increased power usage. This should only be enabled for troubleshooting purposes.
</member>
<member name="interface/editor/use_embedded_menu" type="bool" setter="" getter="">
If [code]true[/code], editor main menu is using embedded [MenuBar] instead of system global menu.
@@ -642,12 +656,46 @@
Depending on the platform and used renderer, the engine will fall back to [b]Enabled[/b] if the desired mode is not supported.
[b]Note:[/b] V-Sync modes other than [b]Enabled[/b] are only supported in the Forward+ and Mobile rendering methods, not Compatibility.
</member>
+ <member name="interface/inspector/auto_unfold_foreign_scenes" type="bool" setter="" getter="">
+ If [code]true[/code], automatically expands property groups in the Inspector dock when opening a scene that hasn't been opened previously. If [code]false[/code], all groups remain collapsed by default.
+ </member>
+ <member name="interface/inspector/default_color_picker_mode" type="int" setter="" getter="">
+ The default color picker mode to use when opening [ColorPicker]s in the editor. This mode can be temporarily adjusted on the color picker itself.
+ </member>
+ <member name="interface/inspector/default_color_picker_shape" type="int" setter="" getter="">
+ The default color picker shape to use when opening [ColorPicker]s in the editor. This shape can be temporarily adjusted on the color picker itself.
+ </member>
+ <member name="interface/inspector/default_float_step" type="float" setter="" getter="">
+ The floating-point precision to use for properties that don't define an explicit precision step. Lower values allow entering more precise values.
+ </member>
+ <member name="interface/inspector/default_property_name_style" type="int" setter="" getter="">
+ The property name style to display in the inspector.
+ - [b]Raw:[/b] Displays properties as they are defined in the script (typically in [code]snake_case[/code] for GDScript and [code]PascalCase[/code] for C#).
+ - [b]Capitalized:[/b] Displays properties using [method String.capitalize].
+ - [b]Localized:[/b] Displays the localized string for the current editor language if a translation is available for the given property. If no translation is available, falls back to [b]Capitalized[/b].
+ [b]Note:[/b] This setting does not control editor setting name display. To display translated editor setting names, enable [member interface/editor/localize_settings] instead.
+ </member>
+ <member name="interface/inspector/disable_folding" type="bool" setter="" getter="">
+ If [code]true[/code], forces all property groups to be expanded in the Inspector dock and prevents collapsing them.
+ </member>
<member name="interface/inspector/float_drag_speed" type="float" setter="" getter="">
Base speed for increasing/decreasing float values by dragging them in the inspector.
</member>
+ <member name="interface/inspector/horizontal_vector2_editing" type="bool" setter="" getter="">
+ If [code]true[/code], [Vector2] and [Vector2i] properties are shown on a single line in the inspector instead of two lines. This is overall more compact, but it can be harder to view and edit large values without expanding the inspector horizontally.
+ </member>
+ <member name="interface/inspector/horizontal_vector_types_editing" type="bool" setter="" getter="">
+ If [code]true[/code], [Vector3], [Vector3i], [Vector4], [Vector4i], [Rect2], [Rect2i], [Plane], and [Quaternion] properties are shown on a single line in the inspector instead of multiple lines. This is overall more compact, but it can be harder to view and edit large values without expanding the inspector horizontally.
+ </member>
<member name="interface/inspector/max_array_dictionary_items_per_page" type="int" setter="" getter="">
The number of [Array] or [Dictionary] items to display on each "page" in the inspector. Higher values allow viewing more values per page, but take more time to load. This increased load time is noticeable when selecting nodes that have array or dictionary properties in the editor.
</member>
+ <member name="interface/inspector/open_resources_in_current_inspector" type="bool" setter="" getter="">
+ If [code]true[/code], subresources can be edited in the current inspector view. If the resource type is defined in [member interface/inspector/resources_to_open_in_new_inspector] or if this setting is [code]false[/code], attempting to edit a subresource always opens a new inspector view.
+ </member>
+ <member name="interface/inspector/resources_to_open_in_new_inspector" type="PackedStringArray" setter="" getter="">
+ List of resources that should always be opened in a new inspector view, even if [member interface/inspector/open_resources_in_current_inspector] is [code]true[/code].
+ </member>
<member name="interface/inspector/show_low_level_opentype_features" type="bool" setter="" getter="">
If [code]true[/code], display OpenType features marked as [code]hidden[/code] by the font file in the [Font] editor.
</member>
@@ -669,6 +717,10 @@
<member name="interface/scene_tabs/maximum_width" type="int" setter="" getter="">
The maximum width of each scene tab at the top editor (in pixels).
</member>
+ <member name="interface/scene_tabs/restore_scenes_on_load" type="bool" setter="" getter="">
+ If [code]true[/code], when a project is loaded, restores scenes that were opened on the last editor session.
+ [b]Note:[/b] With many opened scenes, the editor may take longer to become usable. If starting the editor quickly is necessary, consider setting this to [code]false[/code].
+ </member>
<member name="interface/scene_tabs/show_script_button" type="bool" setter="" getter="">
If [code]true[/code], show a button next to each scene tab that opens the scene's "dominant" script when clicked. The "dominant" script is the one that is at the highest level in the scene's hierarchy.
</member>
diff --git a/doc/classes/GDExtension.xml b/doc/classes/GDExtension.xml
index 533b32218f..c2d46dcf9e 100644
--- a/doc/classes/GDExtension.xml
+++ b/doc/classes/GDExtension.xml
@@ -12,42 +12,18 @@
<link title="GDExtension example in C++">$DOCS_URL/tutorials/scripting/gdextension/gdextension_cpp_example.html</link>
</tutorials>
<methods>
- <method name="close_library">
- <return type="void" />
- <description>
- Closes the current library.
- [b]Note:[/b] You normally should not call this method directly. This is handled automatically by [method GDExtensionManager.unload_extension].
- </description>
- </method>
<method name="get_minimum_library_initialization_level" qualifiers="const">
<return type="int" enum="GDExtension.InitializationLevel" />
<description>
Returns the lowest level required for this extension to be properly initialized (see the [enum InitializationLevel] enum).
</description>
</method>
- <method name="initialize_library">
- <return type="void" />
- <param index="0" name="level" type="int" enum="GDExtension.InitializationLevel" />
- <description>
- Initializes the library bound to this GDextension at the given initialization [param level].
- [b]Note:[/b] You normally should not call this method directly. This is handled automatically by [method GDExtensionManager.load_extension].
- </description>
- </method>
<method name="is_library_open" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this extension's library has been opened.
</description>
</method>
- <method name="open_library">
- <return type="int" enum="Error" />
- <param index="0" name="path" type="String" />
- <param index="1" name="entry_symbol" type="String" />
- <description>
- Opens the library at the specified [param path].
- [b]Note:[/b] You normally should not call this method directly. This is handled automatically by [method GDExtensionManager.load_extension].
- </description>
- </method>
</methods>
<constants>
<constant name="INITIALIZATION_LEVEL_CORE" value="0" enum="InitializationLevel">
diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml
index 5539213619..cd6397137b 100644
--- a/doc/classes/MultiMesh.xml
+++ b/doc/classes/MultiMesh.xml
@@ -92,7 +92,11 @@
</member>
<member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array" deprecated="Use [method set_instance_color] instead.">
</member>
+ <member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
+ Custom AABB for this MultiMesh resource. Setting this manually prevents costly runtime AABB recalculations.
+ </member>
<member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array" deprecated="Use [method set_instance_custom_data] instead.">
+ See [method set_instance_custom_data].
</member>
<member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0">
Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect.
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index ada4bd04e6..a46bb593d4 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -186,6 +186,32 @@
[b]Note:[/b] [SceneTree]'s group methods will [i]not[/i] work on this node if not inside the tree (see [method is_inside_tree]).
</description>
</method>
+ <method name="atr" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="message" type="String" />
+ <param index="1" name="context" type="StringName" default="&quot;&quot;" />
+ <description>
+ Translates a [param message], using the translation catalogs configured in the Project Settings. Further [param context] can be specified to help with the translation.
+ This method works the same as [method Object.tr], with the addition of respecting the [member auto_translate_mode] state.
+ If [method Object.can_translate_messages] is [code]false[/code], or no translation is available, this method returns the [param message] without changes. See [method Object.set_message_translation].
+ For detailed examples, see [url=$DOCS_URL/tutorials/i18n/internationalizing_games.html]Internationalizing games[/url].
+ </description>
+ </method>
+ <method name="atr_n" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="message" type="String" />
+ <param index="1" name="plural_message" type="StringName" />
+ <param index="2" name="n" type="int" />
+ <param index="3" name="context" type="StringName" default="&quot;&quot;" />
+ <description>
+ Translates a [param message] or [param plural_message], using the translation catalogs configured in the Project Settings. Further [param context] can be specified to help with the translation.
+ This method works the same as [method Object.tr_n], with the addition of respecting the [member auto_translate_mode] state.
+ If [method Object.can_translate_messages] is [code]false[/code], or no translation is available, this method returns [param message] or [param plural_message], without changes. See [method Object.set_message_translation].
+ The [param n] is the number, or amount, of the message's subject. It is used by the translation system to fetch the correct plural form for the current language.
+ For detailed examples, see [url=$DOCS_URL/tutorials/i18n/localization_using_gettext.html]Localization using gettext[/url].
+ [b]Note:[/b] Negative and [float] numbers may not properly apply to some countable subjects. It's recommended to handle these cases with [method atr].
+ </description>
+ </method>
<method name="call_deferred_thread_group" qualifiers="vararg">
<return type="Variant" />
<param index="0" name="method" type="StringName" />
@@ -203,7 +229,7 @@
<method name="can_process" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if the node can receive processing notifications and input callbacks ([constant NOTIFICATION_PROCESS], [method _input], etc) from the [SceneTree] and [Viewport]. The returned value depends on [member process_mode]:
+ Returns [code]true[/code] if the node can receive processing notifications and input callbacks ([constant NOTIFICATION_PROCESS], [method _input], etc.) from the [SceneTree] and [Viewport]. The returned value depends on [member process_mode]:
- If set to [constant PROCESS_MODE_PAUSABLE], returns [code]true[/code] when the game is processing, i.e. [member SceneTree.paused] is [code]false[/code];
- If set to [constant PROCESS_MODE_WHEN_PAUSED], returns [code]true[/code] when the game is paused, i.e. [member SceneTree.paused] is [code]true[/code];
- If set to [constant PROCESS_MODE_ALWAYS], always returns [code]true[/code];
@@ -923,6 +949,10 @@
</method>
</methods>
<members>
+ <member name="auto_translate_mode" type="int" setter="set_auto_translate_mode" getter="get_auto_translate_mode" enum="Node.AutoTranslateMode" default="0">
+ Defines if any text should automatically change to its translated version depending on the current locale (for nodes such as [Label], [RichTextLabel], [Window], etc.). See [enum AutoTranslateMode].
+ Also decides if the node's strings should be parsed for POT generation.
+ </member>
<member name="editor_description" type="String" setter="set_editor_description" getter="get_editor_description" default="&quot;&quot;">
An optional description to the node. It will be displayed as a tooltip when hovering over the node in the editor's Scene dock.
</member>
@@ -1180,10 +1210,10 @@
Notification received when the [TextServer] is changed.
</constant>
<constant name="PROCESS_MODE_INHERIT" value="0" enum="ProcessMode">
- Inherits [member process_mode] from the node's parent. For the root node, it is equivalent to [constant PROCESS_MODE_PAUSABLE]. This is the default for any newly created node.
+ Inherits [member process_mode] from the node's parent. This is the default for any newly created node.
</constant>
<constant name="PROCESS_MODE_PAUSABLE" value="1" enum="ProcessMode">
- Stops processing when [member SceneTree.paused] is [code]true[/code]. This is the inverse of [constant PROCESS_MODE_WHEN_PAUSED].
+ Stops processing when [member SceneTree.paused] is [code]true[/code]. This is the inverse of [constant PROCESS_MODE_WHEN_PAUSED], and the default for the root node.
</constant>
<constant name="PROCESS_MODE_WHEN_PAUSED" value="2" enum="ProcessMode">
Process [b]only[/b] when [member SceneTree.paused] is [code]true[/code]. This is the inverse of [constant PROCESS_MODE_PAUSABLE].
@@ -1233,5 +1263,15 @@
<constant name="INTERNAL_MODE_BACK" value="2" enum="InternalMode">
The node will be placed at the end of the parent's children list, after any non-internal sibling.
</constant>
+ <constant name="AUTO_TRANSLATE_MODE_INHERIT" value="0" enum="AutoTranslateMode">
+ Inherits [member auto_translate_mode] from the node's parent. This is the default for any newly created node.
+ </constant>
+ <constant name="AUTO_TRANSLATE_MODE_ALWAYS" value="1" enum="AutoTranslateMode">
+ Always automatically translate. This is the inverse of [constant AUTO_TRANSLATE_MODE_DISABLED], and the default for the root node.
+ </constant>
+ <constant name="AUTO_TRANSLATE_MODE_DISABLED" value="2" enum="AutoTranslateMode">
+ Never automatically translate. This is the inverse of [constant AUTO_TRANSLATE_MODE_ALWAYS].
+ String parsing for POT generation will be skipped for this node and children that are set to [constant AUTO_TRANSLATE_MODE_INHERIT].
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index c2405f8e90..bb27ea6eb7 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -1032,7 +1032,7 @@
If [method can_translate_messages] is [code]false[/code], or no translation is available, this method returns [param message] or [param plural_message], without changes. See [method set_message_translation].
The [param n] is the number, or amount, of the message's subject. It is used by the translation system to fetch the correct plural form for the current language.
For detailed examples, see [url=$DOCS_URL/tutorials/i18n/localization_using_gettext.html]Localization using gettext[/url].
- [b]Note:[/b] Negative and [float] numbers may not properly apply to some countable subjects. It's recommended handling these cases with [method tr].
+ [b]Note:[/b] Negative and [float] numbers may not properly apply to some countable subjects. It's recommended to handle these cases with [method tr].
</description>
</method>
</methods>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index f8e91c94a0..2df34b0488 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -238,6 +238,12 @@
</method>
</methods>
<members>
+ <member name="animation/warnings/check_angle_interpolation_type_conflicting" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], [AnimationMixer] prints the warning of interpolation being forced to choose the shortest rotation path due to multiple angle interpolation types being mixed in the [AnimationMixer] cache.
+ </member>
+ <member name="animation/warnings/check_invalid_track_paths" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], [AnimationMixer] prints the warning of no matching object of the track path in the scene.
+ </member>
<member name="application/boot_splash/bg_color" type="Color" setter="" getter="" default="Color(0.14, 0.14, 0.14, 1)">
Background color for the boot splash.
</member>
diff --git a/doc/classes/PropertyTweener.xml b/doc/classes/PropertyTweener.xml
index 73e594218d..d3875ddfc2 100644
--- a/doc/classes/PropertyTweener.xml
+++ b/doc/classes/PropertyTweener.xml
@@ -43,6 +43,25 @@
[/codeblock]
</description>
</method>
+ <method name="set_custom_interpolator">
+ <return type="PropertyTweener" />
+ <param index="0" name="interpolator_method" type="Callable" />
+ <description>
+ Allows interpolating the value with a custom easing function. The provided [param interpolator_method] will be called with a value ranging from [code]0.0[/code] to [code]1.0[/code] and is expected to return a value within the same range (values outside the range can be used for overshoot). The return value of the method is then used for interpolation between initial and final value. Note that the parameter passed to the method is still subject to the tweener's own easing.
+ [b]Example:[/b]
+ [codeblock]
+ @export var curve: Curve
+
+ func _ready():
+ var tween = create_tween()
+ # Interpolate the value using a custom curve.
+ tween.tween_property(self, "position:x", 300, 1).as_relative().set_custom_interpolator(tween_curve)
+
+ func tween_curve(v):
+ return curve.sample_baked(v)
+ [/codeblock]
+ </description>
+ </method>
<method name="set_delay">
<return type="PropertyTweener" />
<param index="0" name="delay" type="float" />
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 4597e50f37..54a11af629 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -2332,6 +2332,13 @@
[b]Note:[/b] If the buffer is in the engine's internal cache, it will have to be fetched from GPU memory and possibly decompressed. This means [method multimesh_get_buffer] is potentially a slow operation and should be avoided whenever possible.
</description>
</method>
+ <method name="multimesh_get_custom_aabb" qualifiers="const">
+ <return type="AABB" />
+ <param index="0" name="multimesh" type="RID" />
+ <description>
+ Returns the custom AABB defined for this MultiMesh resource.
+ </description>
+ </method>
<method name="multimesh_get_instance_count" qualifiers="const">
<return type="int" />
<param index="0" name="multimesh" type="RID" />
@@ -2442,6 +2449,14 @@
[/codeblock]
</description>
</method>
+ <method name="multimesh_set_custom_aabb">
+ <return type="void" />
+ <param index="0" name="multimesh" type="RID" />
+ <param index="1" name="aabb" type="AABB" />
+ <description>
+ Sets the custom AABB for this MultiMesh resource.
+ </description>
+ </method>
<method name="multimesh_set_mesh">
<return type="void" />
<param index="0" name="multimesh" type="RID" />
diff --git a/doc/classes/VisibleOnScreenEnabler2D.xml b/doc/classes/VisibleOnScreenEnabler2D.xml
index e9017a389b..75987fa21c 100644
--- a/doc/classes/VisibleOnScreenEnabler2D.xml
+++ b/doc/classes/VisibleOnScreenEnabler2D.xml
@@ -15,7 +15,7 @@
Determines how the target node is enabled. Corresponds to [enum Node.ProcessMode]. When the node is disabled, it always uses [constant Node.PROCESS_MODE_DISABLED].
</member>
<member name="enable_node_path" type="NodePath" setter="set_enable_node_path" getter="get_enable_node_path" default="NodePath(&quot;..&quot;)">
- The path to the target node, relative to the [VisibleOnScreenEnabler2D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler2D] is inside the scene tree) and every time the [VisibleOnScreenEnabler2D] enters the scene tree. If the path is invalid, an error will be printed in the editor and no node will be affected.
+ The path to the target node, relative to the [VisibleOnScreenEnabler2D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler2D] is inside the scene tree) and every time the [VisibleOnScreenEnabler2D] enters the scene tree. If the path is empty, no node will be affected. If the path is invalid, an error is also generated.
</member>
</members>
<constants>
diff --git a/doc/classes/VisibleOnScreenEnabler3D.xml b/doc/classes/VisibleOnScreenEnabler3D.xml
index a26a47ac92..ff7547f75b 100644
--- a/doc/classes/VisibleOnScreenEnabler3D.xml
+++ b/doc/classes/VisibleOnScreenEnabler3D.xml
@@ -15,7 +15,7 @@
Determines how the target node is enabled. Corresponds to [enum Node.ProcessMode]. When the node is disabled, it always uses [constant Node.PROCESS_MODE_DISABLED].
</member>
<member name="enable_node_path" type="NodePath" setter="set_enable_node_path" getter="get_enable_node_path" default="NodePath(&quot;..&quot;)">
- The path to the target node, relative to the [VisibleOnScreenEnabler3D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler3D] is inside the scene tree) and every time the [VisibleOnScreenEnabler3D] enters the scene tree. If the path is invalid, an error will be printed in the editor and no node will be affected.
+ The path to the target node, relative to the [VisibleOnScreenEnabler3D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler3D] is inside the scene tree) and every time the [VisibleOnScreenEnabler3D] enters the scene tree. If the path is empty, no node will be affected. If the path is invalid, an error is also generated.
</member>
</members>
<constants>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 57d26bf384..63caaefe07 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -557,7 +557,7 @@
<member name="always_on_top" type="bool" setter="set_flag" getter="get_flag" default="false">
If [code]true[/code], the window will be on top of all other windows. Does not work if [member transient] is enabled.
</member>
- <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true">
+ <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true" deprecated="Use [member Node.auto_translate_mode] instead.">
Toggles if any text should automatically change to its translated version depending on the current locale.
</member>
<member name="borderless" type="bool" setter="set_flag" getter="get_flag" default="false">
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 928e4e23ce..6c4bef10d5 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -1607,6 +1607,9 @@ void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, b
void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) {
ERR_FAIL_COND(multimesh->mesh.is_null());
+ if (multimesh->custom_aabb != AABB()) {
+ return;
+ }
AABB aabb;
AABB mesh_aabb = mesh_get_aabb(multimesh->mesh);
for (int i = 0; i < p_instances; i++) {
@@ -1749,9 +1752,25 @@ RID MeshStorage::multimesh_get_mesh(RID p_multimesh) const {
return multimesh->mesh;
}
+void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
+ MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+ ERR_FAIL_NULL(multimesh);
+ multimesh->custom_aabb = p_aabb;
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+}
+
+AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
+ MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+ ERR_FAIL_NULL_V(multimesh, AABB());
+ return multimesh->custom_aabb;
+}
+
AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
+ if (multimesh->custom_aabb != AABB()) {
+ return multimesh->custom_aabb;
+ }
if (multimesh->aabb_dirty) {
const_cast<MeshStorage *>(this)->_update_dirty_multimeshes();
}
@@ -1943,8 +1962,10 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr();
- _multimesh_re_create_aabb(multimesh, data, multimesh->instances);
- multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ if (multimesh->custom_aabb != AABB()) {
+ _multimesh_re_create_aabb(multimesh, data, multimesh->instances);
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ }
}
}
@@ -2091,9 +2112,11 @@ void MeshStorage::_update_dirty_multimeshes() {
}
if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) {
- _multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->aabb_dirty = false;
- multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ if (multimesh->custom_aabb != AABB()) {
+ _multimesh_re_create_aabb(multimesh, data, visible_instances);
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ }
}
}
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index cea81baa0b..d246e7725c 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -189,6 +189,7 @@ struct MultiMesh {
bool uses_custom_data = false;
int visible_instances = -1;
AABB aabb;
+ AABB custom_aabb;
bool aabb_dirty = false;
bool buffer_set = false;
uint32_t stride_cache = 0;
@@ -505,6 +506,8 @@ public:
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override;
virtual RID multimesh_get_mesh(RID p_multimesh) const override;
+ virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
+ virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override;
virtual AABB multimesh_get_aabb(RID p_multimesh) const override;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const override;
diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp
index 8a9e61c725..793b3f64f0 100644
--- a/drivers/gles3/storage/utilities.cpp
+++ b/drivers/gles3/storage/utilities.cpp
@@ -355,19 +355,16 @@ bool Utilities::has_os_feature(const String &p_feature) const {
if (p_feature == "rgtc") {
return config->rgtc_supported;
}
-
if (p_feature == "s3tc") {
return config->s3tc_supported;
}
-
if (p_feature == "bptc") {
return config->bptc_supported;
}
if (p_feature == "astc") {
return config->astc_supported;
}
-
- if (p_feature == "etc" || p_feature == "etc2") {
+ if (p_feature == "etc2") {
return config->etc2_supported;
}
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 1eab2922d1..f48e6eb7ed 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -2244,19 +2244,31 @@ Error RenderingDeviceDriverVulkan::command_queue_present(CommandQueueID p_cmd_qu
for (uint32_t i = 0; i < p_swap_chains.size(); i++) {
SwapChain *swap_chain = (SwapChain *)(p_swap_chains[i].id);
swap_chain->image_index = UINT_MAX;
- if (results[i] == VK_ERROR_OUT_OF_DATE_KHR || results[i] == VK_SUBOPTIMAL_KHR) {
+ if (results[i] == VK_ERROR_OUT_OF_DATE_KHR) {
context_driver->surface_set_needs_resize(swap_chain->surface, true);
any_result_is_out_of_date = true;
}
}
- if (any_result_is_out_of_date || err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
+ if (any_result_is_out_of_date || err == VK_ERROR_OUT_OF_DATE_KHR) {
// It is possible for presentation to fail with out of date while acquire might've succeeded previously. This case
// will be considered a silent failure as it can be triggered easily by resizing a window in the OS natively.
return FAILED;
}
- ERR_FAIL_COND_V(err != VK_SUCCESS, FAILED);
+ // Handling VK_SUBOPTIMAL_KHR the same as VK_SUCCESS is completely intentional.
+ //
+ // Godot does not currently support native rotation in Android when creating the swap chain. It intentionally uses
+ // VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR instead of the current transform bits available in the surface capabilities.
+ // Choosing the transform that leads to optimal presentation leads to distortion that makes the application unusable,
+ // as the rotation of all the content is not handled at the moment.
+ //
+ // VK_SUBOPTIMAL_KHR is accepted as a successful case even if it's not the most efficient solution to work around this
+ // problem. This behavior should not be changed unless the swap chain recreation uses the current transform bits, as
+ // it'll lead to very low performance in Android by entering an endless loop where it'll always resize the swap chain
+ // every frame.
+
+ ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR, FAILED);
return OK;
}
@@ -2581,6 +2593,8 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
}
// Prefer identity transform if it's supported, use the current transform otherwise.
+ // This behavior is intended as Godot does not supported native rotation in platforms that use these bits.
+ // Refer to the comment in command_queue_present() for more details.
VkSurfaceTransformFlagBitsKHR surface_transform_bits;
if (surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
surface_transform_bits = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
@@ -2718,18 +2732,17 @@ RDD::FramebufferID RenderingDeviceDriverVulkan::swap_chain_acquire_framebuffer(C
swap_chain->command_queues_acquired_semaphores.push_back(semaphore_index);
err = device_functions.AcquireNextImageKHR(vk_device, swap_chain->vk_swapchain, UINT64_MAX, semaphore, VK_NULL_HANDLE, &swap_chain->image_index);
- if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {
- // We choose to treat out of date and suboptimal as the same case, as they both need to be recreated and
- // we don't get much use out of presenting a suboptimal image anyway. Either case leaves the semaphore in
- // a signaled state that will never finish, so it's necessary to recreate it.
+ if (err == VK_ERROR_OUT_OF_DATE_KHR) {
+ // Out of date leaves the semaphore in a signaled state that will never finish, so it's necessary to recreate it.
bool semaphore_recreated = _recreate_image_semaphore(command_queue, semaphore_index, true);
ERR_FAIL_COND_V(!semaphore_recreated, FramebufferID());
// Swap chain is out of date and must be recreated.
r_resize_required = true;
return FramebufferID();
- } else if (err != VK_SUCCESS) {
+ } else if (err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR) {
// Swap chain failed to present but the reason is unknown.
+ // Refer to the comment in command_queue_present() as to why VK_SUBOPTIMAL_KHR is handled the same as VK_SUCCESS.
return FramebufferID();
}
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 1ff403291f..caeae69ede 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -77,10 +77,10 @@ void AnimationTrackKeyEdit::_fix_node_path(Variant &value) {
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
- Node *np_node = root->get_node(np);
+ Node *np_node = root->get_node_or_null(np);
ERR_FAIL_NULL(np_node);
- Node *edited_node = root->get_node(base);
+ Node *edited_node = root->get_node_or_null(base);
ERR_FAIL_NULL(edited_node);
value = edited_node->get_path_to(np_node);
@@ -601,8 +601,8 @@ void AnimationTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const
case Animation::TYPE_ANIMATION: {
String animations;
- if (root_path && root_path->has_node(animation->track_get_path(track))) {
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(track)));
+ if (root_path) {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node_or_null(animation->track_get_path(track)));
if (ap) {
List<StringName> anims;
ap->get_animation_list(&anims);
@@ -663,10 +663,10 @@ void AnimationMultiTrackKeyEdit::_fix_node_path(Variant &value, NodePath &base)
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
- Node *np_node = root->get_node(np);
+ Node *np_node = root->get_node_or_null(np);
ERR_FAIL_NULL(np_node);
- Node *edited_node = root->get_node(base);
+ Node *edited_node = root->get_node_or_null(base);
ERR_FAIL_NULL(edited_node);
value = edited_node->get_path_to(np_node);
@@ -1207,8 +1207,8 @@ void AnimationMultiTrackKeyEdit::_get_property_list(List<PropertyInfo> *p_list)
String animations;
- if (root_path && root_path->has_node(animation->track_get_path(first_track))) {
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node(animation->track_get_path(first_track)));
+ if (root_path) {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(root_path->get_node_or_null(animation->track_get_path(first_track)));
if (ap) {
List<StringName> anims;
ap->get_animation_list(&anims);
@@ -1940,8 +1940,8 @@ void AnimationTrackEdit::_notification(int p_what) {
NodePath anim_path = animation->track_get_path(track);
Node *node = nullptr;
- if (root && root->has_node(anim_path)) {
- node = root->get_node(anim_path);
+ if (root) {
+ node = root->get_node_or_null(anim_path);
}
String text;
@@ -2481,10 +2481,9 @@ void AnimationTrackEdit::_path_submitted(const String &p_text) {
}
bool AnimationTrackEdit::_is_value_key_valid(const Variant &p_key_value, Variant::Type &r_valid_type) const {
- if (root == nullptr) {
+ if (root == nullptr || !root->has_node_and_resource(animation->track_get_path(track))) {
return false;
}
-
Ref<Resource> res;
Vector<StringName> leftover_path;
Node *node = root->get_node_and_resource(animation->track_get_path(track), res, leftover_path);
@@ -2774,11 +2773,11 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
AnimationPlayer *ap = ape->get_player();
if (ap) {
NodePath npath = animation->track_get_path(track);
- Node *a_ap_root_node = ap->get_node(ap->get_root_node());
+ Node *a_ap_root_node = ap->get_node_or_null(ap->get_root_node());
Node *nd = nullptr;
// We must test that we have a valid a_ap_root_node before trying to access its content to init the nd Node.
if (a_ap_root_node) {
- nd = a_ap_root_node->get_node(NodePath(npath.get_concatenated_names()));
+ nd = a_ap_root_node->get_node_or_null(NodePath(npath.get_concatenated_names()));
}
if (nd) {
StringName prop = npath.get_concatenated_subnames();
@@ -3310,8 +3309,8 @@ void AnimationTrackEditGroup::_notification(int p_what) {
int separation = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
Color color = get_theme_color(SNAME("font_color"), SNAME("Label"));
- if (root && root->has_node(node)) {
- Node *n = root->get_node(node);
+ if (root) {
+ Node *n = root->get_node_or_null(node);
if (n && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) {
color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
}
@@ -3353,7 +3352,10 @@ void AnimationTrackEditGroup::gui_input(const Ref<InputEvent> &p_event) {
if (node_name_rect.has_point(pos)) {
EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
editor_selection->clear();
- editor_selection->add_node(root->get_node(node));
+ Node *n = root->get_node_or_null(node);
+ if (n) {
+ editor_selection->add_node(n);
+ }
}
}
}
@@ -4474,8 +4476,8 @@ void AnimationTrackEditor::_update_tracks() {
if (use_filter) {
NodePath path = animation->track_get_path(i);
- if (root && root->has_node(path)) {
- Node *node = root->get_node(path);
+ if (root) {
+ Node *node = root->get_node_or_null(path);
if (!node) {
continue; // No node, no filter.
}
@@ -4527,8 +4529,8 @@ void AnimationTrackEditor::_update_tracks() {
NodePath path = animation->track_get_path(i);
Node *node = nullptr;
- if (root && root->has_node(path)) {
- node = root->get_node(path);
+ if (root) {
+ node = root->get_node_or_null(path);
}
if (node && Object::cast_to<AnimationPlayer>(node)) {
@@ -4557,8 +4559,8 @@ void AnimationTrackEditor::_update_tracks() {
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("Node"));
String name = base_path;
String tooltip;
- if (root && root->has_node(base_path)) {
- Node *n = root->get_node(base_path);
+ if (root) {
+ Node *n = root->get_node_or_null(base_path);
if (n) {
icon = EditorNode::get_singleton()->get_object_icon(n, "Node");
name = n->get_name();
@@ -4808,7 +4810,7 @@ void AnimationTrackEditor::_dropped_track(int p_from_track, int p_to_track) {
void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
ERR_FAIL_NULL(root);
- Node *node = get_node(p_path);
+ Node *node = get_node_or_null(p_path);
ERR_FAIL_NULL(node);
NodePath path_to = root->get_path_to(node, true);
@@ -5129,7 +5131,8 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a method key."));
return;
}
- Node *base = root->get_node(animation->track_get_path(p_track));
+ Node *base = root->get_node_or_null(animation->track_get_path(p_track));
+ ERR_FAIL_NULL(base);
method_selector->select_method_from_instance(base);
@@ -5182,7 +5185,8 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) {
EditorNode::get_singleton()->show_warning(TTR("Track path is invalid, so can't add a method key."));
return;
}
- Node *base = root->get_node(animation->track_get_path(insert_key_from_track_call_track));
+ Node *base = root->get_node_or_null(animation->track_get_path(insert_key_from_track_call_track));
+ ERR_FAIL_NULL(base);
List<MethodInfo> minfo;
base->get_method_list(&minfo);
@@ -5963,8 +5967,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
NodePath path = animation->track_get_path(i);
Node *node = nullptr;
- if (root && root->has_node(path)) {
- node = root->get_node(path);
+ if (root) {
+ node = root->get_node_or_null(path);
}
String text;
@@ -6085,7 +6089,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
if (root) {
NodePath np = track_clipboard[i].full_path;
- exists = root->get_node(np);
+ exists = root->get_node_or_null(np);
if (exists) {
path = NodePath(root->get_path_to(exists).get_names(), track_clipboard[i].full_path.get_subnames(), false);
}
@@ -6587,15 +6591,17 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
for (int i = 0; i < p_animation->get_track_count(); i++) {
- bool prop_exists = false;
- Variant::Type valid_type = Variant::NIL;
- Object *obj = nullptr;
-
+ if (!root->has_node_and_resource(p_animation->track_get_path(i))) {
+ continue;
+ }
Ref<Resource> res;
Vector<StringName> leftover_path;
-
Node *node = root->get_node_and_resource(p_animation->track_get_path(i), res, leftover_path);
+ bool prop_exists = false;
+ Variant::Type valid_type = Variant::NIL;
+ Object *obj = nullptr;
+
if (res.is_valid()) {
obj = res.ptr();
} else if (node) {
@@ -6772,9 +6778,9 @@ void AnimationTrackEditor::_pick_track_select_recursive(TreeItem *p_item, const
}
NodePath np = p_item->get_metadata(0);
- Node *node = get_node(np);
+ Node *node = get_node_or_null(np);
- if (!p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
+ if (node && !p_filter.is_empty() && ((String)node->get_name()).findn(p_filter) != -1) {
p_select_candidates.push_back(node);
}
@@ -7137,14 +7143,14 @@ AnimationTrackEditor::AnimationTrackEditor() {
transition_selection->add_item(TTR("Back", "Transition Type"), Tween::TRANS_BACK);
transition_selection->add_item(TTR("Spring", "Transition Type"), Tween::TRANS_SPRING);
transition_selection->select(Tween::TRANS_LINEAR); // Default
- transition_selection->set_auto_translate(false); // Translation context is needed.
+ transition_selection->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Translation context is needed.
ease_selection = memnew(OptionButton);
ease_selection->add_item(TTR("In", "Ease Type"), Tween::EASE_IN);
ease_selection->add_item(TTR("Out", "Ease Type"), Tween::EASE_OUT);
ease_selection->add_item(TTR("InOut", "Ease Type"), Tween::EASE_IN_OUT);
ease_selection->add_item(TTR("OutIn", "Ease Type"), Tween::EASE_OUT_IN);
ease_selection->select(Tween::EASE_IN_OUT); // Default
- ease_selection->set_auto_translate(false); // Translation context is needed.
+ ease_selection->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Translation context is needed.
ease_fps = memnew(SpinBox);
ease_fps->set_min(1);
ease_fps->set_max(999);
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 6ad8dae3b4..41a3437f20 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -591,6 +591,18 @@ bool ConnectDialog::is_editing() const {
return edit_mode;
}
+void ConnectDialog::shortcut_input(const Ref<InputEvent> &p_event) {
+ const Ref<InputEventKey> &key = p_event;
+
+ if (key.is_valid() && key->is_pressed() && !key->is_echo()) {
+ if (ED_IS_SHORTCUT("editor/open_search", p_event)) {
+ filter_nodes->grab_focus();
+ filter_nodes->select_all();
+ filter_nodes->accept_event();
+ }
+ }
+}
+
/*
* Initialize ConnectDialog and populate fields with expected data.
* If creating a connection from scratch, sensible defaults are used.
@@ -1554,6 +1566,7 @@ ConnectionsDock::ConnectionsDock() {
connect_dialog = memnew(ConnectDialog);
connect_dialog->connect("connected", callable_mp(NodeDock::get_singleton(), &NodeDock::restore_last_valid_node), CONNECT_DEFERRED);
+ connect_dialog->set_process_shortcut_input(true);
add_child(connect_dialog);
disconnect_all_dialog = memnew(ConfirmationDialog);
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index 4f628d5685..fb163fbb5f 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -180,6 +180,8 @@ public:
bool get_one_shot() const;
bool is_editing() const;
+ virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
+
void init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit = false);
void popup_dialog(const String p_for_signal);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index da0182b176..604449b04b 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -794,7 +794,7 @@ CreateDialog::CreateDialog() {
rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
recent = memnew(ItemList);
- recent->set_auto_translate(false);
+ recent->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
rec_vb->add_margin_child(TTR("Recent:"), recent, true);
recent->set_allow_reselect(true);
recent->connect("item_selected", callable_mp(this, &CreateDialog::_history_selected));
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index 4be6afbcff..5eccc7673a 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -398,7 +398,7 @@ DependencyEditorOwners::DependencyEditorOwners() {
file_options->connect("id_pressed", callable_mp(this, &DependencyEditorOwners::_file_option));
owners = memnew(ItemList);
- owners->set_auto_translate(false);
+ owners->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
owners->set_select_mode(ItemList::SELECT_MULTI);
owners->connect("item_clicked", callable_mp(this, &DependencyEditorOwners::_list_rmb_clicked));
owners->connect("item_activated", callable_mp(this, &DependencyEditorOwners::_select_file));
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 6c859ce236..9c0d474f49 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -100,7 +100,7 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St
vbc->add_child(lbl);
ItemList *il = memnew(ItemList);
- il->set_auto_translate(false);
+ il->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
il->set_h_size_flags(Control::SIZE_EXPAND_FILL);
il->set_same_column_width(true);
il->set_auto_height(true);
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index aa44ea380b..c08e99b2a4 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -932,7 +932,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
HBoxContainer *profiles_hbc = memnew(HBoxContainer);
profile_list = memnew(OptionButton);
profile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- profile_list->set_auto_translate(false);
+ profile_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
profiles_hbc->add_child(profile_list);
profile_list->connect("item_selected", callable_mp(this, &EditorFeatureProfileManager::_profile_selected));
diff --git a/editor/editor_layouts_dialog.cpp b/editor/editor_layouts_dialog.cpp
index e1b370f44a..bd9a4ea1bf 100644
--- a/editor/editor_layouts_dialog.cpp
+++ b/editor/editor_layouts_dialog.cpp
@@ -114,7 +114,7 @@ EditorLayoutsDialog::EditorLayoutsDialog() {
add_child(makevb);
layout_names = memnew(ItemList);
- layout_names->set_auto_translate(false);
+ layout_names->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
layout_names->set_auto_height(true);
layout_names->set_custom_minimum_size(Size2(300 * EDSCALE, 50 * EDSCALE));
layout_names->set_visible(true);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 663cddc0da..dddd7345c8 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -170,6 +170,9 @@ static const String META_TEXT_TO_COPY = "text_to_copy";
static const String EDITOR_NODE_CONFIG_SECTION = "EditorNode";
+static const String REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE = "The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"%s\" directory manually before attempting this operation again.";
+static const String INSTALL_ANDROID_BUILD_TEMPLATE_MESSAGE = "This will set up your project for gradle Android builds by installing the source template to \"%s\".\nNote that in order to make gradle builds instead of using pre-built APKs, the \"Use Gradle Build\" option should be enabled in the Android export preset.";
+
void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames) {
ERR_FAIL_COND_MSG(p_full_paths.size() != r_filenames.size(), vformat("disambiguate_filenames requires two string vectors of same length (%d != %d).", p_full_paths.size(), r_filenames.size()));
@@ -516,13 +519,7 @@ void EditorNode::_update_theme(bool p_skip_creation) {
bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)));
- help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
- help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
- help_menu->set_item_icon(help_menu->get_item_index(HELP_REPORT_A_BUG), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons)));
- help_menu->set_item_icon(help_menu->get_item_index(HELP_SUGGEST_A_FEATURE), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
- help_menu->set_item_icon(help_menu->get_item_index(HELP_SEND_DOCS_FEEDBACK), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
- help_menu->set_item_icon(help_menu->get_item_index(HELP_COMMUNITY), theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), theme->get_icon(SNAME("Godot"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), theme->get_icon(SNAME("Heart"), EditorStringName(EditorIcons)));
@@ -966,7 +963,7 @@ void EditorNode::_fs_changed() {
String config_error;
bool missing_templates;
if (export_defer.android_build_template) {
- export_template_manager->install_android_template();
+ export_template_manager->install_android_template(export_preset);
}
if (!platform->can_export(export_preset, config_error, missing_templates, export_defer.debug)) {
ERR_PRINT(vformat("Cannot export project with preset \"%s\" due to configuration errors:\n%s", preset_name, config_error));
@@ -2525,7 +2522,16 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
}
void EditorNode::_android_build_source_selected(const String &p_file) {
- export_template_manager->install_android_template_from_file(p_file);
+ export_template_manager->install_android_template_from_file(p_file, android_export_preset);
+}
+
+void EditorNode::_android_export_preset_selected(int p_index) {
+ if (p_index >= 0) {
+ android_export_preset = EditorExport::get_singleton()->get_export_preset(choose_android_export_profile->get_item_id(p_index));
+ } else {
+ android_export_preset.unref();
+ }
+ install_android_build_template_message->set_text(vformat(TTR(INSTALL_ANDROID_BUILD_TEMPLATE_MESSAGE), export_template_manager->get_android_build_directory(android_export_preset)));
}
void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
@@ -2811,14 +2817,45 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
} break;
case FILE_INSTALL_ANDROID_SOURCE: {
if (p_confirmed) {
- export_template_manager->install_android_template();
- } else {
- if (DirAccess::exists("res://android/build")) {
+ if (export_template_manager->is_android_template_installed(android_export_preset)) {
+ remove_android_build_template->set_text(vformat(TTR(REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE), export_template_manager->get_android_build_directory(android_export_preset)));
remove_android_build_template->popup_centered();
- } else if (export_template_manager->can_install_android_template()) {
+ } else if (!export_template_manager->can_install_android_template(android_export_preset)) {
+ gradle_build_manage_templates->popup_centered();
+ } else {
+ export_template_manager->install_android_template(android_export_preset);
+ }
+ } else {
+ bool has_custom_gradle_build = false;
+ choose_android_export_profile->clear();
+ for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) {
+ Ref<EditorExportPreset> export_preset = EditorExport::get_singleton()->get_export_preset(i);
+ if (export_preset->get_platform()->get_class_name() == "EditorExportPlatformAndroid" && (bool)export_preset->get("gradle_build/use_gradle_build")) {
+ choose_android_export_profile->add_item(export_preset->get_name(), i);
+ String gradle_build_directory = export_preset->get("gradle_build/gradle_build_directory");
+ String android_source_template = export_preset->get("gradle_build/android_source_template");
+ if (!android_source_template.is_empty() || (gradle_build_directory != "" && gradle_build_directory != "res://android")) {
+ has_custom_gradle_build = true;
+ }
+ }
+ }
+ _android_export_preset_selected(choose_android_export_profile->get_item_count() >= 1 ? 0 : -1);
+
+ if (choose_android_export_profile->get_item_count() > 1 && has_custom_gradle_build) {
+ // If there's multiple options and at least one of them uses a custom gradle build then prompt the user to choose.
+ choose_android_export_profile->show();
install_android_build_template->popup_centered();
} else {
- gradle_build_manage_templates->popup_centered();
+ choose_android_export_profile->hide();
+
+ if (export_template_manager->is_android_template_installed(android_export_preset)) {
+ remove_android_build_template->set_text(vformat(TTR(REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE), export_template_manager->get_android_build_directory(android_export_preset)));
+ remove_android_build_template->popup_centered();
+ } else if (export_template_manager->can_install_android_template(android_export_preset)) {
+ install_android_build_template->popup_centered();
+ } else {
+ gradle_build_manage_templates->popup_centered();
+ }
}
}
} break;
@@ -2831,7 +2868,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
OS::get_singleton()->shell_show_in_file_manager(OS::get_singleton()->get_user_data_dir(), true);
} break;
case FILE_EXPLORE_ANDROID_BUILD_TEMPLATES: {
- OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->get_resource_path().path_join("android"), true);
+ OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(export_template_manager->get_android_build_directory(android_export_preset).get_base_dir()), true);
} break;
case FILE_QUIT:
case RUN_PROJECT_MANAGER:
@@ -6493,34 +6530,6 @@ EditorNode::EditorNode() {
register_exporters();
- EDITOR_DEF("interface/editor/save_on_focus_loss", false);
- EDITOR_DEF("interface/editor/update_continuously", false);
- EDITOR_DEF("interface/editor/localize_settings", true);
- EDITOR_DEF_RST("interface/scene_tabs/restore_scenes_on_load", true);
- EDITOR_DEF_RST("interface/inspector/default_property_name_style", EditorPropertyNameProcessor::STYLE_CAPITALIZED);
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_property_name_style", PROPERTY_HINT_ENUM, "Raw,Capitalized,Localized"));
- EDITOR_DEF_RST("interface/inspector/default_float_step", 0.001);
- // The lowest value is equal to the minimum float step for 32-bit floats.
- // The step must be set manually, as changing this setting should not change the step here.
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "interface/inspector/default_float_step", PROPERTY_HINT_RANGE, "0.0000001,1,0.0000001"));
- EDITOR_DEF_RST("interface/inspector/disable_folding", false);
- EDITOR_DEF_RST("interface/inspector/auto_unfold_foreign_scenes", true);
- EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false);
- EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true);
- EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true);
-
- PackedStringArray open_in_new_inspector_defaults;
- // Required for the script editor to work.
- open_in_new_inspector_defaults.push_back("Script");
- // Required for the GridMap editor to work.
- open_in_new_inspector_defaults.push_back("MeshLibrary");
- EDITOR_DEF("interface/inspector/resources_to_open_in_new_inspector", open_in_new_inspector_defaults);
-
- EDITOR_DEF("interface/inspector/default_color_picker_mode", 0);
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW,OKHSL", PROPERTY_USAGE_DEFAULT));
- EDITOR_DEF("interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_OKHSL_CIRCLE);
- EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "interface/inspector/default_color_picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle", PROPERTY_USAGE_DEFAULT));
-
ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), Key::SPACE);
const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
@@ -6912,7 +6921,7 @@ EditorNode::EditorNode() {
editor_layouts = memnew(PopupMenu);
editor_layouts->set_name("Layouts");
- editor_layouts->set_auto_translate(false);
+ editor_layouts->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
settings_menu->add_child(editor_layouts);
editor_layouts->connect("id_pressed", callable_mp(this, &EditorNode::_layout_menu_option));
settings_menu->add_submenu_item(TTR("Editor Layout"), "Layouts");
@@ -6962,15 +6971,15 @@ EditorNode::EditorNode() {
ED_SHORTCUT_OVERRIDE("editor/editor_help", "macos", KeyModifierMask::ALT | Key::SPACE);
help_menu->add_icon_shortcut(theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)), ED_GET_SHORTCUT("editor/editor_help"), HELP_SEARCH);
help_menu->add_separator();
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA);
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY);
help_menu->add_separator();
help_menu->add_icon_shortcut(theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/copy_system_info", TTR("Copy System Info")), HELP_COPY_SYSTEM_INFO);
help_menu->set_item_tooltip(-1, TTR("Copies the system info as a single-line text into the clipboard."));
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE);
+ help_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK);
help_menu->add_separator();
if (!global_menu || !OS::get_singleton()->has_feature("macos")) {
// On macOS "Quit" and "About" options are in the "app" menu.
@@ -7216,14 +7225,26 @@ EditorNode::EditorNode() {
file_android_build_source->connect("file_selected", callable_mp(this, &EditorNode::_android_build_source_selected));
gui_base->add_child(file_android_build_source);
- install_android_build_template = memnew(ConfirmationDialog);
- install_android_build_template->set_text(TTR("This will set up your project for gradle Android builds by installing the source template to \"res://android/build\".\nYou can then apply modifications and build your own custom APK on export (adding modules, changing the AndroidManifest.xml, etc.).\nNote that in order to make gradle builds instead of using pre-built APKs, the \"Use Gradle Build\" option should be enabled in the Android export preset."));
- install_android_build_template->set_ok_button_text(TTR("Install"));
- install_android_build_template->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current));
- gui_base->add_child(install_android_build_template);
+ {
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ install_android_build_template_message = memnew(Label);
+ install_android_build_template_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
+ install_android_build_template_message->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
+ vbox->add_child(install_android_build_template_message);
+
+ choose_android_export_profile = memnew(OptionButton);
+ choose_android_export_profile->connect("item_selected", callable_mp(this, &EditorNode::_android_export_preset_selected));
+ vbox->add_child(choose_android_export_profile);
+
+ install_android_build_template = memnew(ConfirmationDialog);
+ install_android_build_template->set_ok_button_text(TTR("Install"));
+ install_android_build_template->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current));
+ install_android_build_template->add_child(vbox);
+ install_android_build_template->set_min_size(Vector2(500.0 * EDSCALE, 0));
+ gui_base->add_child(install_android_build_template);
+ }
remove_android_build_template = memnew(ConfirmationDialog);
- remove_android_build_template->set_text(TTR("The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"res://android/build\" directory manually before attempting this operation again."));
remove_android_build_template->set_ok_button_text(TTR("Show in File Manager"));
remove_android_build_template->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind(FILE_EXPLORE_ANDROID_BUILD_TEMPLATES));
gui_base->add_child(remove_android_build_template);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 0f6e031424..8a880a00cc 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -80,6 +80,7 @@ class EditorBuildProfileManager;
class EditorCommandPalette;
class EditorDockManager;
class EditorExport;
+class EditorExportPreset;
class EditorExtensionManager;
class EditorFeatureProfileManager;
class EditorFileDialog;
@@ -382,6 +383,9 @@ private:
ConfirmationDialog *gradle_build_manage_templates = nullptr;
ConfirmationDialog *install_android_build_template = nullptr;
ConfirmationDialog *remove_android_build_template = nullptr;
+ Label *install_android_build_template_message = nullptr;
+ OptionButton *choose_android_export_profile = nullptr;
+ Ref<EditorExportPreset> android_export_preset;
PopupMenu *vcs_actions_menu = nullptr;
EditorFileDialog *file = nullptr;
@@ -528,6 +532,7 @@ private:
void _menu_option_confirm(int p_option, bool p_confirmed);
void _android_build_source_selected(const String &p_file);
+ void _android_export_preset_selected(int p_index);
void _request_screenshot();
void _screenshot(bool p_use_utc = false);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index f237649c5d..46a648e1b9 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -374,7 +374,7 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() {
option_button->set_h_size_flags(SIZE_EXPAND_FILL);
option_button->set_clip_text(true);
option_button->set_flat(true);
- option_button->set_auto_translate(false);
+ option_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
default_layout->add_child(option_button);
option_button->connect("item_selected", callable_mp(this, &EditorPropertyTextEnum::_option_selected));
@@ -728,7 +728,7 @@ EditorPropertyEnum::EditorPropertyEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
options->set_flat(true);
- options->set_auto_translate(false);
+ options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(options);
add_focusable(options);
options->connect("item_selected", callable_mp(this, &EditorPropertyEnum::_option_selected));
@@ -2955,7 +2955,7 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
assign->set_flat(true);
assign->set_h_size_flags(SIZE_EXPAND_FILL);
assign->set_clip_text(true);
- assign->set_auto_translate(false);
+ assign->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
assign->set_expand_icon(true);
assign->connect("pressed", callable_mp(this, &EditorPropertyNodePath::_node_assign));
SET_DRAG_FORWARDING_CD(assign, EditorPropertyNodePath);
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index 5dc211bf55..a892ea0f85 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -160,7 +160,6 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["dtls"] = "DTLS";
capitalize_string_remaps["eol"] = "EOL";
capitalize_string_remaps["erp"] = "ERP";
- capitalize_string_remaps["etc"] = "ETC";
capitalize_string_remaps["etc2"] = "ETC2";
capitalize_string_remaps["fabrik"] = "FABRIK";
capitalize_string_remaps["fbx"] = "FBX";
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 27ce27591f..74db32df19 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -1018,7 +1018,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) {
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
assign_button->set_expand_icon(true);
assign_button->set_clip_text(true);
- assign_button->set_auto_translate(false);
+ assign_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
SET_DRAG_FORWARDING_GCD(assign_button, EditorResourcePicker);
add_child(assign_button);
assign_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_resource_selected));
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 623aa3f45c..bae7887c02 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -476,6 +476,7 @@ void EditorResourcePreview::start() {
} else {
SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
ERR_FAIL_NULL_MSG(st, "Editor's MainLoop is not a SceneTree. This is a bug.");
+ st->add_idle_callback(&_idle_callback);
}
}
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index b0977d7413..8192ac2eb4 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -47,7 +47,9 @@
#include "core/version.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
+#include "editor/editor_property_name_processor.h"
#include "editor/editor_translation.h"
+#include "scene/gui/color_picker.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
@@ -396,11 +398,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
/* Interface */
// Editor
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/localize_settings", true, "")
+ EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/ui_layout_direction", 0, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+
// Display what the Auto display scale setting effectively corresponds to.
const String display_scale_hint_string = vformat("Auto (%d%%),75%%,100%%,125%%,150%%,175%%,200%%,Custom", Math::round(get_auto_display_scale() * 100));
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/display_scale", 0, display_scale_hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
-
- EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/ui_layout_direction", 0, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/editor/custom_display_scale", 1.0, "0.5,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
String ed_screen_hints = "Screen With Mouse Pointer:-4,Screen With Keyboard Focus:-3,Primary Screen:-2"; // Note: Main Window Screen:-1 is not used for the main window.
for (int i = 0; i < DisplayServer::get_singleton()->get_screen_count(); i++) {
@@ -415,7 +419,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/use_embedded_menu", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/expand_to_title", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
- EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/editor/custom_display_scale", 1.0, "0.5,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/main_font_size", 14, "8,48,1")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/code_font_size", 14, "8,48,1")
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/code_font_contextual_ligatures", 1, "Enabled,Disable Contextual Alternates (Coding Ligatures),Use Custom OpenType Feature Set")
@@ -432,21 +435,13 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
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")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/code_font", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
- EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/editor/low_processor_mode_sleep_usec", 6900, "1,100000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
- // Default unfocused usec sleep is for 10 FPS. Allow an unfocused FPS limit
- // as low as 1 FPS for those who really need low power usage (but don't need
- // to preview particles or shaders while the editor is unfocused). With very
- // low FPS limits, the editor can take a small while to become usable after
- // being focused again, so this should be used at the user's discretion.
- EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/editor/unfocused_low_processor_mode_sleep_usec", 100000, "1,1000000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
-
- EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox")
_initial_set("interface/editor/separate_distraction_mode", false);
_initial_set("interface/editor/automatically_open_screenshots", true);
EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/single_window_mode", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
_initial_set("interface/editor/mouse_extra_buttons_navigate_history", true);
_initial_set("interface/editor/save_each_scene_on_quit", true); // Regression
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/save_on_focus_loss", false, "")
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/accept_dialog_cancel_ok_buttons", 0,
vformat("Auto (%s),Cancel First,OK First", DisplayServer::get_singleton()->get_swap_cancel_ok() ? "OK First" : "Cancel First"),
PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
@@ -458,10 +453,40 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/show_update_spinner", 0, "Auto (Disabled),Enabled,Disabled")
#endif
+ EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/low_processor_mode_sleep_usec", 6900, "1,100000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ // Default unfocused usec sleep is for 10 FPS. Allow an unfocused FPS limit
+ // as low as 1 FPS for those who really need low power usage (but don't need
+ // to preview particles or shaders while the editor is unfocused). With very
+ // low FPS limits, the editor can take a small while to become usable after
+ // being focused again, so this should be used at the user's discretion.
+ EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/unfocused_low_processor_mode_sleep_usec", 100000, "1,1000000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/vsync_mode", 1, "Disabled,Enabled,Adaptive,Mailbox")
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/update_continuously", false, "")
+
// Inspector
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/show_low_level_opentype_features", false, "")
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/inspector/float_drag_speed", 5.0, "0.1,100,0.01")
+ EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_property_name_style", EditorPropertyNameProcessor::STYLE_CAPITALIZED, "Raw,Capitalized,Localized", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ // The lowest value is equal to the minimum float step for 32-bit floats.
+ // The step must be set manually, as changing this setting should not change the step here.
+ EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/inspector/default_float_step", 0.001, "0.0000001,1,0.0000001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/disable_folding", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/auto_unfold_foreign_scenes", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/horizontal_vector2_editing", false, "")
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/horizontal_vector_types_editing", true, "")
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/open_resources_in_current_inspector", true, "")
+
+ PackedStringArray open_in_new_inspector_defaults;
+ // Required for the script editor to work.
+ open_in_new_inspector_defaults.push_back("Script");
+ // Required for the GridMap editor to work.
+ open_in_new_inspector_defaults.push_back("MeshLibrary");
+ _initial_set("interface/inspector/resources_to_open_in_new_inspector", open_in_new_inspector_defaults);
+
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_color_picker_mode", (int32_t)ColorPicker::MODE_RGB, "RGB,HSV,RAW,OKHSL")
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_OKHSL_CIRCLE, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle")
// Theme
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_ENUM, "interface/theme/preset", "Default", "Default,Breeze Dark,Godot 2,Gray,Light,Solarized (Dark),Solarized (Light),Black (OLED),Custom")
@@ -493,6 +518,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/scene_tabs/show_thumbnail_on_hover", true);
EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "interface/scene_tabs/maximum_width", 350, "0,9999,1", PROPERTY_USAGE_DEFAULT)
_initial_set("interface/scene_tabs/show_script_button", false);
+ EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/scene_tabs/restore_scenes_on_load", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
// Multi Window
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/multi_window/enable", true, "");
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index aeb4966169..2e34685d75 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -111,8 +111,13 @@ void EditorExport::save_presets() {
save_timer->start();
}
+void EditorExport::emit_presets_runnable_changed() {
+ emit_signal(_export_presets_runnable_updated);
+}
+
void EditorExport::_bind_methods() {
- ADD_SIGNAL(MethodInfo("export_presets_updated"));
+ ADD_SIGNAL(MethodInfo(_export_presets_updated));
+ ADD_SIGNAL(MethodInfo(_export_presets_runnable_updated));
}
void EditorExport::add_export_platform(const Ref<EditorExportPlatform> &p_platform) {
@@ -136,6 +141,7 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in
} else {
export_presets.insert(p_at_pos, p_preset);
}
+ emit_presets_runnable_changed();
}
int EditorExport::get_export_preset_count() const {
@@ -150,6 +156,7 @@ Ref<EditorExportPreset> EditorExport::get_export_preset(int p_idx) {
void EditorExport::remove_export_preset(int p_idx) {
export_presets.remove_at(p_idx);
save_presets();
+ emit_presets_runnable_changed();
}
void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) {
@@ -382,6 +389,10 @@ bool EditorExport::poll_export_platforms() {
return changed;
}
+void EditorExport::connect_presets_runnable_updated(const Callable &p_target) {
+ connect(_export_presets_runnable_updated, p_target);
+}
+
EditorExport::EditorExport() {
save_timer = memnew(Timer);
add_child(save_timer);
@@ -390,6 +401,7 @@ EditorExport::EditorExport() {
save_timer->connect("timeout", callable_mp(this, &EditorExport::_save));
_export_presets_updated = "export_presets_updated";
+ _export_presets_runnable_updated = "export_presets_runnable_updated";
singleton = this;
set_process(true);
diff --git a/editor/export/editor_export.h b/editor/export/editor_export.h
index 55dee0c468..f8cb90dc39 100644
--- a/editor/export/editor_export.h
+++ b/editor/export/editor_export.h
@@ -41,7 +41,8 @@ class EditorExport : public Node {
Vector<Ref<EditorExportPreset>> export_presets;
Vector<Ref<EditorExportPlugin>> export_plugins;
- StringName _export_presets_updated;
+ static inline StringName _export_presets_updated;
+ static inline StringName _export_presets_runnable_updated;
Timer *save_timer = nullptr;
bool block_save = false;
@@ -54,6 +55,7 @@ class EditorExport : public Node {
protected:
friend class EditorExportPreset;
void save_presets();
+ void emit_presets_runnable_changed();
void _notification(int p_what);
static void _bind_methods();
@@ -77,6 +79,7 @@ public:
void load_config();
void update_export_presets();
bool poll_export_platforms();
+ void connect_presets_runnable_updated(const Callable &p_target);
EditorExport();
~EditorExport();
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index 42df0d93f6..413db1595b 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -34,17 +34,13 @@
#include "scene/resources/image_texture.h"
void EditorExportPlatformPC::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
- if (p_preset->get("texture_format/bptc")) {
- r_features->push_back("bptc");
- }
- if (p_preset->get("texture_format/s3tc")) {
+ if (p_preset->get("texture_format/s3tc_bptc")) {
r_features->push_back("s3tc");
+ r_features->push_back("bptc");
}
- if (p_preset->get("texture_format/etc")) {
- r_features->push_back("etc");
- }
- if (p_preset->get("texture_format/etc2")) {
+ if (p_preset->get("texture_format/etc2_astc")) {
r_features->push_back("etc2");
+ r_features->push_back("astc");
}
// PC platforms only have one architecture per export, since
// we export a single executable instead of a bundle.
@@ -60,10 +56,8 @@ void EditorExportPlatformPC::get_export_options(List<ExportOption> *r_options) c
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "binary_format/embed_pck"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/bptc"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc_bptc"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2_astc"), false));
}
String EditorExportPlatformPC::get_name() const {
@@ -103,6 +97,14 @@ bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExpo
valid = dvalid || rvalid;
r_missing_templates = !valid;
+ bool uses_s3tc_bptc = p_preset->get("texture_format/s3tc_bptc");
+ bool uses_etc2_astc = p_preset->get("texture_format/etc2_astc");
+
+ if (!uses_s3tc_bptc && !uses_etc2_astc) {
+ valid = false;
+ err += TTR("A texture format must be selected to export the project. Please select at least one texture format.");
+ }
+
if (!err.is_empty()) {
r_error = err;
}
@@ -237,9 +239,8 @@ void EditorExportPlatformPC::set_logo(const Ref<Texture2D> &p_logo) {
}
void EditorExportPlatformPC::get_platform_features(List<String> *r_features) const {
- r_features->push_back("pc"); //all pcs support "pc"
- r_features->push_back("s3tc"); //all pcs support "s3tc" compression
- r_features->push_back(get_os_name().to_lower()); //OS name is a feature
+ r_features->push_back("pc"); // Identify PC platforms as such.
+ r_features->push_back(get_os_name().to_lower()); // OS name is a feature.
}
void EditorExportPlatformPC::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, HashSet<String> &p_features) {
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index 8c3b6693e3..6fc2228bee 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -221,6 +221,7 @@ String EditorExportPreset::get_name() const {
void EditorExportPreset::set_runnable(bool p_enable) {
runnable = p_enable;
+ EditorExport::singleton->emit_presets_runnable_changed();
EditorExport::singleton->save_presets();
}
diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp
index b3951d2c28..c3e48820b2 100644
--- a/editor/export/export_template_manager.cpp
+++ b/editor/export/export_template_manager.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
+#include "editor/export/editor_export.h"
#include "editor/progress_dialog.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/file_dialog.h"
@@ -655,39 +656,78 @@ void ExportTemplateManager::_hide_dialog() {
hide();
}
-bool ExportTemplateManager::can_install_android_template() {
+String ExportTemplateManager::get_android_build_directory(const Ref<EditorExportPreset> &p_preset) {
+ if (p_preset.is_valid()) {
+ String gradle_build_dir = p_preset->get("gradle_build/gradle_build_directory");
+ if (!gradle_build_dir.is_empty()) {
+ return gradle_build_dir.path_join("build");
+ }
+ }
+ return "res://android/build";
+}
+
+String ExportTemplateManager::get_android_source_zip(const Ref<EditorExportPreset> &p_preset) {
+ if (p_preset.is_valid()) {
+ String android_source_zip = p_preset->get("gradle_build/android_source_template");
+ if (!android_source_zip.is_empty()) {
+ return android_source_zip;
+ }
+ }
+
const String templates_dir = EditorPaths::get_singleton()->get_export_templates_dir().path_join(VERSION_FULL_CONFIG);
- return FileAccess::exists(templates_dir.path_join("android_source.zip"));
+ return templates_dir.path_join("android_source.zip");
}
-Error ExportTemplateManager::install_android_template() {
- const String &templates_path = EditorPaths::get_singleton()->get_export_templates_dir().path_join(VERSION_FULL_CONFIG);
- const String &source_zip = templates_path.path_join("android_source.zip");
+String ExportTemplateManager::get_android_template_identifier(const Ref<EditorExportPreset> &p_preset) {
+ // The template identifier is the Godot version for the default template, and the full path plus md5 hash for custom templates.
+ if (p_preset.is_valid()) {
+ String android_source_zip = p_preset->get("gradle_build/android_source_template");
+ if (!android_source_zip.is_empty()) {
+ return android_source_zip + String(" [") + FileAccess::get_md5(android_source_zip) + String("]");
+ }
+ }
+ return VERSION_FULL_CONFIG;
+}
+
+bool ExportTemplateManager::is_android_template_installed(const Ref<EditorExportPreset> &p_preset) {
+ return DirAccess::exists(get_android_build_directory(p_preset));
+}
+
+bool ExportTemplateManager::can_install_android_template(const Ref<EditorExportPreset> &p_preset) {
+ return FileAccess::exists(get_android_source_zip(p_preset));
+}
+
+Error ExportTemplateManager::install_android_template(const Ref<EditorExportPreset> &p_preset) {
+ const String source_zip = get_android_source_zip(p_preset);
ERR_FAIL_COND_V(!FileAccess::exists(source_zip), ERR_CANT_OPEN);
- return install_android_template_from_file(source_zip);
+ return install_android_template_from_file(source_zip, p_preset);
}
-Error ExportTemplateManager::install_android_template_from_file(const String &p_file) {
+
+Error ExportTemplateManager::install_android_template_from_file(const String &p_file, const Ref<EditorExportPreset> &p_preset) {
// To support custom Android builds, we install the Java source code and buildsystem
// from android_source.zip to the project's res://android folder.
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
ERR_FAIL_COND_V(da.is_null(), ERR_CANT_CREATE);
- // Make res://android dir (if it does not exist).
- da->make_dir("android");
+ String build_dir = get_android_build_directory(p_preset);
+ String parent_dir = build_dir.get_base_dir();
+
+ // Make parent of the build dir (if it does not exist).
+ da->make_dir_recursive(parent_dir);
{
- // Add version, to ensure building won't work if template and Godot version don't match.
- Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::WRITE);
+ // Add identifier, to ensure building won't work if the current template doesn't match.
+ Ref<FileAccess> f = FileAccess::open(parent_dir.path_join(".build_version"), FileAccess::WRITE);
ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE);
- f->store_line(VERSION_FULL_CONFIG);
+ f->store_line(get_android_template_identifier(p_preset));
}
// Create the android build directory.
- Error err = da->make_dir_recursive("android/build");
+ Error err = da->make_dir_recursive(build_dir);
ERR_FAIL_COND_V(err != OK, err);
{
// Add an empty .gdignore file to avoid scan.
- Ref<FileAccess> f = FileAccess::open("res://android/build/.gdignore", FileAccess::WRITE);
+ Ref<FileAccess> f = FileAccess::open(build_dir.path_join(".gdignore"), FileAccess::WRITE);
ERR_FAIL_COND_V(f.is_null(), ERR_CANT_CREATE);
f->store_line("");
}
@@ -735,11 +775,11 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_
unzCloseCurrentFile(pkg);
if (!dirs_tested.has(base_dir)) {
- da->make_dir_recursive(String("android/build").path_join(base_dir));
+ da->make_dir_recursive(build_dir.path_join(base_dir));
dirs_tested.insert(base_dir);
}
- String to_write = String("res://android/build").path_join(path);
+ String to_write = build_dir.path_join(path);
Ref<FileAccess> f = FileAccess::open(to_write, FileAccess::WRITE);
if (f.is_valid()) {
f->store_buffer(uncomp_data.ptr(), uncomp_data.size());
diff --git a/editor/export/export_template_manager.h b/editor/export/export_template_manager.h
index 8c26554d13..a00d874580 100644
--- a/editor/export/export_template_manager.h
+++ b/editor/export/export_template_manager.h
@@ -33,6 +33,7 @@
#include "scene/gui/dialogs.h"
+class EditorExportPreset;
class ExportTemplateVersion;
class FileDialog;
class HTTPRequest;
@@ -121,10 +122,15 @@ protected:
static void _bind_methods();
public:
- bool can_install_android_template();
- Error install_android_template();
+ static String get_android_build_directory(const Ref<EditorExportPreset> &p_preset);
+ static String get_android_source_zip(const Ref<EditorExportPreset> &p_preset);
+ static String get_android_template_identifier(const Ref<EditorExportPreset> &p_preset);
- Error install_android_template_from_file(const String &p_file);
+ bool is_android_template_installed(const Ref<EditorExportPreset> &p_preset);
+ bool can_install_android_template(const Ref<EditorExportPreset> &p_preset);
+ Error install_android_template(const Ref<EditorExportPreset> &p_preset);
+
+ Error install_android_template_from_file(const String &p_file, const Ref<EditorExportPreset> &p_preset);
void popup_manager();
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 4a066b3d3e..5de56b9d90 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -1212,7 +1212,7 @@ ProjectExportDialog::ProjectExportDialog() {
preset_vb->add_child(mc);
mc->set_v_size_flags(Control::SIZE_EXPAND_FILL);
presets = memnew(ItemList);
- presets->set_auto_translate(false);
+ presets->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
SET_DRAG_FORWARDING_GCD(presets, ProjectExportDialog);
mc->add_child(presets);
presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset));
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index dde7ddd32d..ac83460542 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -584,7 +584,7 @@ void FileSystemDock::_notification(int p_what) {
file_list_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));
file_list_button_sort->set_icon(get_editor_theme_icon(SNAME("Sort")));
- button_dock_placement->set_icon(get_editor_theme_icon(SNAME("GuiTabMenu")));
+ button_dock_placement->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
if (is_layout_rtl()) {
button_hist_next->set_icon(get_editor_theme_icon(SNAME("Back")));
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 4d1fd1f7b7..c57c22404d 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -1773,7 +1773,7 @@ EditorFileDialog::EditorFileDialog() {
ED_SHORTCUT("file_dialog/go_forward", TTR("Go Forward"), KeyModifierMask::ALT | Key::RIGHT);
ED_SHORTCUT("file_dialog/go_up", TTR("Go Up"), KeyModifierMask::ALT | Key::UP);
ED_SHORTCUT("file_dialog/refresh", TTR("Refresh"), Key::F5);
- ED_SHORTCUT("file_dialog/toggle_hidden_files", TTR("Toggle Hidden Files"), KeyModifierMask::CMD_OR_CTRL | Key::H);
+ ED_SHORTCUT("file_dialog/toggle_hidden_files", TTR("Toggle Hidden Files"), KeyModifierMask::CTRL | Key::H);
ED_SHORTCUT("file_dialog/toggle_favorite", TTR("Toggle Favorite"), KeyModifierMask::ALT | Key::F);
ED_SHORTCUT("file_dialog/toggle_mode", TTR("Toggle Mode"), KeyModifierMask::ALT | Key::V);
ED_SHORTCUT("file_dialog/create_folder", TTR("Create Folder"), KeyModifierMask::CMD_OR_CTRL | Key::N);
@@ -1783,6 +1783,7 @@ EditorFileDialog::EditorFileDialog() {
ED_SHORTCUT("file_dialog/move_favorite_down", TTR("Move Favorite Down"), KeyModifierMask::CMD_OR_CTRL | Key::DOWN);
if (EditorSettings::get_singleton()) {
+ ED_SHORTCUT_OVERRIDE("file_dialog/toggle_hidden_files", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::PERIOD);
ED_SHORTCUT_OVERRIDE("file_dialog/toggle_favorite", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::F);
ED_SHORTCUT_OVERRIDE("file_dialog/toggle_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::V);
}
@@ -1909,7 +1910,7 @@ EditorFileDialog::EditorFileDialog() {
fav_down->connect("pressed", callable_mp(this, &EditorFileDialog::_favorite_move_down));
favorites = memnew(ItemList);
- favorites->set_auto_translate(false);
+ favorites->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
fav_vb->add_child(favorites);
favorites->set_v_size_flags(Control::SIZE_EXPAND_FILL);
favorites->connect("item_selected", callable_mp(this, &EditorFileDialog::_favorite_selected));
@@ -1919,7 +1920,7 @@ EditorFileDialog::EditorFileDialog() {
rec_vb->set_custom_minimum_size(Size2(150, 100) * EDSCALE);
rec_vb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
recent = memnew(ItemList);
- recent->set_auto_translate(false);
+ recent->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
recent->set_allow_reselect(true);
rec_vb->add_margin_child(TTR("Recent:"), recent, true);
recent->connect("item_selected", callable_mp(this, &EditorFileDialog::_recent_selected));
@@ -1943,7 +1944,7 @@ EditorFileDialog::EditorFileDialog() {
// Item (files and folders) list with context menu.
item_list = memnew(ItemList);
- item_list->set_auto_translate(false);
+ item_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
item_list->connect("item_clicked", callable_mp(this, &EditorFileDialog::_item_list_item_rmb_clicked));
item_list->connect("empty_clicked", callable_mp(this, &EditorFileDialog::_item_list_empty_clicked));
diff --git a/editor/gui/editor_object_selector.cpp b/editor/gui/editor_object_selector.cpp
index c97d68fb35..fd46788c73 100644
--- a/editor/gui/editor_object_selector.cpp
+++ b/editor/gui/editor_object_selector.cpp
@@ -238,7 +238,7 @@ EditorObjectSelector::EditorObjectSelector(EditorSelectionHistory *p_history) {
current_object_label = memnew(Label);
current_object_label->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
current_object_label->set_h_size_flags(SIZE_EXPAND_FILL);
- current_object_label->set_auto_translate(false);
+ current_object_label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
main_hb->add_child(current_object_label);
sub_objects_icon = memnew(TextureRect);
@@ -247,7 +247,7 @@ EditorObjectSelector::EditorObjectSelector(EditorSelectionHistory *p_history) {
main_hb->add_child(sub_objects_icon);
sub_objects_menu = memnew(PopupMenu);
- sub_objects_menu->set_auto_translate(false);
+ sub_objects_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(sub_objects_menu);
sub_objects_menu->connect("about_to_popup", callable_mp(this, &EditorObjectSelector::_about_to_show));
sub_objects_menu->connect("id_pressed", callable_mp(this, &EditorObjectSelector::_id_pressed));
diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp
index 915b161372..476b06f52b 100644
--- a/editor/gui/editor_scene_tabs.cpp
+++ b/editor/gui/editor_scene_tabs.cpp
@@ -377,7 +377,7 @@ EditorSceneTabs::EditorSceneTabs() {
scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int());
scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE);
scene_tabs->set_drag_to_rearrange_enabled(true);
- scene_tabs->set_auto_translate(false);
+ scene_tabs->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
tabbar_container->add_child(scene_tabs);
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index dd1440fe0b..fab5784f16 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -68,27 +68,15 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
}
return;
} else {
- grabbing_spinner_attempt = true;
- grabbing_spinner_dist_cache = 0;
- pre_grab_value = get_value();
- grabbing_spinner = false;
- grabbing_spinner_mouse_pos = get_global_mouse_position();
- emit_signal("grabbed");
+ _grab_start();
}
} else {
- if (grabbing_spinner_attempt) {
- if (grabbing_spinner) {
- Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
- Input::get_singleton()->warp_mouse(grabbing_spinner_mouse_pos);
- queue_redraw();
- emit_signal("ungrabbed");
- } else {
- _focus_entered();
- }
-
- grabbing_spinner = false;
- grabbing_spinner_attempt = false;
- }
+ _grab_end();
+ }
+ } else if (mb->get_button_index() == MouseButton::RIGHT) {
+ if (mb->is_pressed() && is_grabbing()) {
+ _grab_end();
+ set_value(pre_grab_value);
}
} else if (mb->get_button_index() == MouseButton::WHEEL_UP || mb->get_button_index() == MouseButton::WHEEL_DOWN) {
if (grabber->is_visible()) {
@@ -142,8 +130,47 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventKey> k = p_event;
- if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept", true)) {
- _focus_entered();
+ if (k.is_valid() && k->is_pressed()) {
+ if (k->is_action("ui_accept", true)) {
+ _focus_entered();
+ } else if (is_grabbing()) {
+ if (k->is_action("ui_cancel", true)) {
+ _grab_end();
+ set_value(pre_grab_value);
+ }
+ accept_event();
+ }
+ }
+}
+
+void EditorSpinSlider::_grab_start() {
+ grabbing_spinner_attempt = true;
+ grabbing_spinner_dist_cache = 0;
+ pre_grab_value = get_value();
+ grabbing_spinner = false;
+ grabbing_spinner_mouse_pos = get_global_mouse_position();
+ emit_signal("grabbed");
+}
+
+void EditorSpinSlider::_grab_end() {
+ if (grabbing_spinner_attempt) {
+ if (grabbing_spinner) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ Input::get_singleton()->warp_mouse(grabbing_spinner_mouse_pos);
+ queue_redraw();
+ grabbing_spinner = false;
+ emit_signal("ungrabbed");
+ } else {
+ _focus_entered();
+ }
+
+ grabbing_spinner_attempt = false;
+ }
+
+ if (grabbing_grabber) {
+ grabbing_grabber = false;
+ mousewheel_over_grabber = false;
+ emit_signal("ungrabbed");
}
}
@@ -173,16 +200,25 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
grabbing_grabber = true;
+ pre_grab_value = get_value();
if (!mousewheel_over_grabber) {
grabbing_ratio = get_as_ratio();
grabbing_from = grabber->get_transform().xform(mb->get_position()).x;
}
+ grab_focus();
emit_signal("grabbed");
} else {
grabbing_grabber = false;
mousewheel_over_grabber = false;
emit_signal("ungrabbed");
}
+ } else if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT) {
+ if (mb->is_pressed() && grabbing_grabber) {
+ grabbing_grabber = false;
+ mousewheel_over_grabber = false;
+ set_value(pre_grab_value);
+ emit_signal("ungrabbed");
+ }
}
Ref<InputEventMouseMotion> mm = p_event;
diff --git a/editor/gui/editor_spin_slider.h b/editor/gui/editor_spin_slider.h
index 8c643157f1..a999f5c48f 100644
--- a/editor/gui/editor_spin_slider.h
+++ b/editor/gui/editor_spin_slider.h
@@ -72,6 +72,9 @@ class EditorSpinSlider : public Range {
bool hide_slider = false;
bool flat = false;
+ void _grab_start();
+ void _grab_end();
+
void _grabber_gui_input(const Ref<InputEvent> &p_event);
void _value_input_closed();
void _value_input_submitted(const String &);
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 6c8e8b75ef..7fb52251ce 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -1601,7 +1601,7 @@ void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) {
Label *label = memnew(Label);
hb->add_child(label);
label->set_text(name);
- label->set_auto_translate(false);
+ label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
}
show_all_nodes->show();
diff --git a/editor/history_dock.cpp b/editor/history_dock.cpp
index 6ec240fdf0..7375abb65a 100644
--- a/editor/history_dock.cpp
+++ b/editor/history_dock.cpp
@@ -248,7 +248,7 @@ HistoryDock::HistoryDock() {
global_history_checkbox->connect("toggled", callable_mp(this, &HistoryDock::refresh_history).unbind(1));
action_list = memnew(ItemList);
- action_list->set_auto_translate(false);
+ action_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(action_list);
action_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
action_list->connect("item_selected", callable_mp(this, &HistoryDock::seek_history));
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index 92b3569c06..d52a2057af 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -804,7 +804,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
animations_menu = memnew(PopupMenu);
menu->add_child(animations_menu);
animations_menu->set_name("AddAnimations");
- animations_menu->set_auto_translate(false);
+ animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_add_animation_type));
open_file = memnew(EditorFileDialog);
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 9e82368a71..30104f0ab4 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -1083,7 +1083,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
animations_menu = memnew(PopupMenu);
menu->add_child(animations_menu);
animations_menu->set_name("AddAnimations");
- animations_menu->set_auto_translate(false);
+ animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_add_animation_type));
open_file = memnew(EditorFileDialog);
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 99151ac760..78b4647975 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -239,7 +239,7 @@ void AnimationNodeBlendTreeEditor::update_graph() {
MenuButton *mb = memnew(MenuButton);
mb->set_text(anim->get_animation());
mb->set_icon(get_editor_theme_icon(SNAME("Animation")));
- mb->set_auto_translate(false);
+ mb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
mb->set_disabled(read_only);
Array options;
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index b08d2e966d..3f9f609dba 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1886,7 +1886,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
animation->set_h_size_flags(SIZE_EXPAND_FILL);
animation->set_tooltip_text(TTR("Display list of animations in player."));
animation->set_clip_text(true);
- animation->set_auto_translate(false);
+ animation->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
autoplay = memnew(Button);
autoplay->set_theme_type_variation("FlatButton");
@@ -1985,7 +1985,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
blend_editor.tree->connect(SNAME("item_edited"), callable_mp(this, &AnimationPlayerEditor::_blend_edited));
blend_editor.next = memnew(OptionButton);
- blend_editor.next->set_auto_translate(false);
+ blend_editor.next->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
blend_vb->add_margin_child(TTR("Next (Auto Queue):"), blend_editor.next);
autoplay->connect(SNAME("pressed"), callable_mp(this, &AnimationPlayerEditor::_autoplay_pressed));
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 9f8fbc6dfc..08c52f6d12 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -1779,7 +1779,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
animations_menu = memnew(PopupMenu);
menu->add_child(animations_menu);
animations_menu->set_name("AddAnimations");
- animations_menu->set_auto_translate(false);
+ animations_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
animations_menu->connect("index_pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_add_animation_type));
connect_menu = memnew(PopupMenu);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 9d7305f9ae..132b85b090 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5544,7 +5544,7 @@ CanvasItemEditor::CanvasItemEditor() {
selection_menu = memnew(PopupMenu);
add_child(selection_menu);
selection_menu->set_min_size(Vector2(100, 0));
- selection_menu->set_auto_translate(false);
+ selection_menu->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
selection_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_selection_result_pressed));
selection_menu->connect("popup_hide", callable_mp(this, &CanvasItemEditor::_selection_menu_hide), CONNECT_DEFERRED);
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
index 7e5fa70f3f..0fd980ff10 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
@@ -77,7 +77,60 @@ void CPUParticles3DEditor::_menu_option(int p_option) {
ur->commit_action(false);
} break;
+ case MENU_OPTION_GENERATE_AABB: {
+ // Add one second to the default generation lifetime, since the progress is updated every second.
+ generate_seconds->set_value(MAX(1.0, trunc(node->get_lifetime()) + 1.0));
+
+ if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
+ // Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
+ generate_aabb->popup_centered();
+ } else {
+ // Generate the visibility AABB immediately.
+ _generate_aabb();
+ }
+ } break;
+ }
+}
+
+void CPUParticles3DEditor::_generate_aabb() {
+ double time = generate_seconds->get_value();
+
+ double running = 0.0;
+
+ EditorProgress ep("gen_aabb", TTR("Generating Visibility AABB (Waiting for Particle Simulation)"), int(time));
+
+ bool was_emitting = node->is_emitting();
+ if (!was_emitting) {
+ node->set_emitting(true);
+ OS::get_singleton()->delay_usec(1000);
+ }
+
+ AABB rect;
+
+ while (running < time) {
+ uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+ ep.step("Generating...", int(running), true);
+ OS::get_singleton()->delay_usec(1000);
+
+ AABB capture = node->capture_aabb();
+ if (rect == AABB()) {
+ rect = capture;
+ } else {
+ rect.merge_with(capture);
+ }
+
+ running += (OS::get_singleton()->get_ticks_usec() - ticks) / 1000000.0;
+ }
+
+ if (!was_emitting) {
+ node->set_emitting(false);
}
+
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Generate Visibility AABB"));
+ ur->add_do_method(node, "set_visibility_aabb", rect);
+ ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb());
+ ur->commit_action();
}
void CPUParticles3DEditor::edit(CPUParticles3D *p_particles) {
@@ -117,9 +170,24 @@ CPUParticles3DEditor::CPUParticles3DEditor() {
options->set_text(TTR("CPUParticles3D"));
options->get_popup()->add_item(TTR("Restart"), MENU_OPTION_RESTART);
+ options->get_popup()->add_item(TTR("Generate AABB"), MENU_OPTION_GENERATE_AABB);
options->get_popup()->add_item(TTR("Create Emission Points From Node"), MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE);
options->get_popup()->add_item(TTR("Convert to GPUParticles3D"), MENU_OPTION_CONVERT_TO_GPU_PARTICLES);
options->get_popup()->connect("id_pressed", callable_mp(this, &CPUParticles3DEditor::_menu_option));
+
+ generate_aabb = memnew(ConfirmationDialog);
+ generate_aabb->set_title(TTR("Generate Visibility AABB"));
+ VBoxContainer *genvb = memnew(VBoxContainer);
+ generate_aabb->add_child(genvb);
+ generate_seconds = memnew(SpinBox);
+ genvb->add_margin_child(TTR("Generation Time (sec):"), generate_seconds);
+ generate_seconds->set_min(0.1);
+ generate_seconds->set_max(25);
+ generate_seconds->set_value(2);
+
+ add_child(generate_aabb);
+
+ generate_aabb->connect("confirmed", callable_mp(this, &CPUParticles3DEditor::_generate_aabb));
}
void CPUParticles3DEditorPlugin::edit(Object *p_object) {
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h
index 6de23fc2b8..99178b7fde 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.h
@@ -38,14 +38,19 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase {
GDCLASS(CPUParticles3DEditor, GPUParticles3DEditorBase);
enum Menu {
+ MENU_OPTION_GENERATE_AABB,
MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE,
MENU_OPTION_CLEAR_EMISSION_VOLUME,
MENU_OPTION_RESTART,
MENU_OPTION_CONVERT_TO_GPU_PARTICLES,
};
+ ConfirmationDialog *generate_aabb = nullptr;
+ SpinBox *generate_seconds = nullptr;
CPUParticles3D *node = nullptr;
+ void _generate_aabb();
+
void _menu_option(int);
friend class CPUParticles3DEditorPlugin;
diff --git a/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp
index 3745b407a3..fe5d8e92d1 100644
--- a/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/cpu_particles_3d_gizmo_plugin.cpp
@@ -31,11 +31,16 @@
#include "cpu_particles_3d_gizmo_plugin.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/cpu_particles_3d.h"
CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() {
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
+ create_material("particles_material", gizmo_color);
+ gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
+ create_material("particles_solid_material", gizmo_color);
create_icon_material("particles_icon", EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GizmoCPUParticles3D"), EditorStringName(EditorIcons)));
}
@@ -56,6 +61,48 @@ bool CPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {
}
void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ CPUParticles3D *particles = Object::cast_to<CPUParticles3D>(p_gizmo->get_node_3d());
+
+ p_gizmo->clear();
+
+ Vector<Vector3> lines;
+ AABB aabb = particles->get_visibility_aabb();
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ Vector<Vector3> handles;
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 ax;
+ ax[i] = aabb.position[i] + aabb.size[i];
+ ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5;
+ ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5;
+ handles.push_back(ax);
+ }
+
+ Vector3 center = aabb.get_center();
+ for (int i = 0; i < 3; i++) {
+ Vector3 ax;
+ ax[i] = 1.0;
+ handles.push_back(center + ax);
+ lines.push_back(center);
+ lines.push_back(center + ax);
+ }
+
+ Ref<Material> material = get_material("particles_material", p_gizmo);
+
+ p_gizmo->add_lines(lines, material);
+
+ if (p_gizmo->is_selected()) {
+ Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo);
+ p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
+ }
+
Ref<Material> icon = get_material("particles_icon", p_gizmo);
p_gizmo->add_unscaled_billboard(icon, 0.05);
}
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 92c1b1be1d..f0cb2aa3a5 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1488,6 +1488,10 @@ Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const
}
void Node3DEditorViewport::_surface_mouse_enter() {
+ if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
+ return;
+ }
+
if (!surface->has_focus() && (!get_viewport()->gui_get_focus_owner() || !get_viewport()->gui_get_focus_owner()->is_text_field())) {
surface->grab_focus();
}
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp
index c5c791ca8e..04d4c6d779 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.cpp
+++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp
@@ -52,32 +52,58 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
Ref<SceneState> state = Ref<PackedScene>(loaded_res)->get_state();
Vector<String> parsed_strings;
- List<String> tabcontainer_paths;
+ Vector<Pair<NodePath, bool>> atr_owners;
+ Vector<String> tabcontainer_paths;
for (int i = 0; i < state->get_node_count(); i++) {
String node_type = state->get_node_type(i);
- bool is_control = ClassDB::is_parent_class(node_type, "Control");
- if (!is_control && !ClassDB::is_parent_class(node_type, "Window")) {
- continue;
- }
+ String parent_path = state->get_node_path(i, true);
- // Find the `auto_translate` property, and abort the string parsing of the node if disabled.
+ // Find the `auto_translate_mode` property.
bool auto_translating = true;
+ bool auto_translate_mode_found = false;
for (int j = 0; j < state->get_node_property_count(i); j++) {
- if (state->get_node_property_name(i, j) == "auto_translate" && (bool)state->get_node_property_value(i, j) == false) {
+ if (state->get_node_property_name(i, j) != "auto_translate_mode") {
+ continue;
+ }
+
+ auto_translate_mode_found = true;
+
+ 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.
+ atr_owners.remove_at(idx_last);
+ idx_last -= 1;
+ }
+
+ int auto_translate_mode = (int)state->get_node_property_value(i, j);
+ if (auto_translate_mode == Node::AUTO_TRANSLATE_MODE_DISABLED) {
auto_translating = false;
- break;
+ }
+
+ atr_owners.push_back(Pair(state->get_node_path(i), auto_translating));
+
+ break;
+ }
+
+ // 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) {
+ auto_translating = atr_owners[idx_last].second;
+ } else {
+ atr_owners.push_back(Pair(state->get_node_path(i), true));
}
}
// Parse the names of children of `TabContainer`s, as they are used for tab titles.
if (!tabcontainer_paths.is_empty()) {
- String parent_path = state->get_node_path(i, true);
if (!parent_path.begins_with(tabcontainer_paths[tabcontainer_paths.size() - 1])) {
// Switch to the previous `TabContainer` this was nested in, if that was the case.
- tabcontainer_paths.pop_back();
+ tabcontainer_paths.remove_at(tabcontainer_paths.size() - 1);
}
- if (is_control && auto_translating && !tabcontainer_paths.is_empty() && parent_path == tabcontainer_paths[tabcontainer_paths.size() - 1]) {
+ if (auto_translating && !tabcontainer_paths.is_empty() && ClassDB::is_parent_class(node_type, "Control") &&
+ parent_path == tabcontainer_paths[tabcontainer_paths.size() - 1]) {
parsed_strings.push_back(state->get_node_name(i));
}
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 335aa33c4a..e6a1b76c78 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -3872,7 +3872,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
scripts_vbox->add_child(filter_scripts);
script_list = memnew(ItemList);
- script_list->set_auto_translate(false);
+ script_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
scripts_vbox->add_child(script_list);
script_list->set_custom_minimum_size(Size2(150, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
script_list->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -3917,7 +3917,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
overview_vbox->add_child(filter_methods);
members_overview = memnew(ItemList);
- members_overview->set_auto_translate(false);
+ members_overview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
overview_vbox->add_child(members_overview);
members_overview->set_allow_reselect(true);
@@ -3926,7 +3926,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
members_overview->set_allow_rmb_select(true);
help_overview = memnew(ItemList);
- help_overview->set_auto_translate(false);
+ help_overview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
overview_vbox->add_child(help_overview);
help_overview->set_allow_reselect(true);
help_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index a018ec095b..8625e468fa 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -659,7 +659,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
window_wrapper->connect("window_visibility_changed", callable_mp(this, &ShaderEditorPlugin::_window_changed));
shader_list = memnew(ItemList);
- shader_list->set_auto_translate(false);
+ shader_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vb->add_child(shader_list);
shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected));
diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp
index 69ad274114..38cc51d3c0 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -256,7 +256,7 @@ ShaderFileEditor::ShaderFileEditor() {
add_child(main_hs);
versions = memnew(ItemList);
- versions->set_auto_translate(false);
+ versions->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
versions->connect("item_selected", callable_mp(this, &ShaderFileEditor::_version_selected));
versions->set_custom_minimum_size(Size2i(200 * EDSCALE, 0));
main_hs->add_child(versions);
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 2476d28986..2495d28e3c 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -1880,7 +1880,7 @@ SpriteFramesEditor::SpriteFramesEditor() {
add_child(file);
frame_list = memnew(ItemList);
- frame_list->set_auto_translate(false);
+ frame_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
frame_list->set_v_size_flags(SIZE_EXPAND_FILL);
frame_list->set_icon_mode(ItemList::ICON_MODE_TOP);
frame_list->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 53bdf79d87..8f369b23b2 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -2246,7 +2246,7 @@ ThemeTypeDialog::ThemeTypeDialog() {
add_type_vb->add_child(add_type_options_label);
add_type_options = memnew(ItemList);
- add_type_options->set_auto_translate(false);
+ add_type_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_type_options->set_v_size_flags(Control::SIZE_EXPAND_FILL);
add_type_vb->add_child(add_type_options);
add_type_options->connect("item_selected", callable_mp(this, &ThemeTypeDialog::_add_type_options_cbk));
@@ -3416,7 +3416,7 @@ ThemeTypeEditor::ThemeTypeEditor() {
theme_type_list = memnew(OptionButton);
theme_type_list->set_h_size_flags(SIZE_EXPAND_FILL);
theme_type_list->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
- theme_type_list->set_auto_translate(false);
+ theme_type_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
type_list_hb->add_child(theme_type_list);
theme_type_list->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_list_type_selected));
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index d03445b412..e49ba5844a 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -311,7 +311,7 @@ AtlasMergingDialog::AtlasMergingDialog() {
// Atlas sources item list.
atlas_merging_atlases_list = memnew(ItemList);
- atlas_merging_atlases_list->set_auto_translate(false);
+ atlas_merging_atlases_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
atlas_merging_atlases_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index bc19735c4f..e2f6cb0f38 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -2401,7 +2401,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
sources_bottom_actions->add_child(source_sort_button);
sources_list = memnew(ItemList);
- sources_list->set_auto_translate(false);
+ sources_list->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED);
sources_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
sources_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -2441,7 +2441,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
// Scenes collection source.
scene_tiles_list = memnew(ItemList);
- scene_tiles_list->set_auto_translate(false);
+ scene_tiles_list->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED);
scene_tiles_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
scene_tiles_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI);
@@ -2467,7 +2467,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
int thumbnail_size = 64;
patterns_item_list = memnew(ItemList);
- patterns_item_list->set_auto_translate(false);
+ patterns_item_list->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED);
patterns_item_list->set_max_columns(0);
patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
@@ -3529,7 +3529,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() {
tilemap_tab_terrains->add_child(terrains_tree);
terrains_tile_list = memnew(ItemList);
- terrains_tile_list->set_auto_translate(false);
+ terrains_tile_list->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED);
terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tile_list->set_max_columns(0);
terrains_tile_list->set_same_column_width(true);
diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
index 4a0b5e2117..ca4ffeecc2 100644
--- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
+++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
@@ -345,7 +345,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
vbox_container->add_child(source_level_label);
source_level_list = memnew(ItemList);
- source_level_list->set_auto_translate(false);
+ source_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
source_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
source_level_list->set_select_mode(ItemList::SELECT_MULTI);
source_level_list->set_allow_rmb_select(true);
@@ -357,7 +357,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
vbox_container->add_child(coords_level_label);
coords_level_list = memnew(ItemList);
- coords_level_list->set_auto_translate(false);
+ coords_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
coords_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
coords_level_list->set_select_mode(ItemList::SELECT_MULTI);
coords_level_list->set_allow_rmb_select(true);
@@ -369,7 +369,7 @@ TileProxiesManagerDialog::TileProxiesManagerDialog() {
vbox_container->add_child(alternative_level_label);
alternative_level_list = memnew(ItemList);
- alternative_level_list->set_auto_translate(false);
+ alternative_level_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
alternative_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
alternative_level_list->set_select_mode(ItemList::SELECT_MULTI);
alternative_level_list->set_allow_rmb_select(true);
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index bb950dbf63..06fcfbfb41 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -854,7 +854,7 @@ TileSetEditor::TileSetEditor() {
p->set_item_checked(TilesEditorUtils::SOURCE_SORT_ID, true);
sources_list = memnew(ItemList);
- sources_list->set_auto_translate(false);
+ sources_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
sources_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -934,7 +934,7 @@ TileSetEditor::TileSetEditor() {
//// Patterns ////
int thumbnail_size = 64;
patterns_item_list = memnew(ItemList);
- patterns_item_list->set_auto_translate(false);
+ patterns_item_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
patterns_item_list->set_max_columns(0);
patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index 6224ab72dc..1529ddadb6 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -560,7 +560,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
split_container_right_side->add_child(right_vbox_container);
scene_tiles_list = memnew(ItemList);
- scene_tiles_list->set_auto_translate(false);
+ scene_tiles_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
SET_DRAG_FORWARDING_CDU(scene_tiles_list, TileSetScenesCollectionSourceEditor);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index ddecf31352..c69e85454f 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -573,7 +573,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
String prop_name = dp.name.strip_edges();
if (!prop_name.is_empty()) {
Label *label = memnew(Label);
- label->set_auto_translate(false); // TODO: Implement proper translation switch.
+ label->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED); // TODO: Implement proper translation switch.
label->set_text(prop_name + ":");
hbox->add_child(label);
}
@@ -845,7 +845,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
hb->add_child(remove_btn);
} else {
Label *label = memnew(Label);
- label->set_auto_translate(false); // TODO: Implement proper translation switch.
+ label->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED); // TODO: Implement proper translation switch.
label->set_text(name_left);
label->add_theme_style_override("normal", editor->get_theme_stylebox(SNAME("label_style"), SNAME("VShaderEditor"))); //more compact
hb->add_child(label);
@@ -895,7 +895,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
type_box->connect("item_selected", callable_mp(editor, &VisualShaderEditor::_change_output_port_type).bind(p_id, i), CONNECT_DEFERRED);
} else {
Label *label = memnew(Label);
- label->set_auto_translate(false); // TODO: Implement proper translation switch.
+ label->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED); // TODO: Implement proper translation switch.
label->set_text(name_right);
label->add_theme_style_override("normal", editor->get_theme_stylebox(SNAME("label_style"), SNAME("VShaderEditor"))); //more compact
hb->add_child(label);
@@ -6771,7 +6771,7 @@ public:
} else {
prop_name_str = prop_name_str.capitalize() + ":";
}
- prop_name->set_auto_translate(false); // TODO: Implement proper translation switch.
+ prop_name->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // TODO: Implement proper translation switch.
prop_name->set_text(prop_name_str);
prop_name->set_visible(false);
hbox->add_child(prop_name);
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index 67aaa85501..234390c136 100644
--- a/editor/project_manager/project_list.cpp
+++ b/editor/project_manager/project_list.cpp
@@ -238,7 +238,7 @@ ProjectListItemControl::ProjectListItemControl() {
main_vbox->add_child(title_hb);
project_title = memnew(Label);
- project_title->set_auto_translate(false);
+ project_title->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
project_title->set_name("ProjectName");
project_title->set_h_size_flags(Control::SIZE_EXPAND_FILL);
project_title->set_clip_text(true);
diff --git a/editor/project_manager/project_tag.cpp b/editor/project_manager/project_tag.cpp
index de9213177b..8fb05b549e 100644
--- a/editor/project_manager/project_tag.cpp
+++ b/editor/project_manager/project_tag.cpp
@@ -66,7 +66,7 @@ ProjectTag::ProjectTag(const String &p_text, bool p_display_close) {
button = memnew(Button);
add_child(button);
- button->set_auto_translate(false);
+ button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
button->set_text(p_text.capitalize());
button->set_focus_mode(FOCUS_NONE);
button->set_icon_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index e59bb76ff4..b7f28068b7 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -280,7 +280,6 @@ void ProjectSettingsEditor::_add_feature_overrides() {
presets.insert("bptc");
presets.insert("s3tc");
- presets.insert("etc");
presets.insert("etc2");
presets.insert("editor");
presets.insert("template_debug");
diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp
index ef700d8352..4c22b37d9d 100644
--- a/editor/scene_create_dialog.cpp
+++ b/editor/scene_create_dialog.cpp
@@ -275,7 +275,7 @@ SceneCreateDialog::SceneCreateDialog() {
root_name_edit = memnew(LineEdit);
gc->add_child(root_name_edit);
root_name_edit->set_tooltip_text(TTR("When empty, the root node name is derived from the scene name based on the \"editor/naming/node_name_casing\" project setting."));
- root_name_edit->set_auto_translate(false);
+ root_name_edit->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
root_name_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
root_name_edit->connect("text_submitted", callable_mp(this, &SceneCreateDialog::accept_create).unbind(1));
}
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index a9390487ef..32795330c4 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -120,3 +120,23 @@ GH-87340
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/RenderingDevice/methods/screen_get_framebuffer_format': arguments
screen_get_framebuffer_format can now specify the screen it should get the format from. The argument defaults to the main window to emulate the behavior of the old function.
+
+
+GH-88418
+--------
+Validate extension JSON: API was removed: classes/GDExtension/methods/close_library
+Validate extension JSON: API was removed: classes/GDExtension/methods/initialize_library
+Validate extension JSON: API was removed: classes/GDExtension/methods/open_library
+
+Since it was basically impossible to use these methods in any useful way, the GDExtension team agreed that breaking compatibility by removing them was OK.
+
+
+GH-86629
+--------
+Validate extension JSON: Error: Field 'classes/Animation/methods/position_track_interpolate/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/Animation/methods/rotation_track_interpolate/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/Animation/methods/scale_track_interpolate/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/Animation/methods/blend_shape_track_interpolate/arguments': size changed value in new API, from 2 to 3.
+Validate extension JSON: Error: Field 'classes/Animation/methods/value_track_interpolate/arguments': size changed value in new API, from 2 to 3.
+
+Added optional argument to track_interpolate to treat playing backward correctly. Compatibility method registered.
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index f538fc6676..c9ea67cb09 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -190,9 +190,9 @@ static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size
} else if (RS::get_singleton()->has_os_feature("s3tc")) {
format = basist::transcoder_texture_format::cTFBC1; // get this from renderer
imgfmt = Image::FORMAT_DXT1;
- } else if (RS::get_singleton()->has_os_feature("etc")) {
+ } else if (RS::get_singleton()->has_os_feature("etc2")) {
format = basist::transcoder_texture_format::cTFETC1; // get this from renderer
- imgfmt = Image::FORMAT_ETC;
+ imgfmt = Image::FORMAT_ETC2_RGB8;
} else {
format = basist::transcoder_texture_format::cTFBGR565; // get this from renderer
imgfmt = Image::FORMAT_RGB565;
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 9982edfbd3..5cfbd3f9dd 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -579,7 +579,7 @@
<param index="2" name="step" type="float" default="1.0" />
<param index="3" name="extra_hints" type="String" default="&quot;&quot;" />
<description>
- Export an [int] or [float] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [code]EditorSettings.interface/inspector/default_float_step[/code] setting.
+ Export an [int] or [float] property as a range value. The range must be defined by [param min] and [param max], as well as an optional [param step] and a variety of extra hints. The [param step] defaults to [code]1[/code] for integer properties. For floating-point numbers this value depends on your [member EditorSettings.interface/inspector/default_float_step] setting.
If hints [code]"or_greater"[/code] and [code]"or_less"[/code] are provided, the editor widget will not cap the value at range boundaries. The [code]"exp"[/code] hint will make the edited values on range to change exponentially. The [code]"hide_slider"[/code] hint will hide the slider element of the editor widget.
Hints also allow to indicate the units for the edited value. Using [code]"radians_as_degrees"[/code] you can specify that the actual value is in radians, but should be displayed in degrees in the Inspector dock (the range values are also in degrees). [code]"degrees"[/code] allows to add a degree sign as a unit suffix (the value is unchanged). Finally, a custom suffix can be provided using [code]"suffix:unit"[/code], where "unit" can be any string.
See also [constant PROPERTY_HINT_RANGE].
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index b8902694c9..c6b68e9d34 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -1316,7 +1316,7 @@ GridMapEditor::GridMapEditor() {
EDITOR_DEF("editors/grid_map/preview_size", 64);
mesh_library_palette = memnew(ItemList);
- mesh_library_palette->set_auto_translate(false);
+ mesh_library_palette->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
add_child(mesh_library_palette);
mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL);
mesh_library_palette->connect("gui_input", callable_mp(this, &GridMapEditor::_mesh_library_palette_input));
diff --git a/modules/ktx/texture_loader_ktx.cpp b/modules/ktx/texture_loader_ktx.cpp
index 155ed56bd0..026c0ce510 100644
--- a/modules/ktx/texture_loader_ktx.cpp
+++ b/modules/ktx/texture_loader_ktx.cpp
@@ -287,7 +287,7 @@ static Ref<Image> load_from_file_access(Ref<FileAccess> f, Error *r_error) {
ktxfmt = KTX_TTF_BC7_RGBA;
} else if (RS::get_singleton()->has_os_feature("s3tc")) {
ktxfmt = KTX_TTF_BC1_RGB;
- } else if (RS::get_singleton()->has_os_feature("etc")) {
+ } else if (RS::get_singleton()->has_os_feature("etc2")) {
ktxfmt = KTX_TTF_ETC1_RGB;
} else {
ktxfmt = KTX_TTF_RGBA32;
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index a6f92158f9..64485afeb0 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -37,14 +37,26 @@
A list of additional command line arguments, exported project will receive when started.
</member>
<member name="custom_template/debug" type="String" setter="" getter="">
- Path to the custom export template. If left empty, default template is used.
+ Path to an APK file to use as a custom export template for debug exports. If left empty, default template is used.
+ [b]Note:[/b] This is only used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is disabled.
</member>
<member name="custom_template/release" type="String" setter="" getter="">
- Path to the custom export template. If left empty, default template is used.
+ Path to an APK file to use as a custom export template for release exports. If left empty, default template is used.
+ [b]Note:[/b] This is only used if [member EditorExportPlatformAndroid.gradle_build/use_gradle_build] is disabled.
+ </member>
+ <member name="gradle_build/android_source_template" type="String" setter="" getter="">
+ Path to a ZIP file holding the source for the export template used in a Gradle build. If left empty, the default template is used.
+ </member>
+ <member name="gradle_build/compress_native_libraries" type="bool" setter="" getter="">
+ If [code]true[/code], native libraries are compressed when performing a Gradle build.
+ [b]Note:[/b] Although your binary may be smaller, your application may load slower because the native libraries are not loaded directly from the binary at runtime.
</member>
<member name="gradle_build/export_format" type="int" setter="" getter="">
Export format for Gradle build.
</member>
+ <member name="gradle_build/gradle_build_directory" type="String" setter="" getter="">
+ Path to the Gradle build directory. If left empty, then [code]res://android[/code] will be used.
+ </member>
<member name="gradle_build/min_sdk" type="String" setter="" getter="">
Minimal Android SDK version for Gradle build.
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 459f5a5983..d0db7b2e6c 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -46,6 +46,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
+#include "editor/export/export_template_manager.h"
#include "editor/import/resource_importer_texture_settings.h"
#include "editor/themes/editor_scale.h"
#include "main/splash.gen.h"
@@ -208,12 +209,14 @@ static const char *android_perms[] = {
nullptr
};
+static const char *MISMATCHED_VERSIONS_MESSAGE = "Android build version mismatch:\n| Template installed: %s\n| Requested version: %s\nPlease reinstall Android build template from 'Project' menu.";
+
static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png";
static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png";
static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png";
static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png";
-static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml";
-static const char *GDEXTENSION_LIBS_PATH = "res://android/build/libs/gdextensionlibs.json";
+static const char *SPLASH_CONFIG_PATH = "res/drawable/splash_drawable.xml";
+static const char *GDEXTENSION_LIBS_PATH = "libs/gdextensionlibs.json";
static const int icon_densities_count = 6;
static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192");
@@ -250,8 +253,8 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
static const int EXPORT_FORMAT_APK = 0;
static const int EXPORT_FORMAT_AAB = 1;
-static const char *APK_ASSETS_DIRECTORY = "res://android/build/assets";
-static const char *AAB_ASSETS_DIRECTORY = "res://android/build/assetPacks/installTime/src/main/assets";
+static const char *APK_ASSETS_DIRECTORY = "assets";
+static const char *AAB_ASSETS_DIRECTORY = "assetPacks/installTime/src/main/assets";
static const int OPENGL_MIN_SDK_VERSION = 21; // Should match the value in 'platform/android/java/app/config.gradle#minSdk'
static const int VULKAN_MIN_SDK_VERSION = 24;
@@ -291,7 +294,7 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
// Check for devices updates
String adb = get_adb_path();
- if (FileAccess::exists(adb)) {
+ if (ea->has_runnable_preset.is_set() && FileAccess::exists(adb)) {
String devices;
List<String> args;
args.push_back("devices");
@@ -423,6 +426,25 @@ void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) {
OS::get_singleton()->execute(adb, args);
}
}
+
+void EditorExportPlatformAndroid::_update_preset_status() {
+ const int preset_count = EditorExport::get_singleton()->get_export_preset_count();
+ bool has_runnable = false;
+
+ for (int i = 0; i < preset_count; i++) {
+ const Ref<EditorExportPreset> &preset = EditorExport::get_singleton()->get_export_preset(i);
+ if (preset->get_platform() == this && preset->is_runnable()) {
+ has_runnable = true;
+ break;
+ }
+ }
+
+ if (has_runnable) {
+ has_runnable_preset.set();
+ } else {
+ has_runnable_preset.clear();
+ }
+}
#endif
String EditorExportPlatformAndroid::get_project_name(const String &p_name) const {
@@ -474,7 +496,8 @@ String EditorExportPlatformAndroid::get_valid_basename() const {
}
String EditorExportPlatformAndroid::get_assets_directory(const Ref<EditorExportPreset> &p_preset, int p_export_format) const {
- return p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY;
+ String gradle_build_directory = ExportTemplateManager::get_android_build_directory(p_preset);
+ return gradle_build_directory.path_join(p_export_format == EXPORT_FORMAT_AAB ? AAB_ASSETS_DIRECTORY : APK_ASSETS_DIRECTORY);
}
bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const {
@@ -774,11 +797,10 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared
}
if (abi_index != -1) {
exported = true;
- String base = "res://android/build/libs";
String type = export_data->debug ? "debug" : "release";
String abi = abis[abi_index].abi;
String filename = p_so.path.get_file();
- String dst_path = base.path_join(type).path_join(abi).path_join(filename);
+ String dst_path = export_data->libs_directory.path_join(type).path_join(abi).path_join(filename);
Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_so.path);
print_verbose("Copying .so file from " + p_so.path + " to " + dst_path);
Error err = store_file_at_path(dst_path, data);
@@ -805,6 +827,15 @@ bool EditorExportPlatformAndroid::_uses_vulkan() {
return uses_vulkan;
}
+void EditorExportPlatformAndroid::_notification(int p_what) {
+#ifndef ANDROID_ENABLED
+ if (p_what == NOTIFICATION_POSTINITIALIZE) {
+ ERR_FAIL_NULL(EditorExport::get_singleton());
+ EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformAndroid::_update_preset_status));
+ }
+#endif
+}
+
void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
const char **aperms = android_perms;
while (*aperms) {
@@ -867,7 +898,7 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
manifest_text += _get_application_tag(Ref<EditorExportPlatform>(this), p_preset, _has_read_write_storage_permission(perms), p_debug);
manifest_text += "</manifest>\n";
- String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
+ String manifest_path = ExportTemplateManager::get_android_build_directory(p_preset).path_join(vformat("src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")));
print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text);
store_string_at_path(manifest_path, manifest_text);
@@ -1628,15 +1659,6 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
}
}
-void EditorExportPlatformAndroid::store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) {
- store_image(launcher_icon.export_path, data);
-}
-
-void EditorExportPlatformAndroid::store_image(const String &export_path, const Vector<uint8_t> &data) {
- String img_path = export_path.insert(0, "res://android/build/");
- store_file_at_path(img_path, data);
-}
-
void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const String &processed_splash_config_xml,
const Ref<Image> &splash_image,
@@ -1644,26 +1666,30 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
const Ref<Image> &main_image,
const Ref<Image> &foreground,
const Ref<Image> &background) {
+ String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
+
// Store the splash configuration
if (!processed_splash_config_xml.is_empty()) {
print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml);
- store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml);
+ store_string_at_path(gradle_build_dir.path_join(SPLASH_CONFIG_PATH), processed_splash_config_xml);
}
// Store the splash image
if (splash_image.is_valid() && !splash_image->is_empty()) {
- print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH));
+ String splash_export_path = gradle_build_dir.path_join(SPLASH_IMAGE_EXPORT_PATH);
+ print_verbose("Storing splash image in " + splash_export_path);
Vector<uint8_t> data;
_load_image_data(splash_image, data);
- store_image(SPLASH_IMAGE_EXPORT_PATH, data);
+ store_file_at_path(splash_export_path, data);
}
// Store the splash bg color image
if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) {
- print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH));
+ String splash_bg_color_path = gradle_build_dir.path_join(SPLASH_BG_COLOR_PATH);
+ print_verbose("Storing splash background image in " + splash_bg_color_path);
Vector<uint8_t> data;
_load_image_data(splash_bg_color_image, data);
- store_image(SPLASH_BG_COLOR_PATH, data);
+ store_file_at_path(splash_bg_color_path, data);
}
// Prepare images to be resized for the icons. If some image ends up being uninitialized,
@@ -1674,7 +1700,7 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path);
Vector<uint8_t> data;
_process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data);
- store_image(launcher_icons[i], data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_icons[i].export_path), data);
}
if (foreground.is_valid() && !foreground->is_empty()) {
@@ -1682,7 +1708,7 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
Vector<uint8_t> data;
_process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground,
launcher_adaptive_icon_foregrounds[i].dimensions, data);
- store_image(launcher_adaptive_icon_foregrounds[i], data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_foregrounds[i].export_path), data);
}
if (background.is_valid() && !background->is_empty()) {
@@ -1690,7 +1716,7 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
Vector<uint8_t> data;
_process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background,
launcher_adaptive_icon_backgrounds[i].dimensions, data);
- store_image(launcher_adaptive_icon_backgrounds[i], data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_backgrounds[i].export_path), data);
}
}
}
@@ -1744,6 +1770,11 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
if (xr_mode_index == XR_MODE_OPENXR && !gradle_build_enabled) {
return TTR("OpenXR requires \"Use Gradle Build\" to be enabled");
}
+ } else if (p_name == "gradle_build/compress_native_libraries") {
+ bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
+ if (bool(p_preset->get("gradle_build/compress_native_libraries")) && !gradle_build_enabled) {
+ return TTR("\"Compress Native Libraries\" is only valid when \"Use Gradle Build\" is enabled.");
+ }
} else if (p_name == "gradle_build/export_format") {
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
if (int(p_preset->get("gradle_build/export_format")) == EXPORT_FORMAT_AAB && !gradle_build_enabled) {
@@ -1798,7 +1829,10 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false, false, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/use_gradle_build"), false, true, true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/gradle_build_directory", PROPERTY_HINT_PLACEHOLDER_TEXT, "res://android"), "", false, false));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "gradle_build/android_source_template", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "gradle_build/compress_native_libraries"), false, false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "gradle_build/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK, false, true));
// Using String instead of int to default to an empty string (no override) with placeholder for instructions (see GH-62465).
// This implies doing validation that the string is a proper int.
@@ -1876,6 +1910,18 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
}
}
+bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
+ if (p_option == "gradle_build/gradle_build_directory" || p_option == "gradle_build/android_source_template") {
+ // @todo These are experimental options - keep them hidden for now.
+ //return (bool)p_preset->get("gradle_build/use_gradle_build");
+ return false;
+ } else if (p_option == "custom_template/debug" || p_option == "custom_template/release") {
+ // The APK templates are ignored if Gradle build is enabled.
+ return !p_preset->get("gradle_build/use_gradle_build");
+ }
+ return true;
+}
+
String EditorExportPlatformAndroid::get_name() const {
return "Android";
}
@@ -2345,7 +2391,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
err += template_err;
}
} else {
- bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle");
+ bool installed_android_build_template = FileAccess::exists(ExportTemplateManager::get_android_build_directory(p_preset).path_join("build.gradle"));
if (!installed_android_build_template) {
r_missing_templates = !exists_export_template("android_source.zip", &err);
err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n";
@@ -2492,6 +2538,19 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Ref<Edit
valid = false;
}
+ if (p_preset->get("gradle_build/use_gradle_build")) {
+ String build_version_path = ExportTemplateManager::get_android_build_directory(p_preset).get_base_dir().path_join(".build_version");
+ Ref<FileAccess> f = FileAccess::open(build_version_path, FileAccess::READ);
+ if (f.is_valid()) {
+ String current_version = ExportTemplateManager::get_android_template_identifier(p_preset);
+ String installed_version = f->get_line().strip_edges();
+ if (current_version != installed_version) {
+ err += vformat(TTR(MISMATCHED_VERSIONS_MESSAGE), installed_version, current_version);
+ err += "\n";
+ }
+ }
+ }
+
String min_sdk_str = p_preset->get("gradle_build/min_sdk");
int min_sdk_int = VULKAN_MIN_SDK_VERSION;
if (!min_sdk_str.is_empty()) { // Empty means no override, nothing to do.
@@ -2734,34 +2793,37 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
return OK;
}
-void EditorExportPlatformAndroid::_clear_assets_directory() {
+void EditorExportPlatformAndroid::_clear_assets_directory(const Ref<EditorExportPreset> &p_preset) {
Ref<DirAccess> da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ String gradle_build_directory = ExportTemplateManager::get_android_build_directory(p_preset);
// Clear the APK assets directory
- if (da_res->dir_exists(APK_ASSETS_DIRECTORY)) {
+ String apk_assets_directory = gradle_build_directory.path_join(APK_ASSETS_DIRECTORY);
+ if (da_res->dir_exists(apk_assets_directory)) {
print_verbose("Clearing APK assets directory...");
- Ref<DirAccess> da_assets = DirAccess::open(APK_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(apk_assets_directory);
ERR_FAIL_COND(da_assets.is_null());
da_assets->erase_contents_recursive();
- da_res->remove(APK_ASSETS_DIRECTORY);
+ da_res->remove(apk_assets_directory);
}
// Clear the AAB assets directory
- if (da_res->dir_exists(AAB_ASSETS_DIRECTORY)) {
+ String aab_assets_directory = gradle_build_directory.path_join(AAB_ASSETS_DIRECTORY);
+ if (da_res->dir_exists(aab_assets_directory)) {
print_verbose("Clearing AAB assets directory...");
- Ref<DirAccess> da_assets = DirAccess::open(AAB_ASSETS_DIRECTORY);
+ Ref<DirAccess> da_assets = DirAccess::open(aab_assets_directory);
ERR_FAIL_COND(da_assets.is_null());
da_assets->erase_contents_recursive();
- da_res->remove(AAB_ASSETS_DIRECTORY);
+ da_res->remove(aab_assets_directory);
}
}
-void EditorExportPlatformAndroid::_remove_copied_libs() {
+void EditorExportPlatformAndroid::_remove_copied_libs(String p_gdextension_libs_path) {
print_verbose("Removing previously installed libraries...");
Error error;
- String libs_json = FileAccess::get_file_as_string(GDEXTENSION_LIBS_PATH, &error);
+ String libs_json = FileAccess::get_file_as_string(p_gdextension_libs_path, &error);
if (error || libs_json.is_empty()) {
print_verbose("No previously installed libraries found");
return;
@@ -2777,7 +2839,7 @@ void EditorExportPlatformAndroid::_remove_copied_libs() {
print_verbose("Removing previously installed library " + libs[i]);
da->remove(libs[i]);
}
- da->remove(GDEXTENSION_LIBS_PATH);
+ da->remove(p_gdextension_libs_path);
}
String EditorExportPlatformAndroid::join_list(const List<String> &p_parts, const String &p_separator) {
@@ -2836,6 +2898,8 @@ String EditorExportPlatformAndroid::_resolve_export_plugin_android_library_path(
bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExportPreset> &p_preset) {
bool first_build = last_gradle_build_time == 0;
bool have_plugins_changed = false;
+ String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
+ bool has_build_dir_changed = last_gradle_build_dir != gradle_build_dir;
String plugin_names = _get_plugins_names(p_preset);
@@ -2855,9 +2919,10 @@ bool EditorExportPlatformAndroid::_is_clean_build_required(const Ref<EditorExpor
}
last_gradle_build_time = OS::get_singleton()->get_unix_time();
+ last_gradle_build_dir = gradle_build_dir;
last_plugin_names = plugin_names;
- return have_plugins_changed || first_build;
+ return have_plugins_changed || has_build_dir_changed || first_build;
}
Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
@@ -2881,6 +2946,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
EditorProgress ep("export", TTR("Exporting for Android"), 105, true);
bool use_gradle_build = bool(p_preset->get("gradle_build/use_gradle_build"));
+ String gradle_build_directory = use_gradle_build ? ExportTemplateManager::get_android_build_directory(p_preset) : "";
bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG);
bool apk_expansion = p_preset->get("apk_expansion/enable");
Vector<ABI> enabled_abis = get_enabled_abis(p_preset);
@@ -2940,15 +3006,17 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
//test that installed build version is alright
{
print_verbose("Checking build version...");
- Ref<FileAccess> f = FileAccess::open("res://android/.build_version", FileAccess::READ);
+ String gradle_base_directory = gradle_build_directory.get_base_dir();
+ Ref<FileAccess> f = FileAccess::open(gradle_base_directory.path_join(".build_version"), FileAccess::READ);
if (f.is_null()) {
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Trying to build from a gradle built template, but no version info for it exists. Please reinstall from the 'Project' menu."));
return ERR_UNCONFIGURED;
}
- String version = f->get_line().strip_edges();
- print_verbose("- build version: " + version);
- if (version != VERSION_FULL_CONFIG) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR("Android build version mismatch: Template installed: %s, Godot version: %s. Please reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG));
+ String current_version = ExportTemplateManager::get_android_template_identifier(p_preset);
+ String installed_version = f->get_line().strip_edges();
+ print_verbose("- build version: " + installed_version);
+ if (installed_version != current_version) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), vformat(TTR(MISMATCHED_VERSIONS_MESSAGE), installed_version, current_version));
return ERR_UNCONFIGURED;
}
}
@@ -2969,9 +3037,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
// TODO: should we use "package/name" or "application/config/name"?
String project_name = get_project_name(p_preset->get("package/name"));
- err = _create_project_name_strings_files(p_preset, project_name); //project name localization.
+ err = _create_project_name_strings_files(p_preset, project_name, gradle_build_directory); //project name localization.
if (err != OK) {
- add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res://android/build/res/*.xml files with project name."));
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res/*.xml files with project name."));
}
// Copies the project icon files into the appropriate Gradle project directory.
_copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background);
@@ -2979,12 +3047,14 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
_write_tmp_manifest(p_preset, p_give_internet, p_debug);
//stores all the project files inside the Gradle project directory. Also includes all ABIs
- _clear_assets_directory();
- _remove_copied_libs();
+ _clear_assets_directory(p_preset);
+ String gdextension_libs_path = gradle_build_directory.path_join(GDEXTENSION_LIBS_PATH);
+ _remove_copied_libs(gdextension_libs_path);
if (!apk_expansion) {
print_verbose("Exporting project files...");
CustomExportData user_data;
user_data.assets_directory = assets_directory;
+ user_data.libs_directory = gradle_build_directory.path_join("libs");
user_data.debug = p_debug;
if (p_flags & DEBUG_FLAG_DUMB_CLIENT) {
err = export_project_files(p_preset, p_debug, ignore_apk_file, &user_data, copy_gradle_so);
@@ -3023,7 +3093,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
build_command = "gradlew";
#endif
- String build_path = ProjectSettings::get_singleton()->get_resource_path().path_join("android/build");
+ String build_path = ProjectSettings::get_singleton()->globalize_path(gradle_build_directory);
build_command = build_path.path_join(build_command);
String package_name = get_package_name(p_preset->get("package/unique_name"));
@@ -3040,6 +3110,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
String enabled_abi_string = join_abis(enabled_abis, "|", false);
String sign_flag = should_sign ? "true" : "false";
String zipalign_flag = "true";
+ String compress_native_libraries_flag = bool(p_preset->get("gradle_build/compress_native_libraries")) ? "true" : "false";
Vector<String> android_libraries;
Vector<String> android_dependencies;
@@ -3104,6 +3175,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pplugins_maven_repos=" + combined_android_dependencies_maven_repos); // argument to specify the list of maven repos for android dependencies provided by plugins.
cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned.
cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed.
+ cmdline.push_back("-Pcompress_native_libraries=" + compress_native_libraries_flag); // argument to specify whether the build should compress native libraries.
cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG));
// NOTE: The release keystore is not included in the verbose logging
@@ -3530,6 +3602,7 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
android_plugins_changed.set();
#endif // DISABLE_DEPRECATED
#ifndef ANDROID_ENABLED
+ _update_preset_status();
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
#endif
}
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index c282055fba..e25655c6cc 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -90,6 +90,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
#endif // DISABLE_DEPRECATED
String last_plugin_names;
uint64_t last_gradle_build_time = 0;
+ String last_gradle_build_dir;
Vector<Device> devices;
SafeFlag devices_changed;
@@ -97,8 +98,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
#ifndef ANDROID_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
+ SafeFlag has_runnable_preset;
static void _check_for_changes_poll_thread(void *ud);
+ void _update_preset_status();
#endif
String get_project_name(const String &p_name) const;
@@ -174,10 +177,6 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background);
- void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data);
-
- void store_image(const String &export_path, const Vector<uint8_t> &data);
-
void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const String &processed_splash_config_xml,
const Ref<Image> &splash_image,
@@ -190,14 +189,18 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
static bool _uses_vulkan();
+protected:
+ void _notification(int p_what);
+
public:
typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
-public:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
virtual void get_export_options(List<ExportOption> *r_options) const override;
+ virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
+
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
virtual String get_name() const override;
@@ -248,9 +251,9 @@ public:
Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep);
- void _clear_assets_directory();
+ void _clear_assets_directory(const Ref<EditorExportPreset> &p_preset);
- void _remove_copied_libs();
+ void _remove_copied_libs(String p_gdextension_libs_path);
static String join_list(const List<String> &p_parts, const String &p_separator);
static String join_abis(const Vector<ABI> &p_parts, const String &p_separator, bool p_use_arch);
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 0915009235..9eddef6a4c 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -191,14 +191,14 @@ String _android_xml_escape(const String &p_string) {
}
// Creates strings.xml files inside the gradle project for different locales.
-Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
+Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir) {
print_verbose("Creating strings resources for supported locales for project " + project_name);
// Stores the string into the default values directory.
String processed_default_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(project_name));
- store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
+ store_string_at_path(p_gradle_build_dir.path_join("res/values/godot_project_name_string.xml"), processed_default_xml_string);
// Searches the Gradle project res/ directory to find all supported locales
- Ref<DirAccess> da = DirAccess::open("res://android/build/res");
+ Ref<DirAccess> da = DirAccess::open(p_gradle_build_dir.path_join("res"));
if (da.is_null()) {
if (OS::get_singleton()->is_stdout_verbose()) {
print_error("Unable to open Android resources directory.");
@@ -217,7 +217,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
continue;
}
String locale = file.replace("values-", "").replace("-r", "_");
- String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml";
+ String locale_directory = p_gradle_build_dir.path_join("res/" + file + "/godot_project_name_string.xml");
if (appnames.has(locale)) {
String locale_project_name = appnames[locale];
String processed_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(locale_project_name));
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 2498394add..9f8e476f73 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -63,6 +63,7 @@ static const int XR_MODE_OPENXR = 1;
struct CustomExportData {
String assets_directory;
+ String libs_directory;
bool debug;
Vector<String> libs;
};
@@ -94,7 +95,7 @@ Error store_string_at_path(const String &p_path, const String &p_data);
Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key);
// Creates strings.xml files inside the gradle project for different locales.
-Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name);
+Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name, const String &p_gradle_build_dir);
String bool_to_string(bool v);
diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle
index f084c60209..7797f4bc9d 100644
--- a/platform/android/java/app/build.gradle
+++ b/platform/android/java/app/build.gradle
@@ -116,6 +116,14 @@ android {
if (shouldNotStrip()) {
doNotStrip '**/*.so'
}
+
+ jniLibs {
+ // Setting this to true causes AGP to package compressed native libraries when building the app
+ // For more background, see:
+ // - https://developer.android.com/build/releases/past-releases/agp-3-6-0-release-notes#extractNativeLibs
+ // - https://stackoverflow.com/a/44704840
+ useLegacyPackaging shouldUseLegacyPackaging()
+ }
}
signingConfigs {
diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle
index 7224765f28..f2c4a5d1b6 100644
--- a/platform/android/java/app/config.gradle
+++ b/platform/android/java/app/config.gradle
@@ -361,3 +361,26 @@ ext.shouldSign = { ->
ext.shouldNotStrip = { ->
return isAndroidStudio() || project.hasProperty("doNotStrip")
}
+
+/**
+ * Whether to use the legacy convention of compressing all .so files in the APK.
+ *
+ * For more background, see:
+ * - https://developer.android.com/build/releases/past-releases/agp-3-6-0-release-notes#extractNativeLibs
+ * - https://stackoverflow.com/a/44704840
+ */
+ext.shouldUseLegacyPackaging = { ->
+ int minSdk = getExportMinSdkVersion()
+ if (minSdk < 23) {
+ // Enforce the default behavior for compatibility with device running api < 23
+ return true
+ }
+
+ String legacyPackagingFlag = project.hasProperty("compress_native_libraries") ? project.property("compress_native_libraries") : ""
+ if (legacyPackagingFlag != null && !legacyPackagingFlag.isEmpty()) {
+ return Boolean.parseBoolean(legacyPackagingFlag)
+ }
+
+ // Default behavior for minSdk >= 23
+ return false
+}
diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle
index 61ae0cd58a..ed967b9660 100644
--- a/platform/android/java/lib/build.gradle
+++ b/platform/android/java/lib/build.gradle
@@ -101,6 +101,7 @@ android {
}
boolean devBuild = buildType == "dev"
+ boolean debugSymbols = devBuild || isAndroidStudio()
boolean runTests = devBuild
boolean productionBuild = !devBuild
boolean storeRelease = buildType == "release"
@@ -168,7 +169,7 @@ android {
def taskName = getSconsTaskName(flavorName, buildType, selectedAbi)
tasks.create(name: taskName, type: Exec) {
executable sconsExecutableFile.absolutePath
- args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${devBuild}", "dev_build=${devBuild}", "tests=${runTests}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
+ args "--directory=${pathToRootDir}", "platform=android", "store_release=${storeRelease}", "production=${productionBuild}", "dev_mode=${devBuild}", "dev_build=${devBuild}", "debug_symbols=${debugSymbols}", "tests=${runTests}", "target=${sconsTarget}", "arch=${selectedAbi}", "-j" + Runtime.runtime.availableProcessors()
}
// Schedule the tasks so the generated libs are present before the aar file is packaged.
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index f518d7607b..ea2b23cfb9 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -124,6 +124,14 @@ String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPres
return String();
}
+void EditorExportPlatformIOS::_notification(int p_what) {
+#ifdef MACOS_ENABLED
+ if (p_what == NOTIFICATION_POSTINITIALIZE) {
+ EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformIOS::_update_preset_status));
+ }
+#endif
+}
+
bool EditorExportPlatformIOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
return true;
}
@@ -2234,7 +2242,7 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
// Enum real devices (via ios_deploy, pre Xcode 15).
String idepl = EDITOR_GET("export/ios/ios_deploy");
- if (!idepl.is_empty()) {
+ if (ea->has_runnable_preset.is_set() && !idepl.is_empty()) {
String devices;
List<String> args;
args.push_back("-c");
@@ -2272,7 +2280,7 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
}
// Enum simulators.
- if (_check_xcode_install() && (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun"))) {
+ if (ea->has_runnable_preset.is_set() && _check_xcode_install() && (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun"))) {
{
String devices;
List<String> args;
@@ -2310,7 +2318,7 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
}
// Enum simulators.
- {
+ if (ea->has_runnable_preset.is_set()) {
String devices;
List<String> args;
args.push_back("simctl");
@@ -2379,6 +2387,25 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
}
}
}
+
+void EditorExportPlatformIOS::_update_preset_status() {
+ const int preset_count = EditorExport::get_singleton()->get_export_preset_count();
+ bool has_runnable = false;
+
+ for (int i = 0; i < preset_count; i++) {
+ const Ref<EditorExportPreset> &preset = EditorExport::get_singleton()->get_export_preset(i);
+ if (preset->get_platform() == this && preset->is_runnable()) {
+ has_runnable = true;
+ break;
+ }
+ }
+
+ if (has_runnable) {
+ has_runnable_preset.set();
+ } else {
+ has_runnable_preset.clear();
+ }
+}
#endif
Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) {
@@ -2643,6 +2670,7 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
plugins_changed.set();
devices_changed.set();
#ifdef MACOS_ENABLED
+ _update_preset_status();
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
#endif
}
diff --git a/platform/ios/export/export_plugin.h b/platform/ios/export/export_plugin.h
index edbe566dab..197f27da76 100644
--- a/platform/ios/export/export_plugin.h
+++ b/platform/ios/export/export_plugin.h
@@ -81,9 +81,11 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
#ifdef MACOS_ENABLED
Thread check_for_changes_thread;
SafeFlag quit_request;
+ SafeFlag has_runnable_preset;
static bool _check_xcode_install();
static void _check_for_changes_poll_thread(void *ud);
+ void _update_preset_status();
#endif
typedef Error (*FileHandler)(String p_file, void *p_userdata);
@@ -152,6 +154,8 @@ protected:
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
+ void _notification(int p_what);
+
public:
virtual String get_name() const override { return "iOS"; }
virtual String get_os_name() const override { return "iOS"; }
diff --git a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
index 07378566c3..a44c86202e 100644
--- a/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
+++ b/platform/linuxbsd/doc_classes/EditorExportPlatformLinuxBSD.xml
@@ -57,17 +57,11 @@
- [code]{exe_name}[/code] - Name of application executable.
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
</member>
- <member name="texture_format/bptc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the BPTC format.
+ <member name="texture_format/etc2_astc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the ETC2/ASTC format.
</member>
- <member name="texture_format/etc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC format.
- </member>
- <member name="texture_format/etc2" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC2 format.
- </member>
- <member name="texture_format/s3tc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the S3TC format.
+ <member name="texture_format/s3tc_bptc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the S3TC/BPTC format.
</member>
</members>
</class>
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 4e87dd42dd..85846335f7 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -127,14 +127,6 @@ void OS_LinuxBSD::alert(const String &p_alert, const String &p_title) {
}
}
-int OS_LinuxBSD::get_low_processor_usage_mode_sleep_usec() const {
- if (DisplayServer::get_singleton() == nullptr || DisplayServer::get_singleton()->get_name() != "Wayland" || is_in_low_processor_usage_mode()) {
- return OS::get_low_processor_usage_mode_sleep_usec();
- }
-
- return 500; // Roughly 2000 FPS, improves frame time when emulating VSync.
-}
-
void OS_LinuxBSD::initialize() {
crash_handler.initialize();
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index d4ffc0e9b4..9084061eb9 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -127,8 +127,6 @@ public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
- virtual int get_low_processor_usage_mode_sleep_usec() const override;
-
virtual bool _check_internal_feature_support(const String &p_feature) override;
void run();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index 85bbfe546a..c957dea32d 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -867,11 +867,11 @@ bool DisplayServerWayland::window_is_focused(WindowID p_window_id) const {
}
bool DisplayServerWayland::window_can_draw(DisplayServer::WindowID p_window_id) const {
- return frame;
+ return !suspended;
}
bool DisplayServerWayland::can_any_window_draw() const {
- return frame;
+ return !suspended;
}
void DisplayServerWayland::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {
@@ -1143,7 +1143,32 @@ void DisplayServerWayland::process_events() {
wayland_thread.keyboard_echo_keys();
- frame = wayland_thread.get_reset_frame();
+ if (!suspended) {
+ if (emulate_vsync) {
+ // Due to various reasons, we manually handle display synchronization by
+ // waiting for a frame event (request to draw) or, if available, the actual
+ // window's suspend status. When a window is suspended, we can avoid drawing
+ // altogether, either because the compositor told us that we don't need to or
+ // because the pace of the frame events became unreliable.
+ bool frame = wayland_thread.wait_frame_suspend_ms(1000);
+ if (!frame) {
+ suspended = true;
+ }
+ } else {
+ if (wayland_thread.is_suspended()) {
+ suspended = true;
+ }
+ }
+
+ if (suspended) {
+ DEBUG_LOG_WAYLAND("Window suspended.");
+ }
+ } else {
+ if (wayland_thread.get_reset_frame()) {
+ // At last, a sign of life! We're no longer suspended.
+ suspended = false;
+ }
+ }
wayland_thread.mutex.unlock();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index d4da80a55f..e42967eb7b 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -117,7 +117,7 @@ class DisplayServerWayland : public DisplayServer {
Context context;
- bool frame = false;
+ bool suspended = false;
bool emulate_vsync = false;
String rendering_driver;
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index ae1d96a3b1..ebb21722e2 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -469,7 +469,7 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
}
if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
- registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(5, (int)version)));
+ registry->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, MAX(2, MIN(6, (int)version)));
registry->xdg_wm_base_name = name;
xdg_wm_base_add_listener(registry->xdg_wm_base, &xdg_wm_base_listener, nullptr);
@@ -1063,9 +1063,10 @@ void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel *
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
- // Expect the window to be in windowed mode. The mode will get overridden if
- // the compositor reports otherwise.
+ // Expect the window to be in a plain state. It will get properly set if the
+ // compositor reports otherwise below.
ws->mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ ws->suspended = false;
uint32_t *state = nullptr;
wl_array_for_each(state, states) {
@@ -1078,6 +1079,10 @@ void WaylandThread::_xdg_toplevel_on_configure(void *data, struct xdg_toplevel *
ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
} break;
+ case XDG_TOPLEVEL_STATE_SUSPENDED: {
+ ws->suspended = true;
+ } break;
+
default: {
// We don't care about the other states (for now).
} break;
@@ -1176,9 +1181,10 @@ void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, st
libdecor_window_state window_state = LIBDECOR_WINDOW_STATE_NONE;
- // Expect the window to be in windowed mode. The mode will get overridden if
- // the compositor reports otherwise.
+ // Expect the window to be in a plain state. It will get properly set if the
+ // compositor reports otherwise below.
ws->mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ ws->suspended = false;
if (libdecor_configuration_get_window_state(configuration, &window_state)) {
if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) {
@@ -1188,6 +1194,10 @@ void WaylandThread::libdecor_frame_on_configure(struct libdecor_frame *frame, st
if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN) {
ws->mode = DisplayServer::WINDOW_MODE_FULLSCREEN;
}
+
+ if (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) {
+ ws->suspended = true;
+ }
}
window_state_update_size(ws, width, height);
@@ -3872,6 +3882,102 @@ bool WaylandThread::get_reset_frame() {
return old_frame;
}
+// Dispatches events until a frame event is received, a window is reported as
+// suspended or the timeout expires.
+bool WaylandThread::wait_frame_suspend_ms(int p_timeout) {
+ if (main_window.suspended) {
+ // The window is suspended! The compositor is telling us _explicitly_ that we
+ // don't need to draw, without letting us guess through the frame event's
+ // timing and stuff like that. Our job here is done.
+ return false;
+ }
+
+ if (frame) {
+ // We already have a frame! Probably it got there while the caller locked :D
+ frame = false;
+ return true;
+ }
+
+ struct pollfd poll_fd;
+ poll_fd.fd = wl_display_get_fd(wl_display);
+ poll_fd.events = POLLIN | POLLHUP;
+
+ int begin_ms = OS::get_singleton()->get_ticks_msec();
+ int remaining_ms = p_timeout;
+
+ while (remaining_ms > 0) {
+ // Empty the event queue while it's full.
+ while (wl_display_prepare_read(wl_display) != 0) {
+ if (wl_display_dispatch_pending(wl_display) == -1) {
+ // Oh no. We'll check and handle any display error below.
+ break;
+ }
+
+ if (main_window.suspended) {
+ return false;
+ }
+
+ if (frame) {
+ // We had a frame event in the queue :D
+ frame = false;
+ return true;
+ }
+ }
+
+ int werror = wl_display_get_error(wl_display);
+
+ if (werror) {
+ if (werror == EPROTO) {
+ struct wl_interface *wl_interface = nullptr;
+ uint32_t id = 0;
+
+ int error_code = wl_display_get_protocol_error(wl_display, (const struct wl_interface **)&wl_interface, &id);
+ CRASH_NOW_MSG(vformat("Wayland protocol error %d on interface %s@%d.", error_code, wl_interface ? wl_interface->name : "unknown", id));
+ } else {
+ CRASH_NOW_MSG(vformat("Wayland client error code %d.", werror));
+ }
+ }
+
+ wl_display_flush(wl_display);
+
+ // Wait for the event file descriptor to have new data.
+ poll(&poll_fd, 1, remaining_ms);
+
+ if (poll_fd.revents | POLLIN) {
+ // Load the queues with fresh new data.
+ wl_display_read_events(wl_display);
+ } else {
+ // Oh well... Stop signaling that we want to read.
+ wl_display_cancel_read(wl_display);
+
+ // We've got no new events :(
+ // We won't even bother with checking the frame flag.
+ return false;
+ }
+
+ // Let's try dispatching now...
+ wl_display_dispatch_pending(wl_display);
+
+ if (main_window.suspended) {
+ return false;
+ }
+
+ if (frame) {
+ frame = false;
+ return true;
+ }
+
+ remaining_ms -= OS::get_singleton()->get_ticks_msec() - begin_ms;
+ }
+
+ DEBUG_LOG_WAYLAND_THREAD("Frame timeout.");
+ return false;
+}
+
+bool WaylandThread::is_suspended() const {
+ return main_window.suspended;
+}
+
void WaylandThread::destroy() {
if (!initialized) {
return;
diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h
index 86033c1a09..f3e3c3a2ac 100644
--- a/platform/linuxbsd/wayland/wayland_thread.h
+++ b/platform/linuxbsd/wayland/wayland_thread.h
@@ -177,6 +177,7 @@ public:
Rect2i rect;
DisplayServer::WindowMode mode = DisplayServer::WINDOW_MODE_WINDOWED;
+ bool suspended = false;
// These are true by default as it isn't guaranteed that we'll find an
// xdg-shell implementation with wm_capabilities available. If and once we
@@ -939,6 +940,9 @@ public:
void set_frame();
bool get_reset_frame();
+ bool wait_frame_suspend_ms(int p_timeout);
+
+ bool is_suspended() const;
Error init();
void destroy();
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 72c07c3337..16eabf6855 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -759,24 +759,49 @@ Ref<Image> DisplayServerWindows::clipboard_get_image() const {
if (ptr != NULL) {
BITMAPINFOHEADER *info = &ptr->bmiHeader;
- PackedByteArray pba;
-
- for (LONG y = info->biHeight - 1; y > -1; y--) {
- for (LONG x = 0; x < info->biWidth; x++) {
- tagRGBQUAD *rgbquad = ptr->bmiColors + (info->biWidth * y) + x;
- pba.append(rgbquad->rgbRed);
- pba.append(rgbquad->rgbGreen);
- pba.append(rgbquad->rgbBlue);
- pba.append(rgbquad->rgbReserved);
+ void *dib_bits = (void *)(ptr->bmiColors);
+
+ // Draw DIB image to temporary DC surface and read it back as BGRA8.
+ HDC dc = GetDC(0);
+ if (dc) {
+ HDC hdc = CreateCompatibleDC(dc);
+ if (hdc) {
+ HBITMAP hbm = CreateCompatibleBitmap(dc, info->biWidth, abs(info->biHeight));
+ if (hbm) {
+ SelectObject(hdc, hbm);
+ SetDIBitsToDevice(hdc, 0, 0, info->biWidth, abs(info->biHeight), 0, 0, 0, abs(info->biHeight), dib_bits, ptr, DIB_RGB_COLORS);
+
+ BITMAPINFO bmp_info = {};
+ bmp_info.bmiHeader.biSize = sizeof(bmp_info.bmiHeader);
+ bmp_info.bmiHeader.biWidth = info->biWidth;
+ bmp_info.bmiHeader.biHeight = -abs(info->biHeight);
+ bmp_info.bmiHeader.biPlanes = 1;
+ bmp_info.bmiHeader.biBitCount = 32;
+ bmp_info.bmiHeader.biCompression = BI_RGB;
+
+ Vector<uint8_t> img_data;
+ img_data.resize(info->biWidth * abs(info->biHeight) * 4);
+ GetDIBits(hdc, hbm, 0, abs(info->biHeight), img_data.ptrw(), &bmp_info, DIB_RGB_COLORS);
+
+ uint8_t *wr = (uint8_t *)img_data.ptrw();
+ for (int i = 0; i < info->biWidth * abs(info->biHeight); i++) {
+ SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
+ if (info->biBitCount != 32) {
+ wr[i * 4 + 3] = 255; // Set A to solid if it's not in the source image.
+ }
+ }
+ image = Image::create_from_data(info->biWidth, abs(info->biHeight), false, Image::Format::FORMAT_RGBA8, img_data);
+
+ DeleteObject(hbm);
+ }
+ DeleteDC(hdc);
}
+ ReleaseDC(NULL, dc);
}
- image = Image::create_from_data(info->biWidth, info->biHeight, false, Image::Format::FORMAT_RGBA8, pba);
-
GlobalUnlock(mem);
}
}
}
-
CloseClipboard();
return image;
@@ -1153,7 +1178,7 @@ Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
uint8_t *wr = (uint8_t *)img_data.ptrw();
for (int i = 0; i < width * height; i++) {
- SWAP(wr[i * 4 + 0], wr[i * 4 + 2]);
+ SWAP(wr[i * 4 + 0], wr[i * 4 + 2]); // Swap B and R.
}
img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, img_data);
diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
index c483d3380b..1239a2b32f 100644
--- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml
+++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml
@@ -129,17 +129,11 @@
- [code]{exe_name}[/code] - Name of application executable.
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
</member>
- <member name="texture_format/bptc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the BPTC format.
+ <member name="texture_format/etc2_astc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the ETC2/ASTC format.
</member>
- <member name="texture_format/etc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC format.
- </member>
- <member name="texture_format/etc2" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the ETC2 format.
- </member>
- <member name="texture_format/s3tc" type="bool" setter="" getter="">
- If [code]true[/code], project textures are exported in the S3TC format.
+ <member name="texture_format/s3tc_bptc" type="bool" setter="" getter="">
+ If [code]true[/code], project textures are exported in the S3TC/BPTC format.
</member>
</members>
</class>
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 9fc0477432..b8ca480e3b 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -213,7 +213,7 @@ void TileMap::add_layer(int p_to_pos) {
// Must clear before adding the layer.
TileMapLayer *new_layer = memnew(TileMapLayer);
layers.insert(p_to_pos, new_layer);
- add_child(new_layer);
+ add_child(new_layer, false, INTERNAL_MODE_FRONT);
new_layer->force_parent_owned();
new_layer->set_name(vformat("Layer%d", p_to_pos));
move_child(new_layer, p_to_pos);
@@ -538,7 +538,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
if (p_value.is_array()) {
if (layers.size() == 0) {
TileMapLayer *new_layer = memnew(TileMapLayer);
- add_child(new_layer);
+ add_child(new_layer, false, INTERNAL_MODE_FRONT);
new_layer->force_parent_owned();
new_layer->set_name("Layer0");
new_layer->set_layer_index_in_tile_map_node(0);
@@ -564,7 +564,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
if (index >= (int)layers.size()) {
while (index >= (int)layers.size()) {
TileMapLayer *new_layer = memnew(TileMapLayer);
- add_child(new_layer);
+ add_child(new_layer, false, INTERNAL_MODE_FRONT);
new_layer->force_parent_owned();
new_layer->set_name(vformat("Layer%d", index));
new_layer->set_layer_index_in_tile_map_node(index);
@@ -1002,7 +1002,7 @@ void TileMap::_bind_methods() {
TileMap::TileMap() {
TileMapLayer *new_layer = memnew(TileMapLayer);
- add_child(new_layer);
+ add_child(new_layer, false, INTERNAL_MODE_FRONT);
new_layer->set_name("Layer0");
new_layer->set_layer_index_in_tile_map_node(0);
new_layer->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileMap::_emit_changed));
diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp
index 1177cdb811..89b2c20b20 100644
--- a/scene/2d/visible_on_screen_notifier_2d.cpp
+++ b/scene/2d/visible_on_screen_notifier_2d.cpp
@@ -138,6 +138,10 @@ void VisibleOnScreenEnabler2D::set_enable_node_path(NodePath p_path) {
return;
}
enable_node_path = p_path;
+ if (enable_node_path.is_empty()) {
+ node_id = ObjectID();
+ return;
+ }
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
node_id = ObjectID();
Node *node = get_node(enable_node_path);
@@ -177,8 +181,11 @@ void VisibleOnScreenEnabler2D::_notification(int p_what) {
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
-
node_id = ObjectID();
+ if (enable_node_path.is_empty()) {
+ return;
+ }
+
Node *node = get_node(enable_node_path);
if (node) {
node_id = node->get_instance_id();
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index fa8599a0a2..32d68f7f78 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -101,6 +101,12 @@ void CPUParticles3D::set_randomness_ratio(real_t p_ratio) {
randomness_ratio = p_ratio;
}
+void CPUParticles3D::set_visibility_aabb(const AABB &p_aabb) {
+ RS::get_singleton()->multimesh_set_custom_aabb(multimesh, p_aabb);
+ visibility_aabb = p_aabb;
+ update_gizmos();
+}
+
void CPUParticles3D::set_lifetime_randomness(double p_random) {
lifetime_randomness = p_random;
}
@@ -141,6 +147,10 @@ real_t CPUParticles3D::get_randomness_ratio() const {
return randomness_ratio;
}
+AABB CPUParticles3D::get_visibility_aabb() const {
+ return visibility_aabb;
+}
+
double CPUParticles3D::get_lifetime_randomness() const {
return lifetime_randomness;
}
@@ -520,6 +530,11 @@ bool CPUParticles3D::get_split_scale() {
return split_scale;
}
+AABB CPUParticles3D::capture_aabb() const {
+ RS::get_singleton()->multimesh_set_custom_aabb(multimesh, AABB());
+ return RS::get_singleton()->multimesh_get_aabb(multimesh);
+}
+
void CPUParticles3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "emission_sphere_radius" && (emission_shape != EMISSION_SHAPE_SPHERE && emission_shape != EMISSION_SHAPE_SPHERE_SURFACE)) {
p_property.usage = PROPERTY_USAGE_NONE;
@@ -1341,6 +1356,7 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) {
set_pre_process_time(gpu_particles->get_pre_process_time());
set_explosiveness_ratio(gpu_particles->get_explosiveness_ratio());
set_randomness_ratio(gpu_particles->get_randomness_ratio());
+ set_visibility_aabb(gpu_particles->get_visibility_aabb());
set_use_local_coordinates(gpu_particles->get_use_local_coordinates());
set_fixed_fps(gpu_particles->get_fixed_fps());
set_fractional_delta(gpu_particles->get_fractional_delta());
@@ -1420,6 +1436,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pre_process_time", "secs"), &CPUParticles3D::set_pre_process_time);
ClassDB::bind_method(D_METHOD("set_explosiveness_ratio", "ratio"), &CPUParticles3D::set_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("set_randomness_ratio", "ratio"), &CPUParticles3D::set_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("set_visibility_aabb", "aabb"), &CPUParticles3D::set_visibility_aabb);
ClassDB::bind_method(D_METHOD("set_lifetime_randomness", "random"), &CPUParticles3D::set_lifetime_randomness);
ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &CPUParticles3D::set_use_local_coordinates);
ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &CPUParticles3D::set_fixed_fps);
@@ -1433,6 +1450,7 @@ void CPUParticles3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pre_process_time"), &CPUParticles3D::get_pre_process_time);
ClassDB::bind_method(D_METHOD("get_explosiveness_ratio"), &CPUParticles3D::get_explosiveness_ratio);
ClassDB::bind_method(D_METHOD("get_randomness_ratio"), &CPUParticles3D::get_randomness_ratio);
+ ClassDB::bind_method(D_METHOD("get_visibility_aabb"), &CPUParticles3D::get_visibility_aabb);
ClassDB::bind_method(D_METHOD("get_lifetime_randomness"), &CPUParticles3D::get_lifetime_randomness);
ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &CPUParticles3D::get_use_local_coordinates);
ClassDB::bind_method(D_METHOD("get_fixed_fps"), &CPUParticles3D::get_fixed_fps);
@@ -1461,6 +1479,7 @@ void CPUParticles3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1,suffix:FPS"), "set_fixed_fps", "get_fixed_fps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta");
ADD_GROUP("Drawing", "");
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_visibility_aabb", "get_visibility_aabb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates");
ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
@@ -1665,6 +1684,7 @@ CPUParticles3D::CPUParticles3D() {
set_emitting(true);
set_amount(8);
+ set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)));
set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index e05bd65fba..e9b75d9140 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -138,6 +138,7 @@ private:
real_t randomness_ratio = 0.0;
double lifetime_randomness = 0.0;
double speed_scale = 1.0;
+ AABB visibility_aabb;
bool local_coords = false;
int fixed_fps = 0;
bool fractional_delta = true;
@@ -210,6 +211,7 @@ public:
void set_pre_process_time(double p_time);
void set_explosiveness_ratio(real_t p_ratio);
void set_randomness_ratio(real_t p_ratio);
+ void set_visibility_aabb(const AABB &p_aabb);
void set_lifetime_randomness(double p_random);
void set_use_local_coordinates(bool p_enable);
void set_speed_scale(double p_scale);
@@ -221,6 +223,7 @@ public:
double get_pre_process_time() const;
real_t get_explosiveness_ratio() const;
real_t get_randomness_ratio() const;
+ AABB get_visibility_aabb() const;
double get_lifetime_randomness() const;
bool get_use_local_coordinates() const;
double get_speed_scale() const;
@@ -308,6 +311,8 @@ public:
void convert_from_particles(Node *p_particles);
+ AABB capture_aabb() const;
+
CPUParticles3D();
~CPUParticles3D();
};
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index fb6a45846c..540e70866a 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -82,7 +82,10 @@ void SoftBodyRenderingServerHandler::commit_changes() {
}
void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const Vector3 &p_vertex) {
- memcpy(&write_buffer[p_vertex_id * stride + offset_vertices], &p_vertex, sizeof(Vector3));
+ float *vertex_buffer = reinterpret_cast<float *>(write_buffer + p_vertex_id * stride + offset_vertices);
+ *vertex_buffer++ = (float)p_vertex.x;
+ *vertex_buffer++ = (float)p_vertex.y;
+ *vertex_buffer++ = (float)p_vertex.z;
}
void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const Vector3 &p_normal) {
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index 23f866bd3b..c10ddec17e 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -138,6 +138,10 @@ void VisibleOnScreenEnabler3D::set_enable_node_path(NodePath p_path) {
return;
}
enable_node_path = p_path;
+ if (enable_node_path.is_empty()) {
+ node_id = ObjectID();
+ return;
+ }
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
node_id = ObjectID();
Node *node = get_node(enable_node_path);
@@ -177,8 +181,11 @@ void VisibleOnScreenEnabler3D::_notification(int p_what) {
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
-
node_id = ObjectID();
+ if (enable_node_path.is_empty()) {
+ return;
+ }
+
Node *node = get_node(enable_node_path);
if (node) {
node_id = node->get_instance_id();
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 9f954fd6c0..078d27aa50 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -32,6 +32,7 @@
#include "animation_mixer.compat.inc"
#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/3d/skeleton_3d.h"
@@ -501,6 +502,17 @@ AnimationMixer::AnimationCallbackModeMethod AnimationMixer::get_callback_mode_me
return callback_mode_method;
}
+void AnimationMixer::set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode) {
+ callback_mode_discrete = p_mode;
+#ifdef TOOLS_ENABLED
+ emit_signal(SNAME("mixer_updated"));
+#endif // TOOLS_ENABLED
+}
+
+AnimationMixer::AnimationCallbackModeDiscrete AnimationMixer::get_callback_mode_discrete() const {
+ return callback_mode_discrete;
+}
+
void AnimationMixer::set_audio_max_polyphony(int p_audio_max_polyphony) {
ERR_FAIL_COND(p_audio_max_polyphony < 0 || p_audio_max_polyphony > 128);
audio_max_polyphony = p_audio_max_polyphony;
@@ -597,6 +609,9 @@ bool AnimationMixer::_update_caches() {
List<StringName> sname;
get_animation_list(&sname);
+ bool check_path = GLOBAL_GET("animation/warnings/check_invalid_track_paths");
+ bool check_angle_interpolation = GLOBAL_GET("animation/warnings/check_angle_interpolation_type_conflicting");
+
Node *parent = get_node_or_null(root_node);
if (!parent) {
cache_valid = false;
@@ -645,10 +660,19 @@ bool AnimationMixer::_update_caches() {
if (!track) {
Ref<Resource> resource;
Vector<StringName> leftover_path;
- Node *child = parent->get_node_and_resource(path, resource, leftover_path);
+ if (!parent->has_node_and_resource(path)) {
+ if (check_path) {
+ WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', couldn't resolve track: '" + String(path) + "'. This warning can be disabled in Project Settings.");
+ }
+ continue;
+ }
+
+ Node *child = parent->get_node_and_resource(path, resource, leftover_path);
if (!child) {
- ERR_PRINT("AnimationMixer: '" + String(E) + "', couldn't resolve track: '" + String(path) + "'.");
+ if (check_path) {
+ WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', couldn't resolve track: '" + String(path) + "'. This warning can be disabled in Project Settings.");
+ }
continue;
}
@@ -657,7 +681,7 @@ bool AnimationMixer::_update_caches() {
case Animation::TYPE_VALUE: {
// If a value track without a key is cached first, the initial value cannot be determined.
// It is a corner case, but which may cause problems with blending.
- ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, "AnimationMixer: '" + String(E) + "', Value Track: '" + String(path) + "' must have at least one key to cache for blending.");
+ ERR_CONTINUE_MSG(anim->track_get_key_count(i) == 0, mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' must have at least one key to cache for blending.");
TrackCacheValue *track_value = memnew(TrackCacheValue);
@@ -667,13 +691,7 @@ bool AnimationMixer::_update_caches() {
track_value->object_id = child->get_instance_id();
}
- if (track_src_type == Animation::TYPE_VALUE) {
- track_value->is_continuous = anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
- track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
- } else {
- track_value->is_continuous = true;
- track_value->is_using_angle = false;
- }
+ track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
track_value->subpath = leftover_path;
@@ -698,7 +716,7 @@ bool AnimationMixer::_update_caches() {
Node3D *node_3d = Object::cast_to<Node3D>(child);
if (!node_3d) {
- ERR_PRINT("AnimationMixer: '" + String(E) + "', transform track does not point to Node3D: '" + String(path) + "'.");
+ ERR_PRINT(mixer_name + ": '" + String(E) + "', transform track does not point to Node3D: '" + String(path) + "'.");
continue;
}
@@ -764,20 +782,20 @@ bool AnimationMixer::_update_caches() {
case Animation::TYPE_BLEND_SHAPE: {
#ifndef _3D_DISABLED
if (path.get_subname_count() != 1) {
- ERR_PRINT("AnimationMixer: '" + String(E) + "', blend shape track does not contain a blend shape subname: '" + String(path) + "'.");
+ ERR_PRINT(mixer_name + ": '" + String(E) + "', blend shape track does not contain a blend shape subname: '" + String(path) + "'.");
continue;
}
MeshInstance3D *mesh_3d = Object::cast_to<MeshInstance3D>(child);
if (!mesh_3d) {
- ERR_PRINT("AnimationMixer: '" + String(E) + "', blend shape track does not point to MeshInstance3D: '" + String(path) + "'.");
+ ERR_PRINT(mixer_name + ": '" + String(E) + "', blend shape track does not point to MeshInstance3D: '" + String(path) + "'.");
continue;
}
StringName blend_shape_name = path.get_subname(0);
int blend_shape_idx = mesh_3d->find_blend_shape_by_name(blend_shape_name);
if (blend_shape_idx == -1) {
- ERR_PRINT("AnimationMixer: '" + String(E) + "', blend shape track points to a non-existing name: '" + String(blend_shape_name) + "'.");
+ ERR_PRINT(mixer_name + ": '" + String(E) + "', blend shape track points to a non-existing name: '" + String(blend_shape_name) + "'.");
continue;
}
@@ -853,38 +871,18 @@ bool AnimationMixer::_update_caches() {
}
}
} else if (track_cache_type == Animation::TYPE_VALUE) {
- // If it has at least one angle interpolation, it also uses angle interpolation for blending.
TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track);
- bool was_continuous = track_value->is_continuous;
- bool was_using_angle = track_value->is_using_angle;
+ if (track_value->init_value.is_string() && anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE) {
+ WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
+ }
+ // If it has at least one angle interpolation, it also uses angle interpolation for blending.
+ bool was_using_angle = track_value->is_using_angle;
if (track_src_type == Animation::TYPE_VALUE) {
- track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE;
track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE;
- } else {
- track_value->is_continuous |= true;
}
-
- // TODO: Currently, misc type cannot be blended.
- // In the future, it should have a separate blend weight, just as bool is converted to 0 and 1.
- // Then, it should provide the correct precedence value.
- bool skip_update_mode_warning = false;
- if (track_value->is_continuous) {
- if (!Animation::is_variant_interpolatable(track_value->init_value)) {
- WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' uses a non-numeric type as key value with UpdateMode.UPDATE_CONTINUOUS. This will not be blended correctly, so it is forced to UpdateMode.UPDATE_DISCRETE.");
- track_value->is_continuous = false;
- skip_update_mode_warning = true;
- }
- if (track_value->init_value.is_string()) {
- WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
- }
- }
-
- if (!skip_update_mode_warning && was_continuous != track_value->is_continuous) {
- WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different update modes between some animations which may be blended together. Blending prioritizes UpdateMode.UPDATE_CONTINUOUS, so the process treats UpdateMode.UPDATE_DISCRETE as UpdateMode.UPDATE_CONTINUOUS with InterpolationType.INTERPOLATION_NEAREST.");
- }
- if (was_using_angle != track_value->is_using_angle) {
- WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different interpolation types for rotation between some animations which may be blended together. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
+ if (check_angle_interpolation && (was_using_angle != track_value->is_using_angle)) {
+ WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' has different interpolation types for rotation between some animations which may be blended together. Blending prioritizes angle interpolation, so the blending result uses the shortest path referenced to the initial (RESET animation) value.");
}
}
@@ -1006,6 +1004,7 @@ void AnimationMixer::_blend_init() {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
t->value = Animation::cast_to_blendwise(t->init_value);
t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0;
+ t->use_discrete = false;
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
@@ -1420,8 +1419,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue; // Nothing to blend.
}
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- if (t->is_continuous) {
- Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time) : Variant(a->bezier_track_interpolate(i, time));
+ bool is_discrete = a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE;
+ bool force_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
+ if (!is_discrete || force_continuous) {
+ Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time, is_discrete && force_continuous ? backward : false) : Variant(a->bezier_track_interpolate(i, time));
value = post_process_key_value(a, i, value, t->object_id);
if (value == Variant()) {
continue;
@@ -1479,6 +1480,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
}
}
}
+ t->use_discrete = true;
}
} break;
case Animation::TYPE_METHOD: {
@@ -1730,7 +1732,7 @@ void AnimationMixer::_blend_apply() {
case Animation::TYPE_VALUE: {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- if (!t->is_continuous) {
+ if (callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT && t->use_discrete) {
break; // Don't overwrite the value set by UPDATE_DISCRETE.
}
@@ -1972,7 +1974,6 @@ void AnimationMixer::_build_backup_track_cache() {
if (t_obj) {
t->value = t_obj->get_indexed(t->subpath);
}
- t->is_continuous = true;
} break;
case Animation::TYPE_AUDIO: {
TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);
@@ -2196,6 +2197,9 @@ void AnimationMixer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_callback_mode_method", "mode"), &AnimationMixer::set_callback_mode_method);
ClassDB::bind_method(D_METHOD("get_callback_mode_method"), &AnimationMixer::get_callback_mode_method);
+ ClassDB::bind_method(D_METHOD("set_callback_mode_discrete", "mode"), &AnimationMixer::set_callback_mode_discrete);
+ ClassDB::bind_method(D_METHOD("get_callback_mode_discrete"), &AnimationMixer::get_callback_mode_discrete);
+
ClassDB::bind_method(D_METHOD("set_audio_max_polyphony", "max_polyphony"), &AnimationMixer::set_audio_max_polyphony);
ClassDB::bind_method(D_METHOD("get_audio_max_polyphony"), &AnimationMixer::get_audio_max_polyphony);
@@ -2239,6 +2243,7 @@ void AnimationMixer::_bind_methods() {
ADD_GROUP("Callback Mode", "callback_mode_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_callback_mode_process", "get_callback_mode_process");
ADD_PROPERTY(PropertyInfo(Variant::INT, "callback_mode_method", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_callback_mode_method", "get_callback_mode_method");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "callback_mode_discrete", PROPERTY_HINT_ENUM, "Dominant,Recessive,Force Continuous"), "set_callback_mode_discrete", "get_callback_mode_discrete");
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_PROCESS_PHYSICS);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_PROCESS_IDLE);
@@ -2247,6 +2252,10 @@ void AnimationMixer::_bind_methods() {
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_METHOD_DEFERRED);
BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE);
+ BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT);
+ BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE);
+ BIND_ENUM_CONSTANT(ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS);
+
ADD_SIGNAL(MethodInfo(SNAME("animation_list_changed")));
ADD_SIGNAL(MethodInfo(SNAME("animation_libraries_updated")));
ADD_SIGNAL(MethodInfo(SNAME("animation_finished"), PropertyInfo(Variant::STRING_NAME, "anim_name")));
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index d618d38332..5447a00ee3 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -61,6 +61,12 @@ public:
ANIMATION_CALLBACK_MODE_METHOD_IMMEDIATE,
};
+ enum AnimationCallbackModeDiscrete {
+ ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT,
+ ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE,
+ ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS,
+ };
+
/* ---- Data ---- */
struct AnimationLibraryData {
StringName name;
@@ -120,6 +126,7 @@ protected:
/* ---- General settings for animation ---- */
AnimationCallbackModeProcess callback_mode_process = ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
AnimationCallbackModeMethod callback_mode_method = ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
+ AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
int audio_max_polyphony = 32;
NodePath root_node;
@@ -215,7 +222,7 @@ protected:
Variant init_value;
Variant value;
Vector<StringName> subpath;
- bool is_continuous = false;
+ bool use_discrete = false;
bool is_using_angle = false;
Variant element_size;
@@ -224,7 +231,7 @@ protected:
init_value(p_other.init_value),
value(p_other.value),
subpath(p_other.subpath),
- is_continuous(p_other.is_continuous),
+ use_discrete(p_other.use_discrete),
is_using_angle(p_other.is_using_angle),
element_size(p_other.element_size) {}
@@ -402,6 +409,9 @@ public:
void set_callback_mode_method(AnimationCallbackModeMethod p_mode);
AnimationCallbackModeMethod get_callback_mode_method() const;
+ void set_callback_mode_discrete(AnimationCallbackModeDiscrete p_mode);
+ AnimationCallbackModeDiscrete get_callback_mode_discrete() const;
+
void set_audio_max_polyphony(int p_audio_max_polyphony);
int get_audio_max_polyphony() const;
@@ -466,5 +476,6 @@ public:
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeProcess);
VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeMethod);
+VARIANT_ENUM_CAST(AnimationMixer::AnimationCallbackModeDiscrete);
#endif // ANIMATION_MIXER_H
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index b208d5af5b..9694e855b5 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -897,6 +897,7 @@ void AnimationTree::_bind_methods() {
AnimationTree::AnimationTree() {
deterministic = true;
+ callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
}
AnimationTree::~AnimationTree() {
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 34fc83500f..2e5fd6180c 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -536,6 +536,11 @@ Ref<PropertyTweener> PropertyTweener::set_ease(Tween::EaseType p_ease) {
return this;
}
+Ref<PropertyTweener> PropertyTweener::set_custom_interpolator(const Callable &p_method) {
+ custom_method = p_method;
+ return this;
+}
+
Ref<PropertyTweener> PropertyTweener::set_delay(double p_delay) {
delay = p_delay;
return this;
@@ -589,7 +594,23 @@ bool PropertyTweener::step(double &r_delta) {
double time = MIN(elapsed_time - delay, duration);
if (time < duration) {
- target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
+ if (custom_method.is_valid()) {
+ const Variant t = tween->interpolate_variant(0.0, 1.0, time, duration, trans_type, ease_type);
+ const Variant *argptr = &t;
+
+ Variant result;
+ Callable::CallError ce;
+ custom_method.callp(&argptr, 1, result, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_V_MSG(false, "Error calling custom method from PropertyTweener: " + Variant::get_callable_error_text(custom_method, &argptr, 1, ce) + ".");
+ } else if (result.get_type() != Variant::FLOAT) {
+ ERR_FAIL_V_MSG(false, vformat("Wrong return type in PropertyTweener custom method. Expected float, got %s.", Variant::get_type_name(result.get_type())));
+ }
+
+ target_instance->set_indexed(property, Animation::interpolate_variant(initial_val, final_val, result));
+ } else {
+ target_instance->set_indexed(property, tween->interpolate_variant(initial_val, delta_val, time, duration, trans_type, ease_type));
+ }
r_delta = 0;
return true;
} else {
@@ -617,6 +638,7 @@ void PropertyTweener::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_relative"), &PropertyTweener::as_relative);
ClassDB::bind_method(D_METHOD("set_trans", "trans"), &PropertyTweener::set_trans);
ClassDB::bind_method(D_METHOD("set_ease", "ease"), &PropertyTweener::set_ease);
+ ClassDB::bind_method(D_METHOD("set_custom_interpolator", "interpolator_method"), &PropertyTweener::set_custom_interpolator);
ClassDB::bind_method(D_METHOD("set_delay", "delay"), &PropertyTweener::set_delay);
}
@@ -693,7 +715,7 @@ bool CallbackTweener::step(double &r_delta) {
Callable::CallError ce;
callback.callp(nullptr, 0, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
}
finished = true;
@@ -773,7 +795,7 @@ bool MethodTweener::step(double &r_delta) {
Callable::CallError ce;
callback.callp(argptr, 1, result, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce));
+ ERR_FAIL_V_MSG(false, "Error calling method from MethodTweener: " + Variant::get_callable_error_text(callback, argptr, 1, ce) + ".");
}
if (time < duration) {
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index 053b4fac46..8dcc3ad7b6 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -197,6 +197,7 @@ public:
Ref<PropertyTweener> as_relative();
Ref<PropertyTweener> set_trans(Tween::TransitionType p_trans);
Ref<PropertyTweener> set_ease(Tween::EaseType p_ease);
+ Ref<PropertyTweener> set_custom_interpolator(const Callable &p_method);
Ref<PropertyTweener> set_delay(double p_delay);
void set_tween(const Ref<Tween> &p_tween) override;
@@ -222,6 +223,7 @@ private:
double duration = 0;
Tween::TransitionType trans_type = Tween::TRANS_MAX; // This is set inside set_tween();
Tween::EaseType ease_type = Tween::EASE_MAX;
+ Callable custom_method;
double delay = 0;
bool do_continue = true;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index b9b68127db..2124ffb806 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -268,6 +268,7 @@ String Control::properties_managed_by_container[] = {
bool Control::_set(const StringName &p_name, const Variant &p_value) {
ERR_MAIN_THREAD_GUARD_V(false);
String name = p_name;
+
if (!name.begins_with("theme_override")) {
return false;
}
@@ -309,7 +310,6 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
} else {
return false;
}
-
} else {
if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
@@ -333,12 +333,14 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
return false;
}
}
+
return true;
}
bool Control::_get(const StringName &p_name, Variant &r_ret) const {
ERR_MAIN_THREAD_GUARD_V(false);
String sname = p_name;
+
if (!sname.begins_with("theme_override")) {
return false;
}
@@ -2996,7 +2998,6 @@ void Control::set_layout_direction(Control::LayoutDirection p_direction) {
ERR_FAIL_INDEX((int)p_direction, 4);
data.layout_dir = p_direction;
- data.is_rtl_dirty = true;
propagate_notification(NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
}
@@ -3097,21 +3098,17 @@ bool Control::is_localizing_numeral_system() const {
return data.localize_numeral_system;
}
+#ifndef DISABLE_DEPRECATED
void Control::set_auto_translate(bool p_enable) {
ERR_MAIN_THREAD_GUARD;
- if (p_enable == data.auto_translate) {
- return;
- }
-
- data.auto_translate = p_enable;
-
- notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ set_auto_translate_mode(p_enable ? AUTO_TRANSLATE_MODE_ALWAYS : AUTO_TRANSLATE_MODE_DISABLED);
}
bool Control::is_auto_translating() const {
ERR_READ_THREAD_GUARD_V(false);
- return data.auto_translate;
+ return can_auto_translate();
}
+#endif
// Extra properties.
@@ -3172,14 +3169,6 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_ENTER_TREE: {
-#ifdef TOOLS_ENABLED
- if (is_part_of_edited_scene()) {
- // Don't translate Controls on scene when inside editor.
- set_message_translation(false);
- notification(NOTIFICATION_TRANSLATION_CHANGED);
- }
-#endif
-
// Emits NOTIFICATION_THEME_CHANGED internally.
set_theme_context(ThemeDB::get_singleton()->get_nearest_theme_context(this));
} break;
@@ -3192,6 +3181,7 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_EXIT_TREE: {
set_theme_context(nullptr, false);
+
release_focus();
get_viewport()->_gui_remove_control(this);
} break;
@@ -3506,8 +3496,10 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Control::get_layout_direction);
ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Control::is_layout_rtl);
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating);
+#endif
ClassDB::bind_method(D_METHOD("set_localize_numeral_system", "enable"), &Control::set_localize_numeral_system);
ClassDB::bind_method(D_METHOD("is_localizing_numeral_system"), &Control::is_localizing_numeral_system);
@@ -3558,9 +3550,12 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
ADD_GROUP("Localization", "");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "localize_numeral_system"), "set_localize_numeral_system", "is_localizing_numeral_system");
+#ifndef DISABLE_DEPRECATED
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_translate", "is_auto_translating");
+#endif
+
ADD_GROUP("Tooltip", "tooltip_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index e0bd624edf..8bcd955457 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -253,7 +253,6 @@ private:
bool is_rtl_dirty = true;
bool is_rtl = false;
- bool auto_translate = true;
bool localize_numeral_system = true;
// Extra properties.
@@ -624,11 +623,10 @@ public:
void set_localize_numeral_system(bool p_enable);
bool is_localizing_numeral_system() const;
+#ifndef DISABLE_DEPRECATED
void set_auto_translate(bool p_enable);
bool is_auto_translating() const;
- _FORCE_INLINE_ String atr(const String p_string) const {
- return is_auto_translating() ? tr(p_string) : p_string;
- };
+#endif
// Extra properties.
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 080b687337..89c627a7a8 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -149,10 +149,6 @@ void MenuButton::_notification(int p_what) {
menu_btn_other->get_popup()->set_focused_item(-1);
}
} break;
-
- case NOTIFICATION_TRANSLATION_CHANGED: {
- popup->set_auto_translate(is_auto_translating());
- } break;
}
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 45cc9623be..8fc21c077b 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -127,10 +127,7 @@ void OptionButton::_notification(int p_what) {
theme_cache.arrow_icon->draw(ci, ofs, clr);
} break;
- case NOTIFICATION_TRANSLATION_CHANGED: {
- popup->set_auto_translate(is_auto_translating());
- [[fallthrough]];
- }
+ case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
[[fallthrough]];
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index dfbc940660..77e00e4ab4 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -99,6 +99,26 @@ void Node::_notification(int p_notification) {
}
}
+ // Update auto translate mode.
+ if (data.auto_translate_mode == AUTO_TRANSLATE_MODE_INHERIT && !data.parent) {
+ ERR_PRINT("The root node can't be set to Inherit auto translate mode, reverting to Always instead.");
+ data.auto_translate_mode = AUTO_TRANSLATE_MODE_ALWAYS;
+ }
+ data.is_auto_translate_dirty = true;
+
+#ifdef TOOLS_ENABLED
+ // Don't translate UI elements when they're being edited.
+ if (is_part_of_edited_scene()) {
+ set_message_translation(false);
+ } else if (data.auto_translate_mode != AUTO_TRANSLATE_MODE_DISABLED) {
+ notification(NOTIFICATION_TRANSLATION_CHANGED);
+ }
+#else
+ if (data.auto_translate_mode != AUTO_TRANSLATE_MODE_DISABLED) {
+ notification(NOTIFICATION_TRANSLATION_CHANGED);
+ }
+#endif
+
if (data.input) {
add_to_group("_vp_input" + itos(get_viewport()->get_instance_id()));
}
@@ -136,11 +156,11 @@ void Node::_notification(int p_notification) {
remove_from_group("_vp_unhandled_key_input" + itos(get_viewport()->get_instance_id()));
}
- // Remove from processing first
+ // Remove from processing first.
if (_is_any_processing()) {
_remove_from_process_thread_group();
}
- // Remove the process group
+ // Remove the process group.
if (data.process_thread_group_owner == this) {
_remove_process_group();
}
@@ -217,6 +237,12 @@ void Node::_notification(int p_notification) {
memdelete(child);
}
} break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ if (data.inside_tree) {
+ data.is_auto_translate_dirty = true;
+ }
+ } break;
}
}
@@ -1149,6 +1175,49 @@ bool Node::is_processing_unhandled_key_input() const {
return data.unhandled_key_input;
}
+void Node::set_auto_translate_mode(AutoTranslateMode p_mode) {
+ ERR_THREAD_GUARD
+ if (data.auto_translate_mode == p_mode) {
+ return;
+ }
+
+ if (p_mode == AUTO_TRANSLATE_MODE_INHERIT && data.inside_tree && !data.parent) {
+ ERR_FAIL_MSG("The root node can't be set to Inherit auto translate mode.");
+ }
+
+ data.auto_translate_mode = p_mode;
+ data.is_auto_translating = p_mode != AUTO_TRANSLATE_MODE_DISABLED;
+ data.is_auto_translate_dirty = true;
+
+ propagate_notification(NOTIFICATION_TRANSLATION_CHANGED);
+}
+
+Node::AutoTranslateMode Node::get_auto_translate_mode() const {
+ return data.auto_translate_mode;
+}
+
+bool Node::can_auto_translate() const {
+ ERR_READ_THREAD_GUARD_V(false);
+ if (!data.is_auto_translate_dirty || data.auto_translate_mode != AUTO_TRANSLATE_MODE_INHERIT) {
+ return data.is_auto_translating;
+ }
+
+ data.is_auto_translate_dirty = false;
+
+ Node *parent = data.parent;
+ while (parent) {
+ if (parent->data.auto_translate_mode == AUTO_TRANSLATE_MODE_INHERIT) {
+ parent = parent->data.parent;
+ continue;
+ }
+
+ data.is_auto_translating = parent->data.auto_translate_mode == AUTO_TRANSLATE_MODE_ALWAYS;
+ break;
+ }
+
+ return data.is_auto_translating;
+}
+
StringName Node::get_name() const {
return data.name;
}
@@ -3488,6 +3557,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_process_internal", "enable"), &Node::set_physics_process_internal);
ClassDB::bind_method(D_METHOD("is_physics_processing_internal"), &Node::is_physics_processing_internal);
+ ClassDB::bind_method(D_METHOD("set_auto_translate_mode", "mode"), &Node::set_auto_translate_mode);
+ ClassDB::bind_method(D_METHOD("get_auto_translate_mode"), &Node::get_auto_translate_mode);
+
ClassDB::bind_method(D_METHOD("get_window"), &Node::get_window);
ClassDB::bind_method(D_METHOD("get_last_exclusive_window"), &Node::get_last_exclusive_window);
ClassDB::bind_method(D_METHOD("get_tree"), &Node::get_tree);
@@ -3525,6 +3597,9 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_unique_name_in_owner", "enable"), &Node::set_unique_name_in_owner);
ClassDB::bind_method(D_METHOD("is_unique_name_in_owner"), &Node::is_unique_name_in_owner);
+ ClassDB::bind_method(D_METHOD("atr", "message", "context"), &Node::atr, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("atr_n", "message", "plural_message", "n", "context"), &Node::atr_n, DEFVAL(""));
+
#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_property_pinned", "property", "pinned"), &Node::set_property_pinned);
#endif
@@ -3635,6 +3710,10 @@ void Node::_bind_methods() {
BIND_ENUM_CONSTANT(INTERNAL_MODE_FRONT);
BIND_ENUM_CONSTANT(INTERNAL_MODE_BACK);
+ BIND_ENUM_CONSTANT(AUTO_TRANSLATE_MODE_INHERIT);
+ BIND_ENUM_CONSTANT(AUTO_TRANSLATE_MODE_ALWAYS);
+ BIND_ENUM_CONSTANT(AUTO_TRANSLATE_MODE_DISABLED);
+
ADD_SIGNAL(MethodInfo("ready"));
ADD_SIGNAL(MethodInfo("renamed"));
ADD_SIGNAL(MethodInfo("tree_entered"));
@@ -3657,11 +3736,15 @@ void Node::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_physics_priority"), "set_physics_process_priority", "get_physics_process_priority");
+
ADD_SUBGROUP("Thread Group", "process_thread");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_group", PROPERTY_HINT_ENUM, "Inherit,Main Thread,Sub Thread"), "set_process_thread_group", "get_process_thread_group");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_group_order"), "set_process_thread_group_order", "get_process_thread_group_order");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_thread_messages", PROPERTY_HINT_FLAGS, "Process,Physics Process"), "set_process_thread_messages", "get_process_thread_messages");
+ ADD_GROUP("Auto Translate", "auto_translate_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "auto_translate_mode", PROPERTY_HINT_ENUM, "Inherit,Always,Disabled"), "set_auto_translate_mode", "get_auto_translate_mode");
+
ADD_GROUP("Editor Description", "editor_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT), "set_editor_description", "get_editor_description");
diff --git a/scene/main/node.h b/scene/main/node.h
index 5d246cc18e..bbbdb87a32 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -108,6 +108,12 @@ public:
INTERNAL_MODE_BACK,
};
+ enum AutoTranslateMode {
+ AUTO_TRANSLATE_MODE_INHERIT,
+ AUTO_TRANSLATE_MODE_ALWAYS,
+ AUTO_TRANSLATE_MODE_DISABLED,
+ };
+
struct Comparator {
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
};
@@ -211,6 +217,10 @@ private:
bool display_folded = false;
bool editable_instance = false;
+ AutoTranslateMode auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
+ mutable bool is_auto_translating = true;
+ mutable bool is_auto_translate_dirty = true;
+
mutable NodePath *path_cache = nullptr;
} data;
@@ -506,6 +516,7 @@ public:
void propagate_call(const StringName &p_method, const Array &p_args = Array(), const bool p_parent_first = false);
/* PROCESSING */
+
void set_physics_process(bool p_process);
double get_physics_process_delta_time() const;
bool is_physics_processing() const;
@@ -647,6 +658,7 @@ public:
void set_display_folded(bool p_folded);
bool is_displayed_folded() const;
+
/* NETWORK */
virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
@@ -666,6 +678,17 @@ public:
Ref<MultiplayerAPI> get_multiplayer() const;
+ /* INTERNATIONALIZATION */
+
+ void set_auto_translate_mode(AutoTranslateMode p_mode);
+ AutoTranslateMode get_auto_translate_mode() const;
+ bool can_auto_translate() const;
+
+ _FORCE_INLINE_ String atr(const String p_message, const StringName p_context = "") const { return can_auto_translate() ? tr(p_message, p_context) : p_message; }
+ _FORCE_INLINE_ String atr_n(const String p_message, const StringName &p_message_plural, int p_n, const StringName p_context = "") const { return can_auto_translate() ? tr_n(p_message, p_message_plural, p_n, p_context) : p_message; }
+
+ /* THREADING */
+
void call_deferred_thread_groupp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
template <typename... VarArgs>
void call_deferred_thread_group(const StringName &p_method, VarArgs... p_args) {
@@ -725,6 +748,7 @@ VARIANT_ENUM_CAST(Node::ProcessMode);
VARIANT_ENUM_CAST(Node::ProcessThreadGroup);
VARIANT_BITFIELD_CAST(Node::ProcessThreadMessages);
VARIANT_ENUM_CAST(Node::InternalMode);
+VARIANT_ENUM_CAST(Node::AutoTranslateMode);
typedef HashSet<Node *, Node::Comparator> NodeSet;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index ee0e7721a0..2cbcc8e33e 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1755,6 +1755,7 @@ SceneTree::SceneTree() {
root = memnew(Window);
root->set_min_size(Size2i(64, 64)); // Define a very small minimum window size to prevent bugs such as GH-37242.
root->set_process_mode(Node::PROCESS_MODE_PAUSABLE);
+ root->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_ALWAYS);
root->set_name("root");
root->set_title(GLOBAL_GET("application/config/name"));
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 9e8047ce49..5952af60ee 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1517,7 +1517,6 @@ void Viewport::_gui_show_tooltip() {
if (!base_tooltip) {
gui.tooltip_label = memnew(Label);
gui.tooltip_label->set_theme_type_variation(SNAME("TooltipLabel"));
- gui.tooltip_label->set_auto_translate(gui.tooltip_control->is_auto_translating());
gui.tooltip_label->set_text(gui.tooltip_text);
base_tooltip = gui.tooltip_label;
panel->connect("mouse_entered", callable_mp(this, &Viewport::_gui_cancel_tooltip));
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index fd64220c2c..3ec36f731c 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -52,8 +52,8 @@ void Window::set_root_layout_direction(int p_root_dir) {
bool Window::_set(const StringName &p_name, const Variant &p_value) {
ERR_MAIN_THREAD_GUARD_V(false);
-
String name = p_name;
+
if (!name.begins_with("theme_override")) {
return false;
}
@@ -95,7 +95,6 @@ bool Window::_set(const StringName &p_name, const Variant &p_value) {
} else {
return false;
}
-
} else {
if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
@@ -124,8 +123,8 @@ bool Window::_set(const StringName &p_name, const Variant &p_value) {
bool Window::_get(const StringName &p_name, Variant &r_ret) const {
ERR_READ_THREAD_GUARD_V(false);
-
String sname = p_name;
+
if (!sname.begins_with("theme_override")) {
return false;
}
@@ -1308,14 +1307,6 @@ void Window::_notification(int p_what) {
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
}
-#ifdef TOOLS_ENABLED
- if (is_part_of_edited_scene()) {
- // Don't translate Windows on scene when inside editor.
- set_message_translation(false);
- notification(NOTIFICATION_TRANSLATION_CHANGED);
- }
-#endif
-
// Emits NOTIFICATION_THEME_CHANGED internally.
set_theme_context(ThemeDB::get_singleton()->get_nearest_theme_context(this));
} break;
@@ -2649,21 +2640,17 @@ bool Window::is_layout_rtl() const {
}
}
+#ifndef DISABLE_DEPRECATED
void Window::set_auto_translate(bool p_enable) {
ERR_MAIN_THREAD_GUARD;
- if (p_enable == auto_translate) {
- return;
- }
-
- auto_translate = p_enable;
-
- notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ set_auto_translate_mode(p_enable ? AUTO_TRANSLATE_MODE_ALWAYS : AUTO_TRANSLATE_MODE_DISABLED);
}
bool Window::is_auto_translating() const {
ERR_READ_THREAD_GUARD_V(false);
- return auto_translate;
+ return can_auto_translate();
}
+#endif
Transform2D Window::get_final_transform() const {
ERR_READ_THREAD_GUARD_V(Transform2D());
@@ -2894,8 +2881,10 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction);
ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl);
+#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Window::set_auto_translate);
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Window::is_auto_translating);
+#endif
ClassDB::bind_method(D_METHOD("popup", "rect"), &Window::popup, DEFVAL(Rect2i()));
ClassDB::bind_method(D_METHOD("popup_on_parent", "parent_rect"), &Window::popup_on_parent);
@@ -2949,8 +2938,9 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor");
- ADD_GROUP("Localization", "");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
+#ifndef DISABLE_DEPRECATED
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_translate", "is_auto_translating");
+#endif
ADD_GROUP("Theme", "theme_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
diff --git a/scene/main/window.h b/scene/main/window.h
index 4ae535848a..73dba67f45 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -34,7 +34,6 @@
#include "scene/main/viewport.h"
#include "scene/resources/theme.h"
-class Control;
class Font;
class Shortcut;
class StyleBox;
@@ -138,8 +137,6 @@ private:
LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
- bool auto_translate = true;
-
void _update_child_controls();
void _update_embedded_window();
@@ -389,15 +386,18 @@ public:
void grab_focus();
bool has_focus() const;
+ Rect2i get_usable_parent_rect() const;
+
+ // Internationalization.
+
void set_layout_direction(LayoutDirection p_direction);
LayoutDirection get_layout_direction() const;
bool is_layout_rtl() const;
+#ifndef DISABLE_DEPRECATED
void set_auto_translate(bool p_enable);
bool is_auto_translating() const;
- _FORCE_INLINE_ String atr(const String p_string) const { return is_auto_translating() ? tr(p_string) : p_string; };
-
- Rect2i get_usable_parent_rect() const;
+#endif
// Theming.
diff --git a/scene/resources/animation.compat.inc b/scene/resources/animation.compat.inc
new file mode 100644
index 0000000000..bbf016b34d
--- /dev/null
+++ b/scene/resources/animation.compat.inc
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* animation.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
+
+Vector3 Animation::_position_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
+ return position_track_interpolate(p_track, p_time, false);
+}
+
+Quaternion Animation::_rotation_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
+ return rotation_track_interpolate(p_track, p_time, false);
+}
+
+Vector3 Animation::_scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
+ return scale_track_interpolate(p_track, p_time, false);
+}
+
+float Animation::_blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
+ return blend_shape_track_interpolate(p_track, p_time, false);
+}
+
+Variant Animation::_value_track_interpolate_bind_compat_86629(int p_track, double p_time) const {
+ return value_track_interpolate(p_track, p_time, false);
+}
+
+void Animation::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec"), &Animation::_position_track_interpolate_bind_compat_86629);
+ ClassDB::bind_compatibility_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec"), &Animation::_rotation_track_interpolate_bind_compat_86629);
+ ClassDB::bind_compatibility_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::_scale_track_interpolate_bind_compat_86629);
+ ClassDB::bind_compatibility_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::_blend_shape_track_interpolate_bind_compat_86629);
+ ClassDB::bind_compatibility_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::_value_track_interpolate_bind_compat_86629);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index d4401f3489..796a03bd8b 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "animation.h"
+#include "animation.compat.inc"
#include "core/io/marshalls.h"
#include "core/math/geometry_3d.h"
@@ -1115,7 +1116,7 @@ Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_posit
return OK;
}
-Error Animation::try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+Error Animation::try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
@@ -1132,7 +1133,7 @@ Error Animation::try_position_track_interpolate(int p_track, double p_time, Vect
bool ok = false;
- Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
+ Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@@ -1141,10 +1142,10 @@ Error Animation::try_position_track_interpolate(int p_track, double p_time, Vect
return OK;
}
-Vector3 Animation::position_track_interpolate(int p_track, double p_time) const {
+Vector3 Animation::position_track_interpolate(int p_track, double p_time, bool p_backward) const {
Vector3 ret = Vector3(0, 0, 0);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
- bool err = try_position_track_interpolate(p_track, p_time, &ret);
+ bool err = try_position_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "3D Position Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@@ -1195,7 +1196,7 @@ Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_ro
return OK;
}
-Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
+Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
@@ -1212,7 +1213,7 @@ Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quat
bool ok = false;
- Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok);
+ Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@@ -1221,10 +1222,10 @@ Error Animation::try_rotation_track_interpolate(int p_track, double p_time, Quat
return OK;
}
-Quaternion Animation::rotation_track_interpolate(int p_track, double p_time) const {
+Quaternion Animation::rotation_track_interpolate(int p_track, double p_time, bool p_backward) const {
Quaternion ret = Quaternion(0, 0, 0, 1);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
- bool err = try_rotation_track_interpolate(p_track, p_time, &ret);
+ bool err = try_rotation_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "3D Rotation Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@@ -1275,7 +1276,7 @@ Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) c
return OK;
}
-Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
@@ -1292,7 +1293,7 @@ Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3
bool ok = false;
- Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok);
+ Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@@ -1301,10 +1302,10 @@ Error Animation::try_scale_track_interpolate(int p_track, double p_time, Vector3
return OK;
}
-Vector3 Animation::scale_track_interpolate(int p_track, double p_time) const {
+Vector3 Animation::scale_track_interpolate(int p_track, double p_time, bool p_backward) const {
Vector3 ret = Vector3(1, 1, 1);
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
- bool err = try_scale_track_interpolate(p_track, p_time, &ret);
+ bool err = try_scale_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "3D Scale Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@@ -1355,7 +1356,7 @@ Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blen
return OK;
}
-Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation) const {
+Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, float *r_interpolation, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
@@ -1372,7 +1373,7 @@ Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, f
bool ok = false;
- float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->loop_wrap, &ok);
+ float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->loop_wrap, &ok, p_backward);
if (!ok) {
return ERR_UNAVAILABLE;
@@ -1381,10 +1382,10 @@ Error Animation::try_blend_shape_track_interpolate(int p_track, double p_time, f
return OK;
}
-float Animation::blend_shape_track_interpolate(int p_track, double p_time) const {
+float Animation::blend_shape_track_interpolate(int p_track, double p_time, bool p_backward) const {
float ret = 0;
ERR_FAIL_INDEX_V(p_track, tracks.size(), ret);
- bool err = try_blend_shape_track_interpolate(p_track, p_time, &ret);
+ bool err = try_blend_shape_track_interpolate(p_track, p_time, &ret, p_backward);
ERR_FAIL_COND_V_MSG(err, ret, "Blend Shape Track: '" + tracks[p_track]->path + "' is unavailable.");
return ret;
}
@@ -2465,7 +2466,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
ERR_FAIL_COND_V(idx == -2, T());
int maxi = len - 1;
- bool is_start_edge = idx == -1;
+ bool is_start_edge = p_backward ? idx >= len : idx == -1;
bool is_end_edge = p_backward ? idx == 0 : idx >= maxi;
real_t c = 0.0;
@@ -2647,7 +2648,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
// do a barrel roll
}
-Variant Animation::value_track_interpolate(int p_track, double p_time) const {
+Variant Animation::value_track_interpolate(int p_track, double p_time, bool p_backward) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_VALUE, Variant());
@@ -2655,7 +2656,7 @@ Variant Animation::value_track_interpolate(int p_track, double p_time) const {
bool ok = false;
- Variant res = _interpolate(vt->values, p_time, (vt->update_mode == UPDATE_CONTINUOUS || vt->update_mode == UPDATE_CAPTURE) ? vt->interpolation : INTERPOLATION_NEAREST, vt->loop_wrap, &ok);
+ Variant res = _interpolate(vt->values, p_time, vt->update_mode == UPDATE_DISCRETE ? INTERPOLATION_NEAREST : vt->interpolation, vt->loop_wrap, &ok, p_backward);
if (ok) {
return res;
@@ -3787,10 +3788,10 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
ClassDB::bind_method(D_METHOD("blend_shape_track_insert_key", "track_idx", "time", "amount"), &Animation::blend_shape_track_insert_key);
- ClassDB::bind_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec"), &Animation::position_track_interpolate);
- ClassDB::bind_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec"), &Animation::rotation_track_interpolate);
- ClassDB::bind_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec"), &Animation::scale_track_interpolate);
- ClassDB::bind_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec"), &Animation::blend_shape_track_interpolate);
+ ClassDB::bind_method(D_METHOD("position_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::position_track_interpolate, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("rotation_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::rotation_track_interpolate, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("scale_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::scale_track_interpolate, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("blend_shape_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::blend_shape_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
@@ -3816,7 +3817,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
- ClassDB::bind_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec"), &Animation::value_track_interpolate);
+ ClassDB::bind_method(D_METHOD("value_track_interpolate", "track_idx", "time_sec", "backward"), &Animation::value_track_interpolate, DEFVAL(false));
ClassDB::bind_method(D_METHOD("method_track_get_name", "track_idx", "key_idx"), &Animation::method_track_get_name);
ClassDB::bind_method(D_METHOD("method_track_get_params", "track_idx", "key_idx"), &Animation::method_track_get_params);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 6c31bbcd29..115a1a5050 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -380,6 +380,15 @@ protected:
static bool inform_variant_array(int &r_min, int &r_max); // Returns true if max and min are swapped.
+#ifndef DISABLE_DEPRECATED
+ Vector3 _position_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
+ Quaternion _rotation_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
+ Vector3 _scale_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
+ float _blend_shape_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
+ Variant _value_track_interpolate_bind_compat_86629(int p_track, double p_time) const;
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
public:
int add_track(TrackType p_type, int p_at_pos = -1);
void remove_track(int p_track);
@@ -419,23 +428,23 @@ public:
int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
- Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
- Vector3 position_track_interpolate(int p_track, double p_time) const;
+ Error try_position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward = false) const;
+ Vector3 position_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
- Error try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
- Quaternion rotation_track_interpolate(int p_track, double p_time) const;
+ Error try_rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation, bool p_backward = false) const;
+ Quaternion rotation_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
- Error try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
- Vector3 scale_track_interpolate(int p_track, double p_time) const;
+ Error try_scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation, bool p_backward = false) const;
+ Vector3 scale_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
int blend_shape_track_insert_key(int p_track, double p_time, float p_blend);
Error blend_shape_track_get_key(int p_track, int p_key, float *r_blend) const;
- Error try_blend_shape_track_interpolate(int p_track, double p_time, float *r_blend) const;
- float blend_shape_track_interpolate(int p_track, double p_time) const;
+ Error try_blend_shape_track_interpolate(int p_track, double p_time, float *r_blend, bool p_backward = false) const;
+ float blend_shape_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
@@ -471,7 +480,7 @@ public:
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
- Variant value_track_interpolate(int p_track, double p_time) const;
+ Variant value_track_interpolate(int p_track, double p_time, bool p_backward = false) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;
diff --git a/scene/resources/compressed_texture.cpp b/scene/resources/compressed_texture.cpp
index f4395d5c7d..588a2b967b 100644
--- a/scene/resources/compressed_texture.cpp
+++ b/scene/resources/compressed_texture.cpp
@@ -808,6 +808,7 @@ RID CompressedTextureLayered::get_rid() const {
Ref<Image> CompressedTextureLayered::get_layer_data(int p_layer) const {
if (texture.is_valid()) {
+ ERR_FAIL_INDEX_V(p_layer, get_layers(), Ref<Image>());
return RS::get_singleton()->texture_2d_layer_get(texture, p_layer);
} else {
return Ref<Image>();
diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp
index a9df766196..8cddfb5840 100644
--- a/scene/resources/multimesh.cpp
+++ b/scene/resources/multimesh.cpp
@@ -269,6 +269,16 @@ Color MultiMesh::get_instance_custom_data(int p_instance) const {
return RenderingServer::get_singleton()->multimesh_instance_get_custom_data(multimesh, p_instance);
}
+void MultiMesh::set_custom_aabb(const AABB &p_custom) {
+ custom_aabb = p_custom;
+ RS::get_singleton()->multimesh_set_custom_aabb(multimesh, custom_aabb);
+ emit_changed();
+}
+
+AABB MultiMesh::get_custom_aabb() const {
+ return custom_aabb;
+}
+
AABB MultiMesh::get_aabb() const {
return RenderingServer::get_singleton()->multimesh_get_aabb(multimesh);
}
@@ -326,6 +336,8 @@ void MultiMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_instance_color", "instance"), &MultiMesh::get_instance_color);
ClassDB::bind_method(D_METHOD("set_instance_custom_data", "instance", "custom_data"), &MultiMesh::set_instance_custom_data);
ClassDB::bind_method(D_METHOD("get_instance_custom_data", "instance"), &MultiMesh::get_instance_custom_data);
+ ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &MultiMesh::set_custom_aabb);
+ ClassDB::bind_method(D_METHOD("get_custom_aabb"), &MultiMesh::get_custom_aabb);
ClassDB::bind_method(D_METHOD("get_aabb"), &MultiMesh::get_aabb);
ClassDB::bind_method(D_METHOD("get_buffer"), &MultiMesh::get_buffer);
@@ -334,6 +346,7 @@ void MultiMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_colors"), "set_use_colors", "is_using_colors");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_custom_data"), "set_use_custom_data", "is_using_custom_data");
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, "suffix:m"), "set_custom_aabb", "get_custom_aabb");
ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
diff --git a/scene/resources/multimesh.h b/scene/resources/multimesh.h
index 98885db0cc..d7bcb13162 100644
--- a/scene/resources/multimesh.h
+++ b/scene/resources/multimesh.h
@@ -48,6 +48,7 @@ private:
Ref<Mesh> mesh;
RID multimesh;
TransformFormat transform_format = TRANSFORM_2D;
+ AABB custom_aabb;
bool use_colors = false;
bool use_custom_data = false;
int instance_count = 0;
@@ -103,6 +104,9 @@ public:
void set_instance_custom_data(int p_instance, const Color &p_custom_data);
Color get_instance_custom_data(int p_instance) const;
+ void set_custom_aabb(const AABB &p_custom);
+ AABB get_custom_aabb() const;
+
virtual AABB get_aabb() const;
virtual RID get_rid() const override;
diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h
index c61656bc77..a4f353359a 100644
--- a/servers/rendering/dummy/rasterizer_dummy.h
+++ b/servers/rendering/dummy/rasterizer_dummy.h
@@ -104,7 +104,7 @@ public:
static void make_current() {
_create_func = _create_current;
- low_end = true;
+ low_end = false;
}
uint64_t get_frame_number() const override { return frame; }
diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h
index ce0e80e8c8..d98b2e2ee7 100644
--- a/servers/rendering/dummy/storage/mesh_storage.h
+++ b/servers/rendering/dummy/storage/mesh_storage.h
@@ -87,6 +87,12 @@ public:
s->index_count = p_surface.index_count;
s->aabb = p_surface.aabb;
s->skin_data = p_surface.skin_data;
+ s->lods = p_surface.lods;
+ s->bone_aabbs = p_surface.bone_aabbs;
+ s->mesh_to_skeleton_xform = p_surface.mesh_to_skeleton_xform;
+ s->blend_shape_data = p_surface.blend_shape_data;
+ s->uv_scale = p_surface.uv_scale;
+ s->material = p_surface.material;
}
virtual int mesh_get_blend_shape_count(RID p_mesh) const override { return 0; }
@@ -153,6 +159,9 @@ public:
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) override {}
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) override {}
+ virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override {}
+ virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override { return AABB(); }
+
virtual RID multimesh_get_mesh(RID p_multimesh) const override { return RID(); }
virtual AABB multimesh_get_aabb(RID p_multimesh) const override { return AABB(); }
diff --git a/servers/rendering/dummy/storage/utilities.h b/servers/rendering/dummy/storage/utilities.h
index 4a640f9594..6e8af9afac 100644
--- a/servers/rendering/dummy/storage/utilities.h
+++ b/servers/rendering/dummy/storage/utilities.h
@@ -108,7 +108,7 @@ public:
virtual void set_debug_generate_wireframes(bool p_generate) override {}
virtual bool has_os_feature(const String &p_feature) const override {
- return p_feature == "rgtc" || p_feature == "bptc" || p_feature == "s3tc" || p_feature == "etc" || p_feature == "etc2";
+ return p_feature == "rgtc" || p_feature == "bptc" || p_feature == "s3tc" || p_feature == "etc2";
}
virtual void update_memory_info() override {}
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 05929a862a..21787b3fcf 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1660,6 +1660,9 @@ void MeshStorage::_multimesh_mark_all_dirty(MultiMesh *multimesh, bool p_data, b
void MeshStorage::_multimesh_re_create_aabb(MultiMesh *multimesh, const float *p_data, int p_instances) {
ERR_FAIL_COND(multimesh->mesh.is_null());
+ if (multimesh->custom_aabb != AABB()) {
+ return;
+ }
AABB aabb;
AABB mesh_aabb = mesh_get_aabb(multimesh->mesh);
for (int i = 0; i < p_instances; i++) {
@@ -1960,8 +1963,10 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr();
- _multimesh_re_create_aabb(multimesh, data, multimesh->instances);
- multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ if (multimesh->custom_aabb != AABB()) {
+ _multimesh_re_create_aabb(multimesh, data, multimesh->instances);
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ }
}
}
@@ -2015,9 +2020,26 @@ int MeshStorage::multimesh_get_visible_instances(RID p_multimesh) const {
return multimesh->visible_instances;
}
+void MeshStorage::multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) {
+ MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+ ERR_FAIL_NULL(multimesh);
+ multimesh->custom_aabb = p_aabb;
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+}
+
+AABB MeshStorage::multimesh_get_custom_aabb(RID p_multimesh) const {
+ MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
+ ERR_FAIL_NULL_V(multimesh, AABB());
+ return multimesh->custom_aabb;
+}
+
AABB MeshStorage::multimesh_get_aabb(RID p_multimesh) const {
MultiMesh *multimesh = multimesh_owner.get_or_null(p_multimesh);
ERR_FAIL_NULL_V(multimesh, AABB());
+ if (multimesh->custom_aabb != AABB()) {
+ return multimesh->custom_aabb;
+ }
+
if (multimesh->aabb_dirty) {
const_cast<MeshStorage *>(this)->_update_dirty_multimeshes();
}
@@ -2064,9 +2086,11 @@ void MeshStorage::_update_dirty_multimeshes() {
if (multimesh->aabb_dirty) {
//aabb is dirty..
- _multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->aabb_dirty = false;
- multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ if (multimesh->custom_aabb != AABB()) {
+ _multimesh_re_create_aabb(multimesh, data, visible_instances);
+ multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
+ }
}
}
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index 771ac6d380..5491f637bc 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -220,6 +220,7 @@ private:
bool uses_custom_data = false;
int visible_instances = -1;
AABB aabb;
+ AABB custom_aabb;
bool aabb_dirty = false;
bool buffer_set = false;
bool motion_vectors_enabled = false;
@@ -646,6 +647,9 @@ public:
virtual void multimesh_set_visible_instances(RID p_multimesh, int p_visible) override;
virtual int multimesh_get_visible_instances(RID p_multimesh) const override;
+ virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) override;
+ virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const override;
+
virtual AABB multimesh_get_aabb(RID p_multimesh) const override;
void _update_dirty_multimeshes();
diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.cpp b/servers/rendering/renderer_rd/storage_rd/utilities.cpp
index cb035c494c..8b780a6f7b 100644
--- a/servers/rendering/renderer_rd/storage_rd/utilities.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/utilities.cpp
@@ -278,7 +278,7 @@ bool Utilities::has_os_feature(const String &p_feature) const {
return true;
}
- if ((p_feature == "etc" || p_feature == "etc2") && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) {
+ if (p_feature == "etc2" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) {
return true;
}
diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp
index 83fb2d1918..3e4890e866 100644
--- a/servers/rendering/rendering_device_graph.cpp
+++ b/servers/rendering/rendering_device_graph.cpp
@@ -1430,7 +1430,13 @@ void RenderingDeviceGraph::add_compute_list_usage(ResourceTracker *p_tracker, Re
compute_instruction_list.command_trackers.push_back(p_tracker);
compute_instruction_list.command_tracker_usages.push_back(p_usage);
p_tracker->compute_list_index = compute_instruction_list.index;
+ p_tracker->compute_list_usage = p_usage;
}
+#ifdef DEV_ENABLED
+ else if (p_tracker->compute_list_usage != p_usage) {
+ ERR_FAIL_MSG(vformat("Tracker can't have more than one type of usage in the same compute list. Compute list usage is %d and the requested usage is %d.", p_tracker->compute_list_usage, p_usage));
+ }
+#endif
}
void RenderingDeviceGraph::add_compute_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages) {
@@ -1614,7 +1620,13 @@ void RenderingDeviceGraph::add_draw_list_usage(ResourceTracker *p_tracker, Resou
draw_instruction_list.command_trackers.push_back(p_tracker);
draw_instruction_list.command_tracker_usages.push_back(p_usage);
p_tracker->draw_list_index = draw_instruction_list.index;
+ p_tracker->draw_list_usage = p_usage;
}
+#ifdef DEV_ENABLED
+ else if (p_tracker->draw_list_usage != p_usage) {
+ ERR_FAIL_MSG(vformat("Tracker can't have more than one type of usage in the same draw list. Draw list usage is %d and the requested usage is %d.", p_tracker->draw_list_usage, p_usage));
+ }
+#endif
}
void RenderingDeviceGraph::add_draw_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages) {
diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h
index 995fdb27d1..9bb109ea0e 100644
--- a/servers/rendering/rendering_device_graph.h
+++ b/servers/rendering/rendering_device_graph.h
@@ -155,7 +155,9 @@ public:
int32_t read_slice_command_list_index = -1;
int32_t write_command_or_list_index = -1;
int32_t draw_list_index = -1;
+ ResourceUsage draw_list_usage = RESOURCE_USAGE_NONE;
int32_t compute_list_index = -1;
+ ResourceUsage compute_list_usage = RESOURCE_USAGE_NONE;
ResourceUsage usage = RESOURCE_USAGE_NONE;
BitField<RDD::BarrierAccessBits> usage_access;
RDD::BufferID buffer_driver_id;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 3bb6ba1c51..577e9accc0 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -335,6 +335,9 @@ public:
FUNC3(multimesh_instance_set_color, RID, int, const Color &)
FUNC3(multimesh_instance_set_custom_data, RID, int, const Color &)
+ FUNC2(multimesh_set_custom_aabb, RID, const AABB &)
+ FUNC1RC(AABB, multimesh_get_custom_aabb, RID)
+
FUNC1RC(RID, multimesh_get_mesh, RID)
FUNC1RC(AABB, multimesh_get_aabb, RID)
diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h
index 3c1b2b495d..39fd4f393d 100644
--- a/servers/rendering/storage/mesh_storage.h
+++ b/servers/rendering/storage/mesh_storage.h
@@ -104,6 +104,9 @@ public:
virtual void multimesh_instance_set_color(RID p_multimesh, int p_index, const Color &p_color) = 0;
virtual void multimesh_instance_set_custom_data(RID p_multimesh, int p_index, const Color &p_color) = 0;
+ virtual void multimesh_set_custom_aabb(RID p_multimesh, const AABB &p_aabb) = 0;
+ virtual AABB multimesh_get_custom_aabb(RID p_multimesh) const = 0;
+
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 99b0b1886c..d03f8113f8 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2427,6 +2427,8 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("multimesh_instance_set_custom_data", "multimesh", "index", "custom_data"), &RenderingServer::multimesh_instance_set_custom_data);
ClassDB::bind_method(D_METHOD("multimesh_get_mesh", "multimesh"), &RenderingServer::multimesh_get_mesh);
ClassDB::bind_method(D_METHOD("multimesh_get_aabb", "multimesh"), &RenderingServer::multimesh_get_aabb);
+ ClassDB::bind_method(D_METHOD("multimesh_set_custom_aabb", "multimesh", "aabb"), &RenderingServer::multimesh_set_custom_aabb);
+ ClassDB::bind_method(D_METHOD("multimesh_get_custom_aabb", "multimesh"), &RenderingServer::multimesh_get_custom_aabb);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform", "multimesh", "index"), &RenderingServer::multimesh_instance_get_transform);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_transform_2d", "multimesh", "index"), &RenderingServer::multimesh_instance_get_transform_2d);
ClassDB::bind_method(D_METHOD("multimesh_instance_get_color", "multimesh", "index"), &RenderingServer::multimesh_instance_get_color);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index d63283ddbe..19b6c35339 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -413,6 +413,9 @@ public:
virtual RID multimesh_get_mesh(RID p_multimesh) const = 0;
virtual AABB multimesh_get_aabb(RID p_multimesh) const = 0;
+ virtual void multimesh_set_custom_aabb(RID p_mesh, const AABB &p_aabb) = 0;
+ virtual AABB multimesh_get_custom_aabb(RID p_mesh) const = 0;
+
virtual Transform3D multimesh_instance_get_transform(RID p_multimesh, int p_index) const = 0;
virtual Transform2D multimesh_instance_get_transform_2d(RID p_multimesh, int p_index) const = 0;
virtual Color multimesh_instance_get_color(RID p_multimesh, int p_index) const = 0;