summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.mailmap2
-rw-r--r--AUTHORS.md5
-rw-r--r--DONORS.md122
-rw-r--r--SConstruct304
-rw-r--r--core/config/engine.cpp16
-rw-r--r--core/config/engine.h7
-rw-r--r--core/config/project_settings.cpp1
-rw-r--r--core/core_bind.cpp5
-rw-r--r--core/core_bind.h1
-rw-r--r--core/io/dir_access.cpp2
-rw-r--r--core/io/marshalls.cpp49
-rw-r--r--core/io/stream_peer_tcp.cpp1
-rw-r--r--core/object/worker_thread_pool.cpp156
-rw-r--r--core/object/worker_thread_pool.h19
-rw-r--r--core/os/os.h1
-rw-r--r--core/templates/command_queue_mt.cpp8
-rw-r--r--core/templates/command_queue_mt.h43
-rw-r--r--core/variant/array.h2
-rw-r--r--doc/classes/@GlobalScope.xml1
-rw-r--r--doc/classes/BoneAttachment3D.xml5
-rw-r--r--doc/classes/Control.xml4
-rw-r--r--doc/classes/EditorSettings.xml13
-rw-r--r--doc/classes/FileAccess.xml2
-rw-r--r--doc/classes/NavigationAgent2D.xml7
-rw-r--r--doc/classes/NavigationAgent3D.xml7
-rw-r--r--doc/classes/NavigationPathQueryParameters2D.xml7
-rw-r--r--doc/classes/NavigationPathQueryParameters3D.xml7
-rw-r--r--doc/classes/NavigationRegion2D.xml21
-rw-r--r--doc/classes/NavigationServer2D.xml9
-rw-r--r--doc/classes/NavigationServer3D.xml9
-rw-r--r--doc/classes/Node.xml4
-rw-r--r--doc/classes/OS.xml13
-rw-r--r--doc/classes/PhysicsServer2DExtension.xml4
-rw-r--r--doc/classes/Popup.xml5
-rw-r--r--doc/classes/PopupMenu.xml3
-rw-r--r--doc/classes/PopupPanel.xml5
-rw-r--r--doc/classes/ProjectSettings.xml10
-rw-r--r--doc/classes/ScriptEditor.xml29
-rw-r--r--doc/classes/Skeleton3D.xml20
-rw-r--r--drivers/egl/egl_manager.cpp15
-rw-r--r--drivers/egl/egl_manager.h1
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp58
-rw-r--r--drivers/gles3/storage/particles_storage.cpp6
-rw-r--r--drivers/unix/dir_access_unix.cpp4
-rw-r--r--drivers/unix/os_unix.cpp43
-rw-r--r--drivers/unix/os_unix.h8
-rw-r--r--editor/code_editor.cpp32
-rw-r--r--editor/code_editor.h4
-rw-r--r--editor/create_dialog.cpp9
-rw-r--r--editor/editor_file_system.cpp12
-rw-r--r--editor/editor_help.cpp3
-rw-r--r--editor/editor_inspector.cpp22
-rw-r--r--editor/editor_inspector.h7
-rw-r--r--editor/editor_node.cpp73
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_settings.cpp12
-rw-r--r--editor/engine_update_label.cpp344
-rw-r--r--editor/engine_update_label.h107
-rw-r--r--editor/export/editor_export.cpp8
-rw-r--r--editor/filesystem_dock.cpp5
-rw-r--r--editor/filesystem_dock.h1
-rw-r--r--editor/gui/editor_bottom_panel.cpp1
-rw-r--r--editor/gui/editor_file_dialog.cpp6
-rw-r--r--editor/gui/editor_scene_tabs.cpp12
-rw-r--r--editor/gui/editor_scene_tabs.h1
-rw-r--r--editor/import/3d/editor_import_collada.cpp11
-rw-r--r--editor/import/3d/resource_importer_obj.cpp15
-rw-r--r--editor/import/3d/resource_importer_scene.cpp3
-rw-r--r--editor/import/3d/scene_import_settings.cpp22
-rw-r--r--editor/import/3d/scene_import_settings.h4
-rw-r--r--editor/multi_node_edit.cpp2
-rw-r--r--editor/multi_node_edit.h15
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp67
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h2
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp7
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp16
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp8
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp3
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp12
-rw-r--r--editor/project_manager.cpp10
-rw-r--r--editor/scene_tree_dock.cpp36
-rw-r--r--editor/scene_tree_dock.h1
-rw-r--r--main/main.cpp27
-rw-r--r--main/performance.cpp4
-rw-r--r--methods.py4
-rw-r--r--misc/extension_api_validation/4.2-stable.expected24
-rwxr-xr-xmisc/scripts/copyright_headers.py2
-rw-r--r--modules/fbx/fbx_document.cpp22
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp14
-rw-r--r--modules/gdscript/gdscript_parser.cpp34
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp3
-rw-r--r--modules/gdscript/gdscript_tokenizer_buffer.cpp6
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp23
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_array.gd33
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_array.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd21
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info.gd1
-rw-r--r--modules/gltf/gltf_document.cpp12
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs41
-rw-r--r--modules/navigation/2d/godot_navigation_server_2d.cpp4
-rw-r--r--modules/navigation/2d/godot_navigation_server_2d.h2
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp6
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.cpp129
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.h7
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.cpp6
-rw-r--r--modules/navigation/3d/navigation_mesh_generator.cpp2
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp3
-rw-r--r--modules/openxr/doc_classes/OpenXRHand.xml7
-rw-r--r--modules/openxr/scene/openxr_hand.cpp37
-rw-r--r--modules/openxr/scene/openxr_hand.h14
-rw-r--r--modules/svg/image_loader_svg.cpp12
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml30
-rw-r--r--platform/android/export/export_plugin.cpp24
-rw-r--r--platform/android/export/export_plugin.h2
-rw-r--r--platform/ios/os_ios.mm8
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.cpp8
-rw-r--r--platform/linuxbsd/wayland/display_server_wayland.h1
-rw-r--r--platform/linuxbsd/x11/display_server_x11.cpp15
-rw-r--r--platform/linuxbsd/x11/display_server_x11.h1
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.cpp14
-rw-r--r--platform/linuxbsd/x11/gl_manager_x11.h1
-rw-r--r--platform/macos/display_server_macos.h1
-rw-r--r--platform/macos/display_server_macos.mm18
-rw-r--r--platform/macos/gl_manager_macos_legacy.h1
-rw-r--r--platform/macos/gl_manager_macos_legacy.mm13
-rw-r--r--platform/macos/godot_open_save_delegate.mm8
-rw-r--r--platform/web/os_web.cpp4
-rw-r--r--platform/web/os_web.h1
-rwxr-xr-xplatform/web/serve.py13
-rw-r--r--platform/windows/detect.py37
-rw-r--r--platform/windows/display_server_windows.cpp17
-rw-r--r--platform/windows/display_server_windows.h1
-rw-r--r--platform/windows/gl_manager_windows_native.cpp24
-rw-r--r--platform/windows/gl_manager_windows_native.h4
-rw-r--r--platform/windows/os_windows.cpp41
-rw-r--r--platform/windows/os_windows.h4
-rw-r--r--scene/2d/navigation_agent_2d.cpp26
-rw-r--r--scene/2d/navigation_agent_2d.h8
-rw-r--r--scene/2d/navigation_region_2d.cpp160
-rw-r--r--scene/2d/navigation_region_2d.h17
-rw-r--r--scene/2d/parallax_2d.cpp7
-rw-r--r--scene/2d/physics/character_body_2d.cpp10
-rw-r--r--scene/2d/physics/character_body_2d.h1
-rw-r--r--scene/2d/physics/kinematic_collision_2d.cpp1
-rw-r--r--scene/2d/physics/kinematic_collision_2d.h2
-rw-r--r--scene/2d/physics/physics_body_2d.cpp10
-rw-r--r--scene/2d/physics/physics_body_2d.h2
-rw-r--r--scene/2d/tile_map.cpp5
-rw-r--r--scene/3d/bone_attachment_3d.compat.inc43
-rw-r--r--scene/3d/bone_attachment_3d.cpp13
-rw-r--r--scene/3d/bone_attachment_3d.h6
-rw-r--r--scene/3d/cpu_particles_3d.cpp4
-rw-r--r--scene/3d/navigation_agent_3d.cpp26
-rw-r--r--scene/3d/navigation_agent_3d.h8
-rw-r--r--scene/3d/navigation_region_3d.cpp2
-rw-r--r--scene/3d/physics/character_body_3d.cpp10
-rw-r--r--scene/3d/physics/character_body_3d.h1
-rw-r--r--scene/3d/physics/kinematic_collision_3d.cpp1
-rw-r--r--scene/3d/physics/kinematic_collision_3d.h2
-rw-r--r--scene/3d/physics/physics_body_3d.cpp10
-rw-r--r--scene/3d/physics/physics_body_3d.h2
-rw-r--r--scene/3d/physics/ray_cast_3d.cpp6
-rw-r--r--scene/3d/skeleton_3d.cpp6
-rw-r--r--scene/animation/animation_mixer.cpp14
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/dialogs.cpp2
-rw-r--r--scene/gui/popup.cpp7
-rw-r--r--scene/gui/popup.h4
-rw-r--r--scene/gui/popup_menu.cpp44
-rw-r--r--scene/gui/rich_text_label.cpp4
-rw-r--r--scene/gui/tree.cpp14
-rw-r--r--scene/main/scene_tree.cpp11
-rw-r--r--scene/main/scene_tree.h3
-rw-r--r--scene/main/viewport.cpp16
-rw-r--r--scene/main/window.cpp5
-rw-r--r--scene/register_scene_types.cpp151
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp (renamed from scene/resources/navigation_mesh_source_geometry_data_2d.cpp)0
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.h (renamed from scene/resources/navigation_mesh_source_geometry_data_2d.h)2
-rw-r--r--scene/resources/2d/navigation_polygon.cpp (renamed from scene/resources/navigation_polygon.cpp)0
-rw-r--r--scene/resources/2d/navigation_polygon.h (renamed from scene/resources/navigation_polygon.h)0
-rw-r--r--scene/resources/2d/tile_set.h2
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp (renamed from scene/resources/navigation_mesh_source_geometry_data_3d.cpp)0
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.h (renamed from scene/resources/navigation_mesh_source_geometry_data_3d.h)0
-rw-r--r--scene/resources/particle_process_material.cpp4
-rw-r--r--scene/resources/resource_format_text.cpp2
-rw-r--r--scene/resources/world_2d.cpp8
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--servers/display_server.cpp6
-rw-r--r--servers/display_server.h1
-rw-r--r--servers/navigation/navigation_path_query_parameters_2d.cpp24
-rw-r--r--servers/navigation/navigation_path_query_parameters_2d.h6
-rw-r--r--servers/navigation/navigation_path_query_parameters_3d.cpp24
-rw-r--r--servers/navigation/navigation_path_query_parameters_3d.h6
-rw-r--r--servers/navigation/navigation_utilities.h2
-rw-r--r--servers/navigation_server_2d.cpp2
-rw-r--r--servers/navigation_server_2d.h6
-rw-r--r--servers/navigation_server_2d_dummy.h2
-rw-r--r--servers/navigation_server_3d.cpp4
-rw-r--r--servers/navigation_server_3d.h6
-rw-r--r--servers/navigation_server_3d_dummy.h4
-rw-r--r--servers/physics_2d/godot_shape_2d.cpp2
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp2
-rw-r--r--servers/physics_server_2d_wrap_mt.cpp55
-rw-r--r--servers/physics_server_2d_wrap_mt.h38
-rw-r--r--servers/physics_server_3d_wrap_mt.cpp49
-rw-r--r--servers/physics_server_3d_wrap_mt.h38
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp6
-rw-r--r--servers/rendering/rendering_server_default.cpp65
-rw-r--r--servers/rendering/rendering_server_default.h21
-rw-r--r--servers/rendering/shader_language.cpp22
-rw-r--r--servers/rendering/shader_warnings.cpp4
-rw-r--r--servers/rendering/shader_warnings.h2
-rw-r--r--servers/rendering_server.cpp9
-rw-r--r--servers/server_wrap_mt_common.h45
-rw-r--r--tests/core/templates/test_command_queue.h44
-rw-r--r--tests/core/threads/test_worker_thread_pool.h67
-rw-r--r--tests/core/variant/test_array.h4
-rw-r--r--tests/scene/test_navigation_region_2d.h2
-rw-r--r--tests/scene/test_navigation_region_3d.h11
-rw-r--r--tests/servers/test_navigation_server_3d.h3
-rw-r--r--tests/test_main.cpp2
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/mbedtls/include/godot_core_mbedtls_config.h7
-rw-r--r--thirdparty/mbedtls/include/psa/crypto.h14
-rw-r--r--thirdparty/mbedtls/include/psa/crypto_extra.h4
-rw-r--r--thirdparty/mbedtls/patches/msvc-redeclaration-bug.diff98
242 files changed, 2871 insertions, 1346 deletions
diff --git a/.mailmap b/.mailmap
index 731f224bb6..9e1ddb95e1 100644
--- a/.mailmap
+++ b/.mailmap
@@ -70,6 +70,8 @@ J08nY <johny@neuromancer.sk> <jancar.jj@gmail.com>
J08nY <johny@neuromancer.sk> <J08nY@users.noreply.github.com>
Jake Young <young9003@gmail.com>
Jakub Grzesik <kubecz3k@gmail.com>
+Jakub Marcowski <chubercikbattle@gmail.com> <01158831@pw.edu.pl>
+Jakub Marcowski <chubercikbattle@gmail.com> <37378746+Chubercik@users.noreply.github.com>
janglee <merupatel123@gmail.com>
Jason Knight <00jknight@gmail.com> <jason@winterpixel.com>
Jean-Michel Bernard <jmb462@gmail.com>
diff --git a/AUTHORS.md b/AUTHORS.md
index 768eb4f904..f2fc58c1be 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -34,8 +34,10 @@ name is available.
Alex Drozd (brno32)
Alexey Khoroshavin (allkhor)
Alfred Reinold Baudisch (alfredbaudisch)
+ Alistair Leslie-Hughes (alesliehughes)
Alket Rexhepi (alketii)
Andrea Catania (AndreaCatania)
+ Andreia Gaita (shana)
Andrii Doroshenko (Xrayez)
Andy Maloney (asmaloney)
Andy Moss (MillionOstrich)
@@ -132,6 +134,7 @@ name is available.
J08nY
Jake Young (Duroxxigar)
Jakub Grzesik (kubecz3k)
+ Jakub Marcowski (Chubercik)
James Buck (jbuck3)
Jan Haller (Bromeon)
Jason Knight (jasonwinterpixel)
@@ -287,7 +290,9 @@ name is available.
Will Nations (willnationsdev)
Wilson E. Alvarez (Rubonnek)
Xavier Cho (mysticfall)
+ Yaohua Xiong (xiongyaohua)
yg2f (SuperUserNameMan)
+ Yordan Dolchinkov (Jordyfel)
Yuri Rubinsky (Chaosus)
Yuri Sizov (YuriSizov)
Zae Chao (zaevi)
diff --git a/DONORS.md b/DONORS.md
index b74232c461..f12f6f4b3a 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -12,40 +12,40 @@ generous deed immortalized in the next stable release of Godot Engine.
## Patrons
- OSS Capital <https://oss.capital>
- Re-Logic <https://re-logic.com>
+ OSS Capital <https://oss.capital/>
+ Re-Logic <https://re-logic.com/>
## Platinum sponsors
- Google Play <https://play.google.com>
- Heroic Labs <https://heroiclabs.com>
- Ramatak <https://ramatak.com>
+ Google Play <https://play.google.com/>
+ Heroic Labs <https://heroiclabs.com/>
+ Ramatak <https://ramatak.com/>
V-Sekai <https://github.com/V-Sekai>
- W4 Games <https://w4games.com>
+ W4 Games <https://w4games.com/>
## Gold sponsors
- Mega Crit <https://www.megacrit.com>
- Prehensile Tales <https://prehensile-tales.com>
- Robot Gentleman <http://robotgentleman.com>
+ Mega Crit <https://www.megacrit.com/>
+ Pirate Software <https://gopiratesoftware.com/>
+ Prehensile Tales <https://prehensile-tales.com/>
+ Robot Gentleman <http://robotgentleman.com/>
## Silver sponsors
- Affray Interactive <https://scp.games/pandemic>
- Broken Rules <https://brokenrul.es>
- Chasing Carrots <https://www.chasing-carrots.com>
- Delton Ding
- Gamblify <https://www.gamblify.com>
- Indoor Astronaut <https://indoorastronaut.ch>
- Null <https://null.com>
- Orbital Knight <https://www.orbitalknight.com>
- Playful Studios <https://playfulstudios.com>
+ Affray Interactive <https://scp.games/pandemic/>
+ Broken Rules <https://brokenrul.es/>
+ Chasing Carrots <https://www.chasing-carrots.com/>
+ Gamblify <https://www.gamblify.com/>
+ Indoor Astronaut <https://indoorastronaut.ch/>
+ Null <https://null.com/>
+ Orbital Knight <https://www.orbitalknight.com/>
+ Playful Studios <https://playfulstudios.com/>
## Diamond members
Sealow
- Sylv <https://rankith.itch.io/unnamed-space-idle-prototype>
- And 3 anonymous donors
+ Sylv <https://rankith.itch.io/unnamed-space-idle-prototype/>
+ And 4 anonymous donors
## Titanium members
@@ -55,43 +55,43 @@ generous deed immortalized in the next stable release of Godot Engine.
Game Dev Artisan <https://gamedevartisan.com/>
Garry Newman
Isaiah Smith <https://www.isaiahsmith.dev/>
- katnamag <https://katnamag.com/>
Kenney <https://kenney.nl/>
Life Art Studios <https://lifeartstudios.net/>
Lucid Silence Games
Matthew Campbell
Maxim Karsten
- Midjiwan AB <https://polytopia.io/>
PolyMars <https://polymars.dev/>
+ RPG in a Box <https://www.rpginabox.com/>
Razenpok <https://www.youtube.com/watch?v=-QxI-RP6-HM>
Smirk Software <https://smirk.gg/>
Sterling Long <https://www.sterlinglong.me/>
Sunshower <https://github.com/Phanterm>
TrampolineTales <https://trampolinetales.com/>
- Wilfred James <https://twitter.com/0430agi>
粟二华 (Su Erhua)
- And 5 anonymous donors
+ And 4 anonymous donors
## Platinum members
- AD Ford
+ Andy Touch
BlockImperiumGames (BIG)
+ Christian Baune
Christoph Woinke
Christopher Shifflett
Darrin Massena
Druvsaft
Edward Flick
+ getIntoGameDev
HP van Braam
iCommitGames
Jonah Stich
Justin McGettigan
Justo Delgado Baudí
+ katnamag
Marek Belski
Matthew Ekenstedt
Memories in 8Bit
Mike King
Nassor Paulino da Silva
- nate-wilkins
Neal Gompa (Conan Kudo)
Ronnie Cheng
Ryan Heath
@@ -103,60 +103,56 @@ generous deed immortalized in the next stable release of Godot Engine.
Stephan Lanfermann
TigerJ
Tim Yuen
- Tobias Holewa
Violin Iliev
Vladimír Chvátil
- And 17 anonymous donors
+ And 16 anonymous donors
## Gold members
@reilaos
- albinaask
alMoo Games
Alva Majo
Antti Vesanen
Artur Ilkaev
+ Asher Glick
Ben Burbank
Ben Rog-Wilhelm
Benito
Benjamin Sarsgard
Bernd Barsuhn
- Brian Ernst
+ Blake Farnsworth
Brian Levinsen
Brut
Carlo del Mundo
- Chen-Pang He (jdh8)
ClarkThyLord
Cosmin Munteanu
Coy Humphrey
+ David Chen Zhen
+ David Coles
David Hubber
David Snopek
- Dehyvis Coronel
+ Delton Ding
Dustuu
- Ed Morley
ElektroFox
endaye
Ends
Eric Phy
Faisal Al-Kubaisi (QatariGameDev)
- getIntoGameDev
+ FeralBytes
GlassBrick
Grau
- HTML5onMobilePLZ
- Hammster
+ Guangzhou Lingchan
Here's my 20 cents
hiulit
Illyan
Jacob (HACKhalo2 Studios)
Jam
- James Green
Jason Cawood
Javier Roman
Joel Martinez
John Gabriel
Jon Woodward
José Canepa
- João Pedro Braz
KAR Games
Karasu Studio
korinVR
@@ -166,20 +162,21 @@ generous deed immortalized in the next stable release of Godot Engine.
Luca Vazzano
LyaaaaaGames
MHDante
+ Malcolm Nixon
+ Manuel Requena
Mara Huldra
- Mark Schramm
- Martin Agnar Dahl
Martin Šenkeřík
- Matthew Hillier
Megabit Interactive
Modus Ponens
nezticle
+ Niklas Wahrman
NotNet
- Officine Pixel S.n.c.
+ Obelisk Island Studios
ohanaya3
Oleksii Nosov
Pav Soor
RAWRLAB Games
+ RadenTheFolf
Rafa Laguna
Request
re:thinc
@@ -187,19 +184,21 @@ generous deed immortalized in the next stable release of Godot Engine.
Saltlight Studio
Samuel Judd
Silverclad Studios
+ Sofox
Space Kraken Studios
Spoony Panda
- Sympa City
ThatGamer
ThePolyglotProgrammer
+ Tim Nedvyga
Tom Langwaldt
Trevor Slocum
tukon
+ Vincent Foulon
Weasel Games
+ WuotanStudios.com
Zhu Li
zikes
- Alexander Erlemann
Alex Khayrullin
Algebrute
Andriy
@@ -222,8 +221,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Fransiska
Harry Tumber
Harvey Fong
- Horváth-Lázár Péter
- illuxxium
James Couzens
Jared White
Jesús Chicharro
@@ -237,11 +234,8 @@ generous deed immortalized in the next stable release of Godot Engine.
kickmaniac
Liam Smyth
LoparPanda
- Marcus Dobler
Martin Gulliksson
Martin Soucek
- Matt Greene
- Matthew Dana
Michael Dürwald
Michael Policastro
n00sh
@@ -257,7 +251,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Rob
Rob McInroy
RodZilla
- Ronnie Ashlock
Ruzgud
Ryan Breaker
"Sage Automatic Systems, LLC"
@@ -269,6 +262,7 @@ generous deed immortalized in the next stable release of Godot Engine.
toto bibi
Valryia
VoidPointer
+ wpbirney420
Yifan Lai
zkip lan
@@ -286,14 +280,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Aki Mimoto
Alan Beauchamp
Alejandro Saucedo
- AleMax
+ Alexander Erlemann
Alex Clavelle
- Alex de la Mare
alex raeside
Andre Altmueller
Andreas Østergaard Nielsen
Andrew
- andrew james morris
Ano Nim
Arch Toasty
Arda Erol
@@ -301,19 +293,16 @@ generous deed immortalized in the next stable release of Godot Engine.
Ash K
Aubrey Falconer
Austin Miller
- Azar Gurbanov
AzulCrescent
Balázs Batári
Beau Seymour
Benedikt
Ben Visness
Bill Thibault
- Bjarne Voigtländer
Bread
Brian Ford
Caleb Makela
Cameron Meyer
- Carlos Rios
Carl van der Geest
Cerno_b
ChainZ
@@ -366,7 +355,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Gary Thomas
gebba
Green Fox
- Greg Burkland
Greyson Richey
Grominet
Guldoman
@@ -389,7 +377,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Jeffrey Berube
Jennifer Graves
Joakim Askenbäck
- John Bruce
Jonas
Jonas Arndt
Jonas Yamazaki
@@ -400,12 +387,9 @@ generous deed immortalized in the next stable release of Godot Engine.
Joshua Heidrich
Juanfran
Juan Maggi
- Juan Uys
- Jueast
Julian le Roux
Justin Spedding
Kalydi Balázs
- Keedong Park
Keegan Scott
Keith Bradner
Kent Jofur
@@ -417,6 +401,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Kyle Jacobs
Leland Vakarian
Levi Berciu
+ liberodark
Linus Lind Lundgren
Ludovic DELVAL
Luigi Renna
@@ -430,7 +415,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Markie Music
Mark Tyler
Markus Michael Egger
- Markus Strompen
Martin Holas
Martin Liška
Martin Trbola
@@ -464,7 +448,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Nicolas Rosset
Nik Rudenko
Noel Billig
- Olexa Tourko
Oscar Domingo
ozrk
Parth Patel
@@ -482,7 +465,6 @@ generous deed immortalized in the next stable release of Godot Engine.
Point08
Portponky
PsycHead
- Puntigames
Quincy Quincy
Quinn Morrison
Raghava Kovvali
@@ -493,12 +475,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Richard Hayes
Riley
RobotCritter
- Rob Ruana
Roka
Roland Rząsa
Russ
Ryan Groom
Sammy Fischer
+ Satnam Singh
Sebastian Michailidis
SeongWan Kim
Sessamekesh
@@ -515,11 +497,10 @@ generous deed immortalized in the next stable release of Godot Engine.
smbe19
smo1704
SpicyCactuar
- Squidgy
+ SquidgySapphic
Squirrel
Stephen Rice
Stephen Schlie
- Sung soo Choi
Sven Walter
SxP
tadashi endo
@@ -536,23 +517,20 @@ generous deed immortalized in the next stable release of Godot Engine.
Tim Riley
Tom Webster
Trent Skinner
- Turgut Temucin
Tyler Stafos
Tyler Stepke
- Ukko K.
Uther
Vaughan Ling
vgmoose
- Vulinux
+ vlnx
Wapiti .
Wiley Thompson
Xatonym
- Yan Shi
Zekim
ケルベロス
貴宏 小松
- And 196 anonymous donors
+ And 208 anonymous donors
## Silver and bronze donors
diff --git a/SConstruct b/SConstruct
index 2cce05a20b..dbcd9dfd68 100644
--- a/SConstruct
+++ b/SConstruct
@@ -121,38 +121,38 @@ elif os.name == "nt" and methods.get_cmdline_bool("use_mingw", False):
# We let SCons build its default ENV as it includes OS-specific things which we don't
# want to have to pull in manually.
# Then we prepend PATH to make it take precedence, while preserving SCons' own entries.
-env_base = Environment(tools=custom_tools)
-env_base.PrependENVPath("PATH", os.getenv("PATH"))
-env_base.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
+env = Environment(tools=custom_tools)
+env.PrependENVPath("PATH", os.getenv("PATH"))
+env.PrependENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
if "TERM" in os.environ: # Used for colored output.
- env_base["ENV"]["TERM"] = os.environ["TERM"]
+ env["ENV"]["TERM"] = os.environ["TERM"]
-env_base.disabled_modules = []
-env_base.module_version_string = ""
-env_base.msvc = False
+env.disabled_modules = []
+env.module_version_string = ""
+env.msvc = False
-env_base.__class__.disable_module = methods.disable_module
+env.__class__.disable_module = methods.disable_module
-env_base.__class__.add_module_version_string = methods.add_module_version_string
+env.__class__.add_module_version_string = methods.add_module_version_string
-env_base.__class__.add_source_files = methods.add_source_files
-env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
+env.__class__.add_source_files = methods.add_source_files
+env.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
-env_base.__class__.add_shared_library = methods.add_shared_library
-env_base.__class__.add_library = methods.add_library
-env_base.__class__.add_program = methods.add_program
-env_base.__class__.CommandNoCache = methods.CommandNoCache
-env_base.__class__.Run = methods.Run
-env_base.__class__.disable_warnings = methods.disable_warnings
-env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
-env_base.__class__.module_add_dependencies = methods.module_add_dependencies
-env_base.__class__.module_check_dependencies = methods.module_check_dependencies
+env.__class__.add_shared_library = methods.add_shared_library
+env.__class__.add_library = methods.add_library
+env.__class__.add_program = methods.add_program
+env.__class__.CommandNoCache = methods.CommandNoCache
+env.__class__.Run = methods.Run
+env.__class__.disable_warnings = methods.disable_warnings
+env.__class__.force_optimization_on_debug = methods.force_optimization_on_debug
+env.__class__.module_add_dependencies = methods.module_add_dependencies
+env.__class__.module_check_dependencies = methods.module_check_dependencies
-env_base["x86_libtheora_opt_gcc"] = False
-env_base["x86_libtheora_opt_vc"] = False
+env["x86_libtheora_opt_gcc"] = False
+env["x86_libtheora_opt_vc"] = False
# avoid issues when building with different versions of python out of the same directory
-env_base.SConsignFile(File("#.sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)).abspath)
+env.SConsignFile(File("#.sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)).abspath)
# Build options
@@ -180,6 +180,7 @@ opts.Add(
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", False))
opts.Add(BoolVariable("separate_debug_symbols", "Extract debugging symbols to a separate file", False))
+opts.Add(BoolVariable("debug_paths_relative", "Make file paths in debug symbols relative (if supported)", False))
opts.Add(EnumVariable("lto", "Link-time optimization (production builds)", "none", ("none", "auto", "thin", "full")))
opts.Add(BoolVariable("production", "Set defaults to build Godot for use in production", False))
opts.Add(BoolVariable("threads", "Enable threading support", True))
@@ -227,6 +228,7 @@ opts.Add(
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
opts.Add(BoolVariable("scu_build", "Use single compilation unit build", False))
opts.Add("scu_limit", "Max includes per SCU file when using scu_build (determines RAM use)", "0")
+opts.Add(BoolVariable("engine_update_check", "Enable engine update checks in the Project Manager", True))
# Thirdparty libraries
opts.Add(BoolVariable("builtin_brotli", "Use the built-in Brotli library", True))
@@ -273,22 +275,22 @@ opts.Add("linkflags", "Custom flags for the linker")
# Update the environment to have all above options defined
# in following code (especially platform and custom_modules).
-opts.Update(env_base)
+opts.Update(env)
# Copy custom environment variables if set.
-if env_base["import_env_vars"]:
- for env_var in str(env_base["import_env_vars"]).split(","):
+if env["import_env_vars"]:
+ for env_var in str(env["import_env_vars"]).split(","):
if env_var in os.environ:
- env_base["ENV"][env_var] = os.environ[env_var]
+ env["ENV"][env_var] = os.environ[env_var]
# Platform selection: validate input, and add options.
selected_platform = ""
-if env_base["platform"] != "":
- selected_platform = env_base["platform"]
-elif env_base["p"] != "":
- selected_platform = env_base["p"]
+if env["platform"] != "":
+ selected_platform = env["platform"]
+elif env["p"] != "":
+ selected_platform = env["p"]
else:
# Missing `platform` argument, try to detect platform automatically
if (
@@ -341,7 +343,7 @@ if selected_platform not in platform_list:
# Make sure to update this to the found, valid platform as it's used through the buildsystem as the reference.
# It should always be re-set after calling `opts.Update()` otherwise it uses the original input value.
-env_base["platform"] = selected_platform
+env["platform"] = selected_platform
# Add platform-specific options.
if selected_platform in platform_opts:
@@ -349,15 +351,15 @@ if selected_platform in platform_opts:
opts.Add(opt)
# Update the environment to take platform-specific options into account.
-opts.Update(env_base)
-env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update().
+opts.Update(env)
+env["platform"] = selected_platform # Must always be re-set after calling opts.Update().
# Detect modules.
modules_detected = OrderedDict()
module_search_paths = ["modules"] # Built-in path.
-if env_base["custom_modules"]:
- paths = env_base["custom_modules"].split(",")
+if env["custom_modules"]:
+ paths = env["custom_modules"].split(",")
for p in paths:
try:
module_search_paths.append(methods.convert_custom_modules_path(p))
@@ -371,13 +373,13 @@ for path in module_search_paths:
# so save the time it takes to parse directories.
modules = methods.detect_modules(path, recursive=False)
else: # Custom.
- modules = methods.detect_modules(path, env_base["custom_modules_recursive"])
+ modules = methods.detect_modules(path, env["custom_modules_recursive"])
# Provide default include path for both the custom module search `path`
# and the base directory containing custom modules, as it may be different
# from the built-in "modules" name (e.g. "custom_modules/summator/summator.h"),
# so it can be referenced simply as `#include "summator/summator.h"`
# independently of where a module is located on user's filesystem.
- env_base.Prepend(CPPPATH=[path, os.path.dirname(path)])
+ env.Prepend(CPPPATH=[path, os.path.dirname(path)])
# Note: custom modules can override built-in ones.
modules_detected.update(modules)
@@ -386,7 +388,7 @@ for name, path in modules_detected.items():
sys.path.insert(0, path)
import config
- if env_base["modules_enabled_by_default"]:
+ if env["modules_enabled_by_default"]:
enabled = True
try:
enabled = config.is_enabled()
@@ -410,17 +412,17 @@ for name, path in modules_detected.items():
methods.write_modules(modules_detected)
# Update the environment again after all the module options are added.
-opts.Update(env_base)
-env_base["platform"] = selected_platform # Must always be re-set after calling opts.Update().
-Help(opts.GenerateHelpText(env_base))
+opts.Update(env)
+env["platform"] = selected_platform # Must always be re-set after calling opts.Update().
+Help(opts.GenerateHelpText(env))
# add default include paths
-env_base.Prepend(CPPPATH=["#"])
+env.Prepend(CPPPATH=["#"])
# configure ENV for platform
-env_base.platform_exporters = platform_exporters
-env_base.platform_apis = platform_apis
+env.platform_exporters = platform_exporters
+env.platform_apis = platform_apis
# Configuration of build targets:
# - Editor or template
@@ -429,68 +431,69 @@ env_base.platform_apis = platform_apis
# - Optimization level
# - Debug symbols for crash traces / debuggers
-env_base.editor_build = env_base["target"] == "editor"
-env_base.dev_build = env_base["dev_build"]
-env_base.debug_features = env_base["target"] in ["editor", "template_debug"]
+env.editor_build = env["target"] == "editor"
+env.dev_build = env["dev_build"]
+env.debug_features = env["target"] in ["editor", "template_debug"]
-if env_base.dev_build:
+if env.dev_build:
opt_level = "none"
-elif env_base.debug_features:
+elif env.debug_features:
opt_level = "speed_trace"
else: # Release
opt_level = "speed"
-env_base["optimize"] = ARGUMENTS.get("optimize", opt_level)
-env_base["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", env_base.dev_build)
+env["optimize"] = ARGUMENTS.get("optimize", opt_level)
+env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", env.dev_build)
-if env_base.editor_build:
- env_base.Append(CPPDEFINES=["TOOLS_ENABLED"])
+if env.editor_build:
+ env.Append(CPPDEFINES=["TOOLS_ENABLED"])
-if env_base.debug_features:
+if env.debug_features:
# DEBUG_ENABLED enables debugging *features* and debug-only code, which is intended
# to give *users* extra debugging information for their game development.
- env_base.Append(CPPDEFINES=["DEBUG_ENABLED"])
+ env.Append(CPPDEFINES=["DEBUG_ENABLED"])
-if env_base.dev_build:
+if env.dev_build:
# DEV_ENABLED enables *engine developer* code which should only be compiled for those
# working on the engine itself.
- env_base.Append(CPPDEFINES=["DEV_ENABLED"])
+ env.Append(CPPDEFINES=["DEV_ENABLED"])
else:
# Disable assert() for production targets (only used in thirdparty code).
- env_base.Append(CPPDEFINES=["NDEBUG"])
+ env.Append(CPPDEFINES=["NDEBUG"])
# SCons speed optimization controlled by the `fast_unsafe` option, which provide
# more than 10 s speed up for incremental rebuilds.
# Unsafe as they reduce the certainty of rebuilding all changed files, so it's
# enabled by default for `debug` builds, and can be overridden from command line.
# Ref: https://github.com/SCons/scons/wiki/GoFastButton
-if methods.get_cmdline_bool("fast_unsafe", env_base.dev_build):
+if methods.get_cmdline_bool("fast_unsafe", env.dev_build):
# Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat.
- env_base.Decider("MD5-timestamp")
- env_base.SetOption("implicit_cache", 1)
- env_base.SetOption("max_drift", 60)
+ env.Decider("MD5-timestamp")
+ env.SetOption("implicit_cache", 1)
+ env.SetOption("max_drift", 60)
-if env_base["use_precise_math_checks"]:
- env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
+if env["use_precise_math_checks"]:
+ env.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
-if not env_base.File("#main/splash_editor.png").exists():
+if env.editor_build and env["engine_update_check"]:
+ env.Append(CPPDEFINES=["ENGINE_UPDATE_CHECK_ENABLED"])
+
+if not env.File("#main/splash_editor.png").exists():
# Force disabling editor splash if missing.
- env_base["no_editor_splash"] = True
-if env_base["no_editor_splash"]:
- env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
+ env["no_editor_splash"] = True
+if env["no_editor_splash"]:
+ env.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
-if not env_base["deprecated"]:
- env_base.Append(CPPDEFINES=["DISABLE_DEPRECATED"])
+if not env["deprecated"]:
+ env.Append(CPPDEFINES=["DISABLE_DEPRECATED"])
-if env_base["precision"] == "double":
- env_base.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
+if env["precision"] == "double":
+ env.Append(CPPDEFINES=["REAL_T_IS_DOUBLE"])
tmppath = "./platform/" + selected_platform
sys.path.insert(0, tmppath)
import detect
-env = env_base.Clone()
-
# Default num_jobs to local cpu count if not user specified.
# SCons has a peculiarity where user-specified options won't be overridden
# by SetOption, so we can rely on this to know if we should use our default.
@@ -565,7 +568,7 @@ if env["production"]:
# Run SCU file generation script if in a SCU build.
if env["scu_build"]:
max_includes_per_scu = 8
- if env_base.dev_build == True:
+ if env.dev_build == True:
max_includes_per_scu = 1024
read_scu_limit = int(env["scu_limit"])
@@ -583,73 +586,6 @@ print(f'Building for platform "{selected_platform}", architecture "{env["arch"]}
if env.dev_build:
print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).")
-# Set optimize and debug_symbols flags.
-# "custom" means do nothing and let users set their own optimization flags.
-# Needs to happen after configure to have `env.msvc` defined.
-if env.msvc:
- if env["debug_symbols"]:
- env.Append(CCFLAGS=["/Zi", "/FS"])
- env.Append(LINKFLAGS=["/DEBUG:FULL"])
- else:
- env.Append(LINKFLAGS=["/DEBUG:NONE"])
-
- if env["optimize"] == "speed":
- env.Append(CCFLAGS=["/O2"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- elif env["optimize"] == "speed_trace":
- env.Append(CCFLAGS=["/O2"])
- env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
- elif env["optimize"] == "size":
- env.Append(CCFLAGS=["/O1"])
- env.Append(LINKFLAGS=["/OPT:REF"])
- elif env["optimize"] == "debug" or env["optimize"] == "none":
- env.Append(CCFLAGS=["/Od"])
-else:
- if env["debug_symbols"]:
- # Adding dwarf-4 explicitly makes stacktraces work with clang builds,
- # otherwise addr2line doesn't understand them
- env.Append(CCFLAGS=["-gdwarf-4"])
- if env.dev_build:
- env.Append(CCFLAGS=["-g3"])
- else:
- env.Append(CCFLAGS=["-g2"])
- else:
- if methods.using_clang(env) and not methods.is_vanilla_clang(env):
- # Apple Clang, its linker doesn't like -s.
- env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
- else:
- env.Append(LINKFLAGS=["-s"])
-
- if env["optimize"] == "speed":
- env.Append(CCFLAGS=["-O3"])
- # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
- elif env["optimize"] == "speed_trace":
- env.Append(CCFLAGS=["-O2"])
- elif env["optimize"] == "size":
- env.Append(CCFLAGS=["-Os"])
- elif env["optimize"] == "debug":
- env.Append(CCFLAGS=["-Og"])
- elif env["optimize"] == "none":
- env.Append(CCFLAGS=["-O0"])
-
-# Needs to happen after configure to handle "auto".
-if env["lto"] != "none":
- print("Using LTO: " + env["lto"])
-
-# Set our C and C++ standard requirements.
-# C++17 is required as we need guaranteed copy elision as per GH-36436.
-# Prepending to make it possible to override.
-# This needs to come after `configure`, otherwise we don't have env.msvc.
-if not env.msvc:
- # Specifying GNU extensions support explicitly, which are supported by
- # both GCC and Clang. Both currently default to gnu11 and gnu++14.
- env.Prepend(CFLAGS=["-std=gnu11"])
- env.Prepend(CXXFLAGS=["-std=gnu++17"])
-else:
- # MSVC doesn't have clear C standard support, /std only covers C++.
- # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
- env.Prepend(CCFLAGS=["/std:c++17"])
-
# Enforce our minimal compiler version requirements
cc_version = methods.get_compiler_version(env) or {
"major": None,
@@ -695,6 +631,9 @@ if methods.using_gcc(env):
"to switch to posix threads."
)
Exit(255)
+ if env["debug_paths_relative"] and cc_version_major < 8:
+ print("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ env["debug_paths_relative"] = False
elif methods.using_clang(env):
if cc_version_major == -1:
print(
@@ -717,12 +656,89 @@ elif methods.using_clang(env):
"support C++17. Supported versions are Apple Clang 10 and later."
)
Exit(255)
+ if env["debug_paths_relative"] and not vanilla and cc_version_major < 12:
+ print("Apple Clang < 12 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ env["debug_paths_relative"] = False
elif cc_version_major < 6:
print(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
Exit(255)
+ if env["debug_paths_relative"] and cc_version_major < 10:
+ print("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
+ env["debug_paths_relative"] = False
+
+# Set optimize and debug_symbols flags.
+# "custom" means do nothing and let users set their own optimization flags.
+# Needs to happen after configure to have `env.msvc` defined.
+if env.msvc:
+ if env["debug_symbols"]:
+ env.Append(CCFLAGS=["/Zi", "/FS"])
+ env.Append(LINKFLAGS=["/DEBUG:FULL"])
+ else:
+ env.Append(LINKFLAGS=["/DEBUG:NONE"])
+
+ if env["optimize"] == "speed":
+ env.Append(CCFLAGS=["/O2"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "speed_trace":
+ env.Append(CCFLAGS=["/O2"])
+ env.Append(LINKFLAGS=["/OPT:REF", "/OPT:NOICF"])
+ elif env["optimize"] == "size":
+ env.Append(CCFLAGS=["/O1"])
+ env.Append(LINKFLAGS=["/OPT:REF"])
+ elif env["optimize"] == "debug" or env["optimize"] == "none":
+ env.Append(CCFLAGS=["/Od"])
+else:
+ if env["debug_symbols"]:
+ # Adding dwarf-4 explicitly makes stacktraces work with clang builds,
+ # otherwise addr2line doesn't understand them
+ env.Append(CCFLAGS=["-gdwarf-4"])
+ if env.dev_build:
+ env.Append(CCFLAGS=["-g3"])
+ else:
+ env.Append(CCFLAGS=["-g2"])
+ if env["debug_paths_relative"]:
+ # Remap absolute paths to relative paths for debug symbols.
+ project_path = Dir("#").abspath
+ env.Append(CCFLAGS=[f"-ffile-prefix-map={project_path}=."])
+ else:
+ if methods.using_clang(env) and not methods.is_vanilla_clang(env):
+ # Apple Clang, its linker doesn't like -s.
+ env.Append(LINKFLAGS=["-Wl,-S", "-Wl,-x", "-Wl,-dead_strip"])
+ else:
+ env.Append(LINKFLAGS=["-s"])
+
+ if env["optimize"] == "speed":
+ env.Append(CCFLAGS=["-O3"])
+ # `-O2` is friendlier to debuggers than `-O3`, leading to better crash backtraces.
+ elif env["optimize"] == "speed_trace":
+ env.Append(CCFLAGS=["-O2"])
+ elif env["optimize"] == "size":
+ env.Append(CCFLAGS=["-Os"])
+ elif env["optimize"] == "debug":
+ env.Append(CCFLAGS=["-Og"])
+ elif env["optimize"] == "none":
+ env.Append(CCFLAGS=["-O0"])
+
+# Needs to happen after configure to handle "auto".
+if env["lto"] != "none":
+ print("Using LTO: " + env["lto"])
+
+# Set our C and C++ standard requirements.
+# C++17 is required as we need guaranteed copy elision as per GH-36436.
+# Prepending to make it possible to override.
+# This needs to come after `configure`, otherwise we don't have env.msvc.
+if not env.msvc:
+ # Specifying GNU extensions support explicitly, which are supported by
+ # both GCC and Clang. Both currently default to gnu11 and gnu++14.
+ env.Prepend(CFLAGS=["-std=gnu11"])
+ env.Prepend(CXXFLAGS=["-std=gnu++17"])
+else:
+ # MSVC doesn't have clear C standard support, /std only covers C++.
+ # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
+ env.Prepend(CCFLAGS=["/std:c++17"])
# Disable exception handling. Godot doesn't use exceptions anywhere, and this
# saves around 20% of binary size and very significant build time (GH-80513).
@@ -826,7 +842,7 @@ suffix += "." + env["target"]
if env.dev_build:
suffix += ".dev"
-if env_base["precision"] == "double":
+if env["precision"] == "double":
suffix += ".double"
suffix += "." + env["arch"]
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 9f4bff3779..f2f8aebe8b 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -82,6 +82,17 @@ int Engine::get_audio_output_latency() const {
return _audio_output_latency;
}
+void Engine::increment_frames_drawn() {
+ if (frame_server_synced) {
+ server_syncs++;
+ } else {
+ server_syncs = 0;
+ }
+ frame_server_synced = false;
+
+ frames_drawn++;
+}
+
uint64_t Engine::get_frames_drawn() {
return frames_drawn;
}
@@ -364,6 +375,11 @@ Engine *Engine::get_singleton() {
return singleton;
}
+bool Engine::notify_frame_server_synced() {
+ frame_server_synced = true;
+ return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING;
+}
+
Engine::Engine() {
singleton = this;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index d1495b36c2..8dece803e3 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -91,6 +91,10 @@ private:
String write_movie_path;
String shader_cache_path;
+ static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5;
+ int server_syncs = 0;
+ bool frame_server_synced = false;
+
public:
static Engine *get_singleton();
@@ -179,6 +183,9 @@ public:
bool is_generate_spirv_debug_info_enabled() const;
int32_t get_gpu_index() const;
+ void increment_frames_drawn();
+ bool notify_frame_server_synced();
+
Engine();
virtual ~Engine() {}
};
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 75eea2ef50..104b17961d 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1528,6 +1528,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0);
+ GLOBAL_DEF_BASIC("internationalization/rendering/root_node_auto_translate", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 8c85030783..03c31bee28 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -338,6 +338,10 @@ bool OS::is_process_running(int p_pid) const {
return ::OS::get_singleton()->is_process_running(p_pid);
}
+int OS::get_process_exit_code(int p_pid) const {
+ return ::OS::get_singleton()->get_process_exit_code(p_pid);
+}
+
int OS::get_process_id() const {
return ::OS::get_singleton()->get_process_id();
}
@@ -602,6 +606,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
+ ClassDB::bind_method(D_METHOD("get_process_exit_code", "pid"), &OS::get_process_exit_code);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
diff --git a/core/core_bind.h b/core/core_bind.h
index d46321cf5c..3c0cdc25ce 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -164,6 +164,7 @@ public:
Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true);
bool is_process_running(int p_pid) const;
+ int get_process_exit_code(int p_pid) const;
int get_process_id() const;
void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>());
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 680a653dfc..e99885befa 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -84,7 +84,7 @@ static Error _erase_recursive(DirAccess *da) {
String n = da->get_next();
while (!n.is_empty()) {
if (n != "." && n != "..") {
- if (da->current_is_dir()) {
+ if (da->current_is_dir() && !da->is_link(n)) {
dirs.push_back(n);
} else {
files.push_back(n);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 4487b8e472..18dbac991c 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -656,10 +656,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V(!ClassDB::can_instantiate(str), ERR_INVALID_DATA);
Object *obj = ClassDB::instantiate(str);
-
ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ // Avoid premature free `RefCounted`. This must be done before properties are initialized,
+ // since script functions (setters, implicit initializer) may be called. See GH-68666.
+ Variant variant;
+ if (Object::cast_to<RefCounted>(obj)) {
+ Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
+ variant = ref;
+ } else {
+ variant = obj;
+ }
+
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
@@ -699,12 +708,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
}
- if (Object::cast_to<RefCounted>(obj)) {
- Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
- r_variant = ref;
- } else {
- r_variant = obj;
- }
+ r_variant = variant;
}
}
@@ -790,11 +794,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
builtin_type = (Variant::Type)bt;
- ERR_FAIL_COND_V(!p_allow_objects && builtin_type == Variant::OBJECT, ERR_UNAUTHORIZED);
+ if (!p_allow_objects && builtin_type == Variant::OBJECT) {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
} break;
case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
- ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
-
String str;
Error err = _decode_string(buf, len, r_len, str);
if (err) {
@@ -802,22 +806,28 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
builtin_type = Variant::OBJECT;
- class_name = str;
+ if (p_allow_objects) {
+ class_name = str;
+ } else {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
} break;
case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
- ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
-
String path;
Error err = _decode_string(buf, len, r_len, path);
if (err) {
return err;
}
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
- script = ResourceLoader::load(path, "Script");
- ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
builtin_type = Variant::OBJECT;
- class_name = script->get_instance_base_type();
+ if (p_allow_objects) {
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+ script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+ class_name = script->get_instance_base_type();
+ } else {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
} break;
default:
ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
@@ -1239,13 +1249,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
if (array.is_typed()) {
Ref<Script> script = array.get_typed_script();
if (script.is_valid()) {
- ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT;
} else if (array.get_typed_class_name() != StringName()) {
- ERR_FAIL_COND_V(!p_full_objects, ERR_UNAVAILABLE);
header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
} else {
- ERR_FAIL_COND_V(!p_full_objects && array.get_typed_builtin() == Variant::OBJECT, ERR_UNAVAILABLE);
header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
}
}
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index 2b9487b9e1..90a8f49a75 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -51,6 +51,7 @@ Error StreamPeerTCP::poll() {
status = STATUS_ERROR;
return err;
}
+ return OK;
} else if (status != STATUS_CONNECTING) {
return OK;
}
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index ef3d315e4b..e0b8730a67 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -35,6 +35,8 @@
#include "core/os/thread_safe.h"
#include "core/templates/command_queue_mt.h"
+WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1;
+
void WorkerThreadPool::Task::free_template_userdata() {
ERR_FAIL_NULL(template_userdata);
ERR_FAIL_NULL(native_func_userdata);
@@ -60,11 +62,13 @@ void WorkerThreadPool::_process_task(Task *p_task) {
// its pre-created threads can't have ScriptServer::thread_enter() called on them early.
// Therefore, we do it late at the first opportunity, so in case the task
// about to be run uses scripting, guarantees are held.
+ task_mutex.lock();
if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) {
+ task_mutex.unlock();
ScriptServer::thread_enter();
+ task_mutex.lock();
curr_thread.ready_for_scripting = true;
}
- task_mutex.lock();
p_task->pool_thread_index = pool_thread_index;
prev_task = curr_thread.current_task;
curr_thread.current_task = p_task;
@@ -389,83 +393,115 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
task_mutex.unlock();
if (caller_pool_thread) {
- while (true) {
- Task *task_to_process = nullptr;
- {
- MutexLock lock(task_mutex);
- bool was_signaled = caller_pool_thread->signaled;
- caller_pool_thread->signaled = false;
-
- if (task->completed) {
- // This thread was awaken also for some reason, but it's about to exit.
- // Let's find out what may be pending and forward the requests.
- if (!exit_threads && was_signaled) {
- uint32_t to_process = task_queue.first() ? 1 : 0;
- uint32_t to_promote = caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0;
- if (to_process || to_promote) {
- // This thread must be left alone since it won't loop again.
- caller_pool_thread->signaled = true;
- _notify_threads(caller_pool_thread, to_process, to_promote);
- }
- }
+ _wait_collaboratively(caller_pool_thread, task);
+ task->waiting_pool--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ }
+ } else {
+ task->done_semaphore.wait();
+ task_mutex.lock();
+ task->waiting_user--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ }
+ task_mutex.unlock();
+ }
- task->waiting_pool--;
- if (task->waiting_pool == 0 && task->waiting_user == 0) {
- tasks.erase(p_task_id);
- task_allocator.free(task);
- }
+ return OK;
+}
- break;
- }
+void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) {
+ // Keep processing tasks until the condition to stop waiting is met.
- if (!exit_threads) {
- // This is a thread from the pool. It shouldn't just idle.
- // Let's try to process other tasks while we wait.
+#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed)
- if (caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
- if (_try_promote_low_priority_task()) {
- _notify_threads(caller_pool_thread, 1, 0);
- }
+ while (true) {
+ Task *task_to_process = nullptr;
+ {
+ MutexLock lock(task_mutex);
+ bool was_signaled = p_caller_pool_thread->signaled;
+ p_caller_pool_thread->signaled = false;
+
+ if (IS_WAIT_OVER) {
+ p_caller_pool_thread->yield_is_over = false;
+ if (!exit_threads && was_signaled) {
+ // This thread was awaken for some additional reason, but it's about to exit.
+ // Let's find out what may be pending and forward the requests.
+ uint32_t to_process = task_queue.first() ? 1 : 0;
+ uint32_t to_promote = p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0;
+ if (to_process || to_promote) {
+ // This thread must be left alone since it won't loop again.
+ p_caller_pool_thread->signaled = true;
+ _notify_threads(p_caller_pool_thread, to_process, to_promote);
}
+ }
- if (singleton->task_queue.first()) {
- task_to_process = task_queue.first()->self();
- task_queue.remove(task_queue.first());
+ break;
+ }
+
+ if (!exit_threads) {
+ if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
+ if (_try_promote_low_priority_task()) {
+ _notify_threads(p_caller_pool_thread, 1, 0);
}
+ }
- if (!task_to_process) {
- caller_pool_thread->awaited_task = task;
+ if (singleton->task_queue.first()) {
+ task_to_process = task_queue.first()->self();
+ task_queue.remove(task_queue.first());
+ }
- if (flushing_cmd_queue) {
- flushing_cmd_queue->unlock();
- }
- caller_pool_thread->cond_var.wait(lock);
- if (flushing_cmd_queue) {
- flushing_cmd_queue->lock();
- }
+ if (!task_to_process) {
+ p_caller_pool_thread->awaited_task = p_task;
- DEV_ASSERT(exit_threads || caller_pool_thread->signaled || task->completed);
- caller_pool_thread->awaited_task = nullptr;
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->unlock();
+ }
+ p_caller_pool_thread->cond_var.wait(lock);
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->lock();
}
- }
- }
- if (task_to_process) {
- _process_task(task_to_process);
+ DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER);
+ p_caller_pool_thread->awaited_task = nullptr;
+ }
}
}
- } else {
- task->done_semaphore.wait();
- task_mutex.lock();
- task->waiting_user--;
- if (task->waiting_pool == 0 && task->waiting_user == 0) {
- tasks.erase(p_task_id);
- task_allocator.free(task);
+
+ if (task_to_process) {
+ _process_task(task_to_process);
}
+ }
+}
+
+void WorkerThreadPool::yield() {
+ int th_index = get_thread_index();
+ ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread.");
+ _wait_collaboratively(&threads[th_index], ThreadData::YIELDING);
+}
+
+void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
+ task_mutex.lock();
+ Task **taskp = tasks.getptr(p_task_id);
+ if (!taskp) {
task_mutex.unlock();
+ ERR_FAIL_MSG("Invalid Task ID.");
+ }
+ Task *task = *taskp;
+ if (task->completed) {
+ task_mutex.unlock();
+ return;
}
- return OK;
+ ThreadData &td = threads[task->pool_thread_index];
+ td.yield_is_over = true;
+ td.signaled = true;
+ td.cond_var.notify_one();
+
+ task_mutex.unlock();
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index fdddc9a647..64f24df79f 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -107,13 +107,21 @@ private:
BinaryMutex task_mutex;
struct ThreadData {
+ static Task *const YIELDING; // Too bad constexpr doesn't work here.
+
uint32_t index = 0;
Thread thread;
- bool ready_for_scripting = false;
- bool signaled = false;
+ bool ready_for_scripting : 1;
+ bool signaled : 1;
+ bool yield_is_over : 1;
Task *current_task = nullptr;
- Task *awaited_task = nullptr; // Null if not awaiting the condition variable. Special value for idle-waiting.
+ Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING).
ConditionVariable cond_var;
+
+ ThreadData() :
+ ready_for_scripting(false),
+ signaled(false),
+ yield_is_over(false) {}
};
TightLocalVector<ThreadData> threads;
@@ -177,6 +185,8 @@ private:
}
};
+ void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task);
+
protected:
static void _bind_methods();
@@ -196,6 +206,9 @@ public:
bool is_task_completed(TaskID p_task_id) const;
Error wait_for_task_completion(TaskID p_task_id);
+ void yield();
+ void notify_yield_over(TaskID p_task_id);
+
template <typename C, typename M, typename U>
GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) {
typedef GroupUserData<C, M, U> GroupUD;
diff --git a/core/os/os.h b/core/os/os.h
index 3827bb273a..06be0e2b41 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -176,6 +176,7 @@ public:
virtual Error kill(const ProcessID &p_pid) = 0;
virtual int get_process_id() const;
virtual bool is_process_running(const ProcessID &p_pid) const = 0;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const = 0;
virtual void vibrate_handheld(int p_duration_ms = 500) {}
virtual Error shell_open(const String &p_uri);
diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp
index 6ecd75ebc1..0c5c6394a1 100644
--- a/core/templates/command_queue_mt.cpp
+++ b/core/templates/command_queue_mt.cpp
@@ -70,14 +70,8 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() {
return &sync_sems[idx];
}
-CommandQueueMT::CommandQueueMT(bool p_sync) {
- if (p_sync) {
- sync = memnew(Semaphore);
- }
+CommandQueueMT::CommandQueueMT() {
}
CommandQueueMT::~CommandQueueMT() {
- if (sync) {
- memdelete(sync);
- }
}
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index e26f11d28a..a4ac338bed 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -248,16 +248,17 @@
#define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
#define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N
-#define DECL_PUSH(N) \
- template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
- void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
- CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
- cmd->instance = p_instance; \
- cmd->method = p_method; \
- SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
- unlock(); \
- if (sync) \
- sync->post(); \
+#define DECL_PUSH(N) \
+ template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
+ void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
+ CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
+ cmd->instance = p_instance; \
+ cmd->method = p_method; \
+ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
+ unlock(); \
}
#define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R>
@@ -272,9 +273,10 @@
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
cmd->ret = r_ret; \
cmd->sync_sem = ss; \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
unlock(); \
- if (sync) \
- sync->post(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -290,9 +292,10 @@
cmd->method = p_method; \
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
cmd->sync_sem = ss; \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
unlock(); \
- if (sync) \
- sync->post(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -340,7 +343,7 @@ class CommandQueueMT {
LocalVector<uint8_t> command_mem;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
- Semaphore *sync = nullptr;
+ WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID;
uint64_t flush_read_ptr = 0;
template <typename T>
@@ -420,12 +423,16 @@ public:
}
void wait_and_flush() {
- ERR_FAIL_NULL(sync);
- sync->wait();
+ ERR_FAIL_COND(pump_task_id == WorkerThreadPool::INVALID_TASK_ID);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(pump_task_id);
_flush();
}
- CommandQueueMT(bool p_sync);
+ void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) {
+ pump_task_id = p_task_id;
+ }
+
+ CommandQueueMT();
~CommandQueueMT();
};
diff --git a/core/variant/array.h b/core/variant/array.h
index d45a6887e2..3aa957b312 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -54,7 +54,7 @@ public:
_FORCE_INLINE_ ConstIterator &operator--();
_FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
- _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr != p_other.element_ptr; }
_FORCE_INLINE_ ConstIterator(const Variant *p_element_ptr, Variant *p_read_only = nullptr) :
element_ptr(p_element_ptr), read_only(p_read_only) {}
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 6d8517927c..0307684588 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -901,6 +901,7 @@
<method name="printraw" qualifiers="vararg">
<description>
Prints one or more arguments to strings in the best way possible to the OS terminal. Unlike [method print], no newline is automatically added at the end.
+ [b]Note:[/b] The OS terminal is [i]not[/i] the same as the editor's Output dock. The output sent to the OS terminal can be seen when running Godot from a terminal. On Windows, this requires using the [code]console.exe[/code] executable.
[codeblocks]
[gdscript]
printraw("A")
diff --git a/doc/classes/BoneAttachment3D.xml b/doc/classes/BoneAttachment3D.xml
index bafa463f82..a51b2d9a94 100644
--- a/doc/classes/BoneAttachment3D.xml
+++ b/doc/classes/BoneAttachment3D.xml
@@ -21,11 +21,10 @@
Returns whether the BoneAttachment3D node is using an external [Skeleton3D] rather than attempting to use its parent node as the [Skeleton3D].
</description>
</method>
- <method name="on_bone_pose_update">
+ <method name="on_skeleton_update">
<return type="void" />
- <param index="0" name="bone_index" type="int" />
<description>
- A function that is called automatically when the [Skeleton3D] the BoneAttachment3D node is using has a bone that has changed its pose. This function is where the BoneAttachment3D node updates its position so it is correctly bound when it is [i]not[/i] set to override the bone pose.
+ A function that is called automatically when the [Skeleton3D] is updated. This function is where the [BoneAttachment3D] node updates its position so it is correctly bound when it is [i]not[/i] set to override the bone pose.
</description>
</method>
<method name="set_external_skeleton">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index cc32964e87..a0c76a3ad6 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -937,14 +937,14 @@
<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" deprecated="Use [member Node.auto_translate_mode] instead.">
+ <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" 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="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.
</member>
<member name="custom_minimum_size" type="Vector2" setter="set_custom_minimum_size" getter="get_custom_minimum_size" default="Vector2(0, 0)">
- The minimum size of the node's bounding rectangle. If you set it to a value greater than (0, 0), the node's bounding rectangle will always have at least this size, even if its content is smaller. If it's set to (0, 0), the node sizes automatically to fit its content, be it a texture or child nodes.
+ The minimum size of the node's bounding rectangle. If you set it to a value greater than [code](0, 0)[/code], the node's bounding rectangle will always have at least this size. Note that [Control] nodes have their internal minimum size returned by [method get_minimum_size]. It depends on the control's contents, like text, textures, or style boxes. The actual minimum size is the maximum value of this property and the internal minimum size (see [method get_combined_minimum_size]).
</member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode" default="0">
The focus access mode for the control (None, Click or All). Only one Control can be focused at the same time, and it will receive keyboard, gamepad, and mouse signals.
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 87ca0536b8..20ee65403c 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -656,6 +656,9 @@
<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/import_resources_when_unfocused" type="bool" setter="" getter="">
+ If [code]true[/code], (re)imports resources even if the editor window is unfocused or minimized. If [code]false[/code], resources are only (re)imported when the editor window is focused. This can be set to [code]true[/code] to speed up iteration by starting the import process earlier when saving files in the project folder. This also allows getting visual feedback on changes without having to click the editor window, which is useful with multi-monitor setups. The downside of setting this to [code]true[/code] is that it increases idle CPU usage and may steal CPU time from other applications when importing resources.
+ </member>
<member name="interface/editor/localize_settings" type="bool" setter="" getter="">
If [code]true[/code], setting names in the editor are localized when possible.
[b]Note:[/b] This setting affects most [EditorInspector]s in the editor UI, primarily Project Settings and Editor Settings. To control names displayed in the Inspector dock, use [member interface/inspector/default_property_name_style] instead.
@@ -866,8 +869,16 @@
Specify the multiplier to apply to the scale for the editor gizmo handles to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]1[/code] on non-touchscreen devices.
</member>
+ <member name="network/connection/engine_version_update_mode" type="int" setter="" getter="">
+ Specifies how the engine should check for updates.
+ - [b]Disable Update Checks[/b] will block the engine from checking updates (see also [member network/connection/network_mode]).
+ - [b]Check Newest Preview[/b] (default for preview versions) will check for the newest available development snapshot.
+ - [b]Check Newest Stable[/b] (default for stable versions) will check for the newest available stable version.
+ - [b]Check Newest Patch[/b] will check for the latest available stable version, but only within the same minor version. E.g. if your version is [code]4.3.stable[/code], you will be notified about [code]4.3.1.stable[/code], but not [code]4.4.stable[/code].
+ All update modes will ignore builds with different major versions (e.g. Godot 4 -&gt; Godot 5).
+ </member>
<member name="network/connection/network_mode" type="int" setter="" getter="">
- Determines whether online features are enabled in the editor, such as the Asset Library. Setting this property to "Offline" is recommended to limit editor's internet activity, especially if privacy is a concern.
+ Determines whether online features are enabled in the editor, such as the Asset Library or update checks. Disabling these online features helps alleviate privacy concerns by preventing the editor from making HTTP requests to the Godot website, GitHub, or third-party platforms hosting assets from the Asset Library.
</member>
<member name="network/debug/remote_host" type="String" setter="" getter="">
The address to listen to when starting the remote debugger. This can be set to [code]0.0.0.0[/code] to allow external clients to connect to the remote debugger (instead of restricting the remote debugger to connections from [code]localhost[/code]).
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index 6b9c0bcc76..4052e48400 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -190,7 +190,7 @@
<method name="get_line" qualifiers="const">
<return type="String" />
<description>
- Returns the next line of the file as a [String].
+ Returns the next line of the file as a [String]. The returned string doesn't include newline ([code]\n[/code]) or carriage return ([code]\r[/code]) characters, but does include any other leading or trailing whitespace.
Text is interpreted as being UTF-8 encoded.
</description>
</method>
diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml
index 132ece53b0..6f0561e66e 100644
--- a/doc/classes/NavigationAgent2D.xml
+++ b/doc/classes/NavigationAgent2D.xml
@@ -198,6 +198,13 @@
The radius of the avoidance agent. This is the "body" of the avoidance agent and not the avoidance maneuver starting radius (which is controlled by [member neighbor_distance]).
Does not affect normal pathfinding. To change an actor's pathfinding radius bake [NavigationMesh] resources with a different [member NavigationMesh.agent_radius] property and use different navigation maps for each actor size.
</member>
+ <member name="simplify_epsilon" type="float" setter="set_simplify_epsilon" getter="get_simplify_epsilon" default="0.0">
+ The path simplification amount in worlds units.
+ </member>
+ <member name="simplify_path" type="bool" setter="set_simplify_path" getter="get_simplify_path" default="false">
+ If [code]true[/code] a simplified version of the path will be returned with less critical path points removed. The simplification amount is controlled by [member simplify_epsilon]. The simplification uses a variant of Ramer-Douglas-Peucker algorithm for curve point decimation.
+ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields".
+ </member>
<member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="10.0">
The distance threshold before the target is considered to be reached. On reaching the target, [signal target_reached] is emitted and navigation ends (see [method is_navigation_finished] and [signal navigation_finished]).
You can make navigation end early by setting this property to a value greater than [member path_desired_distance] (navigation will end before reaching the last waypoint).
diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml
index dd75cedf61..64ee35a84b 100644
--- a/doc/classes/NavigationAgent3D.xml
+++ b/doc/classes/NavigationAgent3D.xml
@@ -204,6 +204,13 @@
The radius of the avoidance agent. This is the "body" of the avoidance agent and not the avoidance maneuver starting radius (which is controlled by [member neighbor_distance]).
Does not affect normal pathfinding. To change an actor's pathfinding radius bake [NavigationMesh] resources with a different [member NavigationMesh.agent_radius] property and use different navigation maps for each actor size.
</member>
+ <member name="simplify_epsilon" type="float" setter="set_simplify_epsilon" getter="get_simplify_epsilon" default="0.0">
+ The path simplification amount in worlds units.
+ </member>
+ <member name="simplify_path" type="bool" setter="set_simplify_path" getter="get_simplify_path" default="false">
+ If [code]true[/code] a simplified version of the path will be returned with less critical path points removed. The simplification amount is controlled by [member simplify_epsilon]. The simplification uses a variant of Ramer-Douglas-Peucker algorithm for curve point decimation.
+ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields".
+ </member>
<member name="target_desired_distance" type="float" setter="set_target_desired_distance" getter="get_target_desired_distance" default="1.0">
The distance threshold before the target is considered to be reached. On reaching the target, [signal target_reached] is emitted and navigation ends (see [method is_navigation_finished] and [signal navigation_finished]).
You can make navigation end early by setting this property to a value greater than [member path_desired_distance] (navigation will end before reaching the last waypoint).
diff --git a/doc/classes/NavigationPathQueryParameters2D.xml b/doc/classes/NavigationPathQueryParameters2D.xml
index 7d9ecf61b0..ce0a00bc83 100644
--- a/doc/classes/NavigationPathQueryParameters2D.xml
+++ b/doc/classes/NavigationPathQueryParameters2D.xml
@@ -25,6 +25,13 @@
<member name="pathfinding_algorithm" type="int" setter="set_pathfinding_algorithm" getter="get_pathfinding_algorithm" enum="NavigationPathQueryParameters2D.PathfindingAlgorithm" default="0">
The pathfinding algorithm used in the path query.
</member>
+ <member name="simplify_epsilon" type="float" setter="set_simplify_epsilon" getter="get_simplify_epsilon" default="0.0">
+ The path simplification amount in worlds units.
+ </member>
+ <member name="simplify_path" type="bool" setter="set_simplify_path" getter="get_simplify_path" default="false">
+ If [code]true[/code] a simplified version of the path will be returned with less critical path points removed. The simplification amount is controlled by [member simplify_epsilon]. The simplification uses a variant of Ramer-Douglas-Peucker algorithm for curve point decimation.
+ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields".
+ </member>
<member name="start_position" type="Vector2" setter="set_start_position" getter="get_start_position" default="Vector2(0, 0)">
The pathfinding start position in global coordinates.
</member>
diff --git a/doc/classes/NavigationPathQueryParameters3D.xml b/doc/classes/NavigationPathQueryParameters3D.xml
index 862f8a8347..fcd913f73b 100644
--- a/doc/classes/NavigationPathQueryParameters3D.xml
+++ b/doc/classes/NavigationPathQueryParameters3D.xml
@@ -25,6 +25,13 @@
<member name="pathfinding_algorithm" type="int" setter="set_pathfinding_algorithm" getter="get_pathfinding_algorithm" enum="NavigationPathQueryParameters3D.PathfindingAlgorithm" default="0">
The pathfinding algorithm used in the path query.
</member>
+ <member name="simplify_epsilon" type="float" setter="set_simplify_epsilon" getter="get_simplify_epsilon" default="0.0">
+ The path simplification amount in worlds units.
+ </member>
+ <member name="simplify_path" type="bool" setter="set_simplify_path" getter="get_simplify_path" default="false">
+ If [code]true[/code] a simplified version of the path will be returned with less critical path points removed. The simplification amount is controlled by [member simplify_epsilon]. The simplification uses a variant of Ramer-Douglas-Peucker algorithm for curve point decimation.
+ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields".
+ </member>
<member name="start_position" type="Vector3" setter="set_start_position" getter="get_start_position" default="Vector3(0, 0, 0)">
The pathfinding start position in global coordinates.
</member>
diff --git a/doc/classes/NavigationRegion2D.xml b/doc/classes/NavigationRegion2D.xml
index c490620fc0..31ce1dba4a 100644
--- a/doc/classes/NavigationRegion2D.xml
+++ b/doc/classes/NavigationRegion2D.xml
@@ -23,13 +23,6 @@
Bakes the [NavigationPolygon]. If [param on_thread] is set to [code]true[/code] (default), the baking is done on a separate thread.
</description>
</method>
- <method name="get_avoidance_layer_value" qualifiers="const">
- <return type="bool" />
- <param index="0" name="layer_number" type="int" />
- <description>
- Returns whether or not the specified layer of the [member avoidance_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
- </description>
- </method>
<method name="get_navigation_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
@@ -61,14 +54,6 @@
Returns [code]true[/code] when the [NavigationPolygon] is being baked on a background thread.
</description>
</method>
- <method name="set_avoidance_layer_value">
- <return type="void" />
- <param index="0" name="layer_number" type="int" />
- <param index="1" name="value" type="bool" />
- <description>
- Based on [param value], enables or disables the specified layer in the [member avoidance_layers] bitmask, given a [param layer_number] between 1 and 32.
- </description>
- </method>
<method name="set_navigation_layer_value">
<return type="void" />
<param index="0" name="layer_number" type="int" />
@@ -86,12 +71,6 @@
</method>
</methods>
<members>
- <member name="avoidance_layers" type="int" setter="set_avoidance_layers" getter="get_avoidance_layers" default="1">
- A bitfield determining all avoidance layers for the avoidance constrain.
- </member>
- <member name="constrain_avoidance" type="bool" setter="set_constrain_avoidance" getter="get_constrain_avoidance" default="false" experimental="When enabled, agents are known to get stuck on the navigation polygon corners and edges, especially at a high frame rate. Not recommended for use in production at this stage.">
- If [code]true[/code] constraints avoidance agent's with an avoidance mask bit that matches with a bit of the [member avoidance_layers] to the navigation polygon. Due to each navigation polygon outline creating an obstacle and each polygon edge creating an avoidance line constrain keep the navigation polygon shape as simple as possible for performance.
- </member>
<member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true">
Determines if the [NavigationRegion2D] is enabled or disabled.
</member>
diff --git a/doc/classes/NavigationServer2D.xml b/doc/classes/NavigationServer2D.xml
index baef7dc02d..a6f6abccbd 100644
--- a/doc/classes/NavigationServer2D.xml
+++ b/doc/classes/NavigationServer2D.xml
@@ -947,6 +947,15 @@
If [code]true[/code] enables debug mode on the NavigationServer.
</description>
</method>
+ <method name="simplify_path">
+ <return type="PackedVector2Array" />
+ <param index="0" name="path" type="PackedVector2Array" />
+ <param index="1" name="epsilon" type="float" />
+ <description>
+ Returns a simplified version of [param path] with less critical path points removed. The simplification amount is in worlds units and controlled by [param epsilon]. The simplification uses a variant of Ramer-Douglas-Peucker algorithm for curve point decimation.
+ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields".
+ </description>
+ </method>
</methods>
<signals>
<signal name="map_changed">
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 6be9d5165f..6fcf033544 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -1094,6 +1094,15 @@
If [code]true[/code] enables debug mode on the NavigationServer.
</description>
</method>
+ <method name="simplify_path">
+ <return type="PackedVector3Array" />
+ <param index="0" name="path" type="PackedVector3Array" />
+ <param index="1" name="epsilon" type="float" />
+ <description>
+ Returns a simplified version of [param path] with less critical path points removed. The simplification amount is in worlds units and controlled by [param epsilon]. The simplification uses a variant of Ramer-Douglas-Peucker algorithm for curve point decimation.
+ Path simplification can be helpful to mitigate various path following issues that can arise with certain agent types and script behaviors. E.g. "steering" agents or avoidance in "open fields".
+ </description>
+ </method>
</methods>
<signals>
<signal name="avoidance_debug_changed">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index aea4082dbe..37e64da8c8 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -970,8 +970,8 @@
</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.
+ Defines if any text should automatically change to its translated version depending on the current locale (for nodes such as [Label], [RichTextLabel], [Window], etc.). Also decides if the node's strings should be parsed for POT generation.
+ [b]Note:[/b] For the root node, auto translate mode can also be set via [member ProjectSettings.internationalization/rendering/root_node_auto_translate].
</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.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index de39901133..a130b06dba 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -408,11 +408,20 @@
[b]Note:[/b] On Web platforms, it is still possible to determine the host platform's OS with feature tags. See [method has_feature].
</description>
</method>
+ <method name="get_process_exit_code" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="pid" type="int" />
+ <description>
+ Returns the exit code of a spawned process once it has finished running (see [method is_process_running]).
+ Returns [code]-1[/code] if the [param pid] is not a PID of a spawned child process, the process is still running, or the method is not implemented for the current platform.
+ [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows.
+ </description>
+ </method>
<method name="get_process_id" qualifiers="const">
<return type="int" />
<description>
Returns the number used by the host machine to uniquely identify this application.
- [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS, and Windows.
</description>
</method>
<method name="get_processor_count" qualifiers="const">
@@ -592,7 +601,7 @@
<param index="0" name="pid" type="int" />
<description>
Returns [code]true[/code] if the child process ID ([param pid]) is still running or [code]false[/code] if it has terminated. [param pid] must be a valid ID generated from [method create_process].
- [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
+ [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS, and Windows.
</description>
</method>
<method name="is_restart_on_exit_set" qualifiers="const">
diff --git a/doc/classes/PhysicsServer2DExtension.xml b/doc/classes/PhysicsServer2DExtension.xml
index 815fc742d1..41826ea7c2 100644
--- a/doc/classes/PhysicsServer2DExtension.xml
+++ b/doc/classes/PhysicsServer2DExtension.xml
@@ -185,7 +185,7 @@
<param index="1" name="pickable" type="bool" />
<description>
If set to [code]true[/code], allows the area with the given [RID] to detect mouse inputs when the mouse cursor is hovering on it.
- Overridable version of [PhysicsServer2D]'s internal [code]area_set_pickable[/code] method. Corresponds to [member PhysicsBody2D.input_pickable].
+ Overridable version of [PhysicsServer2D]'s internal [code]area_set_pickable[/code] method. Corresponds to [member CollisionObject2D.input_pickable].
</description>
</method>
<method name="_area_set_shape" qualifiers="virtual">
@@ -650,7 +650,7 @@
<param index="1" name="pickable" type="bool" />
<description>
If set to [code]true[/code], allows the body with the given [RID] to detect mouse inputs when the mouse cursor is hovering on it.
- Overridable version of [PhysicsServer2D]'s internal [code]body_set_pickable[/code] method. Corresponds to [member PhysicsBody2D.input_pickable].
+ Overridable version of [PhysicsServer2D]'s internal [code]body_set_pickable[/code] method. Corresponds to [member CollisionObject2D.input_pickable].
</description>
</method>
<method name="_body_set_shape" qualifiers="virtual">
diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml
index c435f3d291..29b44a98f2 100644
--- a/doc/classes/Popup.xml
+++ b/doc/classes/Popup.xml
@@ -23,9 +23,4 @@
</description>
</signal>
</signals>
- <theme_items>
- <theme_item name="panel" data_type="style" type="StyleBox">
- Default [StyleBox] for the [Popup].
- </theme_item>
- </theme_items>
</class>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 7831ebd1b9..0f5687f091 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -768,6 +768,9 @@
<theme_item name="labeled_separator_right" data_type="style" type="StyleBox">
[StyleBox] for the right side of labeled separator. See [method add_separator].
</theme_item>
+ <theme_item name="panel" data_type="style" type="StyleBox">
+ [StyleBox] for the the background panel.
+ </theme_item>
<theme_item name="separator" data_type="style" type="StyleBox">
[StyleBox] used for the separators. See [method add_separator].
</theme_item>
diff --git a/doc/classes/PopupPanel.xml b/doc/classes/PopupPanel.xml
index b86972e8af..399e285402 100644
--- a/doc/classes/PopupPanel.xml
+++ b/doc/classes/PopupPanel.xml
@@ -8,4 +8,9 @@
</description>
<tutorials>
</tutorials>
+ <theme_items>
+ <theme_item name="panel" data_type="style" type="StyleBox">
+ [StyleBox] for the the background panel.
+ </theme_item>
+ </theme_items>
</class>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index a5aeee5bc4..1daa1b04e4 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -488,6 +488,9 @@
<member name="debug/gdscript/warnings/enable" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables specific GDScript warnings (see [code]debug/gdscript/warnings/*[/code] settings). If [code]false[/code], disables all GDScript warnings.
</member>
+ <member name="debug/gdscript/warnings/enum_variable_without_default" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a variable has an enum type but no explicit default value, but only if the enum does not contain [code]0[/code] as a valid value.
+ </member>
<member name="debug/gdscript/warnings/exclude_addons" type="bool" setter="" getter="" default="true">
If [code]true[/code], scripts in the [code]res://addons[/code] folder will not generate warnings.
</member>
@@ -637,6 +640,9 @@
<member name="debug/shader_language/warnings/formatting_error" type="bool" setter="" getter="" default="true">
When set to [code]true[/code], produces a warning upon encountering certain formatting errors. Currently this only checks for empty statements. More formatting errors may be added over time.
</member>
+ <member name="debug/shader_language/warnings/magic_position_write" type="bool" setter="" getter="" default="true">
+ When set to [code]true[/code], produces a warning when the shader contains [code]POSITION = vec4(vertex,[/code] as this was very common code written in Godot 4.2 and earlier that was paired with a QuadMesh to produce a full screen post processes pass. With the switch to reversed z in 4.3, this trick no longer works, as it implicitly relied on the [code]VERTEX.z[/code] being 0.
+ </member>
<member name="debug/shader_language/warnings/treat_warnings_as_errors" type="bool" setter="" getter="" default="false">
When set to [code]true[/code], warnings are treated as errors.
</member>
@@ -1436,6 +1442,10 @@
<member name="internationalization/rendering/force_right_to_left_layout_direction" type="bool" setter="" getter="" default="false">
Force layout direction and text writing direction to RTL for all controls.
</member>
+ <member name="internationalization/rendering/root_node_auto_translate" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], root node will use [constant Node.AUTO_TRANSLATE_MODE_ALWAYS], otherwise [constant Node.AUTO_TRANSLATE_MODE_DISABLED] will be used.
+ [b]Note:[/b] This property is only read when the project starts. To change the auto translate mode at runtime, set [member Node.auto_translate_mode] of [member SceneTree.root] instead.
+ </member>
<member name="internationalization/rendering/root_node_layout_direction" type="int" setter="" getter="" default="0">
Root node default layout direction.
</member>
diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml
index 2c88ecd675..43ee4dda60 100644
--- a/doc/classes/ScriptEditor.xml
+++ b/doc/classes/ScriptEditor.xml
@@ -34,6 +34,35 @@
Returns an array with all [Script] objects which are currently open in editor.
</description>
</method>
+ <method name="goto_help">
+ <return type="void" />
+ <param index="0" name="topic" type="String" />
+ <description>
+ Opens help for the given topic. The [param topic] is an encoded string that controls which class, method, constant, signal, annotation, property, or theme item should be focused.
+ The supported [param topic] formats include [code]class_name:class[/code], [code]class_method:class:method[/code], [code]class_constant:class:constant[/code], [code]class_signal:class:signal[/code], [code]class_annotation:class:@annotation[/code], [code]class_property:class:property[/code], and [code]class_theme_item:class:item[/code], where [code]class[/code] is the class name, [code]method[/code] is the method name, [code]constant[/code] is the constant name, [code]signal[/code] is the signal name, [code]annotation[/code] is the annotation name, [code]property[/code] is the property name, and [code]item[/code] is the theme item.
+ [b]Examples:[/b]
+ [codeblock]
+ # Shows help for the Node class.
+ class_name:Node
+ # Shows help for the global min function.
+ # Global objects are accessible in the `@GlobalScope` namespace, shown here.
+ class_method:@GlobalScope:min
+ # Shows help for get_viewport in the Node class.
+ class_method:Node:get_viewport
+ # Shows help for the Input constant MOUSE_BUTTON_MIDDLE.
+ class_constant:Input:MOUSE_BUTTON_MIDDLE
+ # Shows help for the BaseButton signal pressed.
+ class_signal:BaseButton:pressed
+ # Shows help for the CanvasItem property visible.
+ class_property:CanvasItem:visible
+ # Shows help for the GDScript annotation export.
+ # Annotations should be prefixed with the `@` symbol in the descriptor, as shown here.
+ class_annotation:@GDScript:@export
+ # Shows help for the GraphNode theme item named panel_selected.
+ class_theme_item:GraphNode:panel_selected
+ [/codeblock]
+ </description>
+ </method>
<method name="goto_line">
<return type="void" />
<param index="0" name="line_number" type="int" />
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index caa3097a6b..14d9c568d7 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -252,7 +252,7 @@
<param index="1" name="pose" type="Transform3D" />
<description>
Sets the global pose transform, [param pose], for the bone at [param bone_idx].
- [b]Note:[/b] If other bone poses have been changed, this method executes an update process and will cause performance to deteriorate. If you know that multiple global poses will be applied, consider using [method set_bone_pose] with precalculation.
+ [b]Note:[/b] If other bone poses have been changed, this method executes a dirty poses recalculation and will cause performance to deteriorate. If you know that multiple global poses will be applied, consider using [method set_bone_pose] with precalculation.
</description>
</method>
<method name="set_bone_global_pose_override" deprecated="">
@@ -355,15 +355,10 @@
<description>
</description>
</signal>
- <signal name="bone_pose_changed">
- <param index="0" name="bone_idx" type="int" />
- <description>
- Emitted when the bone at [param bone_idx] changes its transform/pose. This can be used to update other nodes that rely on bone positions.
- </description>
- </signal>
<signal name="pose_updated">
<description>
- Emitted when the pose is updated, after [constant NOTIFICATION_UPDATE_SKELETON] is received.
+ Emitted when the pose is updated.
+ [b]Note:[/b] During the update process, this signal is not fired, so modification by [SkeletonModifier3D] is not detected.
</description>
</signal>
<signal name="show_rest_only_changed">
@@ -371,11 +366,16 @@
Emitted when the value of [member show_rest_only] changes.
</description>
</signal>
+ <signal name="skeleton_updated">
+ <description>
+ Emitted when the final pose has been calculated will be applied to the skin in the update process.
+ This means that all [SkeletonModifier3D] processing is complete. In order to detect the completion of the processing of each [SkeletonModifier3D], use [signal SkeletonModifier3D.modification_processed].
+ </description>
+ </signal>
</signals>
<constants>
<constant name="NOTIFICATION_UPDATE_SKELETON" value="50">
- Notification received when this skeleton's pose needs to be updated.
- This notification is received [i]before[/i] the related [signal pose_updated] signal.
+ Notification received when this skeleton's pose needs to be updated. In that case, this is called only once per frame in a deferred process.
</constant>
<constant name="MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS" value="0" enum="ModifierCallbackModeProcess">
Set a flag to process modification during physics frames (see [constant Node.NOTIFICATION_INTERNAL_PHYSICS_PROCESS]).
diff --git a/drivers/egl/egl_manager.cpp b/drivers/egl/egl_manager.cpp
index 10c2119260..4eddfd027b 100644
--- a/drivers/egl/egl_manager.cpp
+++ b/drivers/egl/egl_manager.cpp
@@ -260,21 +260,6 @@ void EGLManager::release_current() {
eglMakeCurrent(current_display.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
-void EGLManager::make_current() {
- if (!current_window) {
- return;
- }
-
- if (!current_window->initialized) {
- WARN_PRINT("Current OpenGL window is uninitialized!");
- return;
- }
-
- GLDisplay &current_display = displays[current_window->gldisplay_id];
-
- eglMakeCurrent(current_display.egl_display, current_window->egl_surface, current_window->egl_surface, current_display.egl_context);
-}
-
void EGLManager::swap_buffers() {
if (!current_window) {
return;
diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h
index 61d3289b2d..83779349f0 100644
--- a/drivers/egl/egl_manager.h
+++ b/drivers/egl/egl_manager.h
@@ -98,7 +98,6 @@ public:
void window_destroy(DisplayServer::WindowID p_window_id);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index ecb563214c..bc1af86938 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -3112,39 +3112,47 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (pass == 0) {
spec_constants |= SceneShaderGLES3::BASE_PASS;
- if (inst->omni_light_gl_cache.size() == 0) {
- spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
- }
- if (inst->spot_light_gl_cache.size() == 0) {
+ if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
- }
-
- if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
- }
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
+ } else {
+ if (inst->omni_light_gl_cache.size() == 0) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
+ }
- if (inst->reflection_probe_rid_cache.size() == 0) {
- // We don't have any probes.
- spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
- } else if (inst->reflection_probe_rid_cache.size() > 1) {
- // We have a second probe.
- spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
- }
+ if (inst->spot_light_gl_cache.size() == 0) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
+ }
- if (inst->lightmap_instance.is_valid()) {
- spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
+ if (p_render_data->directional_light_count == p_render_data->directional_shadow_count) {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
+ }
- GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
- GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
+ if (inst->reflection_probe_rid_cache.size() == 0) {
+ // We don't have any probes.
+ spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
+ } else if (inst->reflection_probe_rid_cache.size() > 1) {
+ // We have a second probe.
+ spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
+ }
- if (lm->uses_spherical_harmonics) {
- spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
+ if (inst->lightmap_instance.is_valid()) {
+ spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
+
+ GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
+ GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);
+
+ if (lm->uses_spherical_harmonics) {
+ spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
+ }
+ } else if (inst->lightmap_sh) {
+ spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
+ } else {
+ spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
- } else if (inst->lightmap_sh) {
- spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
- } else {
- spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
} else {
// Only base pass uses the radiance map.
diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp
index 09878aaee4..fee90599e0 100644
--- a/drivers/gles3/storage/particles_storage.cpp
+++ b/drivers/gles3/storage/particles_storage.cpp
@@ -135,7 +135,6 @@ bool ParticlesStorage::particles_get_emitting(RID p_particles) {
return false;
}
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
@@ -380,10 +379,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) {
}
AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care.");
- }
-
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, AABB());
@@ -1207,7 +1202,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const {
}
bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return !particles->emitting && particles->inactive;
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 46efb45934..2a85a81b91 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -419,7 +419,7 @@ Error DirAccessUnix::remove(String p_path) {
return FAILED;
}
- if (S_ISDIR(flags.st_mode)) {
+ if (S_ISDIR(flags.st_mode) && !is_link(p_path)) {
return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
} else {
return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
@@ -435,7 +435,7 @@ bool DirAccessUnix::is_link(String p_file) {
struct stat flags = {};
if ((lstat(p_file.utf8().get_data(), &flags) != 0)) {
- return FAILED;
+ return false;
}
return S_ISLNK(flags.st_mode);
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index ffeb5ad752..1a8cd53486 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -168,11 +168,13 @@ void OS_Unix::initialize_core() {
NetSocketPosix::make_default();
IPUnix::make_default();
+ process_map = memnew((HashMap<ProcessID, ProcessInfo>));
_setup_clock();
}
void OS_Unix::finalize_core() {
+ memdelete(process_map);
NetSocketPosix::cleanup();
}
@@ -582,6 +584,11 @@ Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &
err_pipe.instantiate();
err_pipe->open_existing(pipe_err[0], 0);
+ ProcessInfo pi;
+ process_map_mutex.lock();
+ process_map->insert(pid, pi);
+ process_map_mutex.unlock();
+
ret["stdio"] = main_pipe;
ret["stderr"] = err_pipe;
ret["pid"] = pid;
@@ -698,6 +705,11 @@ Error OS_Unix::create_process(const String &p_path, const List<String> &p_argume
raise(SIGKILL);
}
+ ProcessInfo pi;
+ process_map_mutex.lock();
+ process_map->insert(pid, pi);
+ process_map_mutex.unlock();
+
if (r_child_id) {
*r_child_id = pid;
}
@@ -720,14 +732,45 @@ int OS_Unix::get_process_id() const {
}
bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ const ProcessInfo *pi = process_map->getptr(p_pid);
+
+ if (pi && !pi->is_running) {
+ return false;
+ }
+
int status = 0;
if (waitpid(p_pid, &status, WNOHANG) != 0) {
+ if (pi) {
+ pi->is_running = false;
+ pi->exit_code = status;
+ }
return false;
}
return true;
}
+int OS_Unix::get_process_exit_code(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ const ProcessInfo *pi = process_map->getptr(p_pid);
+
+ if (pi && !pi->is_running) {
+ return pi->exit_code;
+ }
+
+ int status = 0;
+ if (waitpid(p_pid, &status, WNOHANG) != 0) {
+ status = WIFEXITED(status) ? WEXITSTATUS(status) : status;
+ if (pi) {
+ pi->is_running = false;
+ pi->exit_code = status;
+ }
+ return status;
+ }
+ return -1;
+}
+
String OS_Unix::get_locale() const {
if (!has_environment("LANG")) {
return "en";
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 2936c28797..a107e7a0e3 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -37,6 +37,13 @@
#include "drivers/unix/ip_unix.h"
class OS_Unix : public OS {
+ struct ProcessInfo {
+ mutable bool is_running = true;
+ mutable int exit_code = -1;
+ };
+ HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
+ Mutex process_map_mutex;
+
protected:
// UNIX only handles the core functions.
// inheriting platforms under unix (eg. X11) should handle the rest
@@ -81,6 +88,7 @@ public:
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
virtual bool is_process_running(const ProcessID &p_pid) const override;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index bfad38d341..779080786a 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -196,7 +196,7 @@ bool FindReplaceBar::_search(uint32_t p_flags, int p_from_line, int p_from_col)
text_editor->set_search_flags(p_flags);
}
- _update_matches_label();
+ _update_matches_display();
return pos.x != -1;
}
@@ -434,7 +434,7 @@ void FindReplaceBar::_update_results_count() {
}
}
-void FindReplaceBar::_update_matches_label() {
+void FindReplaceBar::_update_matches_display() {
if (search_text->get_text().is_empty() || results_count == -1) {
matches_label->hide();
} else {
@@ -450,6 +450,10 @@ void FindReplaceBar::_update_matches_label() {
matches_label->set_text(vformat(TTRN("%d of %d match", "%d of %d matches", results_count), results_count_to_current, results_count));
}
}
+ find_prev->set_disabled(results_count < 1);
+ find_next->set_disabled(results_count < 1);
+ replace->set_disabled(search_text->get_text().is_empty());
+ replace_all->set_disabled(search_text->get_text().is_empty());
}
bool FindReplaceBar::search_current() {
@@ -517,13 +521,16 @@ void FindReplaceBar::_hide_bar(bool p_force_focus) {
hide();
}
-void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
+void FindReplaceBar::_show_search(bool p_with_replace, bool p_show_only) {
show();
if (p_show_only) {
return;
}
- if (p_focus_replace) {
+ const bool on_one_line = text_editor->has_selection(0) && text_editor->get_selection_from_line(0) == text_editor->get_selection_to_line(0);
+ const bool focus_replace = p_with_replace && on_one_line;
+
+ if (focus_replace) {
search_text->deselect();
callable_mp((Control *)replace_text, &Control::grab_focus).call_deferred();
} else {
@@ -531,14 +538,14 @@ void FindReplaceBar::_show_search(bool p_focus_replace, bool p_show_only) {
callable_mp((Control *)search_text, &Control::grab_focus).call_deferred();
}
- if (text_editor->has_selection(0) && !is_selection_only()) {
+ if (on_one_line) {
search_text->set_text(text_editor->get_selected_text(0));
result_line = text_editor->get_selection_from_line();
result_col = text_editor->get_selection_from_column();
}
if (!get_search_text().is_empty()) {
- if (p_focus_replace) {
+ if (focus_replace) {
replace_text->select_all();
replace_text->set_caret_column(replace_text->get_text().length());
} else {
@@ -568,9 +575,9 @@ void FindReplaceBar::popup_replace() {
hbc_option_replace->show();
}
- selection_only->set_pressed((text_editor->has_selection(0) && text_editor->get_selection_from_line(0) < text_editor->get_selection_to_line(0)));
+ selection_only->set_pressed(text_editor->has_selection(0) && text_editor->get_selection_from_line(0) < text_editor->get_selection_to_line(0));
- _show_search(is_visible() || text_editor->has_selection(0));
+ _show_search(true, false);
}
void FindReplaceBar::_search_options_changed(bool p_pressed) {
@@ -666,7 +673,7 @@ void FindReplaceBar::set_text_edit(CodeTextEditor *p_text_editor) {
text_editor->connect("text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
_update_results_count();
- _update_matches_label();
+ _update_matches_display();
}
void FindReplaceBar::_bind_methods() {
@@ -700,6 +707,8 @@ FindReplaceBar::FindReplaceBar() {
// Search toolbar
search_text = memnew(LineEdit);
vbc_lineedit->add_child(search_text);
+ search_text->set_placeholder(TTR("Find"));
+ search_text->set_tooltip_text(TTR("Find"));
search_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
search_text->connect("text_changed", callable_mp(this, &FindReplaceBar::_search_text_changed));
search_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_search_text_submitted));
@@ -711,12 +720,14 @@ FindReplaceBar::FindReplaceBar() {
find_prev = memnew(Button);
find_prev->set_flat(true);
+ find_prev->set_tooltip_text(TTR("Previous Match"));
hbc_button_search->add_child(find_prev);
find_prev->set_focus_mode(FOCUS_NONE);
find_prev->connect("pressed", callable_mp(this, &FindReplaceBar::search_prev));
find_next = memnew(Button);
find_next->set_flat(true);
+ find_next->set_tooltip_text(TTR("Next Match"));
hbc_button_search->add_child(find_next);
find_next->set_focus_mode(FOCUS_NONE);
find_next->connect("pressed", callable_mp(this, &FindReplaceBar::search_next));
@@ -736,6 +747,8 @@ FindReplaceBar::FindReplaceBar() {
// Replace toolbar
replace_text = memnew(LineEdit);
vbc_lineedit->add_child(replace_text);
+ replace_text->set_placeholder(TTR("Replace"));
+ replace_text->set_tooltip_text(TTR("Replace"));
replace_text->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
replace_text->connect("text_submitted", callable_mp(this, &FindReplaceBar::_replace_text_submitted));
replace_text->connect("focus_exited", callable_mp(this, &FindReplaceBar::_focus_lost));
@@ -758,6 +771,7 @@ FindReplaceBar::FindReplaceBar() {
hide_button = memnew(TextureButton);
add_child(hide_button);
+ hide_button->set_tooltip_text(TTR("Hide"));
hide_button->set_focus_mode(FOCUS_NONE);
hide_button->connect("pressed", callable_mp(this, &FindReplaceBar::_hide_bar).bind(false));
hide_button->set_v_size_flags(SIZE_SHRINK_CENTER);
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 87031b672c..64b13b9006 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -96,9 +96,9 @@ class FindReplaceBar : public HBoxContainer {
void _get_search_from(int &r_line, int &r_col, bool p_is_searching_next = false);
void _update_results_count();
- void _update_matches_label();
+ void _update_matches_display();
- void _show_search(bool p_focus_replace = false, bool p_show_only = false);
+ void _show_search(bool p_with_replace, bool p_show_only);
void _hide_bar(bool p_force_focus = false);
void _editor_text_changed();
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 4c66068953..f7914d3aaa 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -516,7 +516,14 @@ void CreateDialog::select_type(const String &p_type, bool p_center_on_item) {
favorite->set_disabled(false);
favorite->set_pressed(favorite_list.has(p_type));
- get_ok_button()->set_disabled(false);
+
+ if (to_select->get_meta("__instantiable", true)) {
+ get_ok_button()->set_disabled(false);
+ get_ok_button()->set_tooltip_text(String());
+ } else {
+ get_ok_button()->set_disabled(true);
+ get_ok_button()->set_tooltip_text(TTR("The selected class can't be instantiated."));
+ }
}
void CreateDialog::select_base() {
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 51e1c27070..14190a43a5 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1664,8 +1664,13 @@ void EditorFileSystem::_queue_update_script_class(const String &p_path) {
}
void EditorFileSystem::_update_scene_groups() {
- update_scene_mutex.lock();
+ EditorProgress *ep = nullptr;
+ if (update_scene_paths.size() > 1) {
+ ep = memnew(EditorProgress("update_scene_groups", TTR("Update Scene Groups"), update_scene_paths.size()));
+ }
+ int step_count = 0;
+ update_scene_mutex.lock();
for (const String &path : update_scene_paths) {
ProjectSettings::get_singleton()->remove_scene_groups_cache(path);
@@ -1681,8 +1686,13 @@ void EditorFileSystem::_update_scene_groups() {
if (!scene_groups.is_empty()) {
ProjectSettings::get_singleton()->add_scene_groups_cache(path, scene_groups);
}
+
+ if (ep) {
+ ep->step(TTR("Updating Scene Groups..."), step_count++);
+ }
}
+ memdelete_notnull(ep);
update_scene_paths.clear();
update_scene_mutex.unlock();
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 7fded0b807..1f7505633b 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -2324,7 +2324,8 @@ void EditorHelp::_help_callback(const String &p_topic) {
}
if (class_desc->is_ready()) {
- callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).call_deferred(line);
+ // call_deferred() is not enough.
+ class_desc->connect("draw", callable_mp(class_desc, &RichTextLabel::scroll_to_paragraph).bind(line), CONNECT_ONE_SHOT | CONNECT_DEFERRED);
} else {
scroll_to = line;
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 7919d61f26..aed1462eb6 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -3220,12 +3220,13 @@ void EditorInspector::update_tree() {
}
// Search for the doc path in the cache.
- HashMap<StringName, HashMap<StringName, String>>::Iterator E = doc_path_cache.find(classname);
+ HashMap<StringName, HashMap<StringName, DocCacheInfo>>::Iterator E = doc_cache.find(classname);
if (E) {
- HashMap<StringName, String>::Iterator F = E->value.find(propname);
+ HashMap<StringName, DocCacheInfo>::Iterator F = E->value.find(propname);
if (F) {
found = true;
- doc_path = F->value;
+ doc_path = F->value.doc_path;
+ theme_item_name = F->value.theme_item_name;
}
}
@@ -3246,23 +3247,22 @@ void EditorInspector::update_tree() {
theme_item_name = F->value.theme_properties[i].name;
}
}
-
- if (is_native_class) {
- doc_path_cache[classname][propname] = doc_path;
- }
} else {
for (int i = 0; i < F->value.properties.size(); i++) {
String doc_path_current = "class_property:" + F->value.name + ":" + F->value.properties[i].name;
if (F->value.properties[i].name == propname.operator String()) {
doc_path = doc_path_current;
}
-
- if (is_native_class) {
- doc_path_cache[classname][propname] = doc_path;
- }
}
}
+ if (is_native_class) {
+ DocCacheInfo cache_info;
+ cache_info.doc_path = doc_path;
+ cache_info.theme_item_name = theme_item_name;
+ doc_cache[classname][propname] = cache_info;
+ }
+
if (!doc_path.is_empty() || F->value.inherits.is_empty()) {
break;
}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 8c55950a2b..eff4f9caa5 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -505,7 +505,12 @@ class EditorInspector : public ScrollContainer {
int property_focusable;
int update_scroll_request;
- HashMap<StringName, HashMap<StringName, String>> doc_path_cache;
+ struct DocCacheInfo {
+ String doc_path;
+ String theme_item_name;
+ };
+
+ HashMap<StringName, HashMap<StringName, DocCacheInfo>> doc_cache;
HashSet<StringName> restart_request_props;
HashMap<String, String> custom_property_descriptions;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 4fb1a86ce2..5b24fb1559 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -771,6 +771,12 @@ void EditorNode::_notification(int p_what) {
EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int());
}
+ if (EDITOR_GET("interface/editor/import_resources_when_unfocused")) {
+ scan_changes_timer->start();
+ } else {
+ scan_changes_timer->stop();
+ }
+
follow_system_theme = EDITOR_GET("interface/theme/follow_system_theme");
use_system_accent_color = EDITOR_GET("interface/theme/use_system_accent_color");
@@ -1445,6 +1451,9 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
case ERR_FILE_NOT_FOUND: {
show_accept(vformat(TTR("Missing file '%s' or one of its dependencies."), p_file.get_file()), TTR("OK"));
} break;
+ case ERR_FILE_UNRECOGNIZED: {
+ show_accept(vformat(TTR("File '%s' is saved in a format that is newer than the formats supported by this version of Godot, so it can't be opened."), p_file.get_file()), TTR("OK"));
+ } break;
default: {
show_accept(vformat(TTR("Error while loading file '%s'."), p_file.get_file()), TTR("OK"));
} break;
@@ -2355,7 +2364,13 @@ static bool overrides_external_editor(Object *p_object) {
void EditorNode::_add_to_history(const Object *p_object, const String &p_property, bool p_inspector_only) {
ObjectID id = p_object->get_instance_id();
- if (id != editor_history.get_current()) {
+ ObjectID history_id = editor_history.get_current();
+ if (id != history_id) {
+ const MultiNodeEdit *multi_node_edit = Object::cast_to<const MultiNodeEdit>(p_object);
+ const MultiNodeEdit *history_multi_node_edit = Object::cast_to<const MultiNodeEdit>(ObjectDB::get_instance(history_id));
+ if (multi_node_edit && history_multi_node_edit && multi_node_edit->is_same_selection(history_multi_node_edit)) {
+ return;
+ }
if (p_inspector_only) {
editor_history.add_object(id, String(), true);
} else if (p_property.is_empty()) {
@@ -2449,6 +2464,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
if (current_node->is_inside_tree()) {
NodeDock::get_singleton()->set_node(current_node);
SceneTreeDock::get_singleton()->set_selected(current_node);
+ SceneTreeDock::get_singleton()->set_selection({ current_node });
InspectorDock::get_singleton()->update(current_node);
if (!inspector_only && !skip_main_plugin) {
skip_main_plugin = stay_in_script_editor_on_node_selected && !ScriptEditor::get_singleton()->is_editor_floating() && ScriptEditor::get_singleton()->is_visible_in_tree();
@@ -2470,13 +2486,13 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
} else {
Node *selected_node = nullptr;
+ Vector<Node *> multi_nodes;
if (current_obj->is_class("MultiNodeEdit")) {
Node *scene = get_edited_scene();
if (scene) {
MultiNodeEdit *multi_node_edit = Object::cast_to<MultiNodeEdit>(current_obj);
int node_count = multi_node_edit->get_node_count();
if (node_count > 0) {
- List<Node *> multi_nodes;
for (int node_index = 0; node_index < node_count; ++node_index) {
Node *node = scene->get_node(multi_node_edit->get_node(node_index));
if (node) {
@@ -2486,7 +2502,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
if (!multi_nodes.is_empty()) {
// Pick the top-most node.
multi_nodes.sort_custom<Node::Comparator>();
- selected_node = multi_nodes.front()->get();
+ selected_node = multi_nodes[0];
}
}
}
@@ -2495,6 +2511,7 @@ void EditorNode::_edit_current(bool p_skip_foreign, bool p_skip_inspector_update
InspectorDock::get_inspector_singleton()->edit(current_obj);
NodeDock::get_singleton()->set_node(nullptr);
SceneTreeDock::get_singleton()->set_selected(selected_node);
+ SceneTreeDock::get_singleton()->set_selection(multi_nodes);
InspectorDock::get_singleton()->update(nullptr);
}
@@ -3055,8 +3072,8 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case HELP_DOCS: {
OS::get_singleton()->shell_open(VERSION_DOCS_URL "/");
} break;
- case HELP_QA: {
- OS::get_singleton()->shell_open("https://godotengine.org/qa/");
+ case HELP_FORUM: {
+ OS::get_singleton()->shell_open("https://forum.godotengine.org/");
} break;
case HELP_REPORT_A_BUG: {
OS::get_singleton()->shell_open("https://github.com/godotengine/godot/issues");
@@ -3832,6 +3849,8 @@ void EditorNode::_set_current_scene_nocheck(int p_idx) {
if (tabs_to_close.is_empty()) {
callable_mp(this, &EditorNode::_set_main_scene_state).call_deferred(state, get_edited_scene()); // Do after everything else is done setting up.
}
+
+ _update_undo_redo_allowed();
}
void EditorNode::setup_color_picker(ColorPicker *p_picker) {
@@ -3917,14 +3936,18 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
}
int prev = editor_data.get_edited_scene();
- int idx = editor_data.add_edited_scene(-1);
+ int idx = prev;
- if (!editor_data.get_edited_scene_root() && editor_data.get_edited_scene_count() == 2) {
- _remove_edited_scene();
- } else if (p_silent_change_tab) {
- _set_current_scene_nocheck(idx);
+ if (prev == -1 || editor_data.get_edited_scene_root() || !editor_data.get_scene_path(prev).is_empty()) {
+ idx = editor_data.add_edited_scene(-1);
+
+ if (p_silent_change_tab) {
+ _set_current_scene_nocheck(idx);
+ } else {
+ _set_current_scene(idx);
+ }
} else {
- _set_current_scene(idx);
+ EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_current_edited_scene_history_id());
}
dependency_errors.clear();
@@ -3941,7 +3964,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
dependency_error->show(DependencyErrorDialog::MODE_SCENE, lpath, errors);
opening_prev = false;
- if (prev != -1) {
+ if (prev != -1 && prev != idx) {
_set_current_scene(prev);
editor_data.remove_scene(idx);
}
@@ -3952,7 +3975,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
_dialog_display_load_error(lpath, err);
opening_prev = false;
- if (prev != -1) {
+ if (prev != -1 && prev != idx) {
_set_current_scene(prev);
editor_data.remove_scene(idx);
}
@@ -3988,7 +4011,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
sdata.unref();
_dialog_display_load_error(lpath, ERR_FILE_CORRUPT);
opening_prev = false;
- if (prev != -1) {
+ if (prev != -1 && prev != idx) {
_set_current_scene(prev);
editor_data.remove_scene(idx);
}
@@ -4014,10 +4037,6 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
_load_editor_plugin_states_from_config(editor_state_cf);
}
- _update_title();
- scene_tabs->update_scene_tabs();
- _add_to_recent_scenes(lpath);
-
if (editor_folding.has_folding_data(lpath)) {
editor_folding.load_scene_folding(new_scene, lpath);
} else if (EDITOR_GET("interface/inspector/auto_unfold_foreign_scenes")) {
@@ -4057,6 +4076,14 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
save_editor_layout_delayed();
}
+ if (p_set_inherited) {
+ EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(editor_data.get_current_edited_scene_history_id());
+ }
+
+ _update_title();
+ scene_tabs->update_scene_tabs();
+ _add_to_recent_scenes(lpath);
+
return OK;
}
@@ -6574,6 +6601,12 @@ EditorNode::EditorNode() {
editor_layout_save_delay_timer->set_one_shot(true);
editor_layout_save_delay_timer->connect("timeout", callable_mp(this, &EditorNode::_save_editor_layout));
+ scan_changes_timer = memnew(Timer);
+ scan_changes_timer->set_wait_time(0.5);
+ scan_changes_timer->set_autostart(EDITOR_GET("interface/editor/import_resources_when_unfocused"));
+ scan_changes_timer->connect("timeout", callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::scan_changes));
+ add_child(scan_changes_timer);
+
top_split = memnew(VSplitContainer);
center_split->add_child(top_split);
top_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -6642,6 +6675,8 @@ EditorNode::EditorNode() {
main_menu->set_menu_tooltip(0, TTR("Operations with scene files."));
accept = memnew(AcceptDialog);
+ accept->set_autowrap(true);
+ accept->set_min_size(Vector2i(600, 0));
accept->set_unparent_when_invisible(true);
save_accept = memnew(AcceptDialog);
@@ -6886,7 +6921,7 @@ EditorNode::EditorNode() {
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_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/forum", TTR("Forum")), HELP_FORUM);
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);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 96acc8bf46..ad0a7ec5e0 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -226,7 +226,7 @@ private:
HELP_SEARCH,
HELP_COMMAND_PALETTE,
HELP_DOCS,
- HELP_QA,
+ HELP_FORUM,
HELP_REPORT_A_BUG,
HELP_COPY_SYSTEM_INFO,
HELP_SUGGEST_A_FEATURE,
@@ -420,6 +420,7 @@ private:
EditorDockManager *editor_dock_manager = nullptr;
Timer *editor_layout_save_delay_timer = nullptr;
+ Timer *scan_changes_timer = nullptr;
Button *distraction_free = nullptr;
EditorBottomPanel *bottom_panel = nullptr;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 32d581a26e..452715a577 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -49,6 +49,7 @@
#include "editor/editor_paths.h"
#include "editor/editor_property_name_processor.h"
#include "editor/editor_translation.h"
+#include "editor/engine_update_label.h"
#include "scene/gui/color_picker.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
@@ -413,6 +414,14 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/editor_screen", -2, ed_screen_hints)
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/project_manager_screen", -2, ed_screen_hints)
+ {
+ EngineUpdateLabel::UpdateMode default_update_mode = EngineUpdateLabel::UpdateMode::NEWEST_UNSTABLE;
+ if (String(VERSION_STATUS) == String("stable")) {
+ default_update_mode = EngineUpdateLabel::UpdateMode::NEWEST_STABLE;
+ }
+ EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "network/connection/engine_version_update_mode", int(default_update_mode), "Disable Update Checks,Check Newest Preview,Check Newest Stable,Check Newest Patch"); // Uses EngineUpdateLabel::UpdateMode.
+ }
+
_initial_set("interface/editor/debug/enable_pseudolocalization", false);
set_restart_if_changed("interface/editor/debug/enable_pseudolocalization", true);
// Use pseudolocalization in editor.
@@ -436,7 +445,6 @@ 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")
-
_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)
@@ -462,6 +470,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// 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::BOOL, PROPERTY_HINT_NONE, "interface/editor/import_resources_when_unfocused", false, "")
+
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, "")
diff --git a/editor/engine_update_label.cpp b/editor/engine_update_label.cpp
new file mode 100644
index 0000000000..0b20738e99
--- /dev/null
+++ b/editor/engine_update_label.cpp
@@ -0,0 +1,344 @@
+/**************************************************************************/
+/* engine_update_label.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "engine_update_label.h"
+
+#include "core/os/time.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
+#include "editor/themes/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/main/http_request.h"
+
+bool EngineUpdateLabel::_can_check_updates() const {
+ return int(EDITOR_GET("network/connection/network_mode")) == EditorSettings::NETWORK_ONLINE &&
+ UpdateMode(int(EDITOR_GET("network/connection/engine_version_update_mode"))) != UpdateMode::DISABLED;
+}
+
+void EngineUpdateLabel::_check_update() {
+ checked_update = true;
+ _set_status(UpdateStatus::BUSY);
+ http->request("https://raw.githubusercontent.com/godotengine/godot-website/master/_data/versions.yml");
+}
+
+void EngineUpdateLabel::_http_request_completed(int p_result, int p_response_code, const PackedStringArray &p_headers, const PackedByteArray &p_body) {
+ if (p_result != OK) {
+ _set_status(UpdateStatus::ERROR);
+ _set_message(vformat(TTR("Failed to check for updates. Error: %d."), p_result), theme_cache.error_color);
+ return;
+ }
+
+ if (p_response_code != 200) {
+ _set_status(UpdateStatus::ERROR);
+ _set_message(vformat(TTR("Failed to check for updates. Response code: %d."), p_response_code), theme_cache.error_color);
+ return;
+ }
+
+ PackedStringArray lines;
+ {
+ String s;
+ const uint8_t *r = p_body.ptr();
+ s.parse_utf8((const char *)r, p_body.size());
+ lines = s.split("\n");
+ }
+
+ UpdateMode update_mode = UpdateMode(int(EDITOR_GET("network/connection/engine_version_update_mode")));
+ bool stable_only = update_mode == UpdateMode::NEWEST_STABLE || update_mode == UpdateMode::NEWEST_PATCH;
+
+ const Dictionary version_info = Engine::get_singleton()->get_version_info();
+ int current_major = version_info["major"];
+ int current_minor = version_info["minor"];
+ int current_patch = version_info["patch"];
+
+ int current_version_line = -1;
+ for (int i = 0; i < lines.size(); i++) {
+ const String &line = lines[i];
+ if (!line.begins_with("- name")) {
+ continue;
+ }
+
+ const String version_string = _extract_sub_string(line);
+ const PackedStringArray version_bits = version_string.split(".");
+
+ if (version_bits.size() < 2) {
+ continue;
+ }
+
+ int minor = version_bits[1].to_int();
+ if (version_bits[0].to_int() != current_major || minor < current_minor) {
+ continue;
+ }
+
+ int patch = 0;
+ if (version_bits.size() >= 3) {
+ patch = version_bits[2].to_int();
+ }
+
+ if (minor == current_minor && patch < current_patch) {
+ continue;
+ }
+
+ if (update_mode == UpdateMode::NEWEST_PATCH && minor > current_minor) {
+ continue;
+ }
+
+ if (minor > current_minor || patch > current_patch) {
+ String version_type = _extract_sub_string(lines[i + 1]);
+ if (stable_only && _get_version_type(version_type, nullptr) != VersionType::STABLE) {
+ continue;
+ }
+
+ found_version = version_string;
+ found_version += "-" + version_type;
+ break;
+ } else if (minor == current_minor && patch == current_patch) {
+ current_version_line = i;
+ found_version = version_string;
+ break;
+ }
+ }
+
+ if (current_version_line == -1 && !found_version.is_empty()) {
+ _set_status(UpdateStatus::UPDATE_AVAILABLE);
+ _set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
+ return;
+ } else if (current_version_line == -1 || stable_only) {
+ _set_status(UpdateStatus::UP_TO_DATE);
+ return;
+ }
+
+ int current_version_index;
+ VersionType current_version_type = _get_version_type(version_info["status"], &current_version_index);
+
+ for (int i = current_version_line + 1; i < lines.size(); i++) {
+ const String &line = lines[i];
+ if (line.begins_with("- name")) {
+ break;
+ }
+
+ if (!line.begins_with(" - name") && !line.begins_with(" flavor")) {
+ continue;
+ }
+
+ const String version_string = _extract_sub_string(line);
+ int version_index;
+ VersionType version_type = _get_version_type(version_string, &version_index);
+
+ if (int(version_type) < int(current_version_type) || version_index > current_version_index) {
+ found_version += "-" + version_string;
+
+ _set_status(UpdateStatus::UPDATE_AVAILABLE);
+ _set_message(vformat(TTR("Update available: %s."), found_version), theme_cache.update_color);
+ return;
+ }
+ }
+
+ if (current_version_index == DEV_VERSION) {
+ // Since version index can't be determined and no strictly newer version exists, display a different status.
+ _set_status(UpdateStatus::DEV);
+ } else {
+ _set_status(UpdateStatus::UP_TO_DATE);
+ }
+}
+
+void EngineUpdateLabel::_set_message(const String &p_message, const Color &p_color) {
+ if (is_disabled()) {
+ add_theme_color_override("font_disabled_color", p_color);
+ } else {
+ add_theme_color_override("font_color", p_color);
+ }
+ set_text(p_message);
+}
+
+void EngineUpdateLabel::_set_status(UpdateStatus p_status) {
+ status = p_status;
+ if (compact_mode) {
+ if (status != UpdateStatus::BUSY && status != UpdateStatus::UPDATE_AVAILABLE) {
+ hide();
+ return;
+ } else {
+ show();
+ }
+ }
+
+ switch (status) {
+ case UpdateStatus::DEV: {
+ set_disabled(true);
+ _set_message(TTR("Running a development build."), theme_cache.disabled_color);
+ set_tooltip_text(TTR("Exact version can't be determined for update checking."));
+ break;
+ }
+ case UpdateStatus::OFFLINE: {
+ set_disabled(false);
+ if (int(EDITOR_GET("network/connection/network_mode")) == EditorSettings::NETWORK_OFFLINE) {
+ _set_message(TTR("Offline mode, update checks disabled."), theme_cache.disabled_color);
+ } else {
+ _set_message(TTR("Update checks disabled."), theme_cache.disabled_color);
+ }
+ set_tooltip_text("");
+ break;
+ }
+ case UpdateStatus::BUSY: {
+ set_disabled(true);
+ _set_message(TTR("Checking for updates..."), theme_cache.default_color);
+ set_tooltip_text("");
+ } break;
+
+ case UpdateStatus::ERROR: {
+ set_disabled(false);
+ set_tooltip_text(TTR("An error has occurred. Click to try again."));
+ } break;
+
+ case UpdateStatus::UP_TO_DATE: {
+ set_disabled(false);
+ _set_message(TTR("Current version up to date."), theme_cache.disabled_color);
+ set_tooltip_text(TTR("Click to check again."));
+ } break;
+
+ case UpdateStatus::UPDATE_AVAILABLE: {
+ set_disabled(false);
+ set_tooltip_text(TTR("Click to open download page."));
+ } break;
+
+ default: {
+ }
+ }
+}
+
+EngineUpdateLabel::VersionType EngineUpdateLabel::_get_version_type(const String &p_string, int *r_index) const {
+ VersionType type = VersionType::UNKNOWN;
+ String index_string;
+
+ static HashMap<String, VersionType> type_map;
+ if (type_map.is_empty()) {
+ type_map["stable"] = VersionType::STABLE;
+ type_map["rc"] = VersionType::RC;
+ type_map["beta"] = VersionType::BETA;
+ type_map["alpha"] = VersionType::ALPHA;
+ type_map["dev"] = VersionType::DEV;
+ }
+
+ for (const KeyValue<String, VersionType> &kv : type_map) {
+ if (p_string.begins_with(kv.key)) {
+ index_string = p_string.trim_prefix(kv.key);
+ type = kv.value;
+ break;
+ }
+ }
+
+ if (r_index) {
+ if (index_string.is_empty()) {
+ *r_index = DEV_VERSION;
+ } else {
+ *r_index = index_string.to_int();
+ }
+ }
+ return type;
+}
+
+String EngineUpdateLabel::_extract_sub_string(const String &p_line) const {
+ int j = p_line.find("\"") + 1;
+ return p_line.substr(j, p_line.find("\"", j) - j);
+}
+
+void EngineUpdateLabel::_notification(int p_what) {
+ switch (p_what) {
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("network/connection")) {
+ break;
+ }
+
+ if (_can_check_updates()) {
+ if (!checked_update) {
+ _check_update();
+ } else {
+ // This will be wrong when user toggles online mode twice when update is available, but it's not worth handling.
+ _set_status(UpdateStatus::UP_TO_DATE);
+ }
+ } else {
+ _set_status(UpdateStatus::OFFLINE);
+ }
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ theme_cache.default_color = get_theme_color("font_color", "Button");
+ theme_cache.disabled_color = get_theme_color("font_disabled_color", "Button");
+ theme_cache.error_color = get_theme_color("error_color", EditorStringName(Editor));
+ theme_cache.update_color = get_theme_color("warning_color", EditorStringName(Editor));
+ } break;
+
+ case NOTIFICATION_READY: {
+ if (_can_check_updates()) {
+ _check_update();
+ } else {
+ _set_status(UpdateStatus::OFFLINE);
+ }
+ } break;
+ }
+}
+
+void EngineUpdateLabel::_bind_methods() {
+ ADD_SIGNAL(MethodInfo("offline_clicked"));
+}
+
+void EngineUpdateLabel::pressed() {
+ switch (status) {
+ case UpdateStatus::OFFLINE: {
+ emit_signal("offline_clicked");
+ } break;
+
+ case UpdateStatus::ERROR:
+ case UpdateStatus::UP_TO_DATE: {
+ _check_update();
+ } break;
+
+ case UpdateStatus::UPDATE_AVAILABLE: {
+ OS::get_singleton()->shell_open("https://godotengine.org/download/archive/" + found_version);
+ } break;
+
+ default: {
+ }
+ }
+}
+
+void EngineUpdateLabel::enable_compact_mode() {
+ compact_mode = true;
+}
+
+EngineUpdateLabel::EngineUpdateLabel() {
+ set_underline_mode(UNDERLINE_MODE_ON_HOVER);
+
+ http = memnew(HTTPRequest);
+ http->set_https_proxy(EDITOR_GET("network/http_proxy/host"), EDITOR_GET("network/http_proxy/port"));
+ http->set_timeout(10.0);
+ add_child(http);
+ http->connect("request_completed", callable_mp(this, &EngineUpdateLabel::_http_request_completed));
+}
diff --git a/editor/engine_update_label.h b/editor/engine_update_label.h
new file mode 100644
index 0000000000..d00fe53e1e
--- /dev/null
+++ b/editor/engine_update_label.h
@@ -0,0 +1,107 @@
+/**************************************************************************/
+/* engine_update_label.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef ENGINE_UPDATE_LABEL_H
+#define ENGINE_UPDATE_LABEL_H
+
+#include "scene/gui/link_button.h"
+
+class HTTPRequest;
+
+class EngineUpdateLabel : public LinkButton {
+ GDCLASS(EngineUpdateLabel, LinkButton);
+
+public:
+ enum class UpdateMode {
+ DISABLED,
+ NEWEST_UNSTABLE,
+ NEWEST_STABLE,
+ NEWEST_PATCH,
+ };
+
+private:
+ static constexpr int DEV_VERSION = 9999; // Version index for unnumbered builds (assumed to always be newest).
+
+ enum class VersionType {
+ STABLE,
+ RC,
+ BETA,
+ ALPHA,
+ DEV,
+ UNKNOWN,
+ };
+
+ enum class UpdateStatus {
+ NONE,
+ DEV,
+ OFFLINE,
+ BUSY,
+ ERROR,
+ UPDATE_AVAILABLE,
+ UP_TO_DATE,
+ };
+
+ struct ThemeCache {
+ Color default_color;
+ Color disabled_color;
+ Color error_color;
+ Color update_color;
+ } theme_cache;
+
+ HTTPRequest *http = nullptr;
+ bool compact_mode = false;
+
+ UpdateStatus status = UpdateStatus::NONE;
+ bool checked_update = false;
+ String found_version;
+
+ bool _can_check_updates() const;
+ void _check_update();
+ void _http_request_completed(int p_result, int p_response_code, const PackedStringArray &p_headers, const PackedByteArray &p_body);
+
+ void _set_message(const String &p_message, const Color &p_color);
+ void _set_status(UpdateStatus p_status);
+
+ VersionType _get_version_type(const String &p_string, int *r_index) const;
+ String _extract_sub_string(const String &p_line) const;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ virtual void pressed() override;
+
+public:
+ void enable_compact_mode();
+
+ EngineUpdateLabel();
+};
+
+#endif // ENGINE_UPDATE_LABEL_H
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 1be9fe5740..0f02ab5a7a 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -364,6 +364,7 @@ void EditorExport::update_export_presets() {
if (platform_options.has(preset->get_platform()->get_name())) {
export_presets_updated = true;
+ bool update_value_overrides = false;
List<EditorExportPlatform::ExportOption> options = platform_options[preset->get_platform()->get_name()];
// Clear the preset properties prior to reloading, keep the values to preserve options from plugins that may be currently disabled.
@@ -377,6 +378,13 @@ void EditorExport::update_export_presets() {
preset->values[option_name] = E.default_value;
}
preset->update_visibility[option_name] = E.update_visibility;
+ if (E.update_visibility) {
+ update_value_overrides = true;
+ }
+ }
+
+ if (update_value_overrides) {
+ preset->update_value_overrides();
}
}
}
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 3b6ce8d396..2ed547a970 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -3693,6 +3693,10 @@ void FileSystemDock::_feature_profile_changed() {
_update_display_mode(true);
}
+void FileSystemDock::_project_settings_changed() {
+ assigned_folder_colors = ProjectSettings::get_singleton()->get_setting("file_customization/folder_colors");
+}
+
void FileSystemDock::set_file_sort(FileSortOption p_file_sort) {
for (int i = 0; i != FILE_SORT_MAX; i++) {
tree_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort));
@@ -4122,6 +4126,7 @@ FileSystemDock::FileSystemDock() {
old_display_mode = DISPLAY_MODE_TREE_ONLY;
file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS;
+ ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &FileSystemDock::_project_settings_changed));
add_resource_tooltip_plugin(memnew(EditorTextureTooltipPlugin));
}
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index c9fe3a8baf..8830f31d2d 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -362,6 +362,7 @@ private:
bool _is_file_type_disabled_by_feature_profile(const StringName &p_class);
void _feature_profile_changed();
+ void _project_settings_changed();
static Vector<String> _remove_self_included_paths(Vector<String> selected_strings);
void _change_bottom_dock_placement();
diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp
index 1c95b546f4..b575910976 100644
--- a/editor/gui/editor_bottom_panel.cpp
+++ b/editor/gui/editor_bottom_panel.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
+#include "editor/engine_update_label.h"
#include "editor/gui/editor_toaster.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 25c8610ff4..a7cdb65145 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -968,8 +968,10 @@ void EditorFileDialog::update_file_list() {
}
} else if (!dir_access->current_is_hidden()) {
String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item;
- if (dir_access->current_is_dir() && (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path))) {
- dirs.push_back(item);
+ if (dir_access->current_is_dir()) {
+ if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) {
+ dirs.push_back(item);
+ }
} else {
files.push_back(item);
}
diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp
index b6cb3d7371..5d1e68f008 100644
--- a/editor/gui/editor_scene_tabs.cpp
+++ b/editor/gui/editor_scene_tabs.cpp
@@ -135,6 +135,17 @@ void EditorSceneTabs::_scene_tab_input(const Ref<InputEvent> &p_input) {
}
}
+void EditorSceneTabs::unhandled_key_input(const Ref<InputEvent> &p_event) {
+ if (!tab_preview_panel->is_visible()) {
+ return;
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
+ tab_preview_panel->hide();
+ }
+}
+
void EditorSceneTabs::_reposition_active_tab(int p_to_index) {
EditorNode::get_editor_data().move_edited_scene_to_index(p_to_index);
update_scene_tabs();
@@ -369,6 +380,7 @@ EditorSceneTabs::EditorSceneTabs() {
singleton = this;
set_process_shortcut_input(true);
+ set_process_unhandled_key_input(true);
tabbar_panel = memnew(PanelContainer);
add_child(tabbar_panel);
diff --git a/editor/gui/editor_scene_tabs.h b/editor/gui/editor_scene_tabs.h
index 770114835a..ac9e6b8c43 100644
--- a/editor/gui/editor_scene_tabs.h
+++ b/editor/gui/editor_scene_tabs.h
@@ -79,6 +79,7 @@ class EditorSceneTabs : public MarginContainer {
protected:
void _notification(int p_what);
+ virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
static void _bind_methods();
public:
diff --git a/editor/import/3d/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp
index 58aa6a462d..4b91b1431a 100644
--- a/editor/import/3d/editor_import_collada.cpp
+++ b/editor/import/3d/editor_import_collada.cpp
@@ -996,7 +996,16 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p
surftool->generate_tangents();
}
- if (p_mesh->get_blend_shape_count() != 0 || p_skin_controller) {
+ // Disable compression if all z equals 0 (the mesh is 2D).
+ bool is_mesh_2d = true;
+ for (int k = 0; k < vertex_array.size(); k++) {
+ if (!Math::is_zero_approx(vertex_array[k].vertex.z)) {
+ is_mesh_2d = false;
+ break;
+ }
+ };
+
+ if (p_mesh->get_blend_shape_count() != 0 || p_skin_controller || is_mesh_2d) {
// Can't compress if attributes missing or if using vertex weights.
mesh_flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp
index 62643eaa25..242b483b51 100644
--- a/editor/import/3d/resource_importer_obj.cpp
+++ b/editor/import/3d/resource_importer_obj.cpp
@@ -384,7 +384,22 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
if (p_disable_compression) {
mesh_flags = 0;
+ } else {
+ bool is_mesh_2d = true;
+
+ // Disable compression if all z equals 0 (the mesh is 2D).
+ for (int i = 0; i < vertices.size(); i++) {
+ if (!Math::is_zero_approx(vertices[i].z)) {
+ is_mesh_2d = false;
+ break;
+ }
+ }
+
+ if (is_mesh_2d) {
+ mesh_flags = 0;
+ }
}
+
//groups are too annoying
if (surf_tool->get_vertex_array().size()) {
//another group going on, commit it
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index f7e8657525..dce68a97ac 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -1993,7 +1993,6 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
case INTERNAL_IMPORT_CATEGORY_MESH: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/make_streamable"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
@@ -2147,7 +2146,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
}
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
- if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
+ if (p_option == "save_to_file/path") {
return p_options["save_to_file/enabled"];
}
} break;
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 67b13e3328..620ebce44b 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -1653,6 +1653,28 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
camera->set_attributes(camera_attributes);
}
+ // Use a grayscale gradient sky to avoid skewing the preview towards a specific color,
+ // but still allow shaded areas to be easily distinguished (using the ambient and reflected light).
+ // This also helps the user orient themselves in the preview, since the bottom of the sky is black
+ // and the top of the sky is white.
+ procedural_sky_material.instantiate();
+ procedural_sky_material->set_sky_top_color(Color(1, 1, 1));
+ procedural_sky_material->set_sky_horizon_color(Color(0.5, 0.5, 0.5));
+ procedural_sky_material->set_ground_horizon_color(Color(0.5, 0.5, 0.5));
+ procedural_sky_material->set_ground_bottom_color(Color(0, 0, 0));
+ procedural_sky_material->set_sky_curve(2.0);
+ procedural_sky_material->set_ground_curve(0.5);
+ // Hide the sun from the sky.
+ procedural_sky_material->set_sun_angle_max(0.0);
+ sky.instantiate();
+ sky->set_material(procedural_sky_material);
+ environment.instantiate();
+ environment->set_background(Environment::BG_SKY);
+ environment->set_sky(sky);
+ // A custom FOV must be specified, as an orthogonal camera is used for the preview.
+ environment->set_sky_custom_fov(50.0);
+ camera->set_environment(environment);
+
light = memnew(DirectionalLight3D);
light->set_transform(Transform3D().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0)));
base_viewport->add_child(light);
diff --git a/editor/import/3d/scene_import_settings.h b/editor/import/3d/scene_import_settings.h
index f4954c41db..17d6616fc0 100644
--- a/editor/import/3d/scene_import_settings.h
+++ b/editor/import/3d/scene_import_settings.h
@@ -47,6 +47,7 @@
#include "scene/gui/tab_container.h"
#include "scene/gui/tree.h"
#include "scene/resources/3d/primitive_meshes.h"
+#include "scene/resources/3d/sky_material.h"
class EditorFileDialog;
class EditorInspector;
@@ -78,6 +79,9 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
Camera3D *camera = nullptr;
Ref<CameraAttributesPractical> camera_attributes;
+ Ref<Environment> environment;
+ Ref<Sky> sky;
+ Ref<ProceduralSkyMaterial> procedural_sky_material;
bool first_aabb = false;
AABB contents_aabb;
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 4f0db70681..45786c0ab5 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -244,7 +244,7 @@ int MultiNodeEdit::get_node_count() const {
}
NodePath MultiNodeEdit::get_node(int p_index) const {
- ERR_FAIL_INDEX_V(p_index, nodes.size(), NodePath());
+ ERR_FAIL_INDEX_V(p_index, get_node_count(), NodePath());
return nodes[p_index];
}
diff --git a/editor/multi_node_edit.h b/editor/multi_node_edit.h
index deef88633e..000d41c4c1 100644
--- a/editor/multi_node_edit.h
+++ b/editor/multi_node_edit.h
@@ -36,7 +36,7 @@
class MultiNodeEdit : public RefCounted {
GDCLASS(MultiNodeEdit, RefCounted);
- List<NodePath> nodes;
+ LocalVector<NodePath> nodes;
struct PLData {
int uses = 0;
PropertyInfo info;
@@ -67,6 +67,19 @@ public:
void set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field);
+ // If the nodes selected are the same independently of order then return true.
+ bool is_same_selection(const MultiNodeEdit *p_other) const {
+ if (get_node_count() != p_other->get_node_count()) {
+ return false;
+ }
+ for (int i = 0; i < get_node_count(); i++) {
+ if (nodes.find(p_other->get_node(i)) == -1) {
+ return false;
+ }
+ }
+
+ return true;
+ }
MultiNodeEdit();
};
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index bfac9c8cf9..43928b9c17 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -2041,10 +2041,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
vb->add_child(name_hb);
name_dialog->register_text_enter(name);
- error_dialog = memnew(ConfirmationDialog);
+ error_dialog = memnew(AcceptDialog);
error_dialog->set_ok_button_text(TTR("Close"));
error_dialog->set_title(TTR("Error!"));
- add_child(error_dialog);
+ name_dialog->add_child(error_dialog);
name_dialog->connect(SNAME("confirmed"), callable_mp(this, &AnimationPlayerEditor::_animation_name_edited));
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index c780023c6d..a1175e2a0f 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -128,7 +128,7 @@ class AnimationPlayerEditor : public VBoxContainer {
} blend_editor;
ConfirmationDialog *name_dialog = nullptr;
- ConfirmationDialog *error_dialog = nullptr;
+ AcceptDialog *error_dialog = nullptr;
int name_dialog_op = TOOL_NEW_ANIM;
bool updating = false;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 41e5eee486..8a9118a03e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5860,13 +5860,34 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
}
void CanvasItemEditorViewport::_perform_drop_data() {
+ ERR_FAIL_COND(selected_files.size() <= 0);
+
_remove_preview();
- // Without root dropping multiple files is not allowed
- if (!target_node && selected_files.size() > 1) {
- accept->set_text(TTR("Cannot instantiate multiple nodes without root."));
- accept->popup_centered();
- return;
+ if (!target_node) {
+ // Without root dropping multiple files is not allowed
+ if (selected_files.size() > 1) {
+ accept->set_text(TTR("Cannot instantiate multiple nodes without root."));
+ accept->popup_centered();
+ return;
+ }
+
+ const String &path = selected_files[0];
+ Ref<Resource> res = ResourceLoader::load(path);
+ if (res.is_null()) {
+ return;
+ }
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ // Without root node act the same as "Load Inherited Scene".
+ Error err = EditorNode::get_singleton()->load_scene(path, false, true);
+ if (err != OK) {
+ accept->set_text(vformat(TTR("Error instantiating scene from %s."), path.get_file()));
+ accept->popup_centered();
+ }
+ return;
+ }
}
PackedStringArray error_files;
@@ -5882,27 +5903,21 @@ void CanvasItemEditorViewport::_perform_drop_data() {
if (res.is_null()) {
continue;
}
- Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- if (scene != nullptr && scene.is_valid()) {
- if (!target_node) {
- // Without root node act the same as "Load Inherited Scene"
- Error err = EditorNode::get_singleton()->load_scene(path, false, true);
- if (err != OK) {
- error_files.push_back(path.get_file());
- }
- } else {
- bool success = _create_instance(target_node, path, drop_pos);
- if (!success) {
- error_files.push_back(path.get_file());
- }
- }
- } else {
- Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
- if (texture != nullptr && texture.is_valid()) {
- Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
- _create_nodes(target_node, child, path, drop_pos);
- undo_redo->add_do_method(editor_selection, "add_node", child);
+
+ Ref<PackedScene> scene = res;
+ if (scene.is_valid()) {
+ bool success = _create_instance(target_node, path, drop_pos);
+ if (!success) {
+ error_files.push_back(path.get_file());
}
+ continue;
+ }
+
+ Ref<Texture2D> texture = res;
+ if (texture.is_valid()) {
+ Node *child = Object::cast_to<Node>(ClassDB::instantiate(default_texture_node_type));
+ _create_nodes(target_node, child, path, drop_pos);
+ undo_redo->add_do_method(editor_selection, "add_node", child);
}
}
@@ -5944,7 +5959,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
}
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
- if (_cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
+ if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
memdelete(instantiated_scene);
can_instantiate = false;
is_cyclical_dep = true;
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index fd6622bad6..c4b995b048 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -297,6 +297,7 @@ private:
};
HashMap<BoneKey, BoneList> bone_list;
+ MenuButton *skeleton_menu = nullptr;
struct PoseClipboard {
Vector2 pos;
@@ -329,7 +330,6 @@ private:
Button *group_button = nullptr;
Button *ungroup_button = nullptr;
- MenuButton *skeleton_menu = nullptr;
Button *override_camera_button = nullptr;
MenuButton *view_menu = nullptr;
PopupMenu *grid_menu = nullptr;
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 085e8cddbe..7e98950f32 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -979,10 +979,11 @@ Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_na
Ref<StandardMaterial3D> mat = materials[p_name][index];
- if (current_state == ON_TOP && p_gizmo->is_selected()) {
+ bool on_top_mat = mat->get_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST);
+
+ if (!on_top_mat && current_state == ON_TOP && p_gizmo->is_selected()) {
+ mat = mat->duplicate();
mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
- } else {
- mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, false);
}
return mat;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 88250ea3c3..aea3ea9700 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -4507,7 +4507,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
continue;
}
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
- if (_cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
+ if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) {
memdelete(instantiated_scene);
can_instantiate = false;
is_cyclical_dep = true;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 6c63d9ff0d..c832570fee 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -3886,6 +3886,8 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts);
ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog);
+ ClassDB::bind_method(D_METHOD("goto_help", "topic"), &ScriptEditor::goto_help);
+
ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 3055e178ba..e4e66b38e9 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -2317,15 +2317,15 @@ void SpriteFramesEditorPlugin::edit(Object *p_object) {
}
bool SpriteFramesEditorPlugin::handles(Object *p_object) const {
- AnimatedSprite2D *animated_sprite = Object::cast_to<AnimatedSprite2D>(p_object);
- AnimatedSprite3D *animated_sprite_3d = Object::cast_to<AnimatedSprite3D>(p_object);
- if (animated_sprite && *animated_sprite->get_sprite_frames()) {
+ AnimatedSprite2D *animated_sprite_2d = Object::cast_to<AnimatedSprite2D>(p_object);
+ if (animated_sprite_2d && *animated_sprite_2d->get_sprite_frames()) {
return true;
- } else if (animated_sprite_3d && *animated_sprite_3d->get_sprite_frames()) {
+ }
+ AnimatedSprite3D *animated_sprite_3d = Object::cast_to<AnimatedSprite3D>(p_object);
+ if (animated_sprite_3d && *animated_sprite_3d->get_sprite_frames()) {
return true;
- } else {
- return p_object->is_class("SpriteFrames");
}
+ return p_object->is_class("SpriteFrames");
}
void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
@@ -2334,7 +2334,9 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
EditorNode::get_bottom_panel()->make_item_visible(frames_editor);
} else {
button->hide();
- frames_editor->edit(Ref<SpriteFrames>());
+ if (frames_editor->is_visible_in_tree()) {
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
+ }
}
}
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index c00a6d99d3..e1a58a0998 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -3672,10 +3672,16 @@ void TileMapLayerEditor::_node_change(Node *p_node) {
void TileMapLayerEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY: {
+ case NOTIFICATION_ENTER_TREE: {
get_tree()->connect("node_added", callable_mp(this, &TileMapLayerEditor::_node_change));
get_tree()->connect("node_removed", callable_mp(this, &TileMapLayerEditor::_node_change));
} break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ get_tree()->disconnect("node_added", callable_mp(this, &TileMapLayerEditor::_node_change));
+ get_tree()->disconnect("node_removed", callable_mp(this, &TileMapLayerEditor::_node_change));
+ } break;
+
case NOTIFICATION_THEME_CHANGED: {
missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning"));
warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern"));
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index f9efc62f03..1da2f89c1c 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -1838,7 +1838,8 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i tile_size = tile_set_atlas_source->get_texture_region_size();
Vector2i origin = margins + (area.position * (tile_size + separation));
- TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, area.size * tile_size));
+ Vector2i size = area.size * tile_size + (area.size - Vector2i(1, 1)).max(Vector2i(0, 0)) * separation;
+ TilesEditorUtils::draw_selection_rect(tile_atlas_control, Rect2i(origin, size));
} else {
Vector2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
if (hovered_base_tile_coords.x >= 0 && hovered_base_tile_coords.y >= 0 && hovered_base_tile_coords.x < grid_size.x && hovered_base_tile_coords.y < grid_size.y) {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 05b8b81fa7..0dd845270a 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -953,8 +953,8 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
bool port_left_used = false;
String name_left;
if (valid_left) {
- name_left = vsnode->get_input_port_name(i);
- port_left = vsnode->get_input_port_type(i);
+ name_left = vsnode->get_input_port_name(j);
+ port_left = vsnode->get_input_port_type(j);
for (const VisualShader::Connection &E : connections) {
if (E.to_node == p_id && E.to_port == j) {
port_left_used = true;
@@ -989,15 +989,15 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool
Variant default_value;
if (valid_left && !port_left_used) {
- default_value = vsnode->get_input_port_default_value(i);
+ default_value = vsnode->get_input_port_default_value(j);
}
Button *button = memnew(Button);
hb->add_child(button);
- register_default_input_button(p_id, i, button);
- button->connect("pressed", callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, i));
+ register_default_input_button(p_id, j, button);
+ button->connect("pressed", callable_mp(editor, &VisualShaderEditor::_edit_port_default_input).bind(button, p_id, j));
if (default_value.get_type() != Variant::NIL) { // only a label
- set_input_port_default_value(p_type, p_id, i, default_value);
+ set_input_port_default_value(p_type, p_id, j, default_value);
} else {
button->hide();
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 86c59ec99e..a16767d916 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -43,6 +43,7 @@
#include "editor/editor_about.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
+#include "editor/engine_update_label.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_title_bar.h"
#include "editor/plugins/asset_library_editor_plugin.h"
@@ -526,7 +527,7 @@ void ProjectManager::_open_selected_projects_ask() {
return;
}
- const Size2i popup_min_size = Size2i(600.0 * EDSCALE, 0);
+ const Size2i popup_min_size = Size2i(400.0 * EDSCALE, 0);
if (selected_list.size() > 1) {
multi_open_ask->set_text(vformat(TTR("You requested to open %d projects in parallel. Do you confirm?\nNote that usual checks for engine version compatibility will be bypassed."), selected_list.size()));
@@ -1397,8 +1398,15 @@ ProjectManager::ProjectManager() {
{
HBoxContainer *footer_bar = memnew(HBoxContainer);
footer_bar->set_alignment(BoxContainer::ALIGNMENT_END);
+ footer_bar->add_theme_constant_override("separation", 20 * EDSCALE);
main_vbox->add_child(footer_bar);
+#ifdef ENGINE_UPDATE_CHECK_ENABLED
+ EngineUpdateLabel *update_label = memnew(EngineUpdateLabel);
+ footer_bar->add_child(update_label);
+ update_label->connect("offline_clicked", callable_mp(this, &ProjectManager::_show_quick_settings));
+#endif
+
version_btn = memnew(LinkButton);
String hash = String(VERSION_HASH);
if (hash.length() != 0) {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 757b9e72ea..e1084d4873 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2079,17 +2079,19 @@ bool SceneTreeDock::_validate_no_foreign() {
return false;
}
- // When edited_scene inherits from another one the root Node will be the parent Scene,
- // we don't want to consider that Node a foreign one otherwise we would not be able to
- // delete it.
- if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene == E) {
- continue;
- }
+ if (edited_scene->get_scene_inherited_state().is_valid()) {
+ // When edited_scene inherits from another one the root Node will be the parent Scene,
+ // we don't want to consider that Node a foreign one otherwise we would not be able to
+ // delete it.
+ if (edited_scene == E && current_option != TOOL_REPLACE) {
+ continue;
+ }
- if (edited_scene->get_scene_inherited_state().is_valid() && edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E)) >= 0) {
- accept->set_text(TTR("Can't operate on nodes the current scene inherits from!"));
- accept->popup_centered();
- return false;
+ if (edited_scene == E || edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E)) >= 0) {
+ accept->set_text(TTR("Can't operate on nodes the current scene inherits from!"));
+ accept->popup_centered();
+ return false;
+ }
}
}
@@ -3018,6 +3020,13 @@ void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
+void SceneTreeDock::set_selection(const Vector<Node *> &p_nodes) {
+ editor_selection->clear();
+ for (Node *node : p_nodes) {
+ editor_selection->add_node(node);
+ }
+}
+
void SceneTreeDock::set_selected(Node *p_node, bool p_emit_selected) {
scene_tree->set_selected(p_node, p_emit_selected);
}
@@ -3443,6 +3452,13 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
can_replace = false;
break;
}
+
+ if (edited_scene->get_scene_inherited_state().is_valid()) {
+ if (E == edited_scene || edited_scene->get_scene_inherited_state()->find_node_by_path(edited_scene->get_path_to(E)) >= 0) {
+ can_replace = false;
+ break;
+ }
+ }
}
if (can_replace) {
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 86e9ff8a47..21e1b00f93 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -322,6 +322,7 @@ public:
void set_edited_scene(Node *p_scene);
void instantiate(const String &p_file);
void instantiate_scenes(const Vector<String> &p_files, Node *p_parent = nullptr);
+ void set_selection(const Vector<Node *> &p_nodes);
void set_selected(Node *p_node, bool p_emit_selected = false);
void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames);
void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr);
diff --git a/main/main.cpp b/main/main.cpp
index 357033b6d8..ed74094c9e 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -69,16 +69,18 @@
#include "servers/display_server.h"
#include "servers/movie_writer/movie_writer.h"
#include "servers/movie_writer/movie_writer_mjpeg.h"
-#include "servers/navigation_server_2d.h"
-#include "servers/navigation_server_2d_dummy.h"
#include "servers/navigation_server_3d.h"
#include "servers/navigation_server_3d_dummy.h"
-#include "servers/physics_server_2d.h"
#include "servers/register_server_types.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/text/text_server_dummy.h"
#include "servers/text_server.h"
+// 2D
+#include "servers/navigation_server_2d.h"
+#include "servers/navigation_server_2d_dummy.h"
+#include "servers/physics_server_2d.h"
+
#ifndef _3D_DISABLED
#include "servers/physics_server_3d.h"
#include "servers/xr_server.h"
@@ -143,15 +145,15 @@ static MessageQueue *message_queue = nullptr;
// Initialized in setup2()
static AudioServer *audio_server = nullptr;
+static CameraServer *camera_server = nullptr;
static DisplayServer *display_server = nullptr;
static RenderingServer *rendering_server = nullptr;
-static CameraServer *camera_server = nullptr;
static TextServerManager *tsman = nullptr;
+static ThemeDB *theme_db = nullptr;
+static NavigationServer2D *navigation_server_2d = nullptr;
static PhysicsServer2DManager *physics_server_2d_manager = nullptr;
static PhysicsServer2D *physics_server_2d = nullptr;
static NavigationServer3D *navigation_server_3d = nullptr;
-static NavigationServer2D *navigation_server_2d = nullptr;
-static ThemeDB *theme_db = nullptr;
#ifndef _3D_DISABLED
static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
static PhysicsServer3D *physics_server_3d = nullptr;
@@ -695,7 +697,9 @@ Error Main::test_setup() {
/** INITIALIZE SERVERS **/
register_server_types();
+#ifndef _3D_DISABLED
XRServer::set_xr_mode(XRServer::XRMODE_OFF); // Skip in tests.
+#endif // _3D_DISABLED
initialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
GDExtensionManager::get_singleton()->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
@@ -2274,6 +2278,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Editor and project manager cannot run with rendering in a separate thread (they will crash on startup).
rtm = OS::RENDER_THREAD_SAFE;
}
+#if !defined(THREADS_ENABLED)
+ rtm = OS::RENDER_THREAD_SAFE;
+#endif
OS::get_singleton()->_render_thread_mode = OS::RenderThreadMode(rtm);
}
@@ -2717,7 +2724,9 @@ Error Main::setup2() {
}
if (OS::get_singleton()->_render_thread_mode == OS::RENDER_SEPARATE_THREAD) {
- WARN_PRINT("The Multi-Threaded rendering thread model is experimental, and has known issues which can lead to project crashes. Use the Single-Safe option in the project settings instead.");
+ WARN_PRINT("The Multi-Threaded rendering thread model is experimental. Feel free to try it since it will eventually become a stable feature.\n"
+ "However, bear in mind that at the moment it can lead to project crashes or instability.\n"
+ "So, unless you want to test the engine, use the Single-Safe option in the project settings instead.");
}
/* Initialize Pen Tablet Driver */
@@ -4025,11 +4034,11 @@ bool Main::iteration() {
if ((!force_redraw_requested) && OS::get_singleton()->is_in_low_processor_usage_mode()) {
if (RenderingServer::get_singleton()->has_changed()) {
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
- Engine::get_singleton()->frames_drawn++;
+ Engine::get_singleton()->increment_frames_drawn();
}
} else {
RenderingServer::get_singleton()->draw(true, scaled_step); // flush visual commands
- Engine::get_singleton()->frames_drawn++;
+ Engine::get_singleton()->increment_frames_drawn();
force_redraw_requested = false;
}
}
diff --git a/main/performance.cpp b/main/performance.cpp
index e8d519bb46..9d4e2ae9d5 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -36,9 +36,11 @@
#include "scene/main/scene_tree.h"
#include "servers/audio_server.h"
#include "servers/navigation_server_3d.h"
-#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
+// 2D
+#include "servers/physics_server_2d.h"
+
#ifndef _3D_DISABLED
#include "servers/physics_server_3d.h"
#endif // _3D_DISABLED
diff --git a/methods.py b/methods.py
index dd01469b86..4d1f4c1cda 100644
--- a/methods.py
+++ b/methods.py
@@ -1621,8 +1621,8 @@ def generate_vs_project(env, original_args, project_name="godot"):
sln_template = sln_template.replace("%%NAME%%", project_name)
sln_template = sln_template.replace("%%UUID%%", proj_uuid)
sln_template = sln_template.replace("%%SLNUUID%%", sln_uuid)
- sln_template = sln_template.replace("%%SECTION1%%", "\n ".join(section1))
- sln_template = sln_template.replace("%%SECTION2%%", "\n ".join(section2))
+ sln_template = sln_template.replace("%%SECTION1%%", "\n\t\t".join(section1))
+ sln_template = sln_template.replace("%%SECTION2%%", "\n\t\t".join(section2))
with open(f"{project_name}.sln", "w", encoding="utf-8", newline="\r\n") as f:
f.write(sln_template)
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 9cd732813e..6471c5a142 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -263,12 +263,30 @@ Removed VisualShaderNodeComment, which is replaced by VisualShaderNodeFrame.
GH-87888
--------
-Validate extension JSON: API was removed: classes/OpenXRHand/methods/get_hand_skeleton
-Validate extension JSON: API was removed: classes/OpenXRHand/methods/set_hand_skeleton
-Validate extension JSON: API was removed: classes/OpenXRHand/properties/hand_skeleton
Validate extension JSON: API was removed: classes/Skeleton3D/properties/animate_physical_bones
Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/get_interpolation
Validate extension JSON: API was removed: classes/SkeletonIK3D/methods/set_interpolation
Validate extension JSON: API was removed: classes/SkeletonIK3D/properties/interpolation
These base class is changed to SkeletonModifier3D which is processed by Skeleton3D with the assumption that it is Skeleton3D's child.
+
+
+GH-90575
+--------
+Validate extension JSON: API was removed: classes/BoneAttachment3D/methods/on_bone_pose_update
+Validate extension JSON: API was removed: classes/Skeleton3D/signals/bone_pose_changed
+
+They have been replaced by a safer API due to performance concerns. Compatibility method registered.
+
+GH-90747
+--------
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/get_avoidance_layers
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/set_avoidance_layers
+Validate extension JSON: API was removed: classes/NavigationRegion2D/properties/avoidance_layers
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/get_avoidance_layer_value
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/set_avoidance_layer_value
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/set_constrain_avoidance
+Validate extension JSON: API was removed: classes/NavigationRegion2D/methods/get_constrain_avoidance
+Validate extension JSON: API was removed: classes/NavigationRegion2D/properties/constrain_avoidance
+
+Experimental NavigationRegion2D feature "constrain_avoidance" was discontinued with no replacement.
diff --git a/misc/scripts/copyright_headers.py b/misc/scripts/copyright_headers.py
index 169795921f..2b1201b3c0 100755
--- a/misc/scripts/copyright_headers.py
+++ b/misc/scripts/copyright_headers.py
@@ -73,7 +73,7 @@ for f in sys.argv[1:]:
line = fileread.readline()
header_done = False
- while line.strip() == "": # Skip empty lines at the top
+ while line.strip() == "" and line != "": # Skip empty lines at the top
line = fileread.readline()
if line.find("/**********") == -1: # Godot header starts this way
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index 1dbe4c535a..bde23289f4 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -990,8 +990,8 @@ Error FBXDocument::_parse_images(Ref<FBXState> p_state, const String &p_base_pat
for (int texture_i = 0; texture_i < static_cast<int>(fbx_scene->texture_files.count); texture_i++) {
const ufbx_texture_file &fbx_texture_file = fbx_scene->texture_files[texture_i];
String path = _as_string(fbx_texture_file.filename);
- path = ProjectSettings::get_singleton()->localize_path(path);
- if (path.is_absolute_path() && !path.is_resource_file()) {
+ // Use only filename for absolute paths to avoid portability issues.
+ if (path.is_absolute_path()) {
path = path.get_file();
}
if (!p_base_path.is_empty()) {
@@ -1080,7 +1080,7 @@ Error FBXDocument::_parse_materials(Ref<FBXState> p_state) {
if (fbx_material->pbr.base_color.has_value) {
Color albedo = _material_color(fbx_material->pbr.base_color, fbx_material->pbr.base_factor);
- material->set_albedo(albedo);
+ material->set_albedo(albedo.linear_to_srgb());
}
if (fbx_material->features.double_sided.enabled) {
@@ -1178,7 +1178,11 @@ Error FBXDocument::_parse_materials(Ref<FBXState> p_state) {
}
// Combined textures and factors are very unreliable in FBX
- material->set_albedo(Color(1, 1, 1));
+ Color albedo_factor = Color(1, 1, 1);
+ if (fbx_material->pbr.base_factor.has_value) {
+ albedo_factor *= (float)fbx_material->pbr.base_factor.value_real;
+ }
+ material->set_albedo(albedo_factor.linear_to_srgb());
// TODO: Does not support rotation, could be inverted?
material->set_uv1_offset(_as_vec3(base_texture->uv_transform.translation));
@@ -1232,11 +1236,11 @@ Error FBXDocument::_parse_materials(Ref<FBXState> p_state) {
if (fbx_material->pbr.emission_color.has_value) {
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
- material->set_emission(_material_color(fbx_material->pbr.emission_color));
+ material->set_emission(_material_color(fbx_material->pbr.emission_color).linear_to_srgb());
material->set_emission_energy_multiplier(float(fbx_material->pbr.emission_factor.value_real));
}
- const ufbx_texture *emission_texture = _get_file_texture(fbx_material->pbr.ambient_occlusion.texture);
+ const ufbx_texture *emission_texture = _get_file_texture(fbx_material->pbr.emission_color.texture);
if (emission_texture) {
material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, GLTFTextureIndex(emission_texture->file_index), TEXTURE_TYPE_GENERIC));
material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
@@ -1266,7 +1270,7 @@ Error FBXDocument::_parse_cameras(Ref<FBXState> p_state) {
camera->set_fov(Math::deg_to_rad(real_t(fbx_camera->field_of_view_deg.y)));
} else {
camera->set_perspective(false);
- camera->set_size_mag(real_t(fbx_camera->orthographic_size.y));
+ camera->set_size_mag(real_t(fbx_camera->orthographic_size.y * 0.5f));
}
if (fbx_camera->near_plane != 0.0f) {
camera->set_depth_near(fbx_camera->near_plane);
@@ -2235,6 +2239,10 @@ Error FBXDocument::_parse_lights(Ref<FBXState> p_state) {
}
String FBXDocument::_get_texture_path(const String &p_base_dir, const String &p_source_file_path) const {
+ // Check if the original path exists first.
+ if (FileAccess::exists(p_source_file_path)) {
+ return p_source_file_path.strip_edges();
+ }
const String tex_file_name = p_source_file_path.get_file();
const Vector<String> subdirs = {
"", "textures/", "Textures/", "images/",
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index a30a87f3a1..cd19887d82 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1957,6 +1957,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else {
parser->push_warning(p_assignable, GDScriptWarning::UNTYPED_DECLARATION, declaration_type, p_assignable->identifier->name);
}
+ } else if (specified_type.kind == GDScriptParser::DataType::ENUM && p_assignable->initializer == nullptr) {
+ // Warn about enum variables without default value. Unless the enum defines the "0" value, then it's fine.
+ bool has_zero_value = false;
+ for (const KeyValue<StringName, int64_t> &kv : specified_type.enum_values) {
+ if (kv.value == 0) {
+ has_zero_value = true;
+ break;
+ }
+ }
+ if (!has_zero_value) {
+ parser->push_warning(p_assignable, GDScriptWarning::ENUM_VARIABLE_WITHOUT_DEFAULT, p_assignable->identifier->name);
+ }
}
#endif
@@ -3469,6 +3481,8 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
if (op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
mark_node_unsafe(p_cast);
valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.builtin_type == Variant::INT) {
+ valid = true;
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 173bff575a..bb5d6770c6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2270,28 +2270,31 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
break;
case GDScriptTokenizer::Token::BRACKET_OPEN: {
// Array.
+ push_multiline(true);
advance();
pattern->pattern_type = PatternNode::PT_ARRAY;
-
- if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
- do {
- PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
- if (sub_pattern == nullptr) {
- continue;
- }
- if (pattern->rest_used) {
- push_error(R"(The ".." pattern must be the last element in the pattern array.)");
- } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
- pattern->rest_used = true;
- }
- pattern->array.push_back(sub_pattern);
- } while (match(GDScriptTokenizer::Token::COMMA));
- }
+ do {
+ if (is_at_end() || check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
+ break;
+ }
+ PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
+ if (sub_pattern == nullptr) {
+ continue;
+ }
+ if (pattern->rest_used) {
+ push_error(R"(The ".." pattern must be the last element in the pattern array.)");
+ } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
+ pattern->rest_used = true;
+ }
+ pattern->array.push_back(sub_pattern);
+ } while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" to close the array pattern.)");
+ pop_multiline();
break;
}
case GDScriptTokenizer::Token::BRACE_OPEN: {
// Dictionary.
+ push_multiline(true);
advance();
pattern->pattern_type = PatternNode::PT_DICTIONARY;
do {
@@ -2334,6 +2337,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
}
} while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
+ pop_multiline();
break;
}
default: {
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 2940af585d..5b1639e250 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -1455,10 +1455,11 @@ GDScriptTokenizer::Token GDScriptTokenizerText::scan() {
if (_peek() != '\n') {
return make_error("Expected new line after \"\\\".");
}
- continuation_lines.push_back(line);
_advance();
newline(false);
line_continuation = true;
+ _skip_whitespace(); // Skip whitespace/comment lines after `\`. See GH-89403.
+ continuation_lines.push_back(line);
return scan(); // Recurse to get next token.
}
diff --git a/modules/gdscript/gdscript_tokenizer_buffer.cpp b/modules/gdscript/gdscript_tokenizer_buffer.cpp
index db523ea941..e53bc5bc41 100644
--- a/modules/gdscript/gdscript_tokenizer_buffer.cpp
+++ b/modules/gdscript/gdscript_tokenizer_buffer.cpp
@@ -285,9 +285,9 @@ Vector<uint8_t> GDScriptTokenizerBuffer::parse_code_string(const String &p_code,
// Remove continuation lines from map.
for (int line : tokenizer.get_continuation_lines()) {
- if (rev_token_lines.has(line + 1)) {
- token_lines.erase(rev_token_lines[line + 1]);
- token_columns.erase(rev_token_lines[line + 1]);
+ if (rev_token_lines.has(line)) {
+ token_lines.erase(rev_token_lines[line]);
+ token_columns.erase(rev_token_lines[line]);
}
}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 708966a0a8..48a0abe617 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -126,6 +126,9 @@ String GDScriptWarning::get_message() const {
case INT_AS_ENUM_WITHOUT_MATCH:
CHECK_SYMBOLS(3);
return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)", symbols[0], symbols[1], symbols[2]);
+ case ENUM_VARIABLE_WITHOUT_DEFAULT:
+ CHECK_SYMBOLS(1);
+ return vformat(R"(The variable "%s" has an enum type and does not set an explicit default value. The default will be set to "0".)", symbols[0]);
case EMPTY_FILE:
return "Empty script file.";
case DEPRECATED_KEYWORD:
@@ -221,6 +224,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"NARROWING_CONVERSION",
"INT_AS_ENUM_WITHOUT_CAST",
"INT_AS_ENUM_WITHOUT_MATCH",
+ "ENUM_VARIABLE_WITHOUT_DEFAULT",
"EMPTY_FILE",
"DEPRECATED_KEYWORD",
"RENAMED_IN_GODOT_4_HINT",
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 93c232a0f8..3ad9488138 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -78,6 +78,7 @@ public:
NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INT_AS_ENUM_WITHOUT_CAST, // An integer value was used as an enum value without casting.
INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
+ ENUM_VARIABLE_WITHOUT_DEFAULT, // A variable with an enum type does not have a default value. The default will be set to `0` instead of the first enum value.
EMPTY_FILE, // A script file is empty.
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
@@ -129,6 +130,7 @@ public:
WARN, // NARROWING_CONVERSION
WARN, // INT_AS_ENUM_WITHOUT_CAST
WARN, // INT_AS_ENUM_WITHOUT_MATCH
+ WARN, // ENUM_VARIABLE_WITHOUT_DEFAULT
WARN, // EMPTY_FILE
WARN, // DEPRECATED_KEYWORD
WARN, // RENAMED_IN_GODOT_4_HINT
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index a0329eb8d2..e3d16eaf42 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -300,14 +300,23 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
#endif
String out_file = next.get_basename() + ".out";
- if (!is_generating && !dir->file_exists(out_file)) {
- ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
- }
- GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
- if (binary_tokens) {
- test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ ERR_FAIL_COND_V_MSG(!is_generating && !dir->file_exists(out_file), false, "Could not find output file for " + next);
+
+ if (next.ends_with(".bin.gd")) {
+ // Test text mode first.
+ GDScriptTest text_test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ tests.push_back(text_test);
+ // Test binary mode even without `--use-binary-tokens`.
+ GDScriptTest bin_test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ bin_test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ tests.push_back(bin_test);
+ } else {
+ GDScriptTest test(current_dir.path_join(next), current_dir.path_join(out_file), source_dir);
+ if (binary_tokens) {
+ test.set_tokenizer_mode(GDScriptTest::TOKENIZER_BUFFER);
+ }
+ tests.push_back(test);
}
- tests.push_back(test);
}
}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd
new file mode 100644
index 0000000000..77ef9e2073
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.gd
@@ -0,0 +1,9 @@
+# GH-85882
+
+enum Foo { A, B, C }
+
+func test():
+ var a := Foo.A
+ var b := a as int + 1
+ print(b)
+
diff --git a/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out
new file mode 100644
index 0000000000..a7f1357bb2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/cast_enum_to_int.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd
new file mode 100644
index 0000000000..13e3edf93f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.gd
@@ -0,0 +1,9 @@
+enum HasZero { A = 0, B = 1 }
+enum HasNoZero { A = 1, B = 2 }
+var has_zero: HasZero # No warning, because the default `0` is valid.
+var has_no_zero: HasNoZero # Warning, because there is no `0` in the enum.
+
+
+func test():
+ print(has_zero)
+ print(has_no_zero)
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out
new file mode 100644
index 0000000000..ae40e0bc8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/enum_without_default_value.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> ENUM_VARIABLE_WITHOUT_DEFAULT
+>> The variable "has_no_zero" has an enum type and does not set an explicit default value. The default will be set to "0".
+0
+0
diff --git a/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd
new file mode 100644
index 0000000000..cb0bc94d2e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.gd
@@ -0,0 +1,12 @@
+# GH-89403
+
+func test():
+ var x := 1
+ if x == 0 \
+ # Comment.
+ # Comment.
+ and (x < 1 or x > 2) \
+ # Comment.
+ and x != 3:
+ pass
+ print("Ok")
diff --git a/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out
new file mode 100644
index 0000000000..0e9f482af4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/continuation_lines_comments.bin.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Ok
diff --git a/modules/gdscript/tests/scripts/parser/features/match_array.gd b/modules/gdscript/tests/scripts/parser/features/match_array.gd
new file mode 100644
index 0000000000..9103092cb4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_array.gd
@@ -0,0 +1,33 @@
+func foo(x):
+ match x:
+ ["value1"]:
+ print('["value1"]')
+ ["value1", "value2"]:
+ print('["value1", "value2"]')
+
+func bar(x):
+ match x:
+ [
+ "value1"
+ ]:
+ print('multiline ["value1"]')
+ [
+ "value1",
+ "value2",
+ ]:
+ print('multiline ["value1", "value2",]')
+ [
+ "value1",
+ [
+ "value2",
+ ..,
+ ],
+ ]:
+ print('multiline ["value1", ["value2", ..,],]')
+
+func test():
+ foo(["value1"])
+ foo(["value1", "value2"])
+ bar(["value1"])
+ bar(["value1", "value2"])
+ bar(["value1", ["value2", "value3"]])
diff --git a/modules/gdscript/tests/scripts/parser/features/match_array.out b/modules/gdscript/tests/scripts/parser/features/match_array.out
new file mode 100644
index 0000000000..d0111f07b1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_array.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+["value1"]
+["value1", "value2"]
+multiline ["value1"]
+multiline ["value1", "value2",]
+multiline ["value1", ["value2", ..,],]
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
index 75857fb8ff..7685622e5a 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -26,6 +26,24 @@ func bar(x):
_:
print("wildcard")
+func baz(x):
+ match x:
+ {
+ "key1": "value1"
+ }:
+ print('multiline {"key1": "value1"}')
+ {
+ "key2": "value2",
+ }:
+ print('multiline {"key2": "value2",}')
+ {
+ "key3": {
+ "key1",
+ ..,
+ },
+ }:
+ print('multiline {"key3": {"key1", ..,},}')
+
func test():
foo({"key1": "value1", "key2": "value2"})
foo({"key1": "value1", "key2": ""})
@@ -41,3 +59,6 @@ func test():
bar({1: "1"})
bar({2: "2"})
bar({3: "3"})
+ baz({"key1": "value1"})
+ baz({"key2": "value2"})
+ baz({"key3": {"key1": "value1", "key2": "value2"}})
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.out b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
index 4dee886927..f9adcbd331 100644
--- a/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
@@ -13,3 +13,6 @@ wildcard
1
2
wildcard
+multiline {"key1": "value1"}
+multiline {"key2": "value2",}
+multiline {"key3": {"key1", ..,},}
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.gd b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
index d7485f49e6..42b29eee43 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
@@ -23,6 +23,7 @@ var test_var_hard_int: int
var test_var_hard_variant_type: Variant.Type
@export var test_var_hard_variant_type_exported: Variant.Type
var test_var_hard_node_process_mode: Node.ProcessMode
+@warning_ignore("enum_variable_without_default")
var test_var_hard_my_enum: MyEnum
var test_var_hard_array: Array
var test_var_hard_array_int: Array[int]
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 2b3a00a224..f8c35ab6d1 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -3059,7 +3059,17 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
array[Mesh::ARRAY_TANGENT] = tangents;
}
- if (p_state->force_disable_compression || !a.has("POSITION") || !a.has("NORMAL") || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) {
+ // Disable compression if all z equals 0 (the mesh is 2D).
+ const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX];
+ bool is_mesh_2d = true;
+ for (int k = 0; k < vertices.size(); k++) {
+ if (!Math::is_zero_approx(vertices[k].z)) {
+ is_mesh_2d = false;
+ break;
+ }
+ }
+
+ if (p_state->force_disable_compression || is_mesh_2d || !a.has("POSITION") || !a.has("NORMAL") || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) {
flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index 9cd5498fa8..37f319b697 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -1208,39 +1208,39 @@ namespace Godot
/// Do a simple expression match, where '*' matches zero or more
/// arbitrary characters and '?' matches any single character except '.'.
/// </summary>
- /// <param name="instance">The string to check.</param>
- /// <param name="expr">Expression to check.</param>
+ /// <param name="str">The string to check.</param>
+ /// <param name="pattern">Expression to check.</param>
/// <param name="caseSensitive">
/// If <see langword="true"/>, the check will be case sensitive.
/// </param>
/// <returns>If the expression has any matches.</returns>
- private static bool ExprMatch(this string instance, string expr, bool caseSensitive)
+ private static bool WildcardMatch(ReadOnlySpan<char> str, ReadOnlySpan<char> pattern, bool caseSensitive)
{
// case '\0':
- if (expr.Length == 0)
- return instance.Length == 0;
+ if (pattern.IsEmpty)
+ return str.IsEmpty;
- switch (expr[0])
+ switch (pattern[0])
{
case '*':
- return ExprMatch(instance, expr.Substring(1), caseSensitive) || (instance.Length > 0 &&
- ExprMatch(instance.Substring(1), expr, caseSensitive));
+ return WildcardMatch(str, pattern.Slice(1), caseSensitive)
+ || (!str.IsEmpty && WildcardMatch(str.Slice(1), pattern, caseSensitive));
case '?':
- return instance.Length > 0 && instance[0] != '.' &&
- ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ return !str.IsEmpty && str[0] != '.' &&
+ WildcardMatch(str.Slice(1), pattern.Slice(1), caseSensitive);
default:
- if (instance.Length == 0)
+ if (str.IsEmpty)
return false;
- if (caseSensitive)
- return instance[0] == expr[0];
- return (char.ToUpperInvariant(instance[0]) == char.ToUpperInvariant(expr[0])) &&
- ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ bool charMatches = caseSensitive ?
+ str[0] == pattern[0] :
+ char.ToUpperInvariant(str[0]) == char.ToUpperInvariant(pattern[0]);
+ return charMatches &&
+ WildcardMatch(str.Slice(1), pattern.Slice(1), caseSensitive);
}
}
/// <summary>
- /// Do a simple case sensitive expression match, using ? and * wildcards
- /// (see <see cref="ExprMatch(string, string, bool)"/>).
+ /// Do a simple case sensitive expression match, using ? and * wildcards.
/// </summary>
/// <seealso cref="MatchN(string, string)"/>
/// <param name="instance">The string to check.</param>
@@ -1254,12 +1254,11 @@ namespace Godot
if (instance.Length == 0 || expr.Length == 0)
return false;
- return instance.ExprMatch(expr, caseSensitive);
+ return WildcardMatch(instance, expr, caseSensitive);
}
/// <summary>
- /// Do a simple case insensitive expression match, using ? and * wildcards
- /// (see <see cref="ExprMatch(string, string, bool)"/>).
+ /// Do a simple case insensitive expression match, using ? and * wildcards.
/// </summary>
/// <seealso cref="Match(string, string, bool)"/>
/// <param name="instance">The string to check.</param>
@@ -1270,7 +1269,7 @@ namespace Godot
if (instance.Length == 0 || expr.Length == 0)
return false;
- return instance.ExprMatch(expr, caseSensitive: false);
+ return WildcardMatch(instance, expr, caseSensitive: false);
}
/// <summary>
diff --git a/modules/navigation/2d/godot_navigation_server_2d.cpp b/modules/navigation/2d/godot_navigation_server_2d.cpp
index 28bcd16310..5eefbe4228 100644
--- a/modules/navigation/2d/godot_navigation_server_2d.cpp
+++ b/modules/navigation/2d/godot_navigation_server_2d.cpp
@@ -229,6 +229,10 @@ bool GodotNavigationServer2D::is_baking_navigation_polygon(Ref<NavigationPolygon
#endif
}
+Vector<Vector2> GodotNavigationServer2D::simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) {
+ return vector_v3_to_v2(NavigationServer3D::get_singleton()->simplify_path(vector_v2_to_v3(p_path), p_epsilon));
+}
+
GodotNavigationServer2D::GodotNavigationServer2D() {}
GodotNavigationServer2D::~GodotNavigationServer2D() {}
diff --git a/modules/navigation/2d/godot_navigation_server_2d.h b/modules/navigation/2d/godot_navigation_server_2d.h
index a148887a65..ba375afd33 100644
--- a/modules/navigation/2d/godot_navigation_server_2d.h
+++ b/modules/navigation/2d/godot_navigation_server_2d.h
@@ -252,6 +252,8 @@ public:
virtual void bake_from_source_geometry_data(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual bool is_baking_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) const override;
+
+ virtual Vector<Vector2> simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) override;
};
#endif // GODOT_NAVIGATION_SERVER_2D_H
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index 0738a102eb..9000259524 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -43,9 +43,9 @@
#include "scene/resources/2d/circle_shape_2d.h"
#include "scene/resources/2d/concave_polygon_shape_2d.h"
#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "scene/resources/2d/rectangle_shape_2d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_polygon.h"
#include "thirdparty/clipper2/include/clipper2/clipper.h"
#include "thirdparty/misc/polypartition.h"
@@ -72,7 +72,7 @@ NavMeshGenerator2D::NavMeshGenerator2D() {
// Using threads might cause problems on certain exports or with the Editor on certain devices.
// This is the main switch to turn threaded navmesh baking off should the need arise.
- use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
+ use_threads = baking_use_multiple_threads;
}
NavMeshGenerator2D::~NavMeshGenerator2D() {
diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp
index 15de33f58f..301b4aa8f3 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.cpp
+++ b/modules/navigation/3d/godot_navigation_server_3d.cpp
@@ -1381,11 +1381,140 @@ PathQueryResult GodotNavigationServer3D::_query_path(const PathQueryParameters &
// add path postprocessing
+ if (r_query_result.path.size() > 2 && p_parameters.simplify_path) {
+ const LocalVector<uint32_t> &simplified_path_indices = get_simplified_path_indices(r_query_result.path, p_parameters.simplify_epsilon);
+
+ uint32_t indices_count = simplified_path_indices.size();
+
+ {
+ Vector3 *w = r_query_result.path.ptrw();
+ const Vector3 *r = r_query_result.path.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+ r_query_result.path.resize(indices_count);
+ }
+
+ if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_TYPES)) {
+ int32_t *w = r_query_result.path_types.ptrw();
+ const int32_t *r = r_query_result.path_types.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+ r_query_result.path_types.resize(indices_count);
+ }
+
+ if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_RIDS)) {
+ TypedArray<RID> simplified_path_rids;
+ simplified_path_rids.resize(indices_count);
+ for (uint32_t i = 0; i < indices_count; i++) {
+ simplified_path_rids[i] = r_query_result.path_rids[i];
+ }
+ r_query_result.path_rids = simplified_path_rids;
+ }
+
+ if (p_parameters.metadata_flags.has_flag(PathMetadataFlags::PATH_INCLUDE_OWNERS)) {
+ int64_t *w = r_query_result.path_owner_ids.ptrw();
+ const int64_t *r = r_query_result.path_owner_ids.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+ r_query_result.path_owner_ids.resize(indices_count);
+ }
+ }
+
// add path stats
return r_query_result;
}
+Vector<Vector3> GodotNavigationServer3D::simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) {
+ if (p_path.size() <= 2) {
+ return p_path;
+ }
+
+ p_epsilon = MAX(0.0, p_epsilon);
+
+ LocalVector<uint32_t> simplified_path_indices = get_simplified_path_indices(p_path, p_epsilon);
+
+ uint32_t indices_count = simplified_path_indices.size();
+
+ Vector<Vector3> simplified_path;
+ simplified_path.resize(indices_count);
+
+ Vector3 *w = simplified_path.ptrw();
+ const Vector3 *r = p_path.ptr();
+ for (uint32_t i = 0; i < indices_count; i++) {
+ w[i] = r[simplified_path_indices[i]];
+ }
+
+ return simplified_path;
+}
+
+LocalVector<uint32_t> GodotNavigationServer3D::get_simplified_path_indices(const Vector<Vector3> &p_path, real_t p_epsilon) {
+ p_epsilon = MAX(0.0, p_epsilon);
+ real_t squared_epsilon = p_epsilon * p_epsilon;
+
+ LocalVector<bool> valid_points;
+ valid_points.resize(p_path.size());
+ for (uint32_t i = 0; i < valid_points.size(); i++) {
+ valid_points[i] = false;
+ }
+
+ simplify_path_segment(0, p_path.size() - 1, p_path, squared_epsilon, valid_points);
+
+ int valid_point_index = 0;
+
+ for (bool valid : valid_points) {
+ if (valid) {
+ valid_point_index += 1;
+ }
+ }
+
+ LocalVector<uint32_t> simplified_path_indices;
+ simplified_path_indices.resize(valid_point_index);
+ valid_point_index = 0;
+
+ for (uint32_t i = 0; i < valid_points.size(); i++) {
+ if (valid_points[i]) {
+ simplified_path_indices[valid_point_index] = i;
+ valid_point_index += 1;
+ }
+ }
+
+ return simplified_path_indices;
+}
+
+void GodotNavigationServer3D::simplify_path_segment(int p_start_inx, int p_end_inx, const Vector<Vector3> &p_points, real_t p_epsilon, LocalVector<bool> &r_valid_points) {
+ r_valid_points[p_start_inx] = true;
+ r_valid_points[p_end_inx] = true;
+
+ const Vector3 &start_point = p_points[p_start_inx];
+ const Vector3 &end_point = p_points[p_end_inx];
+
+ Vector3 path_segment[2] = { start_point, end_point };
+
+ real_t point_max_distance = 0.0;
+ int point_max_index = 0;
+
+ for (int i = p_start_inx; i < p_end_inx; i++) {
+ const Vector3 &checked_point = p_points[i];
+
+ const Vector3 closest_point = Geometry3D::get_closest_point_to_segment(checked_point, path_segment);
+ real_t distance_squared = closest_point.distance_squared_to(checked_point);
+
+ if (distance_squared > point_max_distance) {
+ point_max_index = i;
+ point_max_distance = distance_squared;
+ }
+ }
+
+ if (point_max_distance > p_epsilon) {
+ simplify_path_segment(p_start_inx, point_max_index, p_points, p_epsilon, r_valid_points);
+ simplify_path_segment(point_max_index, p_end_inx, p_points, p_epsilon, r_valid_points);
+ }
+}
+
int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const {
switch (p_info) {
case INFO_ACTIVE_MAPS: {
diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h
index f7d991d47a..89839ff459 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.h
+++ b/modules/navigation/3d/godot_navigation_server_3d.h
@@ -264,6 +264,13 @@ public:
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override;
+ virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override;
+
+private:
+ static void simplify_path_segment(int p_start_inx, int p_end_inx, const Vector<Vector3> &p_points, real_t p_epsilon, LocalVector<bool> &r_valid_points);
+ static LocalVector<uint32_t> get_simplified_path_indices(const Vector<Vector3> &p_path, real_t p_epsilon);
+
+public:
COMMAND_1(free, RID, p_object);
virtual void set_active(bool p_active) override;
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp
index 8b43eba080..b1b3cbed5d 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp
@@ -45,12 +45,12 @@
#include "scene/resources/3d/convex_polygon_shape_3d.h"
#include "scene/resources/3d/cylinder_shape_3d.h"
#include "scene/resources/3d/height_map_shape_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/shape_3d.h"
#include "scene/resources/3d/sphere_shape_3d.h"
#include "scene/resources/3d/world_boundary_shape_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
@@ -85,7 +85,7 @@ NavMeshGenerator3D::NavMeshGenerator3D() {
// Using threads might cause problems on certain exports or with the Editor on certain devices.
// This is the main switch to turn threaded navmesh baking off should the need arise.
- use_threads = baking_use_multiple_threads && !Engine::get_singleton()->is_editor_hint();
+ use_threads = baking_use_multiple_threads;
}
NavMeshGenerator3D::~NavMeshGenerator3D() {
@@ -700,7 +700,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
cfg.detailSampleDist = MAX(p_navigation_mesh->get_cell_size() * p_navigation_mesh->get_detail_sample_distance(), 0.1f);
cfg.detailSampleMaxError = p_navigation_mesh->get_cell_height() * p_navigation_mesh->get_detail_sample_max_error();
- if (p_navigation_mesh->get_border_size() > 0.0 && !Math::is_equal_approx(p_navigation_mesh->get_cell_size(), p_navigation_mesh->get_border_size())) {
+ if (p_navigation_mesh->get_border_size() > 0.0 && Math::fmod(p_navigation_mesh->get_border_size(), p_navigation_mesh->get_cell_size()) != 0.0) {
WARN_PRINT("Property border_size is ceiled to cell_size voxel units and loses precision.");
}
if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_navigation_mesh->get_agent_height())) {
diff --git a/modules/navigation/3d/navigation_mesh_generator.cpp b/modules/navigation/3d/navigation_mesh_generator.cpp
index 8393896db1..54df42e266 100644
--- a/modules/navigation/3d/navigation_mesh_generator.cpp
+++ b/modules/navigation/3d/navigation_mesh_generator.cpp
@@ -32,7 +32,7 @@
#include "navigation_mesh_generator.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr;
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index 18d66c7b69..d7bf1cdd38 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -42,7 +42,6 @@
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/label.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
void NavigationMeshEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -99,7 +98,7 @@ void NavigationMeshEditor::_bake_pressed() {
}
}
- node->bake_navigation_mesh(false);
+ node->bake_navigation_mesh(true);
node->update_gizmos();
}
diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml
index 9cc548dd6f..23d932ddd7 100644
--- a/modules/openxr/doc_classes/OpenXRHand.xml
+++ b/modules/openxr/doc_classes/OpenXRHand.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="OpenXRHand" inherits="SkeletonModifier3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="OpenXRHand" inherits="Node3D" deprecated="Use [XRHandModifier3D] instead." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Node supporting hand and finger tracking in OpenXR.
</brief_description>
@@ -18,11 +18,14 @@
<member name="hand" type="int" setter="set_hand" getter="get_hand" enum="OpenXRHand.Hands" default="0">
Specifies whether this node tracks the left or right hand of the player.
</member>
+ <member name="hand_skeleton" type="NodePath" setter="set_hand_skeleton" getter="get_hand_skeleton" default="NodePath(&quot;&quot;)">
+ Set a [Skeleton3D] node for which the pose positions will be updated.
+ </member>
<member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0">
Set the motion range (if supported) limiting the hand motion.
</member>
<member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0">
- Set the type of skeleton rig the parent [Skeleton3D] is compliant with.
+ Set the type of skeleton rig the [member hand_skeleton] is compliant with.
</member>
</members>
<constants>
diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp
index f20d1f8e19..2a4104f6ee 100644
--- a/modules/openxr/scene/openxr_hand.cpp
+++ b/modules/openxr/scene/openxr_hand.cpp
@@ -40,6 +40,9 @@ void OpenXRHand::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_hand", "hand"), &OpenXRHand::set_hand);
ClassDB::bind_method(D_METHOD("get_hand"), &OpenXRHand::get_hand);
+ ClassDB::bind_method(D_METHOD("set_hand_skeleton", "hand_skeleton"), &OpenXRHand::set_hand_skeleton);
+ ClassDB::bind_method(D_METHOD("get_hand_skeleton"), &OpenXRHand::get_hand_skeleton);
+
ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range);
ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range);
@@ -51,6 +54,7 @@ void OpenXRHand::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_update", PROPERTY_HINT_ENUM, "Full,Rotation Only"), "set_bone_update", "get_bone_update");
@@ -86,6 +90,12 @@ OpenXRHand::Hands OpenXRHand::get_hand() const {
return hand;
}
+void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) {
+ hand_skeleton = p_hand_skeleton;
+
+ // TODO if inside tree call _get_bones()
+}
+
void OpenXRHand::set_motion_range(MotionRange p_motion_range) {
ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX);
motion_range = p_motion_range;
@@ -97,6 +107,10 @@ OpenXRHand::MotionRange OpenXRHand::get_motion_range() const {
return motion_range;
}
+NodePath OpenXRHand::get_hand_skeleton() const {
+ return hand_skeleton;
+}
+
void OpenXRHand::_set_motion_range() {
if (!hand_tracking_ext) {
return;
@@ -138,6 +152,20 @@ OpenXRHand::BoneUpdate OpenXRHand::get_bone_update() const {
return bone_update;
}
+Skeleton3D *OpenXRHand::get_skeleton() {
+ if (!has_node(hand_skeleton)) {
+ return nullptr;
+ }
+
+ Node *node = get_node(hand_skeleton);
+ if (!node) {
+ return nullptr;
+ }
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ return skeleton;
+}
+
void OpenXRHand::_get_joint_data() {
// Table of bone names for different rig types.
static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = {
@@ -262,7 +290,7 @@ void OpenXRHand::_get_joint_data() {
}
}
-void OpenXRHand::_process_modification() {
+void OpenXRHand::_update_skeleton() {
if (openxr_api == nullptr || !openxr_api->is_initialized()) {
return;
} else if (hand_tracking_ext == nullptr || !hand_tracking_ext->get_active()) {
@@ -367,14 +395,21 @@ void OpenXRHand::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_get_joint_data();
+
+ set_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
+ set_process_internal(false);
+
// reset
for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) {
joints[i].bone = -1;
joints[i].parent_joint = -1;
}
} break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_skeleton();
+ } break;
default: {
} break;
}
diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h
index fc0a994f48..4c77e7277c 100644
--- a/modules/openxr/scene/openxr_hand.h
+++ b/modules/openxr/scene/openxr_hand.h
@@ -31,15 +31,16 @@
#ifndef OPENXR_HAND_H
#define OPENXR_HAND_H
-#include "scene/3d/skeleton_modifier_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
#include <openxr/openxr.h>
class OpenXRAPI;
class OpenXRHandTrackingExtension;
-class OpenXRHand : public SkeletonModifier3D {
- GDCLASS(OpenXRHand, SkeletonModifier3D);
+class OpenXRHand : public Node3D {
+ GDCLASS(OpenXRHand, Node3D);
public:
enum Hands { // Deprecated, need to change this to OpenXRInterface::Hands.
@@ -85,13 +86,13 @@ private:
void _set_motion_range();
+ Skeleton3D *get_skeleton();
void _get_joint_data();
+ void _update_skeleton();
protected:
static void _bind_methods();
- virtual void _process_modification() override;
-
public:
OpenXRHand();
@@ -101,6 +102,9 @@ public:
void set_motion_range(MotionRange p_motion_range);
MotionRange get_motion_range() const;
+ void set_hand_skeleton(const NodePath &p_hand_skeleton);
+ NodePath get_hand_skeleton() const;
+
void set_skeleton_rig(SkeletonRig p_skeleton_rig);
SkeletonRig get_skeleton_rig() const;
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index affe163aeb..d903137195 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -173,9 +173,17 @@ void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const
}
Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
- String svg = p_fileaccess->get_as_utf8_string();
+ const uint64_t len = p_fileaccess->get_length() - p_fileaccess->get_position();
+ Vector<uint8_t> buffer;
+ buffer.resize(len);
+ p_fileaccess->get_buffer(buffer.ptrw(), buffer.size());
+
+ String svg;
+ Error err = svg.parse_utf8((const char *)buffer.ptr(), buffer.size());
+ if (err != OK) {
+ return err;
+ }
- Error err;
if (p_flags & FLAG_CONVERT_COLORS) {
err = create_image_from_string(p_image, svg, p_scale, false, forced_color_map);
} else {
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 341d14a3b1..020e432155 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -15,11 +15,11 @@
Array of random bytes that the licensing Policy uses to create an [url=https://developer.android.com/google/play/licensing/adding-licensing#impl-Obfuscator]Obfuscator[/url].
</member>
<member name="apk_expansion/enable" type="bool" setter="" getter="">
- If [code]true[/code], project resources are stored in the separate APK expansion file, instead APK.
- [b]Note:[/b] APK expansion should be enabled to use PCK encryption.
+ If [code]true[/code], project resources are stored in the separate APK expansion file, instead of the APK.
+ [b]Note:[/b] APK expansion should be enabled to use PCK encryption. See [url=https://developer.android.com/google/play/expansion-files]APK Expansion Files[/url]
</member>
<member name="apk_expansion/public_key" type="String" setter="" getter="">
- Base64 encoded RSA public key for your publisher account, available from the profile page on the "Play Console".
+ Base64 encoded RSA public key for your publisher account, available from the profile page on the "Google Play Console".
</member>
<member name="architectures/arm64-v8a" type="bool" setter="" getter="">
If [code]true[/code], [code]arm64[/code] binaries are included into exported project.
@@ -34,7 +34,7 @@
If [code]true[/code], [code]x86_64[/code] binaries are included into exported project.
</member>
<member name="command_line/extra_args" type="String" setter="" getter="">
- A list of additional command line arguments, exported project will receive when started.
+ A list of additional command line arguments, separated by space, which the exported project will receive when started.
</member>
<member name="custom_template/debug" type="String" setter="" getter="">
Path to an APK file to use as a custom export template for debug exports. If left empty, default template is used.
@@ -52,16 +52,16 @@
[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.
+ Application export format (*.apk or *.aab).
</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.
+ Minimum Android API level required for the application to run (used during Gradle build). See [url=https://developer.android.com/guide/topics/manifest/uses-sdk-element#uses]android:minSdkVersion[/url].
</member>
<member name="gradle_build/target_sdk" type="String" setter="" getter="">
- Target Android SDK version for Gradle build.
+ The Android API level on which the application is designed to run (used during Gradle build). See [url=https://developer.android.com/guide/topics/manifest/uses-sdk-element#uses]android:targetSdkVersion[/url].
</member>
<member name="gradle_build/use_gradle_build" type="bool" setter="" getter="">
If [code]true[/code], Gradle build is used instead of pre-built APK.
@@ -97,25 +97,25 @@
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_USER[/code].
</member>
<member name="launcher_icons/adaptive_background_432x432" type="String" setter="" getter="">
- Background layer of the application adaptive icon file.
+ Background layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
</member>
<member name="launcher_icons/adaptive_foreground_432x432" type="String" setter="" getter="">
- Foreground layer of the application adaptive icon file.
+ Foreground layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
</member>
<member name="launcher_icons/main_192x192" type="String" setter="" getter="">
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon].
</member>
<member name="package/app_category" type="int" setter="" getter="">
- Application category for the Play Store.
+ Application category for the Google Play Store. Only define this if your application fits one of the categories well. See [url=https://developer.android.com/guide/topics/manifest/application-element#appCategory]android:appCategory[/url].
</member>
<member name="package/exclude_from_recents" type="bool" setter="" getter="">
- If [code]true[/code], task initiated by main activity will be excluded from the list of recently used applications.
+ If [code]true[/code], task initiated by main activity will be excluded from the list of recently used applications. See [url=https://developer.android.com/guide/topics/manifest/activity-element#exclude]android:excludeFromRecents[/url].
</member>
<member name="package/name" type="String" setter="" getter="">
Name of the application.
</member>
<member name="package/retain_data_on_uninstall" type="bool" setter="" getter="">
- If [code]true[/code], when the user uninstalls an app, a prompt to keep the app's data will be shown.
+ If [code]true[/code], when the user uninstalls an app, a prompt to keep the app's data will be shown. See [url=https://developer.android.com/guide/topics/manifest/application-element#fragileuserdata]android:hasFragileUserData[/url].
</member>
<member name="package/show_as_launcher_app" type="bool" setter="" getter="">
If [code]true[/code], the user will be able to set this app as the system launcher in Android preferences.
@@ -378,10 +378,10 @@
Allows applications to perform I/O operations over NFC. See [url=https://developer.android.com/reference/android/Manifest.permission#NFC]NFC[/url].
</member>
<member name="permissions/persistent_activity" type="bool" setter="" getter="" deprecated="Deprecated in API level 15.">
- Allow an application to make its activities persistent.
+ Allows an application to make its activities persistent.
</member>
- <member name="permissions/post_notifications" type="bool" setter="" getter="" deprecated="">
- Allow an application to post notifications. Added in API level 33. See [url=https://developer.android.com/develop/ui/views/notifications/notification-permission]Notification runtime permission[/url].
+ <member name="permissions/post_notifications" type="bool" setter="" getter="">
+ Allows an application to post notifications. Added in API level 33. See [url=https://developer.android.com/develop/ui/views/notifications/notification-permission]Notification runtime permission[/url].
</member>
<member name="permissions/process_outgoing_calls" type="bool" setter="" getter="" deprecated="Deprecated in API level 29.">
Allows an application to see the number being dialed during an outgoing call with the option to redirect the call to a different number or abort the call altogether. See [url=https://developer.android.com/reference/android/Manifest.permission#PROCESS_OUTGOING_CALLS]PROCESS_OUTGOING_CALLS[/url].
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index b19760026a..7cab5e8d90 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1393,6 +1393,14 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
p_manifest = ret;
}
+String EditorExportPlatformAndroid::_get_keystore_path(const Ref<EditorExportPreset> &p_preset, bool p_debug) {
+ String keystore_preference = p_debug ? "keystore/debug" : "keystore/release";
+ String keystore_env_variable = p_debug ? ENV_ANDROID_KEYSTORE_DEBUG_PATH : ENV_ANDROID_KEYSTORE_RELEASE_PATH;
+ String keystore_path = p_preset->get_or_env(keystore_preference, keystore_env_variable);
+
+ return ProjectSettings::get_singleton()->globalize_path(keystore_path).simplify_path();
+}
+
String EditorExportPlatformAndroid::_parse_string(const uint8_t *p_bytes, bool p_utf8) {
uint32_t offset = 0;
uint32_t len = 0;
@@ -2347,10 +2355,10 @@ static bool has_valid_keystore_credentials(String &r_error_str, const String &p_
}
bool EditorExportPlatformAndroid::has_valid_username_and_password(const Ref<EditorExportPreset> &p_preset, String &r_error) {
- String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String dk = _get_keystore_path(p_preset, true);
String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
- String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String rk = _get_keystore_path(p_preset, false);
String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
@@ -2449,7 +2457,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
// Validate the rest of the export configuration.
- String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String dk = _get_keystore_path(p_preset, true);
String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
@@ -2467,7 +2475,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
}
- String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String rk = _get_keystore_path(p_preset, false);
String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
@@ -2724,7 +2732,7 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
- String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String release_keystore = _get_keystore_path(p_preset, false);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
String target_sdk_version = p_preset->get("gradle_build/target_sdk");
@@ -2746,7 +2754,7 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
String password;
String user;
if (p_debug) {
- keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ keystore = _get_keystore_path(p_preset, true);
password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
@@ -3231,7 +3239,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
if (should_sign) {
if (p_debug) {
- String debug_keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
+ String debug_keystore = _get_keystore_path(p_preset, true);
String debug_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
String debug_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
@@ -3253,7 +3261,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password.
} else {
// Pass the release keystore info as well
- String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
+ String release_keystore = _get_keystore_path(p_preset, false);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if (release_keystore.is_relative_path()) {
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index b968302449..7bc7bbf9e9 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -166,6 +166,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet);
+ static String _get_keystore_path(const Ref<EditorExportPreset> &p_preset, bool p_debug);
+
static String _parse_string(const uint8_t *p_bytes, bool p_utf8);
void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest);
diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm
index c989dc6221..bcf21bc802 100644
--- a/platform/ios/os_ios.mm
+++ b/platform/ios/os_ios.mm
@@ -149,10 +149,6 @@ void OS_IOS::deinitialize_modules() {
void OS_IOS::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
-
- if (main_loop) {
- main_loop->initialize();
- }
}
MainLoop *OS_IOS::get_main_loop() const {
@@ -181,7 +177,9 @@ bool OS_IOS::iterate() {
}
void OS_IOS::start() {
- Main::start();
+ if (Main::start() == EXIT_SUCCESS) {
+ main_loop->initialize();
+ }
if (joypad_ios) {
joypad_ios->start_processing();
diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp
index d00d5deb2c..a815db1c05 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.cpp
+++ b/platform/linuxbsd/wayland/display_server_wayland.cpp
@@ -1174,14 +1174,6 @@ void DisplayServerWayland::release_rendering_thread() {
#endif
}
-void DisplayServerWayland::make_rendering_thread() {
-#ifdef GLES3_ENABLED
- if (egl_manager) {
- egl_manager->make_current();
- }
-#endif
-}
-
void DisplayServerWayland::swap_buffers() {
#ifdef GLES3_ENABLED
if (egl_manager) {
diff --git a/platform/linuxbsd/wayland/display_server_wayland.h b/platform/linuxbsd/wayland/display_server_wayland.h
index b7d7bee005..368f1b402b 100644
--- a/platform/linuxbsd/wayland/display_server_wayland.h
+++ b/platform/linuxbsd/wayland/display_server_wayland.h
@@ -276,7 +276,6 @@ public:
virtual void process_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_context(Context p_context) override;
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 9069dd423f..4c7dfbb107 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/platform/linuxbsd/x11/display_server_x11.cpp
@@ -4268,7 +4268,7 @@ bool DisplayServerX11::_window_focus_check() {
}
void DisplayServerX11::process_events() {
- _THREAD_SAFE_METHOD_
+ _THREAD_SAFE_LOCK_
#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
static int frame = 0;
@@ -5097,6 +5097,8 @@ void DisplayServerX11::process_events() {
*/
}
+ _THREAD_SAFE_UNLOCK_
+
Input::get_singleton()->flush_buffered_events();
}
@@ -5111,17 +5113,6 @@ void DisplayServerX11::release_rendering_thread() {
#endif
}
-void DisplayServerX11::make_rendering_thread() {
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->make_current();
- }
- if (gl_manager_egl) {
- gl_manager_egl->make_current();
- }
-#endif
-}
-
void DisplayServerX11::swap_buffers() {
#if defined(GLES3_ENABLED)
if (gl_manager) {
diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h
index 715a8e48e6..8a7062857c 100644
--- a/platform/linuxbsd/x11/display_server_x11.h
+++ b/platform/linuxbsd/x11/display_server_x11.h
@@ -526,7 +526,6 @@ public:
virtual void process_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_context(Context p_context) override;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.cpp b/platform/linuxbsd/x11/gl_manager_x11.cpp
index 602dd784e0..febb7ae584 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.cpp
+++ b/platform/linuxbsd/x11/gl_manager_x11.cpp
@@ -311,20 +311,6 @@ void GLManager_X11::window_make_current(DisplayServer::WindowID p_window_id) {
_internal_set_current_window(&win);
}
-void GLManager_X11::make_current() {
- if (!_current_window) {
- return;
- }
- if (!_current_window->in_use) {
- WARN_PRINT("current window not in use!");
- return;
- }
- const GLDisplay &disp = get_current_display();
- if (!glXMakeCurrent(_x_windisp.x11_display, _x_windisp.x11_window, disp.context->glx_context)) {
- ERR_PRINT("glXMakeCurrent failed");
- }
-}
-
void GLManager_X11::swap_buffers() {
if (!_current_window) {
return;
diff --git a/platform/linuxbsd/x11/gl_manager_x11.h b/platform/linuxbsd/x11/gl_manager_x11.h
index 235c7d22f9..06e147e39f 100644
--- a/platform/linuxbsd/x11/gl_manager_x11.h
+++ b/platform/linuxbsd/x11/gl_manager_x11.h
@@ -117,7 +117,6 @@ public:
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h
index 7c9d073afc..5d38bf55ea 100644
--- a/platform/macos/display_server_macos.h
+++ b/platform/macos/display_server_macos.h
@@ -426,7 +426,6 @@ public:
virtual void force_process_and_drop_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_native_icon(const String &p_filename) override;
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index b7a9fb1bbd..cfe925e79b 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -358,7 +358,6 @@ void DisplayServerMacOS::_dispatch_input_events(const Ref<InputEvent> &p_event)
}
void DisplayServerMacOS::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- _THREAD_SAFE_METHOD_
if (!in_dispatch_input_event) {
in_dispatch_input_event = true;
@@ -2986,7 +2985,7 @@ Key DisplayServerMacOS::keyboard_get_label_from_physical(Key p_keycode) const {
}
void DisplayServerMacOS::process_events() {
- _THREAD_SAFE_METHOD_
+ _THREAD_SAFE_LOCK_
while (true) {
NSEvent *event = [NSApp
@@ -3019,7 +3018,9 @@ void DisplayServerMacOS::process_events() {
if (!drop_events) {
_process_key_events();
+ _THREAD_SAFE_UNLOCK_
Input::get_singleton()->flush_buffered_events();
+ _THREAD_SAFE_LOCK_
}
for (KeyValue<WindowID, WindowData> &E : windows) {
@@ -3045,6 +3046,8 @@ void DisplayServerMacOS::process_events() {
}
}
}
+
+ _THREAD_SAFE_UNLOCK_
}
void DisplayServerMacOS::force_process_and_drop_events() {
@@ -3056,9 +3059,14 @@ void DisplayServerMacOS::force_process_and_drop_events() {
}
void DisplayServerMacOS::release_rendering_thread() {
-}
-
-void DisplayServerMacOS::make_rendering_thread() {
+#if defined(GLES3_ENABLED)
+ if (gl_manager_angle) {
+ gl_manager_angle->release_current();
+ }
+ if (gl_manager_legacy) {
+ gl_manager_legacy->release_current();
+ }
+#endif
}
void DisplayServerMacOS::swap_buffers() {
diff --git a/platform/macos/gl_manager_macos_legacy.h b/platform/macos/gl_manager_macos_legacy.h
index bafe825efb..af9be8f5ba 100644
--- a/platform/macos/gl_manager_macos_legacy.h
+++ b/platform/macos/gl_manager_macos_legacy.h
@@ -73,7 +73,6 @@ public:
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/platform/macos/gl_manager_macos_legacy.mm b/platform/macos/gl_manager_macos_legacy.mm
index 701de6df78..6ce3831d9c 100644
--- a/platform/macos/gl_manager_macos_legacy.mm
+++ b/platform/macos/gl_manager_macos_legacy.mm
@@ -117,6 +117,7 @@ void GLManagerLegacy_MacOS::release_current() {
}
[NSOpenGLContext clearCurrentContext];
+ current_window = DisplayServer::INVALID_WINDOW_ID;
}
void GLManagerLegacy_MacOS::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -133,18 +134,6 @@ void GLManagerLegacy_MacOS::window_make_current(DisplayServer::WindowID p_window
current_window = p_window_id;
}
-void GLManagerLegacy_MacOS::make_current() {
- if (current_window == DisplayServer::INVALID_WINDOW_ID) {
- return;
- }
- if (!windows.has(current_window)) {
- return;
- }
-
- GLWindow &win = windows[current_window];
- [win.context makeCurrentContext];
-}
-
void GLManagerLegacy_MacOS::swap_buffers() {
GLWindow &win = windows[current_window];
[win.context flushBuffer];
diff --git a/platform/macos/godot_open_save_delegate.mm b/platform/macos/godot_open_save_delegate.mm
index 6b55b70629..6ffd939545 100644
--- a/platform/macos/godot_open_save_delegate.mm
+++ b/platform/macos/godot_open_save_delegate.mm
@@ -177,14 +177,14 @@
if ([new_allowed_types count] > 0) {
NSMutableArray *type_filters = [new_allowed_types objectAtIndex:0];
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
- [p_panel setAllowedFileTypes:@[]];
+ [p_panel setAllowedFileTypes:nil];
[p_panel setAllowsOtherFileTypes:true];
} else {
[p_panel setAllowsOtherFileTypes:false];
[p_panel setAllowedFileTypes:type_filters];
}
} else {
- [p_panel setAllowedFileTypes:@[]];
+ [p_panel setAllowedFileTypes:nil];
[p_panel setAllowsOtherFileTypes:true];
}
}
@@ -248,7 +248,7 @@
if (allowed_types && index < [allowed_types count]) {
NSMutableArray *type_filters = [allowed_types objectAtIndex:index];
if (type_filters && [type_filters count] == 1 && [[type_filters objectAtIndex:0] isEqualToString:@"*"]) {
- [dialog setAllowedFileTypes:@[]];
+ [dialog setAllowedFileTypes:nil];
[dialog setAllowsOtherFileTypes:true];
} else {
[dialog setAllowsOtherFileTypes:false];
@@ -256,7 +256,7 @@
}
cur_index = index;
} else {
- [dialog setAllowedFileTypes:@[]];
+ [dialog setAllowedFileTypes:nil];
[dialog setAllowsOtherFileTypes:true];
cur_index = -1;
}
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index 8fc89a79d9..4158295520 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -132,6 +132,10 @@ bool OS_Web::is_process_running(const ProcessID &p_pid) const {
return false;
}
+int OS_Web::get_process_exit_code(const ProcessID &p_pid) const {
+ return -1;
+}
+
int OS_Web::get_processor_count() const {
return godot_js_os_hw_concurrency_get();
}
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index 537b05048a..eeeafdac34 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -85,6 +85,7 @@ public:
Error kill(const ProcessID &p_pid) override;
int get_process_id() const override;
bool is_process_running(const ProcessID &p_pid) const override;
+ int get_process_exit_code(const ProcessID &p_pid) const override;
int get_processor_count() const override;
String get_unique_id() const override;
int get_default_thread_pool_size() const override { return 1; }
diff --git a/platform/web/serve.py b/platform/web/serve.py
index 6a3efcc463..89dff63ca3 100755
--- a/platform/web/serve.py
+++ b/platform/web/serve.py
@@ -5,9 +5,20 @@ from pathlib import Path
import os
import sys
import argparse
+import contextlib
+import socket
import subprocess
+# See cpython GH-17851 and GH-17864.
+class DualStackServer(HTTPServer):
+ def server_bind(self):
+ # Suppress exception when protocol is IPv4.
+ with contextlib.suppress(Exception):
+ self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
+ return super().server_bind()
+
+
class CORSRequestHandler(SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
@@ -32,7 +43,7 @@ def serve(root, port, run_browser):
print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).")
shell_open(f"http://127.0.0.1:{port}")
- test(CORSRequestHandler, HTTPServer, port=port)
+ test(CORSRequestHandler, DualStackServer, port=port)
if __name__ == "__main__":
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 196beb423f..f34d479345 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -202,9 +202,7 @@ def get_opts():
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False),
BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False),
- BoolVariable(
- "silence_msvc", "Silence MSVC's stdout to decrease output log bloat. May hide error messages.", False
- ),
+ BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting any errors to stderr.", True),
("angle_libs", "Path to the ANGLE static libraries", ""),
# Direct3D 12 support.
(
@@ -398,16 +396,35 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable.
if env["silence_msvc"]:
- env.Prepend(CCFLAGS=[">", "NUL"])
- env.Prepend(LINKFLAGS=[">", "NUL"])
+ from tempfile import mkstemp
+
+ old_spawn = env["SPAWN"]
+
+ def spawn_capture(sh, escape, cmd, args, env):
+ # We only care about cl/link, process everything else as normal.
+ if args[0] not in ["cl", "link"]:
+ return old_spawn(sh, escape, cmd, args, env)
- # "> NUL" fails if using a tempfile, circumvent by removing the argument altogether.
- old_esc_func = env["TEMPFILEARGESCFUNC"]
+ tmp_stdout, tmp_stdout_name = mkstemp()
+ os.close(tmp_stdout)
+ args.append(f">{tmp_stdout_name}")
+ ret = old_spawn(sh, escape, cmd, args, env)
+
+ try:
+ with open(tmp_stdout_name, "rb") as tmp_stdout:
+ # First line is always bloat, subsequent lines are always errors. If content
+ # exists after discarding the first line, safely decode & send to stderr.
+ tmp_stdout.readline()
+ content = tmp_stdout.read()
+ if content:
+ sys.stderr.write(content.decode(sys.stdout.encoding, "replace"))
+ os.remove(tmp_stdout_name)
+ except OSError:
+ pass
- def trim_nul(arg):
- return "" if arg in [">", "NUL"] else old_esc_func(arg)
+ return ret
- env["TEMPFILEARGESCFUNC"] = trim_nul
+ env["SPAWN"] = spawn_capture
if env["debug_crt"]:
# Always use dynamic runtime, static debug CRT breaks thread_local.
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 74665664b1..b6b713687f 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -2962,7 +2962,7 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
}
void DisplayServerWindows::process_events() {
- _THREAD_SAFE_METHOD_
+ _THREAD_SAFE_LOCK_
MSG msg;
@@ -2977,7 +2977,10 @@ void DisplayServerWindows::process_events() {
if (!drop_events) {
_process_key_events();
+ _THREAD_SAFE_UNLOCK_
Input::get_singleton()->flush_buffered_events();
+ } else {
+ _THREAD_SAFE_UNLOCK_
}
}
@@ -2990,9 +2993,14 @@ void DisplayServerWindows::force_process_and_drop_events() {
}
void DisplayServerWindows::release_rendering_thread() {
-}
-
-void DisplayServerWindows::make_rendering_thread() {
+#if defined(GLES3_ENABLED)
+ if (gl_manager_angle) {
+ gl_manager_angle->release_current();
+ }
+ if (gl_manager_native) {
+ gl_manager_native->release_current();
+ }
+#endif
}
void DisplayServerWindows::swap_buffers() {
@@ -3433,7 +3441,6 @@ void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event
}
void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- _THREAD_SAFE_METHOD_
if (in_dispatch_input_event) {
return;
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 4792b65801..2fe1b0733d 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -679,7 +679,6 @@ public:
virtual void force_process_and_drop_events() override;
virtual void release_rendering_thread() override;
- virtual void make_rendering_thread() override;
virtual void swap_buffers() override;
virtual void set_native_icon(const String &p_filename) override;
diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp
index da837b3f94..80cf818199 100644
--- a/platform/windows/gl_manager_windows_native.cpp
+++ b/platform/windows/gl_manager_windows_native.cpp
@@ -427,10 +427,6 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id
return OK;
}
-void GLManagerNative_Windows::_internal_set_current_window(GLWindow *p_win) {
- _current_window = p_win;
-}
-
void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
GLWindow &win = get_window(p_window_id);
if (_current_window == &win) {
@@ -447,6 +443,8 @@ void GLManagerNative_Windows::release_current() {
if (!gd_wglMakeCurrent(_current_window->hDC, nullptr)) {
ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError()));
}
+
+ _current_window = nullptr;
}
void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
@@ -467,17 +465,7 @@ void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_wind
ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError()));
}
- _internal_set_current_window(&win);
-}
-
-void GLManagerNative_Windows::make_current() {
- if (!_current_window) {
- return;
- }
- const GLDisplay &disp = get_current_display();
- if (!gd_wglMakeCurrent(_current_window->hDC, disp.hRC)) {
- ERR_PRINT("Could not switch OpenGL context to window marked current: " + format_error_message(GetLastError()));
- }
+ _current_window = &win;
}
void GLManagerNative_Windows::swap_buffers() {
@@ -491,7 +479,6 @@ Error GLManagerNative_Windows::initialize() {
void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use) {
GLWindow &win = get_window(p_window_id);
- GLWindow *current = _current_window;
if (&win != _current_window) {
window_make_current(p_window_id);
@@ -506,11 +493,6 @@ void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id,
} else {
WARN_PRINT("Could not set V-Sync mode. V-Sync is not supported.");
}
-
- if (current != _current_window) {
- _current_window = current;
- make_current();
- }
}
bool GLManagerNative_Windows::is_using_vsync(DisplayServer::WindowID p_window_id) const {
diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h
index 829c53b3d2..b4e2a3acdf 100644
--- a/platform/windows/gl_manager_windows_native.h
+++ b/platform/windows/gl_manager_windows_native.h
@@ -68,9 +68,6 @@ private:
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
- // funcs
- void _internal_set_current_window(GLWindow *p_win);
-
GLWindow &get_window(unsigned int id) { return _windows[id]; }
const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
@@ -91,7 +88,6 @@ public:
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {}
void release_current();
- void make_current();
void swap_buffers();
void window_make_current(DisplayServer::WindowID p_window_id);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index e0e35c6e0f..f2a9989606 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -867,7 +867,9 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String
CloseHandle(pipe_err[1]);
ProcessID pid = pi.pi.dwProcessId;
+ process_map_mutex.lock();
process_map->insert(pid, pi);
+ process_map_mutex.unlock();
Ref<FileAccessWindowsPipe> main_pipe;
main_pipe.instantiate();
@@ -1014,13 +1016,16 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
if (r_child_id) {
*r_child_id = pid;
}
+ process_map_mutex.lock();
process_map->insert(pid, pi);
+ process_map_mutex.unlock();
return OK;
}
Error OS_Windows::kill(const ProcessID &p_pid) {
int ret = 0;
+ MutexLock lock(process_map_mutex);
if (process_map->has(p_pid)) {
const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
process_map->erase(p_pid);
@@ -1046,24 +1051,58 @@ int OS_Windows::get_process_id() const {
}
bool OS_Windows::is_process_running(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
if (!process_map->has(p_pid)) {
return false;
}
- const PROCESS_INFORMATION &pi = (*process_map)[p_pid].pi;
+ const ProcessInfo &info = (*process_map)[p_pid];
+ if (!info.is_running) {
+ return false;
+ }
+ const PROCESS_INFORMATION &pi = info.pi;
DWORD dw_exit_code = 0;
if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
return false;
}
if (dw_exit_code != STILL_ACTIVE) {
+ info.is_running = false;
+ info.exit_code = dw_exit_code;
return false;
}
return true;
}
+int OS_Windows::get_process_exit_code(const ProcessID &p_pid) const {
+ MutexLock lock(process_map_mutex);
+ if (!process_map->has(p_pid)) {
+ return -1;
+ }
+
+ const ProcessInfo &info = (*process_map)[p_pid];
+ if (!info.is_running) {
+ return info.exit_code;
+ }
+
+ const PROCESS_INFORMATION &pi = info.pi;
+
+ DWORD dw_exit_code = 0;
+ if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
+ return -1;
+ }
+
+ if (dw_exit_code == STILL_ACTIVE) {
+ return -1;
+ }
+
+ info.is_running = false;
+ info.exit_code = dw_exit_code;
+ return dw_exit_code;
+}
+
Error OS_Windows::set_cwd(const String &p_cwd) {
if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) {
return ERR_CANT_OPEN;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index c506af13b2..288154745f 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -150,8 +150,11 @@ protected:
struct ProcessInfo {
STARTUPINFO si;
PROCESS_INFORMATION pi;
+ mutable bool is_running = true;
+ mutable int exit_code = -1;
};
HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
+ Mutex process_map_mutex;
public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
@@ -189,6 +192,7 @@ public:
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
virtual bool is_process_running(const ProcessID &p_pid) const override;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index fee774fe2e..5d14358120 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -89,6 +89,12 @@ void NavigationAgent2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent2D::set_target_position);
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent2D::get_target_position);
+ ClassDB::bind_method(D_METHOD("set_simplify_path", "enabled"), &NavigationAgent2D::set_simplify_path);
+ ClassDB::bind_method(D_METHOD("get_simplify_path"), &NavigationAgent2D::get_simplify_path);
+
+ ClassDB::bind_method(D_METHOD("set_simplify_epsilon", "epsilon"), &NavigationAgent2D::set_simplify_epsilon);
+ ClassDB::bind_method(D_METHOD("get_simplify_epsilon"), &NavigationAgent2D::get_simplify_epsilon);
+
ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent2D::get_next_path_position);
ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent2D::set_velocity_forced);
@@ -129,6 +135,8 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon", PROPERTY_HINT_RANGE, "0.0,10.0,0.001,or_greater,suffix:px"), "set_simplify_epsilon", "get_simplify_epsilon");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
@@ -427,6 +435,24 @@ void NavigationAgent2D::set_path_postprocessing(const NavigationPathQueryParamet
navigation_query->set_path_postprocessing(path_postprocessing);
}
+void NavigationAgent2D::set_simplify_path(bool p_enabled) {
+ simplify_path = p_enabled;
+ navigation_query->set_simplify_path(simplify_path);
+}
+
+bool NavigationAgent2D::get_simplify_path() const {
+ return simplify_path;
+}
+
+void NavigationAgent2D::set_simplify_epsilon(real_t p_epsilon) {
+ simplify_epsilon = MAX(0.0, p_epsilon);
+ navigation_query->set_simplify_epsilon(simplify_epsilon);
+}
+
+real_t NavigationAgent2D::get_simplify_epsilon() const {
+ return simplify_epsilon;
+}
+
void NavigationAgent2D::set_path_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_path_metadata_flags) {
if (path_metadata_flags == p_path_metadata_flags) {
return;
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 0e46086a81..0acfc82162 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -63,6 +63,8 @@ class NavigationAgent2D : public Node {
real_t time_horizon_obstacles = 0.0;
real_t max_speed = 100.0;
real_t path_max_distance = 100.0;
+ bool simplify_path = false;
+ real_t simplify_epsilon = 0.0;
Vector2 target_position;
@@ -179,6 +181,12 @@ public:
void set_target_position(Vector2 p_position);
Vector2 get_target_position() const;
+ void set_simplify_path(bool p_enabled);
+ bool get_simplify_path() const;
+
+ void set_simplify_epsilon(real_t p_epsilon);
+ real_t get_simplify_epsilon() const;
+
Vector2 get_next_path_position();
Ref<NavigationPathQueryResult2D> get_current_navigation_result() const { return navigation_result; }
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 5510b59903..ab44e57d05 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -31,7 +31,6 @@
#include "navigation_region_2d.h"
#include "core/math/geometry_2d.h"
-#include "scene/2d/navigation_obstacle_2d.h"
#include "scene/resources/world_2d.h"
#include "servers/navigation_server_2d.h"
@@ -212,11 +211,6 @@ void NavigationRegion2D::set_navigation_map(RID p_navigation_map) {
map_override = p_navigation_map;
NavigationServer2D::get_singleton()->region_set_map(region, map_override);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override);
- }
- }
}
RID NavigationRegion2D::get_navigation_map() const {
@@ -265,7 +259,6 @@ void NavigationRegion2D::_navigation_polygon_changed() {
if (navigation_polygon.is_valid()) {
NavigationServer2D::get_singleton()->region_set_navigation_polygon(region, navigation_polygon);
}
- _update_avoidance_constrain();
}
#ifdef DEBUG_ENABLED
@@ -317,13 +310,6 @@ void NavigationRegion2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &NavigationRegion2D::set_navigation_layer_value);
ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &NavigationRegion2D::get_navigation_layer_value);
- ClassDB::bind_method(D_METHOD("set_constrain_avoidance", "enabled"), &NavigationRegion2D::set_constrain_avoidance);
- ClassDB::bind_method(D_METHOD("get_constrain_avoidance"), &NavigationRegion2D::get_constrain_avoidance);
- ClassDB::bind_method(D_METHOD("set_avoidance_layers", "layers"), &NavigationRegion2D::set_avoidance_layers);
- ClassDB::bind_method(D_METHOD("get_avoidance_layers"), &NavigationRegion2D::get_avoidance_layers);
- ClassDB::bind_method(D_METHOD("set_avoidance_layer_value", "layer_number", "value"), &NavigationRegion2D::set_avoidance_layer_value);
- ClassDB::bind_method(D_METHOD("get_avoidance_layer_value", "layer_number"), &NavigationRegion2D::get_avoidance_layer_value);
-
ClassDB::bind_method(D_METHOD("get_region_rid"), &NavigationRegion2D::get_region_rid);
ClassDB::bind_method(D_METHOD("set_enter_cost", "enter_cost"), &NavigationRegion2D::set_enter_cost);
@@ -343,8 +329,6 @@ void NavigationRegion2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "enter_cost"), "set_enter_cost", "get_enter_cost");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "travel_cost"), "set_travel_cost", "get_travel_cost");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "constrain_avoidance"), "set_constrain_avoidance", "get_constrain_avoidance");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers");
ADD_SIGNAL(MethodInfo("navigation_polygon_changed"));
ADD_SIGNAL(MethodInfo("bake_finished"));
@@ -391,131 +375,12 @@ NavigationRegion2D::~NavigationRegion2D() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(region);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]);
- }
- }
- constrain_avoidance_obstacles.clear();
-
#ifdef DEBUG_ENABLED
NavigationServer2D::get_singleton()->disconnect(SNAME("map_changed"), callable_mp(this, &NavigationRegion2D::_navigation_map_changed));
NavigationServer2D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationRegion2D::_navigation_debug_changed));
#endif // DEBUG_ENABLED
}
-void NavigationRegion2D::_update_avoidance_constrain() {
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->free(constrain_avoidance_obstacles[i]);
- constrain_avoidance_obstacles[i] = RID();
- }
- }
- constrain_avoidance_obstacles.clear();
-
- if (!constrain_avoidance) {
- return;
- }
-
- if (get_navigation_polygon() == nullptr) {
- return;
- }
-
- Ref<NavigationPolygon> _navpoly = get_navigation_polygon();
- int _outline_count = _navpoly->get_outline_count();
- if (_outline_count == 0) {
- return;
- }
-
- for (int outline_index(0); outline_index < _outline_count; outline_index++) {
- const Vector<Vector2> &_outline = _navpoly->get_outline(outline_index);
-
- const int outline_size = _outline.size();
- if (outline_size < 3) {
- ERR_FAIL_COND_MSG(_outline.size() < 3, "NavigationPolygon outline needs to have at least 3 vertex to create avoidance obstacles to constrain avoidance agent's");
- continue;
- }
-
- RID obstacle_rid = NavigationServer2D::get_singleton()->obstacle_create();
- constrain_avoidance_obstacles.push_back(obstacle_rid);
-
- Vector<Vector2> new_obstacle_outline;
-
- if (outline_index == 0) {
- for (int i(0); i < outline_size; i++) {
- new_obstacle_outline.push_back(_outline[outline_size - i - 1]);
- }
- ERR_FAIL_COND_MSG(Geometry2D::is_polygon_clockwise(_outline), "Outer most outline needs to be clockwise to push avoidance agent inside");
- } else {
- for (int i(0); i < outline_size; i++) {
- new_obstacle_outline.push_back(_outline[i]);
- }
- }
- new_obstacle_outline.resize(outline_size);
-
- NavigationServer2D::get_singleton()->obstacle_set_vertices(obstacle_rid, new_obstacle_outline);
- NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(obstacle_rid, avoidance_layers);
- if (is_inside_tree()) {
- if (map_override.is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, map_override);
- } else {
- NavigationServer2D::get_singleton()->obstacle_set_map(obstacle_rid, get_world_2d()->get_navigation_map());
- }
- NavigationServer2D::get_singleton()->obstacle_set_position(obstacle_rid, get_global_position());
- }
- }
- constrain_avoidance_obstacles.resize(_outline_count);
-}
-
-void NavigationRegion2D::set_constrain_avoidance(bool p_enabled) {
- constrain_avoidance = p_enabled;
- _update_avoidance_constrain();
- notify_property_list_changed();
-}
-
-bool NavigationRegion2D::get_constrain_avoidance() const {
- return constrain_avoidance;
-}
-
-void NavigationRegion2D::_validate_property(PropertyInfo &p_property) const {
- if (p_property.name == "avoidance_layers") {
- if (!constrain_avoidance) {
- p_property.usage = PROPERTY_USAGE_NO_EDITOR;
- }
- }
-}
-
-void NavigationRegion2D::set_avoidance_layers(uint32_t p_layers) {
- avoidance_layers = p_layers;
- if (constrain_avoidance_obstacles.size() > 0) {
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- NavigationServer2D::get_singleton()->obstacle_set_avoidance_layers(constrain_avoidance_obstacles[i], avoidance_layers);
- }
- }
-}
-
-uint32_t NavigationRegion2D::get_avoidance_layers() const {
- return avoidance_layers;
-}
-
-void NavigationRegion2D::set_avoidance_layer_value(int p_layer_number, bool p_value) {
- ERR_FAIL_COND_MSG(p_layer_number < 1, "Avoidance layer number must be between 1 and 32 inclusive.");
- ERR_FAIL_COND_MSG(p_layer_number > 32, "Avoidance layer number must be between 1 and 32 inclusive.");
- uint32_t avoidance_layers_new = get_avoidance_layers();
- if (p_value) {
- avoidance_layers_new |= 1 << (p_layer_number - 1);
- } else {
- avoidance_layers_new &= ~(1 << (p_layer_number - 1));
- }
- set_avoidance_layers(avoidance_layers_new);
-}
-
-bool NavigationRegion2D::get_avoidance_layer_value(int p_layer_number) const {
- ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Avoidance layer number must be between 1 and 32 inclusive.");
- ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Avoidance layer number must be between 1 and 32 inclusive.");
- return get_avoidance_layers() & (1 << (p_layer_number - 1));
-}
-
void NavigationRegion2D::_region_enter_navigation_map() {
if (!is_inside_tree()) {
return;
@@ -523,27 +388,12 @@ void NavigationRegion2D::_region_enter_navigation_map() {
if (map_override.is_valid()) {
NavigationServer2D::get_singleton()->region_set_map(region, map_override);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], map_override);
- }
- }
} else {
NavigationServer2D::get_singleton()->region_set_map(region, get_world_2d()->get_navigation_map());
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], get_world_2d()->get_navigation_map());
- }
- }
}
current_global_transform = get_global_transform();
NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
- }
- }
NavigationServer2D::get_singleton()->region_set_enabled(region, enabled);
@@ -552,11 +402,6 @@ void NavigationRegion2D::_region_enter_navigation_map() {
void NavigationRegion2D::_region_exit_navigation_map() {
NavigationServer2D::get_singleton()->region_set_map(region, RID());
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_map(constrain_avoidance_obstacles[i], RID());
- }
- }
}
void NavigationRegion2D::_region_update_transform() {
@@ -568,11 +413,6 @@ void NavigationRegion2D::_region_update_transform() {
if (current_global_transform != new_global_transform) {
current_global_transform = new_global_transform;
NavigationServer2D::get_singleton()->region_set_transform(region, current_global_transform);
- for (uint32_t i = 0; i < constrain_avoidance_obstacles.size(); i++) {
- if (constrain_avoidance_obstacles[i].is_valid()) {
- NavigationServer2D::get_singleton()->obstacle_set_position(constrain_avoidance_obstacles[i], get_global_position());
- }
- }
}
queue_redraw();
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 87c2365b15..5a86dd607d 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -31,7 +31,7 @@
#ifndef NAVIGATION_REGION_2D_H
#define NAVIGATION_REGION_2D_H
-#include "scene/resources/navigation_polygon.h"
+#include "scene/resources/2d/navigation_polygon.h"
class NavigationRegion2D : public Node2D {
GDCLASS(NavigationRegion2D, Node2D);
@@ -46,10 +46,6 @@ class NavigationRegion2D : public Node2D {
real_t travel_cost = 1.0;
Ref<NavigationPolygon> navigation_polygon;
- bool constrain_avoidance = false;
- LocalVector<RID> constrain_avoidance_obstacles;
- uint32_t avoidance_layers = 1;
-
Transform2D current_global_transform;
void _navigation_polygon_changed();
@@ -65,7 +61,6 @@ private:
protected:
void _notification(int p_what);
- void _validate_property(PropertyInfo &p_property) const;
static void _bind_methods();
#ifndef DISABLE_DEPRECATED
@@ -106,15 +101,6 @@ public:
void set_navigation_polygon(const Ref<NavigationPolygon> &p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon() const;
- void set_constrain_avoidance(bool p_enabled);
- bool get_constrain_avoidance() const;
-
- void set_avoidance_layers(uint32_t p_layers);
- uint32_t get_avoidance_layers() const;
-
- void set_avoidance_layer_value(int p_layer_number, bool p_value);
- bool get_avoidance_layer_value(int p_layer_number) const;
-
PackedStringArray get_configuration_warnings() const override;
void bake_navigation_polygon(bool p_on_thread);
@@ -125,7 +111,6 @@ public:
~NavigationRegion2D();
private:
- void _update_avoidance_constrain();
void _region_enter_navigation_map();
void _region_exit_navigation_map();
void _region_update_transform();
diff --git a/scene/2d/parallax_2d.cpp b/scene/2d/parallax_2d.cpp
index 7e47e0f061..555f3b031c 100644
--- a/scene/2d/parallax_2d.cpp
+++ b/scene/2d/parallax_2d.cpp
@@ -102,14 +102,14 @@ void Parallax2D::_update_scroll() {
scroll_ofs *= scroll_scale;
if (repeat_size.x) {
- real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x);
+ real_t mod = Math::fposmod(scroll_ofs.x - scroll_offset.x - autoscroll_offset.x, repeat_size.x * get_scale().x);
scroll_ofs.x = screen_offset.x - mod;
} else {
scroll_ofs.x = screen_offset.x + scroll_offset.x - scroll_ofs.x;
}
if (repeat_size.y) {
- real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y);
+ real_t mod = Math::fposmod(scroll_ofs.y - scroll_offset.y - autoscroll_offset.y, repeat_size.y * get_scale().y);
scroll_ofs.y = screen_offset.y - mod;
} else {
scroll_ofs.y = screen_offset.y + scroll_offset.y - scroll_ofs.y;
@@ -127,8 +127,7 @@ void Parallax2D::_update_repeat() {
return;
}
- Point2 repeat_scale = repeat_size * get_scale();
- RenderingServer::get_singleton()->canvas_set_item_repeat(get_canvas_item(), repeat_scale, repeat_times);
+ RenderingServer::get_singleton()->canvas_set_item_repeat(get_canvas_item(), repeat_size, repeat_times);
RenderingServer::get_singleton()->canvas_item_set_interpolated(get_canvas_item(), false);
}
diff --git a/scene/2d/physics/character_body_2d.cpp b/scene/2d/physics/character_body_2d.cpp
index e5d575a159..a503f3cb78 100644
--- a/scene/2d/physics/character_body_2d.cpp
+++ b/scene/2d/physics/character_body_2d.cpp
@@ -501,7 +501,7 @@ Ref<KinematicCollision2D> CharacterBody2D::_get_slide_collision(int p_bounce) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) {
slide_colliders.write[p_bounce].instantiate();
- slide_colliders.write[p_bounce]->owner = this;
+ slide_colliders.write[p_bounce]->owner_id = get_instance_id();
}
slide_colliders.write[p_bounce]->result = motion_results[p_bounce];
@@ -745,11 +745,3 @@ void CharacterBody2D::_bind_methods() {
CharacterBody2D::CharacterBody2D() :
PhysicsBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
}
-
-CharacterBody2D::~CharacterBody2D() {
- for (int i = 0; i < slide_colliders.size(); i++) {
- if (slide_colliders[i].is_valid()) {
- slide_colliders.write[i]->owner = nullptr;
- }
- }
-}
diff --git a/scene/2d/physics/character_body_2d.h b/scene/2d/physics/character_body_2d.h
index 395438a1f1..536d0a1ebd 100644
--- a/scene/2d/physics/character_body_2d.h
+++ b/scene/2d/physics/character_body_2d.h
@@ -111,7 +111,6 @@ public:
PlatformOnLeave get_platform_on_leave() const;
CharacterBody2D();
- ~CharacterBody2D();
private:
real_t margin = 0.08;
diff --git a/scene/2d/physics/kinematic_collision_2d.cpp b/scene/2d/physics/kinematic_collision_2d.cpp
index 7e7c33b259..18b0254769 100644
--- a/scene/2d/physics/kinematic_collision_2d.cpp
+++ b/scene/2d/physics/kinematic_collision_2d.cpp
@@ -59,6 +59,7 @@ real_t KinematicCollision2D::get_depth() const {
}
Object *KinematicCollision2D::get_local_shape() const {
+ PhysicsBody2D *owner = Object::cast_to<PhysicsBody2D>(ObjectDB::get_instance(owner_id));
if (!owner) {
return nullptr;
}
diff --git a/scene/2d/physics/kinematic_collision_2d.h b/scene/2d/physics/kinematic_collision_2d.h
index 0d187b87a5..8d3d6ca8c1 100644
--- a/scene/2d/physics/kinematic_collision_2d.h
+++ b/scene/2d/physics/kinematic_collision_2d.h
@@ -40,7 +40,7 @@ class PhysicsBody2D;
class KinematicCollision2D : public RefCounted {
GDCLASS(KinematicCollision2D, RefCounted);
- PhysicsBody2D *owner = nullptr;
+ ObjectID owner_id;
friend class PhysicsBody2D;
friend class CharacterBody2D;
PhysicsServer2D::MotionResult result;
diff --git a/scene/2d/physics/physics_body_2d.cpp b/scene/2d/physics/physics_body_2d.cpp
index 81120d0b01..fc14e6ed62 100644
--- a/scene/2d/physics/physics_body_2d.cpp
+++ b/scene/2d/physics/physics_body_2d.cpp
@@ -30,8 +30,6 @@
#include "physics_body_2d.h"
-#include "scene/scene_string_names.h"
-
void PhysicsBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08), DEFVAL(false));
ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08), DEFVAL(false));
@@ -48,12 +46,6 @@ PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) :
set_pickable(false);
}
-PhysicsBody2D::~PhysicsBody2D() {
- if (motion_cache.is_valid()) {
- motion_cache->owner = nullptr;
- }
-}
-
Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin, bool p_recovery_as_collision) {
PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_motion, p_margin);
parameters.recovery_as_collision = p_recovery_as_collision;
@@ -64,7 +56,7 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) {
motion_cache.instantiate();
- motion_cache->owner = this;
+ motion_cache->owner_id = get_instance_id();
}
motion_cache->result = result;
diff --git a/scene/2d/physics/physics_body_2d.h b/scene/2d/physics/physics_body_2d.h
index 43bc479881..d44eebabee 100644
--- a/scene/2d/physics/physics_body_2d.h
+++ b/scene/2d/physics/physics_body_2d.h
@@ -56,8 +56,6 @@ public:
TypedArray<PhysicsBody2D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
-
- virtual ~PhysicsBody2D();
};
#endif // PHYSICS_BODY_2D_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 115a39340d..165d4d5a67 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -433,6 +433,7 @@ Color TileMap::get_layer_modulate(int p_layer) const {
void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_enabled, p_y_sort_enabled);
+ update_configuration_warnings();
}
bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
@@ -441,6 +442,7 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) {
TILEMAP_CALL_FOR_LAYER(p_layer, set_y_sort_origin, p_y_sort_origin);
+ update_configuration_warnings();
}
int TileMap::get_layer_y_sort_origin(int p_layer) const {
@@ -522,9 +524,6 @@ void TileMap::set_y_sort_enabled(bool p_enable) {
return;
}
Node2D::set_y_sort_enabled(p_enable);
- for (TileMapLayer *layer : layers) {
- layer->set_y_sort_enabled(p_enable);
- }
_emit_changed();
update_configuration_warnings();
}
diff --git a/scene/3d/bone_attachment_3d.compat.inc b/scene/3d/bone_attachment_3d.compat.inc
new file mode 100644
index 0000000000..1b21612d5f
--- /dev/null
+++ b/scene/3d/bone_attachment_3d.compat.inc
@@ -0,0 +1,43 @@
+/**************************************************************************/
+/* bone_attachment_3d.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
+
+#include "bone_attachment_3d.h"
+
+void BoneAttachment3D::_on_bone_pose_update_bind_compat_90575(int p_bone_index) {
+ return on_skeleton_update();
+}
+
+void BoneAttachment3D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::_on_bone_pose_update_bind_compat_90575);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 221200284d..6afbc68e42 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "bone_attachment_3d.h"
+#include "bone_attachment_3d.compat.inc"
void BoneAttachment3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "bone_name") {
@@ -148,9 +149,9 @@ void BoneAttachment3D::_check_bind() {
bone_idx = sk->find_bone(bone_name);
}
if (bone_idx != -1) {
- sk->connect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
+ sk->connect(SNAME("skeleton_updated"), callable_mp(this, &BoneAttachment3D::on_skeleton_update));
bound = true;
- callable_mp(this, &BoneAttachment3D::on_bone_pose_update).call_deferred(bone_idx);
+ callable_mp(this, &BoneAttachment3D::on_skeleton_update);
}
}
}
@@ -176,7 +177,7 @@ void BoneAttachment3D::_check_unbind() {
Skeleton3D *sk = _get_skeleton3d();
if (sk) {
- sk->disconnect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update));
+ sk->disconnect(SNAME("skeleton_updated"), callable_mp(this, &BoneAttachment3D::on_skeleton_update));
}
bound = false;
}
@@ -308,12 +309,12 @@ void BoneAttachment3D::_notification(int p_what) {
}
}
-void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {
+void BoneAttachment3D::on_skeleton_update() {
if (updating) {
return;
}
updating = true;
- if (bone_idx == p_bone_index) {
+ if (bone_idx >= 0) {
Skeleton3D *sk = _get_skeleton3d();
if (sk) {
if (!override_pose) {
@@ -371,7 +372,7 @@ void BoneAttachment3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bone_idx", "bone_idx"), &BoneAttachment3D::set_bone_idx);
ClassDB::bind_method(D_METHOD("get_bone_idx"), &BoneAttachment3D::get_bone_idx);
- ClassDB::bind_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::on_bone_pose_update);
+ ClassDB::bind_method(D_METHOD("on_skeleton_update"), &BoneAttachment3D::on_skeleton_update);
ClassDB::bind_method(D_METHOD("set_override_pose", "override_pose"), &BoneAttachment3D::set_override_pose);
ClassDB::bind_method(D_METHOD("get_override_pose"), &BoneAttachment3D::get_override_pose);
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index ec0f7344d7..e19180b0ea 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -67,6 +67,10 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ virtual void _on_bone_pose_update_bind_compat_90575(int p_bone_index);
+ static void _bind_compatibility_methods();
+#endif
public:
#ifdef TOOLS_ENABLED
@@ -89,7 +93,7 @@ public:
void set_external_skeleton(NodePath p_skeleton);
NodePath get_external_skeleton() const;
- virtual void on_bone_pose_update(int p_bone_index);
+ virtual void on_skeleton_update();
#ifdef TOOLS_ENABLED
virtual void notify_rebind_required();
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index e7b6aa1dba..0dc9834539 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -881,9 +881,9 @@ void CPUParticles3D::_particles_process(double p_delta) {
case EMISSION_SHAPE_RING: {
real_t ring_random_angle = Math::randf() * Math_TAU;
real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;
- Vector3 axis = emission_ring_axis.normalized();
+ Vector3 axis = emission_ring_axis == Vector3(0.0, 0.0, 0.0) ? Vector3(0.0, 0.0, 1.0) : emission_ring_axis.normalized();
Vector3 ortho_axis;
- if (axis == Vector3(1.0, 0.0, 0.0)) {
+ if (axis.abs() == Vector3(1.0, 0.0, 0.0)) {
ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis);
} else {
ortho_axis = Vector3(1.0, 0.0, 0.0).cross(axis);
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index eb52d4540e..43c41c1b5b 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -99,6 +99,12 @@ void NavigationAgent3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_target_position", "position"), &NavigationAgent3D::set_target_position);
ClassDB::bind_method(D_METHOD("get_target_position"), &NavigationAgent3D::get_target_position);
+ ClassDB::bind_method(D_METHOD("set_simplify_path", "enabled"), &NavigationAgent3D::set_simplify_path);
+ ClassDB::bind_method(D_METHOD("get_simplify_path"), &NavigationAgent3D::get_simplify_path);
+
+ ClassDB::bind_method(D_METHOD("set_simplify_epsilon", "epsilon"), &NavigationAgent3D::set_simplify_epsilon);
+ ClassDB::bind_method(D_METHOD("get_simplify_epsilon"), &NavigationAgent3D::get_simplify_epsilon);
+
ClassDB::bind_method(D_METHOD("get_next_path_position"), &NavigationAgent3D::get_next_path_position);
ClassDB::bind_method(D_METHOD("set_velocity_forced", "velocity"), &NavigationAgent3D::set_velocity_forced);
@@ -140,6 +146,8 @@ void NavigationAgent3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_path_metadata_flags", "get_path_metadata_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon", PROPERTY_HINT_RANGE, "0.0,10.0,0.001,or_greater,suffix:m"), "set_simplify_epsilon", "get_simplify_epsilon");
ADD_GROUP("Avoidance", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled");
@@ -464,6 +472,24 @@ void NavigationAgent3D::set_path_postprocessing(const NavigationPathQueryParamet
navigation_query->set_path_postprocessing(path_postprocessing);
}
+void NavigationAgent3D::set_simplify_path(bool p_enabled) {
+ simplify_path = p_enabled;
+ navigation_query->set_simplify_path(simplify_path);
+}
+
+bool NavigationAgent3D::get_simplify_path() const {
+ return simplify_path;
+}
+
+void NavigationAgent3D::set_simplify_epsilon(real_t p_epsilon) {
+ simplify_epsilon = MAX(0.0, p_epsilon);
+ navigation_query->set_simplify_epsilon(simplify_epsilon);
+}
+
+real_t NavigationAgent3D::get_simplify_epsilon() const {
+ return simplify_epsilon;
+}
+
void NavigationAgent3D::set_path_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_path_metadata_flags) {
if (path_metadata_flags == p_path_metadata_flags) {
return;
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index 4eaed83149..ade6afd445 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -66,6 +66,8 @@ class NavigationAgent3D : public Node {
real_t time_horizon_obstacles = 0.0;
real_t max_speed = 10.0;
real_t path_max_distance = 5.0;
+ bool simplify_path = false;
+ real_t simplify_epsilon = 0.0;
Vector3 target_position;
@@ -200,6 +202,12 @@ public:
void set_target_position(Vector3 p_position);
Vector3 get_target_position() const;
+ void set_simplify_path(bool p_enabled);
+ bool get_simplify_path() const;
+
+ void set_simplify_epsilon(real_t p_epsilon);
+ real_t get_simplify_epsilon() const;
+
Vector3 get_next_path_position();
Ref<NavigationPathQueryResult3D> get_current_navigation_result() const { return navigation_result; }
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index d8a63c60a2..856c52b5d5 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -30,7 +30,7 @@
#include "navigation_region_3d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation_server_3d.h"
RID NavigationRegion3D::get_rid() const {
diff --git a/scene/3d/physics/character_body_3d.cpp b/scene/3d/physics/character_body_3d.cpp
index 6759033358..b13c279234 100644
--- a/scene/3d/physics/character_body_3d.cpp
+++ b/scene/3d/physics/character_body_3d.cpp
@@ -704,7 +704,7 @@ Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (slide_colliders[p_bounce].is_null() || slide_colliders[p_bounce]->get_reference_count() > 1) {
slide_colliders.write[p_bounce].instantiate();
- slide_colliders.write[p_bounce]->owner = this;
+ slide_colliders.write[p_bounce]->owner_id = get_instance_id();
}
slide_colliders.write[p_bounce]->result = motion_results[p_bounce];
@@ -936,11 +936,3 @@ void CharacterBody3D::_validate_property(PropertyInfo &p_property) const {
CharacterBody3D::CharacterBody3D() :
PhysicsBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) {
}
-
-CharacterBody3D::~CharacterBody3D() {
- for (int i = 0; i < slide_colliders.size(); i++) {
- if (slide_colliders[i].is_valid()) {
- slide_colliders.write[i]->owner = nullptr;
- }
- }
-}
diff --git a/scene/3d/physics/character_body_3d.h b/scene/3d/physics/character_body_3d.h
index cffc0f8e63..430d9c35cb 100644
--- a/scene/3d/physics/character_body_3d.h
+++ b/scene/3d/physics/character_body_3d.h
@@ -113,7 +113,6 @@ public:
PlatformOnLeave get_platform_on_leave() const;
CharacterBody3D();
- ~CharacterBody3D();
private:
real_t margin = 0.001;
diff --git a/scene/3d/physics/kinematic_collision_3d.cpp b/scene/3d/physics/kinematic_collision_3d.cpp
index 54371425bc..de13831ac3 100644
--- a/scene/3d/physics/kinematic_collision_3d.cpp
+++ b/scene/3d/physics/kinematic_collision_3d.cpp
@@ -67,6 +67,7 @@ real_t KinematicCollision3D::get_angle(int p_collision_index, const Vector3 &p_u
Object *KinematicCollision3D::get_local_shape(int p_collision_index) const {
ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, nullptr);
+ PhysicsBody3D *owner = Object::cast_to<PhysicsBody3D>(ObjectDB::get_instance(owner_id));
if (!owner) {
return nullptr;
}
diff --git a/scene/3d/physics/kinematic_collision_3d.h b/scene/3d/physics/kinematic_collision_3d.h
index 656531c82b..0573af0c21 100644
--- a/scene/3d/physics/kinematic_collision_3d.h
+++ b/scene/3d/physics/kinematic_collision_3d.h
@@ -40,7 +40,7 @@ class PhysicsBody3D;
class KinematicCollision3D : public RefCounted {
GDCLASS(KinematicCollision3D, RefCounted);
- PhysicsBody3D *owner = nullptr;
+ ObjectID owner_id;
friend class PhysicsBody3D;
friend class CharacterBody3D;
PhysicsServer3D::MotionResult result;
diff --git a/scene/3d/physics/physics_body_3d.cpp b/scene/3d/physics/physics_body_3d.cpp
index 711ca60a81..b723b452c1 100644
--- a/scene/3d/physics/physics_body_3d.cpp
+++ b/scene/3d/physics/physics_body_3d.cpp
@@ -30,8 +30,6 @@
#include "physics_body_3d.h"
-#include "scene/scene_string_names.h"
-
void PhysicsBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "motion", "test_only", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(false), DEFVAL(1));
ClassDB::bind_method(D_METHOD("test_move", "from", "motion", "collision", "safe_margin", "recovery_as_collision", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(false), DEFVAL(1));
@@ -58,12 +56,6 @@ PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) :
set_body_mode(p_mode);
}
-PhysicsBody3D::~PhysicsBody3D() {
- if (motion_cache.is_valid()) {
- motion_cache->owner = nullptr;
- }
-}
-
TypedArray<PhysicsBody3D> PhysicsBody3D::get_collision_exceptions() {
List<RID> exceptions;
PhysicsServer3D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions);
@@ -102,7 +94,7 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->get_reference_count() > 1) {
motion_cache.instantiate();
- motion_cache->owner = this;
+ motion_cache->owner_id = get_instance_id();
}
motion_cache->result = result;
diff --git a/scene/3d/physics/physics_body_3d.h b/scene/3d/physics/physics_body_3d.h
index 92b3850085..71253be0b8 100644
--- a/scene/3d/physics/physics_body_3d.h
+++ b/scene/3d/physics/physics_body_3d.h
@@ -65,8 +65,6 @@ public:
TypedArray<PhysicsBody3D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
void remove_collision_exception_with(Node *p_node);
-
- virtual ~PhysicsBody3D();
};
#endif // PHYSICS_BODY_3D_H
diff --git a/scene/3d/physics/ray_cast_3d.cpp b/scene/3d/physics/ray_cast_3d.cpp
index 0cb722a77e..a9272388c1 100644
--- a/scene/3d/physics/ray_cast_3d.cpp
+++ b/scene/3d/physics/ray_cast_3d.cpp
@@ -204,8 +204,10 @@ void RayCast3D::_notification(int p_what) {
bool prev_collision_state = collided;
_update_raycast_state();
- if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) {
- _update_debug_shape_material(true);
+ if (get_tree()->is_debugging_collisions_hint()) {
+ if (prev_collision_state != collided) {
+ _update_debug_shape_material(true);
+ }
if (is_inside_tree() && debug_instance.is_valid()) {
RenderingServer::get_singleton()->instance_set_transform(debug_instance, get_global_transform());
}
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 95e94de0e3..db36a0a630 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -327,6 +327,8 @@ void Skeleton3D::_notification(int p_what) {
_process_modifiers();
}
+ emit_signal(SceneStringNames::get_singleton()->skeleton_updated);
+
// Update skins.
RenderingServer *rs = RenderingServer::get_singleton();
for (SkinReference *E : skin_bindings) {
@@ -921,8 +923,6 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(b.child_bones[i]);
}
-
- emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx);
}
}
@@ -1059,7 +1059,7 @@ void Skeleton3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process");
ADD_SIGNAL(MethodInfo("pose_updated"));
- ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("skeleton_updated"));
ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
ADD_SIGNAL(MethodInfo("bone_list_changed"));
ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 01e1de8f23..5a3a5f9bc0 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -698,9 +698,15 @@ bool AnimationMixer::_update_caches() {
if (has_reset_anim) {
int rt = reset_anim->find_track(path, track_src_type);
if (rt >= 0) {
- track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous.
- if (reset_anim->track_get_key_count(rt) > 0) {
- track_value->init_value = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
+ if (track_src_type == Animation::TYPE_VALUE) {
+ track_value->init_use_continuous = track_value->init_use_continuous || (reset_anim->value_track_get_update_mode(rt) != Animation::UPDATE_DISCRETE); // Take precedence Force Continuous.
+ if (reset_anim->track_get_key_count(rt) > 0) {
+ track_value->init_value = reset_anim->track_get_key_value(rt, 0);
+ }
+ } else {
+ if (reset_anim->track_get_key_count(rt) > 0) {
+ track_value->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0];
+ }
}
}
}
@@ -875,7 +881,7 @@ bool AnimationMixer::_update_caches() {
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.");
}
- 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->is_using_angle = 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;
}
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.");
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index f1a8c2f6ae..d430fe9bfc 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -3549,7 +3549,7 @@ void Control::_bind_methods() {
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");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_auto_translate", "is_auto_translating");
#endif
ADD_GROUP("Tooltip", "tooltip_");
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index c13a2e281a..4d00646391 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -202,7 +202,7 @@ void AcceptDialog::register_text_enter(LineEdit *p_line_edit) {
}
void AcceptDialog::_update_child_rects() {
- Size2 dlg_size = get_size();
+ Size2 dlg_size = Vector2(get_size()) / get_content_scale_factor();
float h_margins = theme_cache.panel_style->get_margin(SIDE_LEFT) + theme_cache.panel_style->get_margin(SIDE_RIGHT);
float v_margins = theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM);
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 0927404947..87383283fd 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -194,8 +194,6 @@ Rect2i Popup::_popup_adjust_rect() const {
void Popup::_bind_methods() {
ADD_SIGNAL(MethodInfo("popup_hide"));
-
- BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Popup, panel_style, "panel");
}
Popup::Popup() {
@@ -232,7 +230,8 @@ Size2 PopupPanel::_get_contents_minimum_size() const {
void PopupPanel::_update_child_rects() {
Vector2 cpos(theme_cache.panel_style->get_offset());
- Vector2 csize(get_size() - theme_cache.panel_style->get_minimum_size());
+ Vector2 panel_size = Vector2(get_size()) / get_content_scale_factor();
+ Vector2 csize = panel_size - theme_cache.panel_style->get_minimum_size();
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -246,7 +245,7 @@ void PopupPanel::_update_child_rects() {
if (c == panel) {
c->set_position(Vector2());
- c->set_size(get_size());
+ c->set_size(panel_size);
} else {
c->set_position(cpos);
c->set_size(csize);
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 25edca3657..48818686f7 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -43,10 +43,6 @@ class Popup : public Window {
LocalVector<Window *> visible_parents;
bool popped_up = false;
- struct ThemeCache {
- Ref<StyleBox> panel_style;
- } theme_cache;
-
void _initialize_visible_parents();
void _deinitialize_visible_parents();
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 56346f5edc..260956a775 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -312,18 +312,17 @@ int PopupMenu::_get_items_total_height() const {
}
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
- if (p_over.x < 0 || p_over.x >= get_size().width || p_over.y < theme_cache.panel_style->get_margin(Side::SIDE_TOP)) {
+ float win_scale = get_content_scale_factor();
+ if (p_over.x < 0 || p_over.x >= get_size().width * win_scale || p_over.y < theme_cache.panel_style->get_margin(Side::SIDE_TOP) * win_scale) {
return -1;
}
- Point2 ofs;
+ Point2 ofs = Point2(0, theme_cache.v_separation * 0.5) * win_scale;
for (int i = 0; i < items.size(); i++) {
- ofs.y += theme_cache.v_separation;
-
- ofs.y += _get_item_height(i);
-
- if (p_over.y - control->get_position().y < ofs.y) {
+ ofs.y += i > 0 ? (float)theme_cache.v_separation * win_scale : (float)theme_cache.v_separation * win_scale * 0.5;
+ ofs.y += _get_item_height(i) * win_scale;
+ if (p_over.y - control->get_position().y * win_scale < ofs.y) {
return i;
}
}
@@ -341,15 +340,17 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
Rect2 this_rect(this_pos, get_size());
float scroll_offset = control->get_position().y;
+ float scaled_ofs_cache = items[p_over]._ofs_cache * get_content_scale_factor();
+ float scaled_height_cache = items[p_over]._height_cache * get_content_scale_factor();
submenu_popup->reset_size(); // Shrink the popup size to its contents.
Size2 submenu_size = submenu_popup->get_size();
Point2 submenu_pos;
if (control->is_layout_rtl()) {
- submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset - theme_cache.v_separation / 2);
+ submenu_pos = this_pos + Point2(-submenu_size.width, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2);
} else {
- submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset - theme_cache.v_separation / 2);
+ submenu_pos = this_pos + Point2(this_rect.size.width, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2);
}
// Fix pos if going outside parent rect.
@@ -386,8 +387,8 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
// Set autohide areas.
Rect2 safe_area = this_rect;
- safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2;
- safe_area.size.y = items[p_over]._height_cache + theme_cache.v_separation;
+ safe_area.position.y += scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2;
+ safe_area.size.y = scaled_height_cache + theme_cache.v_separation;
Viewport *vp = submenu_popup->get_embedder();
if (vp) {
vp->subwindow_set_popup_safe_rect(submenu_popup, safe_area);
@@ -400,11 +401,11 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
// Autohide area above the submenu item.
submenu_pum->clear_autohide_areas();
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2));
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2));
// If there is an area below the submenu item, add an autohide area there.
- if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
- int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height;
+ if (scaled_ofs_cache + scaled_height_cache + scroll_offset <= control->get_size().height) {
+ int from = scaled_ofs_cache + scaled_height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height;
submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
}
}
@@ -576,6 +577,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
}
+ item_clickable_area.size = item_clickable_area.size * get_content_scale_factor();
Ref<InputEventMouseButton> b = p_event;
@@ -640,11 +642,17 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
for (const Rect2 &E : autohide_areas) {
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) {
+ // The mouse left the safe area, prepare to close.
_close_pressed();
return;
}
}
+ if (!minimum_lifetime_timer->is_stopped()) {
+ // The mouse left the safe area, but came back again, so cancel the auto-closing.
+ minimum_lifetime_timer->stop();
+ }
+
if (!item_clickable_area.has_point(m->get_position())) {
return;
}
@@ -2821,6 +2829,14 @@ void PopupMenu::popup(const Rect2i &p_bounds) {
} else {
moved = Vector2();
popup_time_msec = OS::get_singleton()->get_ticks_msec();
+ if (!is_embedded()) {
+ float win_scale = get_parent_visible_window()->get_content_scale_factor();
+ set_content_scale_factor(win_scale);
+ Size2 minsize = get_contents_minimum_size();
+ minsize.height += 0.5 * win_scale; // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing.
+ set_min_size(minsize * win_scale);
+ set_size(Vector2(0, 0)); // Shrinkwraps to min size.
+ }
Popup::popup(p_bounds);
}
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f8bbedde09..4a838fc7f6 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -4410,6 +4410,10 @@ void RichTextLabel::append_text(const String &p_bbcode) {
push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag.begins_with("char=")) {
+ int32_t char_code = tag.substr(5, tag.length()).hex_to_int();
+ add_text(String::chr(char_code));
+ pos = brk_end + 1;
} else if (tag == "lb") {
add_text("[");
pos = brk_end + 1;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 1daf86fe0f..1242d3bd16 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3995,12 +3995,14 @@ bool Tree::edit_selected(bool p_force_edit) {
return false;
}
+ float popup_scale = popup_editor->is_embedded() ? 1.0 : popup_editor->get_parent_visible_window()->get_content_scale_factor();
Rect2 rect;
if (select_mode == SELECT_ROW) {
rect = s->get_meta("__focus_col_" + itos(selected_col));
} else {
rect = s->get_meta("__focus_rect");
}
+ rect.position *= popup_scale;
popup_edited_item = s;
popup_edited_item_col = col;
@@ -4043,7 +4045,7 @@ bool Tree::edit_selected(bool p_force_edit) {
popup_rect.size = rect.size;
// Account for icon.
- Size2 icon_size = _get_cell_icon_size(c);
+ Size2 icon_size = _get_cell_icon_size(c) * popup_scale;
popup_rect.position.x += icon_size.x;
popup_rect.size.x -= icon_size.x;
@@ -4070,7 +4072,10 @@ bool Tree::edit_selected(bool p_force_edit) {
}
popup_editor->set_position(popup_rect.position);
- popup_editor->set_size(popup_rect.size);
+ popup_editor->set_size(popup_rect.size * popup_scale);
+ if (!popup_editor->is_embedded()) {
+ popup_editor->set_content_scale_factor(popup_scale);
+ }
popup_editor->popup();
popup_editor->child_controls_changed();
@@ -4086,7 +4091,10 @@ bool Tree::edit_selected(bool p_force_edit) {
text_editor->show();
popup_editor->set_position(get_screen_position() + rect.position);
- popup_editor->set_size(rect.size);
+ popup_editor->set_size(rect.size * popup_scale);
+ if (!popup_editor->is_embedded()) {
+ popup_editor->set_content_scale_factor(popup_scale);
+ }
popup_editor->popup();
popup_editor->child_controls_changed();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 0c77a60af6..c465a3385f 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -120,7 +120,6 @@ void SceneTreeTimer::release_connections() {
SceneTreeTimer::SceneTreeTimer() {}
void SceneTree::tree_changed() {
- tree_version++;
emit_signal(tree_changed_name);
}
@@ -153,7 +152,6 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod
ERR_FAIL_COND_V_MSG(E->value.nodes.has(p_node), &E->value, "Already in group: " + p_group + ".");
E->value.nodes.push_back(p_node);
- //E->value.last_tree_version=0;
E->value.changed = true;
return &E->value;
}
@@ -476,8 +474,6 @@ void SceneTree::iteration_prepare() {
}
bool SceneTree::physics_process(double p_time) {
- root_lock++;
-
current_frame++;
flush_transform_notifications();
@@ -501,7 +497,6 @@ bool SceneTree::physics_process(double p_time) {
process_tweens(p_time, true);
flush_transform_notifications();
- root_lock--;
_flush_delete_queue();
_call_idle_callbacks();
@@ -510,8 +505,6 @@ bool SceneTree::physics_process(double p_time) {
}
bool SceneTree::process(double p_time) {
- root_lock++;
-
if (MainLoop::process(p_time)) {
_quit = true;
}
@@ -537,8 +530,6 @@ bool SceneTree::process(double p_time) {
MessageQueue::get_singleton()->flush(); //small little hack
flush_transform_notifications(); //transforms after world update, to avoid unnecessary enter/exit notifications
- root_lock--;
-
_flush_delete_queue();
if (unlikely(pending_new_scene)) {
@@ -1761,7 +1752,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_auto_translate_mode(GLOBAL_GET("internationalization/rendering/root_node_auto_translate") ? Node::AUTO_TRANSLATE_MODE_ALWAYS : Node::AUTO_TRANSLATE_MODE_DISABLED);
root->set_name("root");
root->set_title(GLOBAL_GET("application/config/name"));
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 603438cc5f..c9f3a4de1f 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -122,7 +122,6 @@ private:
Window *root = nullptr;
- uint64_t tree_version = 1;
double physics_process_time = 0.0;
double process_time = 0.0;
bool accept_quit = true;
@@ -134,7 +133,6 @@ private:
bool debug_navigation_hint = false;
#endif
bool paused = false;
- int root_lock = 0;
HashMap<StringName, Group> group_map;
bool _quit = false;
@@ -165,7 +163,6 @@ private:
// Safety for when a node is deleted while a group is being called.
- bool processing = false;
int nodes_removed_on_group_call_lock = 0;
HashSet<Node *> nodes_removed_on_group_call; // Skip erased nodes.
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 17d7fec230..2d30ea345d 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1461,6 +1461,8 @@ void Viewport::_gui_show_tooltip() {
panel->set_flag(Window::FLAG_NO_FOCUS, true);
panel->set_flag(Window::FLAG_POPUP, false);
panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
+ // A non-embedded tooltip window will only be transparent if per_pixel_transparency is allowed in the main Viewport.
+ panel->set_flag(Window::FLAG_TRANSPARENT, true);
panel->set_wrap_controls(true);
panel->add_child(base_tooltip);
panel->gui_parent = this;
@@ -1469,17 +1471,25 @@ void Viewport::_gui_show_tooltip() {
tooltip_owner->add_child(gui.tooltip_popup);
+ Window *window = Object::cast_to<Window>(gui.tooltip_popup->get_embedder());
+ if (!window) { // Not embedded.
+ window = gui.tooltip_popup->get_parent_visible_window();
+ }
+ float win_scale = window->content_scale_factor;
Point2 tooltip_offset = GLOBAL_GET("display/mouse_cursor/tooltip_position_offset");
+ if (!gui.tooltip_popup->is_embedded()) {
+ tooltip_offset *= win_scale;
+ }
Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size());
- r.size = r.size.min(panel->get_max_size());
-
- Window *window = gui.tooltip_popup->get_parent_visible_window();
Rect2i vr;
if (gui.tooltip_popup->is_embedded()) {
vr = gui.tooltip_popup->get_embedder()->get_visible_rect();
} else {
+ panel->content_scale_factor = win_scale;
+ r.size *= win_scale;
vr = window->get_usable_parent_rect();
}
+ r.size = r.size.min(panel->get_max_size());
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
// Place it in the opposite direction. If it fails, just hug the border.
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 65f1365e67..0ccc056a8d 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1215,10 +1215,10 @@ void Window::set_force_native(bool p_force_native) {
if (force_native == p_force_native) {
return;
}
- force_native = p_force_native;
if (is_visible() && !is_in_edited_scene_root()) {
- WARN_PRINT("Can't change \"force_native\" while a window is displayed. Consider hiding window before changing this value.");
+ ERR_FAIL_MSG("Can't change \"force_native\" while a window is displayed. Consider hiding window before changing this value.");
}
+ force_native = p_force_native;
}
bool Window::get_force_native() const {
@@ -1579,6 +1579,7 @@ bool Window::_can_consume_input_events() const {
}
void Window::_window_input(const Ref<InputEvent> &p_ev) {
+ ERR_MAIN_THREAD_GUARD;
if (EngineDebugger::is_active()) {
// Quit from game window using the stop shortcut (F8 by default).
// The custom shortcut is provided via environment variable when running from the editor.
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 752cfe2288..1c8833494d 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -33,53 +33,6 @@
#include "core/config/project_settings.h"
#include "core/object/class_db.h"
#include "core/os/os.h"
-#include "scene/2d/animated_sprite_2d.h"
-#include "scene/2d/audio_listener_2d.h"
-#include "scene/2d/audio_stream_player_2d.h"
-#include "scene/2d/back_buffer_copy.h"
-#include "scene/2d/camera_2d.h"
-#include "scene/2d/canvas_group.h"
-#include "scene/2d/canvas_modulate.h"
-#include "scene/2d/cpu_particles_2d.h"
-#include "scene/2d/gpu_particles_2d.h"
-#include "scene/2d/light_2d.h"
-#include "scene/2d/light_occluder_2d.h"
-#include "scene/2d/line_2d.h"
-#include "scene/2d/marker_2d.h"
-#include "scene/2d/mesh_instance_2d.h"
-#include "scene/2d/multimesh_instance_2d.h"
-#include "scene/2d/navigation_agent_2d.h"
-#include "scene/2d/navigation_link_2d.h"
-#include "scene/2d/navigation_obstacle_2d.h"
-#include "scene/2d/navigation_region_2d.h"
-#include "scene/2d/parallax_2d.h"
-#include "scene/2d/parallax_background.h"
-#include "scene/2d/parallax_layer.h"
-#include "scene/2d/path_2d.h"
-#include "scene/2d/physics/animatable_body_2d.h"
-#include "scene/2d/physics/area_2d.h"
-#include "scene/2d/physics/character_body_2d.h"
-#include "scene/2d/physics/collision_polygon_2d.h"
-#include "scene/2d/physics/collision_shape_2d.h"
-#include "scene/2d/physics/joints/damped_spring_joint_2d.h"
-#include "scene/2d/physics/joints/groove_joint_2d.h"
-#include "scene/2d/physics/joints/joint_2d.h"
-#include "scene/2d/physics/joints/pin_joint_2d.h"
-#include "scene/2d/physics/kinematic_collision_2d.h"
-#include "scene/2d/physics/physical_bone_2d.h"
-#include "scene/2d/physics/physics_body_2d.h"
-#include "scene/2d/physics/ray_cast_2d.h"
-#include "scene/2d/physics/rigid_body_2d.h"
-#include "scene/2d/physics/shape_cast_2d.h"
-#include "scene/2d/physics/static_body_2d.h"
-#include "scene/2d/polygon_2d.h"
-#include "scene/2d/remote_transform_2d.h"
-#include "scene/2d/skeleton_2d.h"
-#include "scene/2d/sprite_2d.h"
-#include "scene/2d/tile_map.h"
-#include "scene/2d/tile_map_layer.h"
-#include "scene/2d/touch_screen_button.h"
-#include "scene/2d/visible_on_screen_notifier_2d.h"
#include "scene/animation/animation_blend_space_1d.h"
#include "scene/animation/animation_blend_space_2d.h"
#include "scene/animation/animation_blend_tree.h"
@@ -146,29 +99,11 @@
#include "scene/main/multiplayer_api.h"
#include "scene/main/resource_preloader.h"
#include "scene/main/scene_tree.h"
+#include "scene/main/shader_globals_override.h"
#include "scene/main/status_indicator.h"
#include "scene/main/timer.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
-#include "scene/resources/2d/capsule_shape_2d.h"
-#include "scene/resources/2d/circle_shape_2d.h"
-#include "scene/resources/2d/concave_polygon_shape_2d.h"
-#include "scene/resources/2d/convex_polygon_shape_2d.h"
-#include "scene/resources/2d/polygon_path_finder.h"
-#include "scene/resources/2d/rectangle_shape_2d.h"
-#include "scene/resources/2d/segment_shape_2d.h"
-#include "scene/resources/2d/separation_ray_shape_2d.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_lookat.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_stackholder.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.h"
-#include "scene/resources/2d/skeleton/skeleton_modification_stack_2d.h"
-#include "scene/resources/2d/tile_set.h"
-#include "scene/resources/2d/world_boundary_shape_2d.h"
#include "scene/resources/animated_texture.h"
#include "scene/resources/animation_library.h"
#include "scene/resources/atlas_texture.h"
@@ -193,9 +128,6 @@
#include "scene/resources/mesh_texture.h"
#include "scene/resources/multimesh.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
-#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/particle_process_material.h"
#include "scene/resources/physics_material.h"
@@ -226,7 +158,75 @@
#include "scene/scene_string_names.h"
#include "scene/theme/theme_db.h"
-#include "scene/main/shader_globals_override.h"
+// 2D
+#include "scene/2d/animated_sprite_2d.h"
+#include "scene/2d/audio_listener_2d.h"
+#include "scene/2d/audio_stream_player_2d.h"
+#include "scene/2d/back_buffer_copy.h"
+#include "scene/2d/camera_2d.h"
+#include "scene/2d/canvas_group.h"
+#include "scene/2d/canvas_modulate.h"
+#include "scene/2d/cpu_particles_2d.h"
+#include "scene/2d/gpu_particles_2d.h"
+#include "scene/2d/light_2d.h"
+#include "scene/2d/light_occluder_2d.h"
+#include "scene/2d/line_2d.h"
+#include "scene/2d/marker_2d.h"
+#include "scene/2d/mesh_instance_2d.h"
+#include "scene/2d/multimesh_instance_2d.h"
+#include "scene/2d/navigation_agent_2d.h"
+#include "scene/2d/navigation_link_2d.h"
+#include "scene/2d/navigation_obstacle_2d.h"
+#include "scene/2d/navigation_region_2d.h"
+#include "scene/2d/parallax_2d.h"
+#include "scene/2d/parallax_background.h"
+#include "scene/2d/parallax_layer.h"
+#include "scene/2d/path_2d.h"
+#include "scene/2d/physics/animatable_body_2d.h"
+#include "scene/2d/physics/area_2d.h"
+#include "scene/2d/physics/character_body_2d.h"
+#include "scene/2d/physics/collision_polygon_2d.h"
+#include "scene/2d/physics/collision_shape_2d.h"
+#include "scene/2d/physics/joints/damped_spring_joint_2d.h"
+#include "scene/2d/physics/joints/groove_joint_2d.h"
+#include "scene/2d/physics/joints/joint_2d.h"
+#include "scene/2d/physics/joints/pin_joint_2d.h"
+#include "scene/2d/physics/kinematic_collision_2d.h"
+#include "scene/2d/physics/physical_bone_2d.h"
+#include "scene/2d/physics/physics_body_2d.h"
+#include "scene/2d/physics/ray_cast_2d.h"
+#include "scene/2d/physics/rigid_body_2d.h"
+#include "scene/2d/physics/shape_cast_2d.h"
+#include "scene/2d/physics/static_body_2d.h"
+#include "scene/2d/polygon_2d.h"
+#include "scene/2d/remote_transform_2d.h"
+#include "scene/2d/skeleton_2d.h"
+#include "scene/2d/sprite_2d.h"
+#include "scene/2d/tile_map.h"
+#include "scene/2d/tile_map_layer.h"
+#include "scene/2d/touch_screen_button.h"
+#include "scene/2d/visible_on_screen_notifier_2d.h"
+#include "scene/resources/2d/capsule_shape_2d.h"
+#include "scene/resources/2d/circle_shape_2d.h"
+#include "scene/resources/2d/concave_polygon_shape_2d.h"
+#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
+#include "scene/resources/2d/polygon_path_finder.h"
+#include "scene/resources/2d/rectangle_shape_2d.h"
+#include "scene/resources/2d/segment_shape_2d.h"
+#include "scene/resources/2d/separation_ray_shape_2d.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_ccdik.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_fabrik.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_jiggle.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_lookat.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_physicalbones.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_stackholder.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_2d_twoboneik.h"
+#include "scene/resources/2d/skeleton/skeleton_modification_stack_2d.h"
+#include "scene/resources/2d/tile_set.h"
+#include "scene/resources/2d/world_boundary_shape_2d.h"
#ifndef _3D_DISABLED
#include "scene/3d/audio_listener_3d.h"
@@ -298,6 +298,7 @@
#include "scene/resources/3d/height_map_shape_3d.h"
#include "scene/resources/3d/importer_mesh.h"
#include "scene/resources/3d/mesh_library.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/3d/primitive_meshes.h"
#include "scene/resources/3d/separation_ray_shape_3d.h"
#include "scene/resources/3d/sky_material.h"
@@ -858,6 +859,7 @@ void register_scene_types() {
GDREGISTER_CLASS(MeshDataTool);
#ifndef _3D_DISABLED
+ GDREGISTER_CLASS(AudioStreamPlayer3D);
GDREGISTER_VIRTUAL_CLASS(PrimitiveMesh);
GDREGISTER_CLASS(BoxMesh);
GDREGISTER_CLASS(CapsuleMesh);
@@ -881,6 +883,7 @@ void register_scene_types() {
BaseMaterial3D::init_shaders();
GDREGISTER_CLASS(MeshLibrary);
+ GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D);
OS::get_singleton()->yield(); // may take time to init
@@ -969,7 +972,6 @@ void register_scene_types() {
GDREGISTER_CLASS(StyleBoxLine);
GDREGISTER_CLASS(Theme);
- GDREGISTER_CLASS(PolygonPathFinder);
GDREGISTER_CLASS(BitMap);
GDREGISTER_CLASS(Gradient);
@@ -980,16 +982,13 @@ void register_scene_types() {
OS::get_singleton()->yield(); // may take time to init
GDREGISTER_CLASS(AudioStreamPlayer);
- GDREGISTER_CLASS(AudioStreamPlayer2D);
-#ifndef _3D_DISABLED
- GDREGISTER_CLASS(AudioStreamPlayer3D);
-#endif
GDREGISTER_CLASS(AudioStreamWAV);
GDREGISTER_CLASS(AudioStreamPolyphonic);
GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPolyphonic);
OS::get_singleton()->yield(); // may take time to init
+ GDREGISTER_CLASS(AudioStreamPlayer2D);
GDREGISTER_ABSTRACT_CLASS(Shape2D);
GDREGISTER_CLASS(WorldBoundaryShape2D);
GDREGISTER_CLASS(SegmentShape2D);
@@ -1002,10 +1001,10 @@ void register_scene_types() {
GDREGISTER_CLASS(Curve2D);
GDREGISTER_CLASS(Path2D);
GDREGISTER_CLASS(PathFollow2D);
+ GDREGISTER_CLASS(PolygonPathFinder);
GDREGISTER_CLASS(NavigationMesh);
GDREGISTER_CLASS(NavigationMeshSourceGeometryData2D);
- GDREGISTER_CLASS(NavigationMeshSourceGeometryData3D);
GDREGISTER_CLASS(NavigationPolygon);
GDREGISTER_CLASS(NavigationRegion2D);
GDREGISTER_CLASS(NavigationAgent2D);
diff --git a/scene/resources/navigation_mesh_source_geometry_data_2d.cpp b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
index d613b498a1..d613b498a1 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_2d.cpp
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
diff --git a/scene/resources/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
index 0e321fbeb9..11fc5d3850 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_2d.h
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
@@ -33,7 +33,7 @@
#include "core/os/rw_lock.h"
#include "scene/2d/node_2d.h"
-#include "scene/resources/navigation_polygon.h"
+#include "scene/resources/2d/navigation_polygon.h"
class NavigationMeshSourceGeometryData2D : public Resource {
GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp
index 274b13a487..274b13a487 100644
--- a/scene/resources/navigation_polygon.cpp
+++ b/scene/resources/2d/navigation_polygon.cpp
diff --git a/scene/resources/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h
index b9816f900c..b9816f900c 100644
--- a/scene/resources/navigation_polygon.h
+++ b/scene/resources/2d/navigation_polygon.h
diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h
index dceda1a791..e6d3f7e15d 100644
--- a/scene/resources/2d/tile_set.h
+++ b/scene/resources/2d/tile_set.h
@@ -38,8 +38,8 @@
#include "scene/2d/light_occluder_2d.h"
#include "scene/main/canvas_item.h"
#include "scene/resources/2d/convex_polygon_shape_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "scene/resources/image_texture.h"
-#include "scene/resources/navigation_polygon.h"
#include "scene/resources/packed_scene.h"
#include "scene/resources/physics_material.h"
diff --git a/scene/resources/navigation_mesh_source_geometry_data_3d.cpp b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
index 39a17946fa..39a17946fa 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_3d.cpp
+++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
diff --git a/scene/resources/navigation_mesh_source_geometry_data_3d.h b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
index 79e2f3740d..79e2f3740d 100644
--- a/scene/resources/navigation_mesh_source_geometry_data_3d.h
+++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index 5acb08de14..30b90841e3 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -635,9 +635,9 @@ void ParticleProcessMaterial::_update_shader() {
code += " \n";
code += " float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n";
code += " float ring_random_radius = rand_from_seed(alt_seed) * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;\n";
- code += " vec3 axis = normalize(emission_ring_axis);\n";
+ code += " vec3 axis = emission_ring_axis == vec3(0.0) ? vec3(0.0, 0.0, 1.0) : normalize(emission_ring_axis);\n";
code += " vec3 ortho_axis = vec3(0.0);\n";
- code += " if (axis == vec3(1.0, 0.0, 0.0)) {\n";
+ code += " if (abs(axis) == vec3(1.0, 0.0, 0.0)) {\n";
code += " ortho_axis = cross(axis, vec3(0.0, 1.0, 0.0));\n";
code += " } else {\n";
code += " ortho_axis = cross(axis, vec3(1.0, 0.0, 0.0));\n";
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 07868e7e49..98a12f1400 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1067,7 +1067,7 @@ void ResourceLoaderText::open(Ref<FileAccess> p_f, bool p_skip_first_tag) {
if (fmt > FORMAT_VERSION) {
error_text = "Saved with newer format version";
_printerr();
- error = ERR_PARSE_ERROR;
+ error = ERR_FILE_UNRECOGNIZED;
return;
}
}
diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp
index e52e5d47bb..ec2c8ddb7b 100644
--- a/scene/resources/world_2d.cpp
+++ b/scene/resources/world_2d.cpp
@@ -66,6 +66,10 @@ RID World2D::get_navigation_map() const {
return navigation_map;
}
+PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
+ return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space());
+}
+
void World2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_canvas"), &World2D::get_canvas);
ClassDB::bind_method(D_METHOD("get_space"), &World2D::get_space);
@@ -79,10 +83,6 @@ void World2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "direct_space_state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectSpaceState2D", PROPERTY_USAGE_NONE), "", "get_direct_space_state");
}
-PhysicsDirectSpaceState2D *World2D::get_direct_space_state() {
- return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space());
-}
-
void World2D::register_viewport(Viewport *p_viewport) {
viewports.insert(p_viewport);
}
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index cf8ece0d4c..5610b2f642 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -63,7 +63,7 @@ SceneStringNames::SceneStringNames() {
RESET = StaticCString::create("RESET");
pose_updated = StaticCString::create("pose_updated");
- bone_pose_changed = StaticCString::create("bone_pose_changed");
+ skeleton_updated = StaticCString::create("skeleton_updated");
bone_enabled_changed = StaticCString::create("bone_enabled_changed");
show_rest_only_changed = StaticCString::create("show_rest_only_changed");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 10b71e2a2a..60254e3006 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -99,7 +99,7 @@ public:
StringName RESET;
StringName pose_updated;
- StringName bone_pose_changed;
+ StringName skeleton_updated;
StringName bone_enabled_changed;
StringName show_rest_only_changed;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 9ceb6909fe..9600caa214 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -212,7 +212,7 @@ String DisplayServer::global_menu_get_item_submenu(const String &p_menu_root, in
ERR_FAIL_NULL_V(nmenu, String());
RID rid = nmenu->get_item_submenu(_get_rid_from_name(nmenu, p_menu_root), p_idx);
if (!nmenu->is_system_menu(rid)) {
- for (HashMap<String, RID>::Iterator E = menu_names.begin(); E;) {
+ for (HashMap<String, RID>::Iterator E = menu_names.begin(); E; ++E) {
if (E->value == rid) {
return E->key;
}
@@ -697,10 +697,6 @@ void DisplayServer::release_rendering_thread() {
WARN_PRINT("Rendering thread not supported by this display server.");
}
-void DisplayServer::make_rendering_thread() {
- WARN_PRINT("Rendering thread not supported by this display server.");
-}
-
void DisplayServer::swap_buffers() {
WARN_PRINT("Swap buffers not supported by this display server.");
}
diff --git a/servers/display_server.h b/servers/display_server.h
index f1a98c2c17..aab51644c0 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -559,7 +559,6 @@ public:
virtual void force_process_and_drop_events();
virtual void release_rendering_thread();
- virtual void make_rendering_thread();
virtual void swap_buffers();
virtual void set_native_icon(const String &p_filename);
diff --git a/servers/navigation/navigation_path_query_parameters_2d.cpp b/servers/navigation/navigation_path_query_parameters_2d.cpp
index 78da87d677..6c1f88e349 100644
--- a/servers/navigation/navigation_path_query_parameters_2d.cpp
+++ b/servers/navigation/navigation_path_query_parameters_2d.cpp
@@ -119,6 +119,22 @@ BitField<NavigationPathQueryParameters2D::PathMetadataFlags> NavigationPathQuery
return (int64_t)parameters.metadata_flags;
}
+void NavigationPathQueryParameters2D::set_simplify_path(bool p_enabled) {
+ parameters.simplify_path = p_enabled;
+}
+
+bool NavigationPathQueryParameters2D::get_simplify_path() const {
+ return parameters.simplify_path;
+}
+
+void NavigationPathQueryParameters2D::set_simplify_epsilon(real_t p_epsilon) {
+ parameters.simplify_epsilon = MAX(0.0, p_epsilon);
+}
+
+real_t NavigationPathQueryParameters2D::get_simplify_epsilon() const {
+ return parameters.simplify_epsilon;
+}
+
void NavigationPathQueryParameters2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pathfinding_algorithm", "pathfinding_algorithm"), &NavigationPathQueryParameters2D::set_pathfinding_algorithm);
ClassDB::bind_method(D_METHOD("get_pathfinding_algorithm"), &NavigationPathQueryParameters2D::get_pathfinding_algorithm);
@@ -141,6 +157,12 @@ void NavigationPathQueryParameters2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_metadata_flags", "flags"), &NavigationPathQueryParameters2D::set_metadata_flags);
ClassDB::bind_method(D_METHOD("get_metadata_flags"), &NavigationPathQueryParameters2D::get_metadata_flags);
+ ClassDB::bind_method(D_METHOD("set_simplify_path", "enabled"), &NavigationPathQueryParameters2D::set_simplify_path);
+ ClassDB::bind_method(D_METHOD("get_simplify_path"), &NavigationPathQueryParameters2D::get_simplify_path);
+
+ ClassDB::bind_method(D_METHOD("set_simplify_epsilon", "epsilon"), &NavigationPathQueryParameters2D::set_simplify_epsilon);
+ ClassDB::bind_method(D_METHOD("get_simplify_epsilon"), &NavigationPathQueryParameters2D::get_simplify_epsilon);
+
ADD_PROPERTY(PropertyInfo(Variant::RID, "map"), "set_map", "get_map");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "start_position"), "set_start_position", "get_start_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "target_position"), "set_target_position", "get_target_position");
@@ -148,6 +170,8 @@ void NavigationPathQueryParameters2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon"), "set_simplify_epsilon", "get_simplify_epsilon");
BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR);
diff --git a/servers/navigation/navigation_path_query_parameters_2d.h b/servers/navigation/navigation_path_query_parameters_2d.h
index 48bb93aa7c..a1d5f2d109 100644
--- a/servers/navigation/navigation_path_query_parameters_2d.h
+++ b/servers/navigation/navigation_path_query_parameters_2d.h
@@ -82,6 +82,12 @@ public:
void set_metadata_flags(BitField<NavigationPathQueryParameters2D::PathMetadataFlags> p_flags);
BitField<NavigationPathQueryParameters2D::PathMetadataFlags> get_metadata_flags() const;
+
+ void set_simplify_path(bool p_enabled);
+ bool get_simplify_path() const;
+
+ void set_simplify_epsilon(real_t p_epsilon);
+ real_t get_simplify_epsilon() const;
};
VARIANT_ENUM_CAST(NavigationPathQueryParameters2D::PathfindingAlgorithm);
diff --git a/servers/navigation/navigation_path_query_parameters_3d.cpp b/servers/navigation/navigation_path_query_parameters_3d.cpp
index 52333edbc1..b0a5b0ad82 100644
--- a/servers/navigation/navigation_path_query_parameters_3d.cpp
+++ b/servers/navigation/navigation_path_query_parameters_3d.cpp
@@ -119,6 +119,22 @@ BitField<NavigationPathQueryParameters3D::PathMetadataFlags> NavigationPathQuery
return (int64_t)parameters.metadata_flags;
}
+void NavigationPathQueryParameters3D::set_simplify_path(bool p_enabled) {
+ parameters.simplify_path = p_enabled;
+}
+
+bool NavigationPathQueryParameters3D::get_simplify_path() const {
+ return parameters.simplify_path;
+}
+
+void NavigationPathQueryParameters3D::set_simplify_epsilon(real_t p_epsilon) {
+ parameters.simplify_epsilon = MAX(0.0, p_epsilon);
+}
+
+real_t NavigationPathQueryParameters3D::get_simplify_epsilon() const {
+ return parameters.simplify_epsilon;
+}
+
void NavigationPathQueryParameters3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pathfinding_algorithm", "pathfinding_algorithm"), &NavigationPathQueryParameters3D::set_pathfinding_algorithm);
ClassDB::bind_method(D_METHOD("get_pathfinding_algorithm"), &NavigationPathQueryParameters3D::get_pathfinding_algorithm);
@@ -141,6 +157,12 @@ void NavigationPathQueryParameters3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_metadata_flags", "flags"), &NavigationPathQueryParameters3D::set_metadata_flags);
ClassDB::bind_method(D_METHOD("get_metadata_flags"), &NavigationPathQueryParameters3D::get_metadata_flags);
+ ClassDB::bind_method(D_METHOD("set_simplify_path", "enabled"), &NavigationPathQueryParameters3D::set_simplify_path);
+ ClassDB::bind_method(D_METHOD("get_simplify_path"), &NavigationPathQueryParameters3D::get_simplify_path);
+
+ ClassDB::bind_method(D_METHOD("set_simplify_epsilon", "epsilon"), &NavigationPathQueryParameters3D::set_simplify_epsilon);
+ ClassDB::bind_method(D_METHOD("get_simplify_epsilon"), &NavigationPathQueryParameters3D::get_simplify_epsilon);
+
ADD_PROPERTY(PropertyInfo(Variant::RID, "map"), "set_map", "get_map");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "start_position"), "set_start_position", "get_start_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position");
@@ -148,6 +170,8 @@ void NavigationPathQueryParameters3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "pathfinding_algorithm", PROPERTY_HINT_ENUM, "AStar"), "set_pathfinding_algorithm", "get_pathfinding_algorithm");
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_postprocessing", PROPERTY_HINT_ENUM, "Corridorfunnel,Edgecentered"), "set_path_postprocessing", "get_path_postprocessing");
ADD_PROPERTY(PropertyInfo(Variant::INT, "metadata_flags", PROPERTY_HINT_FLAGS, "Include Types,Include RIDs,Include Owners"), "set_metadata_flags", "get_metadata_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simplify_path"), "set_simplify_path", "get_simplify_path");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "simplify_epsilon"), "set_simplify_epsilon", "get_simplify_epsilon");
BIND_ENUM_CONSTANT(PATHFINDING_ALGORITHM_ASTAR);
diff --git a/servers/navigation/navigation_path_query_parameters_3d.h b/servers/navigation/navigation_path_query_parameters_3d.h
index b12c2d49d5..2eb85db787 100644
--- a/servers/navigation/navigation_path_query_parameters_3d.h
+++ b/servers/navigation/navigation_path_query_parameters_3d.h
@@ -82,6 +82,12 @@ public:
void set_metadata_flags(BitField<NavigationPathQueryParameters3D::PathMetadataFlags> p_flags);
BitField<NavigationPathQueryParameters3D::PathMetadataFlags> get_metadata_flags() const;
+
+ void set_simplify_path(bool p_enabled);
+ bool get_simplify_path() const;
+
+ void set_simplify_epsilon(real_t p_epsilon);
+ real_t get_simplify_epsilon() const;
};
VARIANT_ENUM_CAST(NavigationPathQueryParameters3D::PathfindingAlgorithm);
diff --git a/servers/navigation/navigation_utilities.h b/servers/navigation/navigation_utilities.h
index 04d0ab0d98..7ae22b1d3a 100644
--- a/servers/navigation/navigation_utilities.h
+++ b/servers/navigation/navigation_utilities.h
@@ -66,6 +66,8 @@ struct PathQueryParameters {
Vector3 target_position;
uint32_t navigation_layers = 1;
BitField<PathMetadataFlags> metadata_flags = PATH_INCLUDE_ALL;
+ bool simplify_path = false;
+ real_t simplify_epsilon = 0.0;
};
struct PathQueryResult {
diff --git a/servers/navigation_server_2d.cpp b/servers/navigation_server_2d.cpp
index d87ac00e32..625ae8abde 100644
--- a/servers/navigation_server_2d.cpp
+++ b/servers/navigation_server_2d.cpp
@@ -165,6 +165,8 @@ void NavigationServer2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_polygon", "source_geometry_data", "callback"), &NavigationServer2D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("is_baking_navigation_polygon", "navigation_polygon"), &NavigationServer2D::is_baking_navigation_polygon);
+ ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer2D::simplify_path);
+
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer2D::free);
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationServer2D::set_debug_enabled);
diff --git a/servers/navigation_server_2d.h b/servers/navigation_server_2d.h
index d7dc9d6526..39d4c19064 100644
--- a/servers/navigation_server_2d.h
+++ b/servers/navigation_server_2d.h
@@ -34,8 +34,8 @@
#include "core/object/class_db.h"
#include "core/templates/rid.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_2d.h"
-#include "scene/resources/navigation_polygon.h"
+#include "scene/resources/2d/navigation_mesh_source_geometry_data_2d.h"
+#include "scene/resources/2d/navigation_polygon.h"
#include "servers/navigation/navigation_path_query_parameters_2d.h"
#include "servers/navigation/navigation_path_query_result_2d.h"
@@ -306,6 +306,8 @@ public:
virtual void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual bool is_baking_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) const = 0;
+ virtual Vector<Vector2> simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) = 0;
+
NavigationServer2D();
~NavigationServer2D() override;
diff --git a/servers/navigation_server_2d_dummy.h b/servers/navigation_server_2d_dummy.h
index 94c6dfd6a7..5d4cfbf91b 100644
--- a/servers/navigation_server_2d_dummy.h
+++ b/servers/navigation_server_2d_dummy.h
@@ -170,6 +170,8 @@ public:
void bake_from_source_geometry_data_async(const Ref<NavigationPolygon> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData2D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
bool is_baking_navigation_polygon(Ref<NavigationPolygon> p_navigation_polygon) const override { return false; }
+ Vector<Vector2> simplify_path(const Vector<Vector2> &p_path, real_t p_epsilon) override { return Vector<Vector2>(); }
+
void set_debug_enabled(bool p_enabled) {}
bool get_debug_enabled() const { return false; }
};
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index e460bcb9c6..fda26aacc1 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -181,10 +181,14 @@ void NavigationServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("obstacle_set_avoidance_layers", "obstacle", "layers"), &NavigationServer3D::obstacle_set_avoidance_layers);
ClassDB::bind_method(D_METHOD("obstacle_get_avoidance_layers", "obstacle"), &NavigationServer3D::obstacle_get_avoidance_layers);
+#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("parse_source_geometry_data", "navigation_mesh", "source_geometry_data", "root_node", "callback"), &NavigationServer3D::parse_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("bake_from_source_geometry_data_async", "navigation_mesh", "source_geometry_data", "callback"), &NavigationServer3D::bake_from_source_geometry_data_async, DEFVAL(Callable()));
ClassDB::bind_method(D_METHOD("is_baking_navigation_mesh", "navigation_mesh"), &NavigationServer3D::is_baking_navigation_mesh);
+#endif // _3D_DISABLED
+
+ ClassDB::bind_method(D_METHOD("simplify_path", "path", "epsilon"), &NavigationServer3D::simplify_path);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &NavigationServer3D::free);
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 975ffdee94..5a93c662b2 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -34,8 +34,8 @@
#include "core/object/class_db.h"
#include "core/templates/rid.h"
+#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
#include "scene/resources/navigation_mesh.h"
-#include "scene/resources/navigation_mesh_source_geometry_data_3d.h"
#include "servers/navigation/navigation_path_query_parameters_3d.h"
#include "servers/navigation/navigation_path_query_result_3d.h"
@@ -344,10 +344,14 @@ public:
virtual NavigationUtilities::PathQueryResult _query_path(const NavigationUtilities::PathQueryParameters &p_parameters) const = 0;
+#ifndef _3D_DISABLED
virtual void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const = 0;
+#endif // _3D_DISABLED
+
+ virtual Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) = 0;
NavigationServer3D();
~NavigationServer3D() override;
diff --git a/servers/navigation_server_3d_dummy.h b/servers/navigation_server_3d_dummy.h
index 9ba9b20d00..7079aa66be 100644
--- a/servers/navigation_server_3d_dummy.h
+++ b/servers/navigation_server_3d_dummy.h
@@ -175,10 +175,14 @@ public:
void obstacle_set_avoidance_layers(RID p_obstacle, uint32_t p_layers) override {}
uint32_t obstacle_get_avoidance_layers(RID p_obstacle) const override { return 0; }
+#ifndef _3D_DISABLED
void parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, Node *p_root_node, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override { return false; }
+#endif // _3D_DISABLED
+
+ Vector<Vector3> simplify_path(const Vector<Vector3> &p_path, real_t p_epsilon) override { return Vector<Vector3>(); }
void free(RID p_object) override {}
void set_active(bool p_active) override {}
diff --git a/servers/physics_2d/godot_shape_2d.cpp b/servers/physics_2d/godot_shape_2d.cpp
index db5e6b2353..d77b1a77e3 100644
--- a/servers/physics_2d/godot_shape_2d.cpp
+++ b/servers/physics_2d/godot_shape_2d.cpp
@@ -123,7 +123,7 @@ void GodotWorldBoundaryShape2D::set_data(const Variant &p_data) {
ERR_FAIL_COND(arr.size() != 2);
normal = arr[0];
d = arr[1];
- configure(Rect2(Vector2(-1e4, -1e4), Vector2(1e4 * 2, 1e4 * 2)));
+ configure(Rect2(Vector2(-1e15, -1e15), Vector2(1e15 * 2, 1e15 * 2)));
}
Variant GodotWorldBoundaryShape2D::get_data() const {
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index ea389ff59c..6eb983d5e0 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -152,7 +152,7 @@ Vector3 GodotWorldBoundaryShape3D::get_moment_of_inertia(real_t p_mass) const {
void GodotWorldBoundaryShape3D::_setup(const Plane &p_plane) {
plane = p_plane;
- configure(AABB(Vector3(-1e4, -1e4, -1e4), Vector3(1e4 * 2, 1e4 * 2, 1e4 * 2)));
+ configure(AABB(Vector3(-1e15, -1e15, -1e15), Vector3(1e15 * 2, 1e15 * 2, 1e15 * 2)));
}
void GodotWorldBoundaryShape3D::set_data(const Variant &p_data) {
diff --git a/servers/physics_server_2d_wrap_mt.cpp b/servers/physics_server_2d_wrap_mt.cpp
index a23bb5e701..4548bb91cb 100644
--- a/servers/physics_server_2d_wrap_mt.cpp
+++ b/servers/physics_server_2d_wrap_mt.cpp
@@ -33,7 +33,7 @@
#include "core/os/os.h"
void PhysicsServer2DWrapMT::thread_exit() {
- exit.set();
+ exit = true;
}
void PhysicsServer2DWrapMT::thread_step(real_t p_delta) {
@@ -41,25 +41,18 @@ void PhysicsServer2DWrapMT::thread_step(real_t p_delta) {
step_sem.post();
}
-void PhysicsServer2DWrapMT::_thread_callback(void *_instance) {
- PhysicsServer2DWrapMT *vsmt = reinterpret_cast<PhysicsServer2DWrapMT *>(_instance);
-
- vsmt->thread_loop();
-}
-
void PhysicsServer2DWrapMT::thread_loop() {
server_thread = Thread::get_caller_id();
physics_server_2d->init();
- exit.clear();
- step_thread_up.set();
- while (!exit.is_set()) {
- // flush commands one by one, until exit is requested
- command_queue.wait_and_flush();
+ command_queue.set_pump_task_id(server_task_id);
+ while (!exit) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.flush_all();
}
- command_queue.flush_all(); // flush all
+ command_queue.flush_all();
physics_server_2d->finish();
}
@@ -70,18 +63,14 @@ void PhysicsServer2DWrapMT::step(real_t p_step) {
if (create_thread) {
command_queue.push(this, &PhysicsServer2DWrapMT::thread_step, p_step);
} else {
- command_queue.flush_all(); //flush all pending from other threads
+ command_queue.flush_all(); // Flush all pending from other threads.
physics_server_2d->step(p_step);
}
}
void PhysicsServer2DWrapMT::sync() {
if (create_thread) {
- if (first_frame) {
- first_frame = false;
- } else {
- step_sem.wait(); //must not wait if a step was not issued
- }
+ step_sem.wait();
}
physics_server_2d->sync();
}
@@ -96,40 +85,34 @@ void PhysicsServer2DWrapMT::end_sync() {
void PhysicsServer2DWrapMT::init() {
if (create_thread) {
- //OS::get_singleton()->release_rendering_thread();
- thread.start(_thread_callback, this);
- while (!step_thread_up.is_set()) {
- OS::get_singleton()->delay_usec(1000);
- }
+ exit = false;
+ server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer2DWrapMT::thread_loop), true);
+ step_sem.post();
} else {
physics_server_2d->init();
}
}
void PhysicsServer2DWrapMT::finish() {
- if (thread.is_started()) {
+ if (create_thread) {
command_queue.push(this, &PhysicsServer2DWrapMT::thread_exit);
- thread.wait_to_finish();
+ if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
+ server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ }
} else {
physics_server_2d->finish();
}
}
-PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) :
- command_queue(p_create_thread) {
+PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool p_create_thread) {
physics_server_2d = p_contained;
create_thread = p_create_thread;
-
- if (!p_create_thread) {
- server_thread = Thread::get_caller_id();
- } else {
- server_thread = 0;
+ if (!create_thread) {
+ server_thread = Thread::MAIN_ID;
}
-
- main_thread = Thread::get_caller_id();
}
PhysicsServer2DWrapMT::~PhysicsServer2DWrapMT() {
memdelete(physics_server_2d);
- //finish();
}
diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h
index 3bebe5df85..5e2b3b4086 100644
--- a/servers/physics_server_2d_wrap_mt.h
+++ b/servers/physics_server_2d_wrap_mt.h
@@ -32,6 +32,7 @@
#define PHYSICS_SERVER_2D_WRAP_MT_H
#include "core/config/project_settings.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
#include "core/templates/safe_refcount.h"
@@ -43,30 +44,27 @@
#define SYNC_DEBUG
#endif
+#ifdef DEBUG_ENABLED
+#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer2D synchronizations on every frame. This significantly affects performance.");
+#endif
+
class PhysicsServer2DWrapMT : public PhysicsServer2D {
- mutable PhysicsServer2D *physics_server_2d;
+ mutable PhysicsServer2D *physics_server_2d = nullptr;
mutable CommandQueueMT command_queue;
- static void _thread_callback(void *_instance);
void thread_loop();
- Thread::ID server_thread;
- Thread::ID main_thread;
- SafeFlag exit;
- Thread thread;
- SafeFlag step_thread_up;
+ Thread::ID server_thread = Thread::UNASSIGNED_ID;
+ WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ bool exit = false;
+ Semaphore step_sem;
bool create_thread = false;
- Semaphore step_sem;
void thread_step(real_t p_delta);
void thread_exit();
- bool first_frame = true;
-
- Mutex alloc_mutex;
-
public:
#define ServerName PhysicsServer2D
#define ServerNameWrapMT PhysicsServer2DWrapMT
@@ -94,7 +92,7 @@ public:
//these work well, but should be used from the main thread only
bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_2d->shape_collide(p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, r_result_count);
}
@@ -109,18 +107,18 @@ public:
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectSpaceState2D *space_get_direct_state(RID p_space) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_2d->space_get_direct_state(p_space);
}
FUNC2(space_set_debug_contacts, RID, int);
virtual Vector<Vector2> space_get_contacts(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), Vector<Vector2>());
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), Vector<Vector2>());
return physics_server_2d->space_get_contacts(p_space);
}
virtual int space_get_contact_count(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), 0);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), 0);
return physics_server_2d->space_get_contact_count(p_space);
}
@@ -261,13 +259,13 @@ public:
FUNC2(body_set_pickable, RID, bool);
bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_2d->body_test_motion(p_body, p_parameters, r_result);
}
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_2d->body_get_direct_state(p_body);
}
@@ -338,4 +336,8 @@ public:
#endif
#undef SYNC_DEBUG
+#ifdef DEBUG_ENABLED
+#undef MAIN_THREAD_SYNC_WARN
+#endif
+
#endif // PHYSICS_SERVER_2D_WRAP_MT_H
diff --git a/servers/physics_server_3d_wrap_mt.cpp b/servers/physics_server_3d_wrap_mt.cpp
index feb17cad84..f8f60281a7 100644
--- a/servers/physics_server_3d_wrap_mt.cpp
+++ b/servers/physics_server_3d_wrap_mt.cpp
@@ -41,22 +41,15 @@ void PhysicsServer3DWrapMT::thread_step(real_t p_delta) {
step_sem.post();
}
-void PhysicsServer3DWrapMT::_thread_callback(void *_instance) {
- PhysicsServer3DWrapMT *vsmt = reinterpret_cast<PhysicsServer3DWrapMT *>(_instance);
-
- vsmt->thread_loop();
-}
-
void PhysicsServer3DWrapMT::thread_loop() {
server_thread = Thread::get_caller_id();
physics_server_3d->init();
- exit = false;
- step_thread_up = true;
+ command_queue.set_pump_task_id(server_task_id);
while (!exit) {
- // flush commands one by one, until exit is requested
- command_queue.wait_and_flush();
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.flush_all();
}
command_queue.flush_all(); // flush all
@@ -70,18 +63,14 @@ void PhysicsServer3DWrapMT::step(real_t p_step) {
if (create_thread) {
command_queue.push(this, &PhysicsServer3DWrapMT::thread_step, p_step);
} else {
- command_queue.flush_all(); //flush all pending from other threads
+ command_queue.flush_all(); // Flush all pending from other threads.
physics_server_3d->step(p_step);
}
}
void PhysicsServer3DWrapMT::sync() {
if (create_thread) {
- if (first_frame) {
- first_frame = false;
- } else {
- step_sem.wait(); //must not wait if a step was not issued
- }
+ step_sem.wait();
}
physics_server_3d->sync();
}
@@ -96,40 +85,34 @@ void PhysicsServer3DWrapMT::end_sync() {
void PhysicsServer3DWrapMT::init() {
if (create_thread) {
- //OS::get_singleton()->release_rendering_thread();
- thread.start(_thread_callback, this);
- while (!step_thread_up) {
- OS::get_singleton()->delay_usec(1000);
- }
+ exit = false;
+ server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &PhysicsServer3DWrapMT::thread_loop), true);
+ step_sem.post();
} else {
physics_server_3d->init();
}
}
void PhysicsServer3DWrapMT::finish() {
- if (thread.is_started()) {
+ if (create_thread) {
command_queue.push(this, &PhysicsServer3DWrapMT::thread_exit);
- thread.wait_to_finish();
+ if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
+ server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ }
} else {
physics_server_3d->finish();
}
}
-PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool p_create_thread) :
- command_queue(p_create_thread) {
+PhysicsServer3DWrapMT::PhysicsServer3DWrapMT(PhysicsServer3D *p_contained, bool p_create_thread) {
physics_server_3d = p_contained;
create_thread = p_create_thread;
-
- if (!p_create_thread) {
- server_thread = Thread::get_caller_id();
- } else {
- server_thread = 0;
+ if (!create_thread) {
+ server_thread = Thread::MAIN_ID;
}
-
- main_thread = Thread::get_caller_id();
}
PhysicsServer3DWrapMT::~PhysicsServer3DWrapMT() {
memdelete(physics_server_3d);
- //finish();
}
diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h
index fc8930977d..22f3ee0e45 100644
--- a/servers/physics_server_3d_wrap_mt.h
+++ b/servers/physics_server_3d_wrap_mt.h
@@ -32,6 +32,7 @@
#define PHYSICS_SERVER_3D_WRAP_MT_H
#include "core/config/project_settings.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
#include "servers/physics_server_3d.h"
@@ -42,30 +43,27 @@
#define SYNC_DEBUG
#endif
+#ifdef DEBUG_ENABLED
+#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing PhysicsServer3D synchronizations on every frame. This significantly affects performance.");
+#endif
+
class PhysicsServer3DWrapMT : public PhysicsServer3D {
- mutable PhysicsServer3D *physics_server_3d;
+ mutable PhysicsServer3D *physics_server_3d = nullptr;
mutable CommandQueueMT command_queue;
- static void _thread_callback(void *_instance);
void thread_loop();
- Thread::ID server_thread;
- Thread::ID main_thread;
- volatile bool exit = false;
- Thread thread;
- volatile bool step_thread_up = false;
+ Thread::ID server_thread = Thread::UNASSIGNED_ID;
+ WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ bool exit = false;
+ Semaphore step_sem;
bool create_thread = false;
- Semaphore step_sem;
void thread_step(real_t p_delta);
void thread_exit();
- bool first_frame = true;
-
- Mutex alloc_mutex;
-
public:
#define ServerName PhysicsServer3D
#define ServerNameWrapMT PhysicsServer3DWrapMT
@@ -98,7 +96,7 @@ public:
#if 0
//these work well, but should be used from the main thread only
bool shape_collide(RID p_shape_A, const Transform &p_xform_A, const Vector3 &p_motion_A, RID p_shape_B, const Transform &p_xform_B, const Vector3 &p_motion_B, Vector3 *r_results, int p_result_max, int &r_result_count) {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_3d->shape_collide(p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, r_result_count);
}
#endif
@@ -113,18 +111,18 @@ public:
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectSpaceState3D *space_get_direct_state(RID p_space) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_3d->space_get_direct_state(p_space);
}
FUNC2(space_set_debug_contacts, RID, int);
virtual Vector<Vector3> space_get_contacts(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), Vector<Vector3>());
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), Vector<Vector3>());
return physics_server_3d->space_get_contacts(p_space);
}
virtual int space_get_contact_count(RID p_space) const override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), 0);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), 0);
return physics_server_3d->space_get_contact_count(p_space);
}
@@ -260,13 +258,13 @@ public:
FUNC2(body_set_ray_pickable, RID, bool);
bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), false);
return physics_server_3d->body_test_motion(p_body, p_parameters, r_result);
}
// this function only works on physics process, errors and returns null otherwise
PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override {
- ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), nullptr);
+ ERR_FAIL_COND_V(!Thread::is_main_thread(), nullptr);
return physics_server_3d->body_get_direct_state(p_body);
}
@@ -411,4 +409,8 @@ public:
#endif
#undef SYNC_DEBUG
+#ifdef DEBUG_ENABLED
+#undef MAIN_THREAD_SYNC_WARN
+#endif
+
#endif // PHYSICS_SERVER_3D_WRAP_MT_H
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index e32164ea98..46c84fd230 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -332,7 +332,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
child_item_count = ci->ysort_children_count + 1;
child_items = (Item **)alloca(child_item_count * sizeof(Item *));
- ci->ysort_xform = final_xform.affine_inverse();
+ ci->ysort_xform = ci->xform_curr.affine_inverse();
ci->ysort_pos = Vector2();
ci->ysort_modulate = Color(1, 1, 1, 1);
ci->ysort_index = 0;
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index e61bb9eae8..6cb03871c9 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1157,6 +1157,7 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render
render_data.lights = &empty;
render_data.reflection_probes = &empty;
render_data.voxel_gi_instances = &empty;
+ render_data.lightmaps = &empty;
}
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED ||
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index c9c7c53d04..f7b28e7a1e 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -257,7 +257,6 @@ void ParticlesStorage::particles_set_emitting(RID p_particles, bool p_emitting)
}
bool ParticlesStorage::particles_get_emitting(RID p_particles) {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
@@ -608,10 +607,6 @@ void ParticlesStorage::particles_request_process(RID p_particles) {
}
AABB ParticlesStorage::particles_get_current_aabb(RID p_particles) {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Calling this function with threaded rendering enabled stalls the renderer, use with care.");
- }
-
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, AABB());
@@ -1642,7 +1637,6 @@ Dependency *ParticlesStorage::particles_get_dependency(RID p_particles) const {
}
bool ParticlesStorage::particles_is_inactive(RID p_particles) const {
- ERR_FAIL_COND_V_MSG(RSG::threaded, false, "This function should never be used with threaded rendering, as it stalls the renderer.");
const Particles *particles = particles_owner.get_or_null(p_particles);
ERR_FAIL_NULL_V(particles, false);
return !particles->emitting && particles->inactive;
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 5bf0ab0ba6..7e5ccee0e3 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -69,9 +69,6 @@ void RenderingServerDefault::request_frame_drawn_callback(const Callable &p_call
}
void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
- //needs to be done before changes is reset to 0, to not force the editor to redraw
- RS::get_singleton()->emit_signal(SNAME("frame_pre_draw"));
-
changes = 0;
RSG::rasterizer->begin_frame(frame_step);
@@ -220,16 +217,9 @@ void RenderingServerDefault::_finish() {
void RenderingServerDefault::init() {
if (create_thread) {
- print_verbose("RenderingServerWrapMT: Creating render thread");
+ print_verbose("RenderingServerWrapMT: Starting render thread");
DisplayServer::get_singleton()->release_rendering_thread();
- if (create_thread) {
- thread.start(_thread_callback, this);
- print_verbose("RenderingServerWrapMT: Starting render thread");
- }
- while (!draw_thread_up.is_set()) {
- OS::get_singleton()->delay_usec(1000);
- }
- print_verbose("RenderingServerWrapMT: Finished render thread");
+ server_task_id = WorkerThreadPool::get_singleton()->add_task(callable_mp(this, &RenderingServerDefault::_thread_loop), true);
} else {
_init();
}
@@ -238,8 +228,9 @@ void RenderingServerDefault::init() {
void RenderingServerDefault::finish() {
if (create_thread) {
command_queue.push(this, &RenderingServerDefault::_thread_exit);
- if (thread.is_started()) {
- thread.wait_to_finish();
+ if (server_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(server_task_id);
+ server_task_id = WorkerThreadPool::INVALID_TASK_ID;
}
} else {
_finish();
@@ -337,38 +328,29 @@ Size2i RenderingServerDefault::get_maximum_viewport_size() const {
}
void RenderingServerDefault::_thread_exit() {
- exit.set();
+ exit = true;
}
void RenderingServerDefault::_thread_draw(bool p_swap_buffers, double frame_step) {
_draw(p_swap_buffers, frame_step);
}
-void RenderingServerDefault::_thread_flush() {
-}
-
-void RenderingServerDefault::_thread_callback(void *_instance) {
- RenderingServerDefault *vsmt = reinterpret_cast<RenderingServerDefault *>(_instance);
-
- vsmt->_thread_loop();
-}
-
void RenderingServerDefault::_thread_loop() {
server_thread = Thread::get_caller_id();
- DisplayServer::get_singleton()->make_rendering_thread();
-
+ DisplayServer::get_singleton()->gl_window_make_current(DisplayServer::MAIN_WINDOW_ID); // Move GL to this thread.
_init();
- draw_thread_up.set();
- while (!exit.is_set()) {
- // flush commands one by one, until exit is requested
- command_queue.wait_and_flush();
+ command_queue.set_pump_task_id(server_task_id);
+ while (!exit) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.flush_all();
}
- command_queue.flush_all(); // flush all
+ command_queue.flush_all();
_finish();
+ DisplayServer::get_singleton()->release_rendering_thread();
}
/* INTERPOLATION */
@@ -384,15 +366,15 @@ void RenderingServerDefault::set_physics_interpolation_enabled(bool p_enabled) {
/* EVENT QUEUING */
void RenderingServerDefault::sync() {
- if (create_thread) {
- command_queue.push_and_sync(this, &RenderingServerDefault::_thread_flush);
- } else {
- command_queue.flush_all(); //flush all pending from other threads
+ if (!create_thread) {
+ command_queue.flush_all(); // Flush all pending from other threads.
}
}
void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Manually triggering the draw function from the RenderingServer can only be done on the main thread. Call this function from the main thread or use call_deferred().");
+ // Needs to be done before changes is reset to 0, to not force the editor to redraw.
+ RS::get_singleton()->emit_signal(SNAME("frame_pre_draw"));
if (create_thread) {
command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step);
} else {
@@ -404,21 +386,14 @@ void RenderingServerDefault::_call_on_render_thread(const Callable &p_callable)
p_callable.call();
}
-RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
- command_queue(p_create_thread) {
+RenderingServerDefault::RenderingServerDefault(bool p_create_thread) {
RenderingServer::init();
-#ifdef THREADS_ENABLED
create_thread = p_create_thread;
if (!create_thread) {
- server_thread = Thread::get_caller_id();
- } else {
- server_thread = 0;
+ server_thread = Thread::MAIN_ID;
}
-#else
- create_thread = false;
- server_thread = Thread::get_main_id();
-#endif
+
RSG::threaded = create_thread;
RSG::canvas = memnew(RendererCanvasCull);
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index c50472c0cd..f94323f198 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -31,6 +31,7 @@
#ifndef RENDERING_SERVER_DEFAULT_H
#define RENDERING_SERVER_DEFAULT_H
+#include "core/object/worker_thread_pool.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
#include "core/templates/hash_map.h"
@@ -75,22 +76,17 @@ class RenderingServerDefault : public RenderingServer {
mutable CommandQueueMT command_queue;
- static void _thread_callback(void *_instance);
void _thread_loop();
- Thread::ID server_thread = 0;
- SafeFlag exit;
- Thread thread;
- SafeFlag draw_thread_up;
- bool create_thread;
+ Thread::ID server_thread = Thread::UNASSIGNED_ID;
+ WorkerThreadPool::TaskID server_task_id = WorkerThreadPool::INVALID_TASK_ID;
+ bool exit = false;
+ bool create_thread = false;
void _thread_draw(bool p_swap_buffers, double frame_step);
- void _thread_flush();
void _thread_exit();
- Mutex alloc_mutex;
-
void _draw(bool p_swap_buffers, double frame_step);
void _init();
void _finish();
@@ -127,6 +123,10 @@ public:
#define SYNC_DEBUG
#endif
+#ifdef DEBUG_ENABLED
+#define MAIN_THREAD_SYNC_WARN WARN_PRINT("Call to " + String(__FUNCTION__) + " causing RenderingServer synchronizations on every frame. This significantly affects performance.");
+#endif
+
#include "servers/server_wrap_mt_common.h"
/* TEXTURE API */
@@ -1013,6 +1013,9 @@ public:
#undef ServerName
#undef WRITE_ACTION
#undef SYNC_DEBUG
+#ifdef DEBUG_ENABLED
+#undef MAIN_THREAD_SYNC_WARN
+#endif
virtual uint64_t get_rendering_info(RenderingInfo p_info) override;
virtual RenderingDevice::DeviceType get_video_adapter_type() const override;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 8fbad346a4..99b3f54379 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -5029,6 +5029,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
Vector<Expression> expression;
//Vector<TokenType> operators;
+#ifdef DEBUG_ENABLED
+ bool check_position_write = check_warnings && HAS_WARNING(ShaderWarning::MAGIC_POSITION_WRITE_FLAG);
+ check_position_write = check_position_write && String(shader_type_identifier) == "spatial" && current_function == "vertex";
+#endif
while (true) {
Node *expr = nullptr;
@@ -5589,6 +5593,24 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error(vformat(RTR("Can't use function as identifier: '%s'."), String(identifier)));
return nullptr;
}
+#ifdef DEBUG_ENABLED
+ if (check_position_write && ident_type == IDENTIFIER_BUILTIN_VAR) {
+ if (String(identifier) == "POSITION") {
+ // Check if the user wrote "POSITION = vec4(VERTEX," and warn if they did.
+ TkPos prev_pos = _get_tkpos();
+ if (_get_token().type == TK_OP_ASSIGN &&
+ _get_token().type == TK_TYPE_VEC4 &&
+ _get_token().type == TK_PARENTHESIS_OPEN &&
+ _get_token().text == "VERTEX" &&
+ _get_token().type == TK_COMMA) {
+ _add_line_warning(ShaderWarning::MAGIC_POSITION_WRITE);
+ }
+
+ // Reset the position so compiling can continue as normal.
+ _set_tkpos(prev_pos);
+ }
+ }
+#endif
if (is_const) {
last_type = IDENTIFIER_CONSTANT;
} else {
diff --git a/servers/rendering/shader_warnings.cpp b/servers/rendering/shader_warnings.cpp
index dce8f6cff1..3b99f6c2bf 100644
--- a/servers/rendering/shader_warnings.cpp
+++ b/servers/rendering/shader_warnings.cpp
@@ -65,6 +65,8 @@ String ShaderWarning::get_message() const {
return subject;
case DEVICE_LIMIT_EXCEEDED:
return vformat(RTR("The total size of the %s for this shader on this device has been exceeded (%d/%d). The shader may not work correctly."), subject, (int)extra_args[0], (int)extra_args[1]);
+ case MAGIC_POSITION_WRITE:
+ return vformat(RTR("You are attempting to assign the VERTEX position in model space to the vertex POSITION in clip space. The definition of clip space changed in version 4.3, so if this code was written prior to 4.3, it will not continue to work. Consider specifying the clip space z-component directly i.e. use `vec4(VERTEX.xy, 1.0, 1.0)`."));
default:
break;
}
@@ -92,6 +94,7 @@ String ShaderWarning::get_name_from_code(Code p_code) {
"UNUSED_LOCAL_VARIABLE",
"FORMATTING_ERROR",
"DEVICE_LIMIT_EXCEEDED",
+ "MAGIC_POSITION_WRITE",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
@@ -122,6 +125,7 @@ static void init_code_to_flags_map() {
code_to_flags_map->insert(ShaderWarning::UNUSED_LOCAL_VARIABLE, ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG);
code_to_flags_map->insert(ShaderWarning::FORMATTING_ERROR, ShaderWarning::FORMATTING_ERROR_FLAG);
code_to_flags_map->insert(ShaderWarning::DEVICE_LIMIT_EXCEEDED, ShaderWarning::DEVICE_LIMIT_EXCEEDED_FLAG);
+ code_to_flags_map->insert(ShaderWarning::MAGIC_POSITION_WRITE, ShaderWarning::MAGIC_POSITION_WRITE_FLAG);
}
ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const HashMap<Code, bool> &p_map) {
diff --git a/servers/rendering/shader_warnings.h b/servers/rendering/shader_warnings.h
index ed28ebdd2b..69d684850f 100644
--- a/servers/rendering/shader_warnings.h
+++ b/servers/rendering/shader_warnings.h
@@ -51,6 +51,7 @@ public:
UNUSED_LOCAL_VARIABLE,
FORMATTING_ERROR,
DEVICE_LIMIT_EXCEEDED,
+ MAGIC_POSITION_WRITE,
WARNING_MAX,
};
@@ -65,6 +66,7 @@ public:
UNUSED_LOCAL_VARIABLE_FLAG = 64U,
FORMATTING_ERROR_FLAG = 128U,
DEVICE_LIMIT_EXCEEDED_FLAG = 256U,
+ MAGIC_POSITION_WRITE_FLAG = 512U,
};
private:
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 96d317ebd3..bbe6b1ad0d 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -83,25 +83,16 @@ static PackedInt64Array to_int_array(const Vector<ObjectID> &ids) {
}
PackedInt64Array RenderingServer::_instances_cull_aabb_bind(const AABB &p_aabb, RID p_scenario) const {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall.");
- }
Vector<ObjectID> ids = instances_cull_aabb(p_aabb, p_scenario);
return to_int_array(ids);
}
PackedInt64Array RenderingServer::_instances_cull_ray_bind(const Vector3 &p_from, const Vector3 &p_to, RID p_scenario) const {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall.");
- }
Vector<ObjectID> ids = instances_cull_ray(p_from, p_to, p_scenario);
return to_int_array(ids);
}
PackedInt64Array RenderingServer::_instances_cull_convex_bind(const TypedArray<Plane> &p_convex, RID p_scenario) const {
- if (RSG::threaded) {
- WARN_PRINT_ONCE("Using this function with a threaded renderer hurts performance, as it causes a server stall.");
- }
Vector<Plane> planes;
for (int i = 0; i < p_convex.size(); ++i) {
const Variant &v = p_convex[i];
diff --git a/servers/server_wrap_mt_common.h b/servers/server_wrap_mt_common.h
index 1a73c97fc7..40867490ca 100644
--- a/servers/server_wrap_mt_common.h
+++ b/servers/server_wrap_mt_common.h
@@ -31,12 +31,22 @@
#ifndef SERVER_WRAP_MT_COMMON_H
#define SERVER_WRAP_MT_COMMON_H
+#ifdef DEBIG_ENABLED
+#define MAIN_THREAD_SYNC_CHECK \
+ if (unlikely(Thread::is_main_thread() && Engine::get_singleton()->notify_frame_server_synced())) { \
+ MAIN_THREAD_SYNC_WARN \
+ }
+#else
+#define MAIN_THREAD_SYNC_CHECK
+#endif
+
#define FUNC0R(m_r, m_type) \
virtual m_r m_type() override { \
if (Thread::get_caller_id() != server_thread) { \
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -68,6 +78,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -102,6 +113,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(); \
@@ -113,6 +125,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(); \
@@ -128,6 +141,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -141,6 +155,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -154,6 +169,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1); \
@@ -165,6 +181,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1); \
@@ -199,6 +216,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -212,6 +230,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -225,6 +244,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2); \
@@ -236,6 +256,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2); \
@@ -270,6 +291,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -283,6 +305,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -296,6 +319,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3); \
@@ -307,6 +331,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3); \
@@ -341,6 +366,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -354,6 +380,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -367,6 +394,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4); \
@@ -378,6 +406,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4); \
@@ -412,6 +441,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -425,6 +455,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -438,6 +469,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5); \
@@ -449,6 +481,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5); \
@@ -483,6 +516,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -496,6 +530,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -509,6 +544,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6); \
@@ -520,6 +556,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6); \
@@ -554,6 +591,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -567,6 +605,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -580,6 +619,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7); \
@@ -591,6 +631,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7); \
@@ -625,6 +666,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -638,6 +680,7 @@
m_r ret; \
command_queue.push_and_ret(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8, &ret); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
return ret; \
} else { \
command_queue.flush_if_pending(); \
@@ -651,6 +694,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8); \
@@ -662,6 +706,7 @@
if (Thread::get_caller_id() != server_thread) { \
command_queue.push_and_sync(server_name, &ServerName::m_type, p1, p2, p3, p4, p5, p6, p7, p8); \
SYNC_DEBUG \
+ MAIN_THREAD_SYNC_CHECK \
} else { \
command_queue.flush_if_pending(); \
server_name->m_type(p1, p2, p3, p4, p5, p6, p7, p8); \
diff --git a/tests/core/templates/test_command_queue.h b/tests/core/templates/test_command_queue.h
index e94c108694..d2957b5c40 100644
--- a/tests/core/templates/test_command_queue.h
+++ b/tests/core/templates/test_command_queue.h
@@ -33,6 +33,7 @@
#include "core/config/project_settings.h"
#include "core/math/random_number_generator.h"
+#include "core/object/worker_thread_pool.h"
#include "core/os/os.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
@@ -100,7 +101,7 @@ public:
ThreadWork reader_threadwork;
ThreadWork writer_threadwork;
- CommandQueueMT command_queue = CommandQueueMT(true);
+ CommandQueueMT command_queue;
enum TestMsgType {
TEST_MSG_FUNC1_TRANSFORM,
@@ -119,6 +120,7 @@ public:
bool exit_threads = false;
Thread reader_thread;
+ WorkerThreadPool::TaskID reader_task_id = WorkerThreadPool::INVALID_TASK_ID;
Thread writer_thread;
int func1_count = 0;
@@ -148,11 +150,16 @@ public:
void reader_thread_loop() {
reader_threadwork.thread_wait_for_work();
while (!exit_threads) {
- if (message_count_to_read < 0) {
+ if (reader_task_id == WorkerThreadPool::INVALID_TASK_ID) {
command_queue.flush_all();
- }
- for (int i = 0; i < message_count_to_read; i++) {
- command_queue.wait_and_flush();
+ } else {
+ if (message_count_to_read < 0) {
+ command_queue.flush_all();
+ }
+ for (int i = 0; i < message_count_to_read; i++) {
+ WorkerThreadPool::get_singleton()->yield();
+ command_queue.wait_and_flush();
+ }
}
message_count_to_read = 0;
@@ -216,8 +223,13 @@ public:
sts->writer_thread_loop();
}
- void init_threads() {
- reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ void init_threads(bool p_use_thread_pool_sync = false) {
+ if (p_use_thread_pool_sync) {
+ reader_task_id = WorkerThreadPool::get_singleton()->add_native_task(&SharedThreadState::static_reader_thread_loop, this, true);
+ command_queue.set_pump_task_id(reader_task_id);
+ } else {
+ reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
+ }
writer_thread.start(&SharedThreadState::static_writer_thread_loop, this);
}
void destroy_threads() {
@@ -225,16 +237,20 @@ public:
reader_threadwork.main_start_work();
writer_threadwork.main_start_work();
- reader_thread.wait_to_finish();
+ if (reader_task_id != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(reader_task_id);
+ } else {
+ reader_thread.wait_to_finish();
+ }
writer_thread.wait_to_finish();
}
};
-TEST_CASE("[CommandQueue] Test Queue Basics") {
+static void test_command_queue_basic(bool p_use_thread_pool_sync) {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
SharedThreadState sts;
- sts.init_threads();
+ sts.init_threads(p_use_thread_pool_sync);
sts.add_msg_to_write(SharedThreadState::TEST_MSG_FUNC1_TRANSFORM);
sts.writer_threadwork.main_start_work();
@@ -272,6 +288,14 @@ TEST_CASE("[CommandQueue] Test Queue Basics") {
ProjectSettings::get_singleton()->property_get_revert(COMMAND_QUEUE_SETTING));
}
+TEST_CASE("[CommandQueue] Test Queue Basics") {
+ test_command_queue_basic(false);
+}
+
+TEST_CASE("[CommandQueue] Test Queue Basics with WorkerThreadPool sync.") {
+ test_command_queue_basic(true);
+}
+
TEST_CASE("[CommandQueue] Test Queue Wrapping to same spot.") {
const char *COMMAND_QUEUE_SETTING = "memory/limits/command_queue/multithreading_queue_size_kb";
ProjectSettings::get_singleton()->set_setting(COMMAND_QUEUE_SETTING, 1);
diff --git a/tests/core/threads/test_worker_thread_pool.h b/tests/core/threads/test_worker_thread_pool.h
index e9a762b57b..0a0291d11b 100644
--- a/tests/core/threads/test_worker_thread_pool.h
+++ b/tests/core/threads/test_worker_thread_pool.h
@@ -38,6 +38,7 @@
namespace TestWorkerThreadPool {
static LocalVector<SafeNumeric<int>> counter;
+static SafeFlag exit;
static void static_test(void *p_arg) {
counter[(uint64_t)p_arg].increment();
@@ -106,6 +107,72 @@ TEST_CASE("[WorkerThreadPool] Process elements using group tasks") {
}
}
+static void static_test_daemon(void *p_arg) {
+ while (!exit.is_set()) {
+ counter[0].add(1);
+ WorkerThreadPool::get_singleton()->yield();
+ }
+}
+
+static void static_busy_task(void *p_arg) {
+ while (!exit.is_set()) {
+ OS::get_singleton()->delay_usec(1);
+ }
+}
+
+static void static_legit_task(void *p_arg) {
+ *((bool *)p_arg) = counter[0].get() > 0;
+ counter[1].add(1);
+}
+
+TEST_CASE("[WorkerThreadPool] Run a yielding daemon as the only hope for other tasks to run") {
+ exit.clear();
+ counter.clear();
+ counter.resize(2);
+
+ WorkerThreadPool::TaskID daemon_task_id = WorkerThreadPool::get_singleton()->add_native_task(static_test_daemon, nullptr, true);
+
+ int num_threads = WorkerThreadPool::get_singleton()->get_thread_count();
+
+ // Keep all the other threads busy.
+ LocalVector<WorkerThreadPool::TaskID> task_ids;
+ for (int i = 0; i < num_threads - 1; i++) {
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_busy_task, nullptr, true));
+ }
+
+ LocalVector<WorkerThreadPool::TaskID> legit_task_ids;
+ LocalVector<bool> legit_task_needed_yield;
+ int legit_tasks_count = num_threads * 4;
+ legit_task_needed_yield.resize(legit_tasks_count);
+ for (int i = 0; i < legit_tasks_count; i++) {
+ legit_task_needed_yield[i] = false;
+ task_ids.push_back(WorkerThreadPool::get_singleton()->add_native_task(static_legit_task, &legit_task_needed_yield[i], i >= legit_tasks_count / 2));
+ }
+
+ while (counter[1].get() != legit_tasks_count) {
+ OS::get_singleton()->delay_usec(1);
+ }
+
+ exit.set();
+ for (uint32_t i = 0; i < task_ids.size(); i++) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task_ids[i]);
+ }
+ WorkerThreadPool::get_singleton()->notify_yield_over(daemon_task_id);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(daemon_task_id);
+
+ CHECK_MESSAGE(counter[0].get() > 0, "Daemon task should have looped at least once.");
+ CHECK_MESSAGE(counter[1].get() == legit_tasks_count, "All legit tasks should have been able to run.");
+
+ bool all_needed_yield = true;
+ for (int i = 0; i < legit_tasks_count; i++) {
+ if (!legit_task_needed_yield[i]) {
+ all_needed_yield = false;
+ break;
+ }
+ }
+ CHECK_MESSAGE(all_needed_yield, "All legit tasks should have needed the daemon yielding to run.");
+}
+
} // namespace TestWorkerThreadPool
#endif // TEST_WORKER_THREAD_POOL_H
diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h
index 287345e831..c54854e4d7 100644
--- a/tests/core/variant/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -555,6 +555,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
idx = 0;
for (const Variant &E : (const Array &)a1) {
@@ -562,6 +564,8 @@ TEST_CASE("[Array] Iteration") {
idx++;
}
+ CHECK_EQ(idx, a1.size());
+
a1.clear();
}
diff --git a/tests/scene/test_navigation_region_2d.h b/tests/scene/test_navigation_region_2d.h
index 4574893c8d..fcb5aeacbe 100644
--- a/tests/scene/test_navigation_region_2d.h
+++ b/tests/scene/test_navigation_region_2d.h
@@ -41,7 +41,7 @@ namespace TestNavigationRegion2D {
TEST_SUITE("[Navigation]") {
TEST_CASE("[SceneTree][NavigationRegion2D] New region should have valid RID") {
NavigationRegion2D *region_node = memnew(NavigationRegion2D);
- CHECK(region_node->get_region_rid().is_valid());
+ CHECK(region_node->get_rid().is_valid());
memdelete(region_node);
}
}
diff --git a/tests/scene/test_navigation_region_3d.h b/tests/scene/test_navigation_region_3d.h
index 372f6dc505..0b20b3a1b2 100644
--- a/tests/scene/test_navigation_region_3d.h
+++ b/tests/scene/test_navigation_region_3d.h
@@ -43,7 +43,7 @@ namespace TestNavigationRegion3D {
TEST_SUITE("[Navigation]") {
TEST_CASE("[SceneTree][NavigationRegion3D] New region should have valid RID") {
NavigationRegion3D *region_node = memnew(NavigationRegion3D);
- CHECK(region_node->get_region_rid().is_valid());
+ CHECK(region_node->get_rid().is_valid());
memdelete(region_node);
}
@@ -71,15 +71,6 @@ TEST_SUITE("[Navigation]") {
CHECK_NE(navigation_mesh->get_vertices().size(), 0);
}
- // Race condition is present in the below subcase, but baking should take many
- // orders of magnitude longer than basic checks on the main thread, so it's fine.
- SUBCASE("Asynchronous bake should not be immediate") {
- navigation_region->bake_navigation_mesh(true);
- CHECK(navigation_region->is_baking());
- CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
- CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
- }
-
memdelete(mesh_instance);
memdelete(navigation_region);
memdelete(node_3d);
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index 827a1bed17..8778ea86a6 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -764,6 +764,8 @@ TEST_SUITE("[Navigation]") {
navigation_server->process(0.0); // Give server some cycles to commit.
}
+ // FIXME: The race condition mentioned below is actually a problem and fails on CI (GH-90613).
+ /*
TEST_CASE("[NavigationServer3D] Server should be able to bake asynchronously") {
NavigationServer3D *navigation_server = NavigationServer3D::get_singleton();
Ref<NavigationMesh> navigation_mesh = memnew(NavigationMesh);
@@ -781,6 +783,7 @@ TEST_SUITE("[Navigation]") {
CHECK_EQ(navigation_mesh->get_polygon_count(), 0);
CHECK_EQ(navigation_mesh->get_vertices().size(), 0);
}
+ */
}
} //namespace TestNavigationServer3D
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index bb6837c965..56bd8739c6 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -100,7 +100,6 @@
#include "tests/core/variant/test_variant.h"
#include "tests/core/variant/test_variant_utility.h"
#include "tests/scene/test_animation.h"
-#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_audio_stream_wav.h"
#include "tests/scene/test_bit_map.h"
#include "tests/scene/test_camera_2d.h"
@@ -127,6 +126,7 @@
#include "tests/test_validate_testing.h"
#ifndef _3D_DISABLED
+#include "tests/scene/test_arraymesh.h"
#include "tests/scene/test_camera_3d.h"
#include "tests/scene/test_navigation_agent_2d.h"
#include "tests/scene/test_navigation_agent_3d.h"
diff --git a/thirdparty/README.md b/thirdparty/README.md
index dab3c9c04a..4c44a9b6f6 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -532,6 +532,8 @@ File extracted from upstream release tarball:
- The `LICENSE` file (edited to keep only the Apache 2.0 variant)
- Applied the patch `no-flexible-arrays.diff` to fix Windows build (see
upstream GH-9020)
+- Applied the patch `msvc-redeclaration-bug.diff` to fix a compilation error
+ with some MSVC versions
- Added 2 files `godot_core_mbedtls_platform.c` and `godot_core_mbedtls_config.h`
providing configuration for light bundling with core
- Added the file `godot_module_mbedtls_config.h` to customize the build
diff --git a/thirdparty/mbedtls/include/godot_core_mbedtls_config.h b/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
index d27bf608fb..f22ef3aa08 100644
--- a/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
+++ b/thirdparty/mbedtls/include/godot_core_mbedtls_config.h
@@ -50,4 +50,11 @@
#define MBEDTLS_PLATFORM_ZEROIZE_ALT
#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES
+// This is only to pass a check in the mbedtls check_config.h header, none of
+// the files we include as part of the core build uses it anyway, we already
+// define MBEDTLS_PLATFORM_ZEROIZE_ALT which is the only relevant function.
+#if defined(__MINGW32__)
+#define MBEDTLS_PLATFORM_C
+#endif
+
#endif // GODOT_CORE_MBEDTLS_CONFIG_H
diff --git a/thirdparty/mbedtls/include/psa/crypto.h b/thirdparty/mbedtls/include/psa/crypto.h
index 92f9c824e9..390534edf5 100644
--- a/thirdparty/mbedtls/include/psa/crypto.h
+++ b/thirdparty/mbedtls/include/psa/crypto.h
@@ -107,7 +107,9 @@ psa_status_t psa_crypto_init(void);
/** Return an initial value for a key attributes structure.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_key_attributes_t psa_key_attributes_init(void);
+#endif
/** Declare a key as persistent and set its key identifier.
*
@@ -333,7 +335,9 @@ static void psa_set_key_bits(psa_key_attributes_t *attributes,
*
* \return The key type stored in the attribute structure.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_key_type_t psa_get_key_type(const psa_key_attributes_t *attributes);
+#endif
/** Retrieve the key size from key attributes.
*
@@ -936,7 +940,9 @@ typedef struct psa_hash_operation_s psa_hash_operation_t;
/** Return an initial value for a hash operation object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_hash_operation_t psa_hash_operation_init(void);
+#endif
/** Set up a multipart hash operation.
*
@@ -1295,7 +1301,9 @@ typedef struct psa_mac_operation_s psa_mac_operation_t;
/** Return an initial value for a MAC operation object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_mac_operation_t psa_mac_operation_init(void);
+#endif
/** Set up a multipart MAC calculation operation.
*
@@ -1708,7 +1716,9 @@ typedef struct psa_cipher_operation_s psa_cipher_operation_t;
/** Return an initial value for a cipher operation object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_cipher_operation_t psa_cipher_operation_init(void);
+#endif
/** Set the key for a multipart symmetric encryption operation.
*
@@ -2226,7 +2236,9 @@ typedef struct psa_aead_operation_s psa_aead_operation_t;
/** Return an initial value for an AEAD operation object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_aead_operation_t psa_aead_operation_init(void);
+#endif
/** Set the key for a multipart authenticated encryption operation.
*
@@ -3213,7 +3225,9 @@ typedef struct psa_key_derivation_s psa_key_derivation_operation_t;
/** Return an initial value for a key derivation operation object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_key_derivation_operation_t psa_key_derivation_operation_init(void);
+#endif
/** Set up a key derivation operation.
*
diff --git a/thirdparty/mbedtls/include/psa/crypto_extra.h b/thirdparty/mbedtls/include/psa/crypto_extra.h
index 6ed1f6c43a..bd985e9b78 100644
--- a/thirdparty/mbedtls/include/psa/crypto_extra.h
+++ b/thirdparty/mbedtls/include/psa/crypto_extra.h
@@ -915,7 +915,9 @@ typedef struct psa_pake_cipher_suite_s psa_pake_cipher_suite_t;
/** Return an initial value for a PAKE cipher suite object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_pake_cipher_suite_t psa_pake_cipher_suite_init(void);
+#endif
/** Retrieve the PAKE algorithm from a PAKE cipher suite.
*
@@ -1048,7 +1050,9 @@ typedef struct psa_jpake_computation_stage_s psa_jpake_computation_stage_t;
/** Return an initial value for a PAKE operation object.
*/
+#if !(defined(__cplusplus) && defined(_MSC_VER))
static psa_pake_operation_t psa_pake_operation_init(void);
+#endif
/** Get the length of the password in bytes from given inputs.
*
diff --git a/thirdparty/mbedtls/patches/msvc-redeclaration-bug.diff b/thirdparty/mbedtls/patches/msvc-redeclaration-bug.diff
new file mode 100644
index 0000000000..c5f1970223
--- /dev/null
+++ b/thirdparty/mbedtls/patches/msvc-redeclaration-bug.diff
@@ -0,0 +1,98 @@
+diff --git a/thirdparty/mbedtls/include/psa/crypto.h b/thirdparty/mbedtls/include/psa/crypto.h
+index 92f9c824e9..1cc2e7e729 100644
+--- a/thirdparty/mbedtls/include/psa/crypto.h
++++ b/thirdparty/mbedtls/include/psa/crypto.h
+@@ -107,7 +107,9 @@ psa_status_t psa_crypto_init(void);
+
+ /** Return an initial value for a key attributes structure.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_key_attributes_t psa_key_attributes_init(void);
++#endif
+
+ /** Declare a key as persistent and set its key identifier.
+ *
+@@ -333,7 +335,9 @@ static void psa_set_key_bits(psa_key_attributes_t *attributes,
+ *
+ * \return The key type stored in the attribute structure.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_key_type_t psa_get_key_type(const psa_key_attributes_t *attributes);
++#endif
+
+ /** Retrieve the key size from key attributes.
+ *
+@@ -936,7 +940,9 @@ typedef struct psa_hash_operation_s psa_hash_operation_t;
+
+ /** Return an initial value for a hash operation object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_hash_operation_t psa_hash_operation_init(void);
++#endif
+
+ /** Set up a multipart hash operation.
+ *
+@@ -1295,7 +1301,9 @@ typedef struct psa_mac_operation_s psa_mac_operation_t;
+
+ /** Return an initial value for a MAC operation object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_mac_operation_t psa_mac_operation_init(void);
++#endif
+
+ /** Set up a multipart MAC calculation operation.
+ *
+@@ -1708,7 +1716,9 @@ typedef struct psa_cipher_operation_s psa_cipher_operation_t;
+
+ /** Return an initial value for a cipher operation object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_cipher_operation_t psa_cipher_operation_init(void);
++#endif
+
+ /** Set the key for a multipart symmetric encryption operation.
+ *
+@@ -2226,7 +2236,9 @@ typedef struct psa_aead_operation_s psa_aead_operation_t;
+
+ /** Return an initial value for an AEAD operation object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_aead_operation_t psa_aead_operation_init(void);
++#endif
+
+ /** Set the key for a multipart authenticated encryption operation.
+ *
+@@ -3213,7 +3225,9 @@ typedef struct psa_key_derivation_s psa_key_derivation_operation_t;
+
+ /** Return an initial value for a key derivation operation object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_key_derivation_operation_t psa_key_derivation_operation_init(void);
++#endif
+
+ /** Set up a key derivation operation.
+ *
+diff --git a/thirdparty/mbedtls/include/psa/crypto_extra.h b/thirdparty/mbedtls/include/psa/crypto_extra.h
+index 6ed1f6c43a..2686b9d74d 100644
+--- a/thirdparty/mbedtls/include/psa/crypto_extra.h
++++ b/thirdparty/mbedtls/include/psa/crypto_extra.h
+@@ -915,7 +915,9 @@ typedef struct psa_pake_cipher_suite_s psa_pake_cipher_suite_t;
+
+ /** Return an initial value for a PAKE cipher suite object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_pake_cipher_suite_t psa_pake_cipher_suite_init(void);
++#endif
+
+ /** Retrieve the PAKE algorithm from a PAKE cipher suite.
+ *
+@@ -1048,7 +1050,9 @@ typedef struct psa_jpake_computation_stage_s psa_jpake_computation_stage_t;
+
+ /** Return an initial value for a PAKE operation object.
+ */
++#if !(defined(__cplusplus) && defined(_MSC_VER))
+ static psa_pake_operation_t psa_pake_operation_init(void);
++#endif
+
+ /** Get the length of the password in bytes from given inputs.
+ *