summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml2
-rw-r--r--.github/workflows/linux_builds.yml5
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--core/extension/gdextension.cpp107
-rw-r--r--core/extension/gdextension.h12
-rw-r--r--core/extension/gdextension_interface.cpp2
-rw-r--r--core/extension/gdextension_interface.h59
-rw-r--r--core/object/class_db.cpp230
-rw-r--r--core/object/class_db.h8
-rw-r--r--core/object/object.cpp46
-rw-r--r--core/object/object.h6
-rw-r--r--core/os/midi_driver.cpp3
-rw-r--r--core/os/midi_driver.h2
-rw-r--r--core/templates/search_array.h10
-rw-r--r--core/templates/sort_array.h72
-rw-r--r--doc/classes/@GlobalScope.xml8
-rw-r--r--doc/classes/AnimatedSprite2D.xml1
-rw-r--r--doc/classes/AnimatedTexture.xml2
-rw-r--r--doc/classes/AnimationMixer.xml10
-rw-r--r--doc/classes/AnimationPlayer.xml6
-rw-r--r--doc/classes/AnimationTree.xml2
-rw-r--r--doc/classes/CPUParticles3D.xml2
-rw-r--r--doc/classes/CanvasItem.xml6
-rw-r--r--doc/classes/Control.xml4
-rw-r--r--doc/classes/EditorProperty.xml3
-rw-r--r--doc/classes/Environment.xml22
-rw-r--r--doc/classes/HTTPClient.xml4
-rw-r--r--doc/classes/Input.xml1
-rw-r--r--doc/classes/LightmapGIData.xml2
-rw-r--r--doc/classes/MultiMesh.xml13
-rw-r--r--doc/classes/NavigationMeshGenerator.xml4
-rw-r--r--doc/classes/NavigationServer3D.xml3
-rw-r--r--doc/classes/Node.xml9
-rw-r--r--doc/classes/ParticleProcessMaterial.xml2
-rw-r--r--doc/classes/ProjectSettings.xml5
-rw-r--r--doc/classes/RenderingDevice.xml5
-rw-r--r--doc/classes/RenderingServer.xml16
-rw-r--r--doc/classes/Resource.xml2
-rw-r--r--doc/classes/Skeleton3D.xml2
-rw-r--r--doc/classes/Sprite2D.xml1
-rw-r--r--doc/classes/SurfaceTool.xml2
-rw-r--r--doc/classes/Texture3D.xml2
-rw-r--r--doc/classes/TextureRect.xml3
-rw-r--r--doc/classes/TileMap.xml13
-rw-r--r--doc/classes/TreeItem.xml4
-rw-r--r--doc/classes/Tween.xml6
-rw-r--r--doc/classes/Viewport.xml3
-rw-r--r--doc/classes/VisualShaderNodeCubemap.xml2
-rw-r--r--doc/classes/VisualShaderNodeTexture.xml2
-rw-r--r--doc/classes/Window.xml2
-rw-r--r--doc/classes/XRFaceModifier3D.xml22
-rw-r--r--doc/classes/XRFaceTracker.xml469
-rw-r--r--doc/classes/XRInterface.xml6
-rw-r--r--doc/classes/XRServer.xml48
-rwxr-xr-xdoc/tools/make_rst.py47
-rw-r--r--drivers/alsamidi/midi_driver_alsamidi.cpp14
-rw-r--r--drivers/alsamidi/midi_driver_alsamidi.h4
-rw-r--r--drivers/coremidi/midi_driver_coremidi.cpp5
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp8
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h8
-rw-r--r--drivers/gles3/shaders/scene.glsl26
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp4
-rw-r--r--drivers/winmidi/midi_driver_winmidi.cpp4
-rw-r--r--editor/animation_bezier_editor.cpp62
-rw-r--r--editor/animation_bezier_editor.h3
-rw-r--r--editor/animation_track_editor.cpp100
-rw-r--r--editor/animation_track_editor.h4
-rw-r--r--editor/code_editor.cpp14
-rw-r--r--editor/connections_dialog.cpp4
-rw-r--r--editor/debugger/debug_adapter/debug_adapter_server.cpp5
-rw-r--r--editor/debugger/editor_debugger_node.cpp3
-rw-r--r--editor/debugger/editor_profiler.cpp2
-rw-r--r--editor/editor_about.cpp107
-rw-r--r--editor/editor_about.h10
-rw-r--r--editor/editor_help.cpp7
-rw-r--r--editor/editor_help_search.cpp186
-rw-r--r--editor/editor_help_search.h49
-rw-r--r--editor/editor_inspector.cpp146
-rw-r--r--editor/editor_inspector.h12
-rw-r--r--editor/editor_node.cpp83
-rw-r--r--editor/editor_plugin_settings.cpp161
-rw-r--r--editor/editor_plugin_settings.h11
-rw-r--r--editor/editor_properties_vector.cpp2
-rw-r--r--editor/editor_resource_picker.cpp11
-rw-r--r--editor/editor_run_native.cpp2
-rw-r--r--editor/editor_settings_dialog.cpp5
-rw-r--r--editor/editor_vcs_interface.cpp1
-rw-r--r--editor/export/editor_export.cpp2
-rw-r--r--editor/export/editor_export_preset.cpp13
-rw-r--r--editor/export/editor_export_preset.h4
-rw-r--r--editor/export/project_export.cpp30
-rw-r--r--editor/export/project_export.h2
-rw-r--r--editor/filesystem_dock.cpp31
-rw-r--r--editor/gui/editor_file_dialog.cpp3
-rw-r--r--editor/gui/editor_run_bar.cpp12
-rw-r--r--editor/gui/editor_scene_tabs.cpp6
-rw-r--r--editor/gui/editor_toaster.cpp2
-rw-r--r--editor/gui/scene_tree_editor.cpp50
-rw-r--r--editor/import/resource_importer_csv_translation.cpp9
-rw-r--r--editor/import_dock.cpp5
-rw-r--r--editor/inspector_dock.cpp14
-rw-r--r--editor/plugin_config_dialog.cpp14
-rw-r--r--editor/plugin_config_dialog.h1
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp10
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp5
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp24
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp7
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp12
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp7
-rw-r--r--editor/plugins/script_editor_plugin.cpp5
-rw-r--r--editor/plugins/shader_editor_plugin.cpp10
-rw-r--r--editor/plugins/text_shader_editor.cpp3
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp9
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp7
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp9
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp14
-rw-r--r--editor/scene_tree_dock.cpp24
-rw-r--r--editor/themes/editor_theme_manager.cpp53
-rw-r--r--editor/themes/editor_theme_manager.h4
-rw-r--r--misc/extension_api_validation/4.2-stable.expected14
-rw-r--r--modules/basis_universal/SCsub7
-rw-r--r--modules/basis_universal/config.py1
-rw-r--r--modules/basis_universal/image_compress_basisu.cpp251
-rw-r--r--modules/basis_universal/image_compress_basisu.h (renamed from scene/main/node.compat.inc)29
-rw-r--r--modules/basis_universal/patches/external-jpgd.patch13
-rw-r--r--modules/basis_universal/register_types.cpp252
-rw-r--r--modules/gdscript/gdscript.cpp2
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp4
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp3
-rw-r--r--modules/mono/config.py10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs11
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs10
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs16
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs29
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic(Of T)_ScriptPath.generated.cs (renamed from modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs)2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs11
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs12
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs6
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs494
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs5
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs40
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs17
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs12
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs72
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs9
-rw-r--r--modules/multiplayer/doc_classes/SceneReplicationConfig.xml8
-rw-r--r--modules/multiplayer/editor/replication_editor.cpp8
-rw-r--r--modules/multiplayer/multiplayer_spawner.cpp4
-rw-r--r--modules/multiplayer/multiplayer_spawner.h2
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.cpp4
-rw-r--r--modules/multiplayer/multiplayer_synchronizer.h2
-rw-r--r--modules/noise/SCsub17
-rw-r--r--modules/openxr/doc_classes/OpenXRAPIExtension.xml7
-rw-r--r--modules/openxr/openxr_api.cpp7
-rw-r--r--modules/openxr/openxr_api.h2
-rw-r--r--modules/openxr/openxr_api_extension.cpp7
-rw-r--r--modules/openxr/openxr_api_extension.h2
-rw-r--r--modules/regex/regex.cpp2
-rw-r--r--modules/regex/tests/test_regex.h12
-rw-r--r--platform/android/detect.py1
-rw-r--r--platform/android/export/export_plugin.cpp24
-rw-r--r--platform/ios/detect.py1
-rw-r--r--platform/ios/export/export_plugin.cpp4
-rw-r--r--platform/linuxbsd/detect.py1
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp1
-rw-r--r--platform/macos/SCsub2
-rw-r--r--platform/macos/detect.py5
-rw-r--r--platform/macos/display_server_macos.mm2
-rw-r--r--platform/macos/joypad_macos.cpp619
-rw-r--r--platform/macos/joypad_macos.h111
-rw-r--r--platform/macos/joypad_macos.mm608
-rw-r--r--platform/macos/os_macos.h2
-rw-r--r--platform/macos/os_macos.mm4
-rw-r--r--platform/macos/platform_config.h7
-rw-r--r--platform/web/detect.py6
-rw-r--r--platform/windows/detect.py1
-rw-r--r--scene/2d/animated_sprite_2d.cpp4
-rw-r--r--scene/2d/animated_sprite_2d.h2
-rw-r--r--scene/2d/canvas_modulate.cpp4
-rw-r--r--scene/2d/canvas_modulate.h2
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_object_2d.h2
-rw-r--r--scene/2d/collision_polygon_2d.cpp4
-rw-r--r--scene/2d/collision_polygon_2d.h2
-rw-r--r--scene/2d/collision_shape_2d.cpp4
-rw-r--r--scene/2d/collision_shape_2d.h2
-rw-r--r--scene/2d/cpu_particles_2d.cpp4
-rw-r--r--scene/2d/cpu_particles_2d.h2
-rw-r--r--scene/2d/gpu_particles_2d.cpp4
-rw-r--r--scene/2d/gpu_particles_2d.h2
-rw-r--r--scene/2d/joint_2d.cpp4
-rw-r--r--scene/2d/joint_2d.h2
-rw-r--r--scene/2d/light_2d.cpp9
-rw-r--r--scene/2d/light_2d.h2
-rw-r--r--scene/2d/light_occluder_2d.cpp4
-rw-r--r--scene/2d/light_occluder_2d.h2
-rw-r--r--scene/2d/navigation_agent_2d.cpp4
-rw-r--r--scene/2d/navigation_agent_2d.h2
-rw-r--r--scene/2d/navigation_link_2d.cpp4
-rw-r--r--scene/2d/navigation_link_2d.h2
-rw-r--r--scene/2d/navigation_region_2d.cpp4
-rw-r--r--scene/2d/navigation_region_2d.h2
-rw-r--r--scene/2d/parallax_layer.cpp4
-rw-r--r--scene/2d/parallax_layer.h2
-rw-r--r--scene/2d/path_2d.cpp4
-rw-r--r--scene/2d/path_2d.h2
-rw-r--r--scene/2d/physical_bone_2d.cpp4
-rw-r--r--scene/2d/physical_bone_2d.h2
-rw-r--r--scene/2d/physics_body_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/2d/remote_transform_2d.cpp4
-rw-r--r--scene/2d/remote_transform_2d.h2
-rw-r--r--scene/2d/shape_cast_2d.cpp4
-rw-r--r--scene/2d/shape_cast_2d.h2
-rw-r--r--scene/2d/skeleton_2d.cpp4
-rw-r--r--scene/2d/skeleton_2d.h2
-rw-r--r--scene/2d/tile_map.cpp4
-rw-r--r--scene/2d/tile_map.h2
-rw-r--r--scene/3d/bone_attachment_3d.cpp4
-rw-r--r--scene/3d/bone_attachment_3d.h3
-rw-r--r--scene/3d/collision_object_3d.cpp4
-rw-r--r--scene/3d/collision_object_3d.h2
-rw-r--r--scene/3d/collision_polygon_3d.cpp4
-rw-r--r--scene/3d/collision_polygon_3d.h2
-rw-r--r--scene/3d/collision_shape_3d.cpp4
-rw-r--r--scene/3d/collision_shape_3d.h2
-rw-r--r--scene/3d/cpu_particles_3d.cpp5
-rw-r--r--scene/3d/cpu_particles_3d.h2
-rw-r--r--scene/3d/decal.cpp4
-rw-r--r--scene/3d/decal.h2
-rw-r--r--scene/3d/fog_volume.cpp4
-rw-r--r--scene/3d/fog_volume.h2
-rw-r--r--scene/3d/gpu_particles_3d.cpp4
-rw-r--r--scene/3d/gpu_particles_3d.h2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp4
-rw-r--r--scene/3d/gpu_particles_collision_3d.h2
-rw-r--r--scene/3d/joint_3d.cpp4
-rw-r--r--scene/3d/joint_3d.h2
-rw-r--r--scene/3d/light_3d.cpp12
-rw-r--r--scene/3d/light_3d.h6
-rw-r--r--scene/3d/lightmap_gi.cpp4
-rw-r--r--scene/3d/lightmap_gi.h2
-rw-r--r--scene/3d/navigation_agent_3d.cpp4
-rw-r--r--scene/3d/navigation_agent_3d.h2
-rw-r--r--scene/3d/navigation_link_3d.cpp4
-rw-r--r--scene/3d/navigation_link_3d.h2
-rw-r--r--scene/3d/navigation_region_3d.cpp4
-rw-r--r--scene/3d/navigation_region_3d.h2
-rw-r--r--scene/3d/occluder_instance_3d.cpp4
-rw-r--r--scene/3d/occluder_instance_3d.h2
-rw-r--r--scene/3d/path_3d.cpp4
-rw-r--r--scene/3d/path_3d.h2
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/3d/reflection_probe.cpp4
-rw-r--r--scene/3d/reflection_probe.h2
-rw-r--r--scene/3d/remote_transform_3d.cpp4
-rw-r--r--scene/3d/remote_transform_3d.h2
-rw-r--r--scene/3d/shape_cast_3d.cpp4
-rw-r--r--scene/3d/shape_cast_3d.h2
-rw-r--r--scene/3d/skeleton_3d.cpp31
-rw-r--r--scene/3d/soft_body_3d.cpp4
-rw-r--r--scene/3d/soft_body_3d.h2
-rw-r--r--scene/3d/sprite_3d.cpp4
-rw-r--r--scene/3d/sprite_3d.h2
-rw-r--r--scene/3d/vehicle_body_3d.cpp4
-rw-r--r--scene/3d/vehicle_body_3d.h2
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.cpp4
-rw-r--r--scene/3d/visible_on_screen_notifier_3d.h2
-rw-r--r--scene/3d/visual_instance_3d.cpp4
-rw-r--r--scene/3d/visual_instance_3d.h2
-rw-r--r--scene/3d/voxel_gi.cpp4
-rw-r--r--scene/3d/voxel_gi.h2
-rw-r--r--scene/3d/world_environment.cpp4
-rw-r--r--scene/3d/world_environment.h2
-rw-r--r--scene/3d/xr_face_modifier_3d.cpp615
-rw-r--r--scene/3d/xr_face_modifier_3d.h73
-rw-r--r--scene/3d/xr_nodes.cpp12
-rw-r--r--scene/3d/xr_nodes.h6
-rw-r--r--scene/animation/animation_mixer.cpp27
-rw-r--r--scene/animation/animation_mixer.h2
-rw-r--r--scene/animation/animation_tree.cpp11
-rw-r--r--scene/animation/animation_tree.h2
-rw-r--r--scene/audio/audio_stream_player_internal.cpp2
-rw-r--r--scene/gui/base_button.cpp4
-rw-r--r--scene/gui/base_button.h2
-rw-r--r--scene/gui/container.cpp4
-rw-r--r--scene/gui/container.h2
-rw-r--r--scene/gui/control.cpp6
-rw-r--r--scene/gui/control.h2
-rw-r--r--scene/gui/graph_edit.cpp4
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/gui/label.cpp4
-rw-r--r--scene/gui/label.h2
-rw-r--r--scene/gui/line_edit.cpp41
-rw-r--r--scene/gui/line_edit.h2
-rw-r--r--scene/gui/popup_menu.cpp91
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/range.h2
-rw-r--r--scene/gui/rich_text_label.cpp7
-rw-r--r--scene/gui/scroll_container.cpp4
-rw-r--r--scene/gui/scroll_container.h2
-rw-r--r--scene/gui/subviewport_container.cpp4
-rw-r--r--scene/gui/subviewport_container.h2
-rw-r--r--scene/gui/tab_bar.cpp16
-rw-r--r--scene/gui/tab_bar.h3
-rw-r--r--scene/main/canvas_item.cpp6
-rw-r--r--scene/main/canvas_item.h1
-rw-r--r--scene/main/missing_node.cpp4
-rw-r--r--scene/main/missing_node.h2
-rw-r--r--scene/main/node.cpp107
-rw-r--r--scene/main/node.h9
-rw-r--r--scene/main/scene_tree.cpp4
-rw-r--r--scene/main/shader_globals_override.cpp4
-rw-r--r--scene/main/shader_globals_override.h2
-rw-r--r--scene/main/timer.cpp4
-rw-r--r--scene/main/timer.h2
-rw-r--r--scene/main/viewport.cpp13
-rw-r--r--scene/main/viewport.h2
-rw-r--r--scene/main/window.cpp5
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/environment.cpp84
-rw-r--r--scene/resources/environment.h24
-rw-r--r--servers/audio/effects/audio_effect_pitch_shift.cpp8
-rw-r--r--servers/debugger/servers_debugger.cpp2
-rw-r--r--servers/register_server_types.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp17
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h5
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h1
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl11
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl11
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_data_inc.glsl13
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp5
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h13
-rw-r--r--servers/rendering/renderer_scene_cull.h9
-rw-r--r--servers/rendering/renderer_scene_render.cpp26
-rw-r--r--servers/rendering/renderer_scene_render.h9
-rw-r--r--servers/rendering/rendering_method.h10
-rw-r--r--servers/rendering/rendering_server_default.h4
-rw-r--r--servers/rendering/storage/environment_storage.cpp37
-rw-r--r--servers/rendering/storage/environment_storage.h15
-rw-r--r--servers/rendering_server.compat.inc41
-rw-r--r--servers/rendering_server.cpp6
-rw-r--r--servers/rendering_server.h14
-rw-r--r--servers/xr/xr_face_tracker.cpp222
-rw-r--r--servers/xr/xr_face_tracker.h213
-rw-r--r--servers/xr_server.cpp48
-rw-r--r--servers/xr_server.h11
-rw-r--r--tests/scene/test_image_texture.h111
-rw-r--r--tests/test_main.cpp8
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/basis_universal/encoder/basisu_enc.cpp2
-rw-r--r--thirdparty/basis_universal/encoder/jpgd.cpp3230
-rw-r--r--thirdparty/basis_universal/encoder/jpgd.h347
-rw-r--r--thirdparty/noise/FastNoiseLite.h15
-rw-r--r--thirdparty/noise/patches/FastNoiseLite.patch455
-rw-r--r--thirdparty/noise/patches/namespace-warnings.patch43
374 files changed, 5615 insertions, 6677 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 9dea66914f..7ffae10a2a 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -45,7 +45,7 @@ body:
label: Issue description
description: |
Describe your issue briefly. What doesn't work, and how do you expect it to work instead?
- You can include images or videos with drag and drop, and format code blocks or logs with <code>```</code> tags, on separate lines before and after the text. (Use <code>```gdscript</code> to add GDScript syntax highlighting.)
+ You can include images or videos with drag and drop, and format code blocks or logs with <code>\`\`\`</code> tags, on separate lines before and after the text. (Use <code>\`\`\`gdscript</code> to add GDScript syntax highlighting.)
Please do not add code examples or error messages as screenshots, but as text, this helps searching for issues and testing the code. If you are reporting a bug in the editor interface, like the script editor, please provide both a screenshot *and* the text of the code to help with testing.
validations:
required: true
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 914f0394e4..e3cd03d279 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -169,6 +169,11 @@ jobs:
${{ matrix.bin }} --help
${{ matrix.bin }} --headless --test --force-colors
+ - name: .NET source generators tests
+ if: ${{ matrix.build-mono }}
+ run: |
+ dotnet test modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests
+
# Check class reference
- name: Check for class reference updates
if: ${{ matrix.doc-test }}
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index bb29c88443..0fe1a11f71 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -397,7 +397,7 @@ License: Expat
Files: ./thirdparty/noise/FastNoiseLite.h
Comment: FastNoise Lite
-Copyright: 2020, Jordan Peck and contributors
+Copyright: 2023, Jordan Peck and contributors
License: Expat
Files: ./thirdparty/misc/pcg.cpp
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 9b3282ecba..c1298c8687 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -205,6 +205,7 @@ public:
virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override {
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_V_MSG(!valid, Variant(), vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
+ ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name));
#endif
Variant ret;
GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
@@ -218,6 +219,7 @@ public:
virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override {
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name));
#endif
ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have validated call support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
@@ -249,6 +251,7 @@ public:
virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override {
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
+ ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name));
#endif
ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance();
@@ -341,10 +344,11 @@ public:
#ifndef DISABLE_DEPRECATED
void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) {
- const GDExtensionClassCreationInfo2 class_info2 = {
+ const GDExtensionClassCreationInfo3 class_info3 = {
p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
true, // GDExtensionBool is_exposed;
+ false, // GDExtensionBool is_runtime;
p_extension_funcs->set_func, // GDExtensionClassSet set_func;
p_extension_funcs->get_func, // GDExtensionClassGet get_func;
p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
@@ -369,15 +373,45 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
const ClassCreationDeprecatedInfo legacy = {
p_extension_funcs->notification_func,
};
- _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info2, &legacy);
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy);
}
-#endif // DISABLE_DEPRECATED
void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) {
+ const GDExtensionClassCreationInfo3 class_info3 = {
+ p_extension_funcs->is_virtual, // GDExtensionBool is_virtual;
+ p_extension_funcs->is_abstract, // GDExtensionBool is_abstract;
+ p_extension_funcs->is_exposed, // GDExtensionBool is_exposed;
+ false, // GDExtensionBool is_runtime;
+ p_extension_funcs->set_func, // GDExtensionClassSet set_func;
+ p_extension_funcs->get_func, // GDExtensionClassGet get_func;
+ p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func;
+ p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func;
+ p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func;
+ p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func;
+ p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func;
+ p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func;
+ p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func;
+ p_extension_funcs->reference_func, // GDExtensionClassReference reference_func;
+ p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func;
+ p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
+ p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
+ p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func;
+ p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
+ p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func;
+ p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
+ p_extension_funcs->class_userdata, // void *class_userdata;
+ };
+
+ _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3);
+}
+#endif // DISABLE_DEPRECATED
+
+void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) {
_register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs);
}
-void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
+void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
@@ -402,10 +436,15 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
#ifdef TOOLS_ENABLED
Extension *extension = nullptr;
+ bool is_runtime = (bool)p_extension_funcs->is_runtime;
if (self->is_reloading && self->extension_classes.has(class_name)) {
extension = &self->extension_classes[class_name];
if (!parent_extension && parent_class_name != extension->gdextension.parent_class_name) {
- ERR_FAIL_MSG(vformat("GDExtension class '%s' attempt to change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name));
+ ERR_FAIL_MSG(vformat("GDExtension class '%s' cannot change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name));
+ }
+ if (extension->gdextension.is_runtime != is_runtime) {
+ ERR_PRINT(vformat("GDExtension class '%s' cannot change to/from runtime class on hot reload. Restart Godot for this change to take effect.", class_name));
+ is_runtime = extension->gdextension.is_runtime;
}
extension->is_reloading = false;
} else {
@@ -434,6 +473,9 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
extension->gdextension.is_virtual = p_extension_funcs->is_virtual;
extension->gdextension.is_abstract = p_extension_funcs->is_abstract;
extension->gdextension.is_exposed = p_extension_funcs->is_exposed;
+#ifdef TOOLS_ENABLED
+ extension->gdextension.is_runtime = is_runtime;
+#endif
extension->gdextension.set = p_extension_funcs->set_func;
extension->gdextension.get = p_extension_funcs->get_func;
extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func;
@@ -836,8 +878,9 @@ void GDExtension::initialize_gdextensions() {
#ifndef DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class);
-#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
+#endif // DISABLE_DEPRECATED
+ register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3);
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
@@ -910,6 +953,39 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
return ERR_INVALID_DATA;
}
+ // Optionally check maximum compatibility.
+ if (config->has_section_key("configuration", "compatibility_maximum")) {
+ uint32_t compatibility_maximum[3] = { 0, 0, 0 };
+ String compat_string = config->get_value("configuration", "compatibility_maximum");
+ Vector<int> parts = compat_string.split_ints(".");
+ for (int i = 0; i < 3; i++) {
+ if (i < parts.size() && parts[i] >= 0) {
+ compatibility_maximum[i] = parts[i];
+ } else {
+ // If a version part is missing, set the maximum to an arbitrary high value.
+ compatibility_maximum[i] = 9999;
+ }
+ }
+
+ compatible = true;
+ if (VERSION_MAJOR != compatibility_maximum[0]) {
+ compatible = VERSION_MAJOR < compatibility_maximum[0];
+ } else if (VERSION_MINOR != compatibility_maximum[1]) {
+ compatible = VERSION_MINOR < compatibility_maximum[1];
+ }
+#if VERSION_PATCH
+ // #if check to avoid -Wtype-limits warning when 0.
+ else {
+ compatible = VERSION_PATCH <= compatibility_maximum[2];
+ }
+#endif
+
+ if (!compatible) {
+ ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path));
+ return ERR_INVALID_DATA;
+ }
+ }
+
String library_path = GDExtension::find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); });
if (library_path.is_empty()) {
@@ -1056,7 +1132,10 @@ void GDExtension::prepare_reload() {
state.push_back(Pair<String, Variant>(P.name, value));
}
- E.value.instance_state[obj_id] = state;
+ E.value.instance_state[obj_id] = {
+ state, // List<Pair<String, Variant>> properties;
+ obj->is_extension_placeholder(), // bool is_placeholder;
+ };
}
}
}
@@ -1131,25 +1210,29 @@ void GDExtension::finish_reload() {
for (KeyValue<StringName, Extension> &E : extension_classes) {
// Loop over 'instance_state' rather than 'instance' because new instances
// may have been created when re-initializing the extension.
- for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) {
+ for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) {
Object *obj = ObjectDB::get_instance(S.key);
if (!obj) {
continue;
}
- obj->reset_internal_extension(&E.value.gdextension);
+ if (S.value.is_placeholder) {
+ obj->reset_internal_extension(ClassDB::get_placeholder_extension(E.value.gdextension.class_name));
+ } else {
+ obj->reset_internal_extension(&E.value.gdextension);
+ }
}
}
// Now that all the classes are back, restore the state.
for (KeyValue<StringName, Extension> &E : extension_classes) {
- for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) {
+ for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) {
Object *obj = ObjectDB::get_instance(S.key);
if (!obj) {
continue;
}
- for (const Pair<String, Variant> &state : S.value) {
+ for (const Pair<String, Variant> &state : S.value.properties) {
obj->set(state.first, state.second);
}
}
@@ -1157,7 +1240,7 @@ void GDExtension::finish_reload() {
// Finally, let the objects know that we are done reloading them.
for (KeyValue<StringName, Extension> &E : extension_classes) {
- for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) {
+ for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) {
Object *obj = ObjectDB::get_instance(S.key);
if (!obj) {
continue;
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index dbb39acb2e..a2b948a38a 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -59,7 +59,12 @@ class GDExtension : public Resource {
bool is_reloading = false;
HashMap<StringName, GDExtensionMethodBind *> methods;
HashSet<ObjectID> instances;
- HashMap<ObjectID, List<Pair<String, Variant>>> instance_state;
+
+ struct InstanceState {
+ List<Pair<String, Variant>> properties;
+ bool is_placeholder = false;
+ };
+ HashMap<ObjectID, InstanceState> instance_state;
#endif
};
@@ -73,9 +78,10 @@ class GDExtension : public Resource {
#ifndef DISABLE_DEPRECATED
static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs);
-#endif // DISABLE_DEPRECATED
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
- static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
+#endif // DISABLE_DEPRECATED
+ static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
+ static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index c6d7779473..0c96c32187 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -1379,7 +1379,7 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC
static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
- return (GDExtensionObjectPtr)ClassDB::instantiate(classname);
+ return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname);
}
static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) {
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 48a66c9fae..65ee647a51 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -290,7 +290,7 @@ typedef struct {
GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo2 instead.
+} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
typedef struct {
GDExtensionBool is_virtual;
@@ -323,7 +323,41 @@ typedef struct {
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
GDExtensionClassGetRID get_rid_func;
void *class_userdata; // Per-class user data, later accessible in instance bindings.
-} GDExtensionClassCreationInfo2;
+} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead.
+
+typedef struct {
+ GDExtensionBool is_virtual;
+ GDExtensionBool is_abstract;
+ GDExtensionBool is_exposed;
+ GDExtensionBool is_runtime;
+ GDExtensionClassSet set_func;
+ GDExtensionClassGet get_func;
+ GDExtensionClassGetPropertyList get_property_list_func;
+ GDExtensionClassFreePropertyList free_property_list_func;
+ GDExtensionClassPropertyCanRevert property_can_revert_func;
+ GDExtensionClassPropertyGetRevert property_get_revert_func;
+ GDExtensionClassValidateProperty validate_property_func;
+ GDExtensionClassNotification2 notification_func;
+ GDExtensionClassToString to_string_func;
+ GDExtensionClassReference reference_func;
+ GDExtensionClassUnreference unreference_func;
+ GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
+ GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
+ GDExtensionClassRecreateInstance recreate_instance_func;
+ // Queries a virtual function by name and returns a callback to invoke the requested virtual function.
+ GDExtensionClassGetVirtual get_virtual_func;
+ // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
+ // need or benefit from extra data when calling virtual functions.
+ // Returns user data that will be passed to `call_virtual_with_data_func`.
+ // Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
+ // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
+ // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
+ GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
+ // Used to call virtual functions when `get_virtual_call_data_func` is not null.
+ GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
+ GDExtensionClassGetRID get_rid_func;
+ void *class_userdata; // Per-class user data, later accessible in instance bindings.
+} GDExtensionClassCreationInfo3;
typedef void *GDExtensionClassLibraryPtr;
@@ -2240,6 +2274,9 @@ typedef void (*GDExtensionInterfaceObjectSetInstance)(GDExtensionObjectPtr p_o,
*
* Gets the class name of an Object.
*
+ * If the GDExtension wraps the Godot object in an abstraction specific to its class, this is the
+ * function that should be used to determine which wrapper to use.
+ *
* @param p_object A pointer to the Object.
* @param p_library A pointer the library received by the GDExtension's entry point function.
* @param r_class_name A pointer to a String to receive the class name.
@@ -2486,7 +2523,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa
/**
* @name classdb_register_extension_class
* @since 4.1
- * @deprecated in Godot 4.2. Use `classdb_register_extension_class2` instead.
+ * @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2502,6 +2539,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla
/**
* @name classdb_register_extension_class2
* @since 4.2
+ * @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead.
*
* Registers an extension class in the ClassDB.
*
@@ -2515,6 +2553,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
/**
+ * @name classdb_register_extension_class3
+ * @since 4.3
+ *
+ * Registers an extension class in the ClassDB.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_parent_class_name A pointer to a StringName with the parent class name.
+ * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs);
+
+/**
* @name classdb_register_extension_class_method
* @since 4.1
*
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index f2a9a68d08..7671b7972e 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -70,6 +70,138 @@ HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes;
HashMap<StringName, StringName> ClassDB::resource_base_extensions;
HashMap<StringName, StringName> ClassDB::compat_classes;
+#ifdef TOOLS_ENABLED
+HashMap<StringName, ObjectGDExtension> ClassDB::placeholder_extensions;
+
+class PlaceholderExtensionInstance {
+ StringName class_name;
+ HashMap<StringName, Variant> properties;
+
+public:
+ PlaceholderExtensionInstance(const StringName &p_class_name) {
+ class_name = p_class_name;
+ }
+
+ ~PlaceholderExtensionInstance() {}
+
+ void set(const StringName &p_name, const Variant &p_value) {
+ bool is_default_valid = false;
+ Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid);
+
+ // If there's a default value, then we know it's a valid property.
+ if (is_default_valid) {
+ properties[p_name] = p_value;
+ }
+ }
+
+ Variant get(const StringName &p_name) {
+ const Variant *value = properties.getptr(p_name);
+ Variant ret;
+
+ if (value) {
+ ret = *value;
+ } else {
+ bool is_default_valid = false;
+ Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid);
+ if (is_default_valid) {
+ ret = default_value;
+ }
+ }
+
+ return ret;
+ }
+
+ static GDExtensionBool placeholder_instance_set(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value) {
+ PlaceholderExtensionInstance *self = (PlaceholderExtensionInstance *)p_instance;
+ const StringName &name = *(StringName *)p_name;
+ const Variant &value = *(const Variant *)p_value;
+
+ self->set(name, value);
+
+ // We have to return true so Godot doesn't try to call the real setter function.
+ return true;
+ }
+
+ static GDExtensionBool placeholder_instance_get(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) {
+ PlaceholderExtensionInstance *self = (PlaceholderExtensionInstance *)p_instance;
+ const StringName &name = *(StringName *)p_name;
+ Variant *value = (Variant *)r_ret;
+
+ *value = self->get(name);
+
+ // We have to return true so Godot doesn't try to call the real getter function.
+ return true;
+ }
+
+ static const GDExtensionPropertyInfo *placeholder_instance_get_property_list(GDExtensionClassInstancePtr p_instance, uint32_t *r_count) {
+ *r_count = 0;
+ return nullptr;
+ }
+
+ static void placeholder_instance_free_property_list(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list) {
+ }
+
+ static GDExtensionBool placeholder_instance_property_can_revert(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name) {
+ return false;
+ }
+
+ static GDExtensionBool placeholder_instance_property_get_revert(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) {
+ return false;
+ }
+
+ static GDExtensionBool placeholder_instance_validate_property(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property) {
+ return false;
+ }
+
+ static void placeholder_instance_notification(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed) {
+ }
+
+ static void placeholder_instance_to_string(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out) {
+ *r_is_valid = true;
+ }
+
+ static void placeholder_instance_reference(GDExtensionClassInstancePtr p_instance) {
+ }
+
+ static void placeholder_instance_unreference(GDExtensionClassInstancePtr p_instance) {
+ }
+
+ static uint64_t placeholder_instance_get_rid(GDExtensionClassInstancePtr p_instance) {
+ return 0;
+ }
+
+ static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata) {
+ ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
+
+ // Find the closest native parent.
+ ClassDB::ClassInfo *native_parent = ti->inherits_ptr;
+ while (native_parent->gdextension) {
+ native_parent = native_parent->inherits_ptr;
+ }
+
+ // Construct a placeholder.
+ Object *obj = native_parent->creation_func();
+ obj->_extension = ClassDB::get_placeholder_extension(ti->name);
+ obj->_extension_instance = memnew(PlaceholderExtensionInstance(ti->name));
+ return obj;
+ }
+
+ static GDExtensionObjectPtr placeholder_class_recreate_instance(void *p_class_userdata, GDExtensionObjectPtr p_object) {
+ ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata;
+ return memnew(PlaceholderExtensionInstance(ti->name));
+ }
+
+ static void placeholder_class_free_instance(void *p_class_userdata, GDExtensionClassInstancePtr p_instance) {
+ PlaceholderExtensionInstance *instance = (PlaceholderExtensionInstance *)p_instance;
+ memdelete(instance);
+ }
+
+ static GDExtensionClassCallVirtual placeholder_class_get_virtual(void *p_class_userdata, GDExtensionConstStringNamePtr p_name) {
+ return nullptr;
+ }
+};
+#endif
+
bool ClassDB::_is_parent_class(const StringName &p_class, const StringName &p_inherits) {
if (!classes.has(p_class)) {
return false;
@@ -346,7 +478,7 @@ StringName ClassDB::get_compatibility_class(const StringName &p_class) {
return StringName();
}
-Object *ClassDB::instantiate(const StringName &p_class) {
+Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class) {
ClassInfo *ti;
{
OBJTYPE_RLOCK;
@@ -367,10 +499,17 @@ Object *ClassDB::instantiate(const StringName &p_class) {
}
#endif
if (ti->gdextension && ti->gdextension->create_instance) {
- Object *obj = (Object *)ti->gdextension->create_instance(ti->gdextension->class_userdata);
+ ObjectGDExtension *extension = ti->gdextension;
+#ifdef TOOLS_ENABLED
+ if (!p_require_real_class && ti->gdextension->is_runtime && Engine::get_singleton()->is_editor_hint()) {
+ extension = get_placeholder_extension(ti->name);
+ }
+#endif
+ Object *obj = (Object *)extension->create_instance(extension->class_userdata);
+
#ifdef TOOLS_ENABLED
- if (ti->gdextension->track_instance) {
- ti->gdextension->track_instance(ti->gdextension->tracking_userdata, obj);
+ if (extension->track_instance) {
+ extension->track_instance(extension->tracking_userdata, obj);
}
#endif
return obj;
@@ -379,6 +518,82 @@ Object *ClassDB::instantiate(const StringName &p_class) {
}
}
+Object *ClassDB::instantiate(const StringName &p_class) {
+ return _instantiate_internal(p_class);
+}
+
+Object *ClassDB::instantiate_no_placeholders(const StringName &p_class) {
+ return _instantiate_internal(p_class, true);
+}
+
+#ifdef TOOLS_ENABLED
+ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) {
+ ObjectGDExtension *placeholder_extension = placeholder_extensions.getptr(p_class);
+ if (placeholder_extension) {
+ return placeholder_extension;
+ }
+
+ ClassInfo *ti;
+ {
+ OBJTYPE_RLOCK;
+ ti = classes.getptr(p_class);
+ if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) {
+ if (compat_classes.has(p_class)) {
+ ti = classes.getptr(compat_classes[p_class]);
+ }
+ }
+ ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'.");
+ ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled.");
+ ERR_FAIL_NULL_V_MSG(ti->gdextension, nullptr, "Class '" + String(p_class) + "' has no native extension.");
+ }
+
+ placeholder_extensions[p_class] = ObjectGDExtension();
+ placeholder_extension = placeholder_extensions.getptr(p_class);
+
+ // Make a "fake" extension to act as a placeholder.
+ placeholder_extension->library = ti->gdextension->library;
+ placeholder_extension->parent = ti->gdextension->parent;
+ placeholder_extension->children = ti->gdextension->children;
+ placeholder_extension->parent_class_name = ti->gdextension->parent_class_name;
+ placeholder_extension->class_name = ti->gdextension->class_name;
+ placeholder_extension->editor_class = ti->gdextension->editor_class;
+ placeholder_extension->reloadable = ti->gdextension->reloadable;
+ placeholder_extension->is_virtual = ti->gdextension->is_virtual;
+ placeholder_extension->is_abstract = ti->gdextension->is_abstract;
+ placeholder_extension->is_exposed = ti->gdextension->is_exposed;
+ placeholder_extension->is_runtime = true;
+ placeholder_extension->is_placeholder = true;
+
+ placeholder_extension->set = &PlaceholderExtensionInstance::placeholder_instance_set;
+ placeholder_extension->get = &PlaceholderExtensionInstance::placeholder_instance_get;
+ placeholder_extension->get_property_list = &PlaceholderExtensionInstance::placeholder_instance_get_property_list;
+ placeholder_extension->free_property_list = &PlaceholderExtensionInstance::placeholder_instance_free_property_list;
+ placeholder_extension->property_can_revert = &PlaceholderExtensionInstance::placeholder_instance_property_can_revert;
+ placeholder_extension->property_get_revert = &PlaceholderExtensionInstance::placeholder_instance_property_get_revert;
+ placeholder_extension->validate_property = &PlaceholderExtensionInstance::placeholder_instance_validate_property;
+ placeholder_extension->notification = nullptr;
+ placeholder_extension->notification2 = &PlaceholderExtensionInstance::placeholder_instance_notification;
+ placeholder_extension->to_string = &PlaceholderExtensionInstance::placeholder_instance_to_string;
+ placeholder_extension->reference = &PlaceholderExtensionInstance::placeholder_instance_reference;
+ placeholder_extension->unreference = &PlaceholderExtensionInstance::placeholder_instance_unreference;
+ placeholder_extension->get_rid = &PlaceholderExtensionInstance::placeholder_instance_get_rid;
+
+ placeholder_extension->class_userdata = ti;
+ placeholder_extension->create_instance = &PlaceholderExtensionInstance::placeholder_class_create_instance;
+ placeholder_extension->free_instance = &PlaceholderExtensionInstance::placeholder_class_free_instance;
+ placeholder_extension->get_virtual = &PlaceholderExtensionInstance::placeholder_class_get_virtual;
+ placeholder_extension->get_virtual_call_data = nullptr;
+ placeholder_extension->call_virtual_with_data = nullptr;
+ placeholder_extension->recreate_instance = &PlaceholderExtensionInstance::placeholder_class_recreate_instance;
+
+ placeholder_extension->tracking_userdata = ti->gdextension->tracking_userdata;
+ placeholder_extension->track_instance = ti->gdextension->track_instance;
+ placeholder_extension->untrack_instance = ti->gdextension->untrack_instance;
+
+ return placeholder_extension;
+}
+#endif
+
void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) {
ERR_FAIL_NULL(p_object);
ClassInfo *ti;
@@ -1716,7 +1931,7 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
c = Engine::get_singleton()->get_singleton_object(p_class);
cleanup_c = false;
} else if (ClassDB::can_instantiate(p_class) && !ClassDB::is_virtual(p_class)) { // Keep this condition in sync with doc_tools.cpp get_documentation_default_value.
- c = ClassDB::instantiate(p_class);
+ c = ClassDB::instantiate_no_placeholders(p_class);
cleanup_c = true;
}
@@ -1826,6 +2041,11 @@ void ClassDB::unregister_extension_class(const StringName &p_class, bool p_free_
}
}
classes.erase(p_class);
+ default_values_cached.erase(p_class);
+ default_values.erase(p_class);
+#ifdef TOOLS_ENABLED
+ placeholder_extensions.erase(p_class);
+#endif
}
HashMap<StringName, ClassDB::NativeStruct> ClassDB::native_structs;
diff --git a/core/object/class_db.h b/core/object/class_db.h
index c910b30d11..5b4f218845 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -149,6 +149,10 @@ public:
static HashMap<StringName, StringName> resource_base_extensions;
static HashMap<StringName, StringName> compat_classes;
+#ifdef TOOLS_ENABLED
+ static HashMap<StringName, ObjectGDExtension> placeholder_extensions;
+#endif
+
#ifdef DEBUG_METHODS_ENABLED
static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount);
#else
@@ -178,6 +182,8 @@ private:
static MethodBind *_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector<Variant> &p_default_args, bool p_compatibility);
static void _bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility);
+ static Object *_instantiate_internal(const StringName &p_class, bool p_require_real_class = false);
+
public:
// DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!!
template <class T>
@@ -253,6 +259,7 @@ public:
static void get_class_list(List<StringName> *p_classes);
#ifdef TOOLS_ENABLED
static void get_extensions_class_list(List<StringName> *p_classes);
+ static ObjectGDExtension *get_placeholder_extension(const StringName &p_class);
#endif
static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
@@ -264,6 +271,7 @@ public:
static bool can_instantiate(const StringName &p_class);
static bool is_virtual(const StringName &p_class);
static Object *instantiate(const StringName &p_class);
+ static Object *instantiate_no_placeholders(const StringName &p_class);
static void set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance);
static APIType get_api_type(const StringName &p_class);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index cc33d0ab8a..ee2810f808 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -492,14 +492,22 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
ClassDB::get_property_list(current_extension->class_name, p_list, true, this);
if (current_extension->get_property_list) {
- uint32_t pcount;
- const GDExtensionPropertyInfo *pinfo = current_extension->get_property_list(_extension_instance, &pcount);
- for (uint32_t i = 0; i < pcount; i++) {
- p_list->push_back(PropertyInfo(pinfo[i]));
- }
- if (current_extension->free_property_list) {
- current_extension->free_property_list(_extension_instance, pinfo);
+#ifdef TOOLS_ENABLED
+ // If this is a placeholder, we can't call into the GDExtension on the parent class,
+ // because we don't have a real instance of the class to give it.
+ if (likely(!_extension->is_placeholder)) {
+#endif
+ uint32_t pcount;
+ const GDExtensionPropertyInfo *pinfo = current_extension->get_property_list(_extension_instance, &pcount);
+ for (uint32_t i = 0; i < pcount; i++) {
+ p_list->push_back(PropertyInfo(pinfo[i]));
+ }
+ if (current_extension->free_property_list) {
+ current_extension->free_property_list(_extension_instance, pinfo);
+ }
+#ifdef TOOLS_ENABLED
}
+#endif
}
current_extension = current_extension->parent;
@@ -1792,6 +1800,16 @@ uint32_t Object::get_edited_version() const {
#endif
StringName Object::get_class_name_for_extension(const GDExtension *p_library) const {
+#ifdef TOOLS_ENABLED
+ // If this is the library this extension comes from and it's a placeholder, we
+ // have to return the closest native parent's class name, so that it doesn't try to
+ // use this like the real object.
+ if (unlikely(_extension && _extension->library == p_library && _extension->is_placeholder)) {
+ const StringName *class_name = _get_class_namev();
+ return *class_name;
+ }
+#endif
+
// Only return the class name per the extension if it matches the given p_library.
if (_extension && _extension->library == p_library) {
return _extension->class_name;
@@ -1919,13 +1937,15 @@ void Object::clear_internal_extension() {
// Clear the instance bindings.
_instance_binding_mutex.lock();
- if (_instance_bindings[0].free_callback) {
- _instance_bindings[0].free_callback(_instance_bindings[0].token, this, _instance_bindings[0].binding);
+ if (_instance_bindings) {
+ if (_instance_bindings[0].free_callback) {
+ _instance_bindings[0].free_callback(_instance_bindings[0].token, this, _instance_bindings[0].binding);
+ }
+ _instance_bindings[0].binding = nullptr;
+ _instance_bindings[0].token = nullptr;
+ _instance_bindings[0].free_callback = nullptr;
+ _instance_bindings[0].reference_callback = nullptr;
}
- _instance_bindings[0].binding = nullptr;
- _instance_bindings[0].token = nullptr;
- _instance_bindings[0].free_callback = nullptr;
- _instance_bindings[0].reference_callback = nullptr;
_instance_binding_mutex.unlock();
// Clear the virtual methods.
diff --git a/core/object/object.h b/core/object/object.h
index 27f28b4aae..3974ef7295 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -317,6 +317,10 @@ struct ObjectGDExtension {
bool is_virtual = false;
bool is_abstract = false;
bool is_exposed = true;
+#ifdef TOOLS_ENABLED
+ bool is_runtime = false;
+ bool is_placeholder = false;
+#endif
GDExtensionClassSet set;
GDExtensionClassGet get;
GDExtensionClassGetPropertyList get_property_list;
@@ -755,6 +759,7 @@ protected:
void _clear_internal_resource_paths(const Variant &p_var);
friend class ClassDB;
+ friend class PlaceholderExtensionInstance;
bool _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false);
@@ -977,6 +982,7 @@ public:
#ifdef TOOLS_ENABLED
void clear_internal_extension();
void reset_internal_extension(ObjectGDExtension *p_extension);
+ bool is_extension_placeholder() const { return _extension && _extension->is_placeholder; }
#endif
void clear_internal_resource_paths();
diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp
index 037851661b..6870c84b49 100644
--- a/core/os/midi_driver.cpp
+++ b/core/os/midi_driver.cpp
@@ -42,9 +42,10 @@ void MIDIDriver::set_singleton() {
singleton = this;
}
-void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) {
+void MIDIDriver::receive_input_packet(int device_index, uint64_t timestamp, uint8_t *data, uint32_t length) {
Ref<InputEventMIDI> event;
event.instantiate();
+ event->set_device(device_index);
uint32_t param_position = 1;
if (length >= 1) {
diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h
index 6ad21c319e..cad3d8189e 100644
--- a/core/os/midi_driver.h
+++ b/core/os/midi_driver.h
@@ -51,7 +51,7 @@ public:
virtual PackedStringArray get_connected_inputs();
- static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length);
+ static void receive_input_packet(int device_index, uint64_t timestamp, uint8_t *data, uint32_t length);
MIDIDriver();
virtual ~MIDIDriver() {}
diff --git a/core/templates/search_array.h b/core/templates/search_array.h
index f537ef67fd..9c3f527323 100644
--- a/core/templates/search_array.h
+++ b/core/templates/search_array.h
@@ -38,12 +38,12 @@ class SearchArray {
public:
Comparator compare;
- inline int bisect(const T *p_array, int p_len, const T &p_value, bool p_before) const {
- int lo = 0;
- int hi = p_len;
+ inline int64_t bisect(const T *p_array, int64_t p_len, const T &p_value, bool p_before) const {
+ int64_t lo = 0;
+ int64_t hi = p_len;
if (p_before) {
while (lo < hi) {
- const int mid = (lo + hi) / 2;
+ const int64_t mid = (lo + hi) / 2;
if (compare(p_array[mid], p_value)) {
lo = mid + 1;
} else {
@@ -52,7 +52,7 @@ public:
}
} else {
while (lo < hi) {
- const int mid = (lo + hi) / 2;
+ const int64_t mid = (lo + hi) / 2;
if (compare(p_value, p_array[mid])) {
hi = mid;
} else {
diff --git a/core/templates/sort_array.h b/core/templates/sort_array.h
index fbe2a89a84..45aeaf1579 100644
--- a/core/templates/sort_array.h
+++ b/core/templates/sort_array.h
@@ -78,8 +78,8 @@ public:
}
}
- inline int bitlog(int n) const {
- int k;
+ inline int64_t bitlog(int64_t n) const {
+ int64_t k;
for (k = 0; n != 1; n >>= 1) {
++k;
}
@@ -88,8 +88,8 @@ public:
/* Heap / Heapsort functions */
- inline void push_heap(int p_first, int p_hole_idx, int p_top_index, T p_value, T *p_array) const {
- int parent = (p_hole_idx - 1) / 2;
+ inline void push_heap(int64_t p_first, int64_t p_hole_idx, int64_t p_top_index, T p_value, T *p_array) const {
+ int64_t parent = (p_hole_idx - 1) / 2;
while (p_hole_idx > p_top_index && compare(p_array[p_first + parent], p_value)) {
p_array[p_first + p_hole_idx] = p_array[p_first + parent];
p_hole_idx = parent;
@@ -98,17 +98,17 @@ public:
p_array[p_first + p_hole_idx] = p_value;
}
- inline void pop_heap(int p_first, int p_last, int p_result, T p_value, T *p_array) const {
+ inline void pop_heap(int64_t p_first, int64_t p_last, int64_t p_result, T p_value, T *p_array) const {
p_array[p_result] = p_array[p_first];
adjust_heap(p_first, 0, p_last - p_first, p_value, p_array);
}
- inline void pop_heap(int p_first, int p_last, T *p_array) const {
+ inline void pop_heap(int64_t p_first, int64_t p_last, T *p_array) const {
pop_heap(p_first, p_last - 1, p_last - 1, p_array[p_last - 1], p_array);
}
- inline void adjust_heap(int p_first, int p_hole_idx, int p_len, T p_value, T *p_array) const {
- int top_index = p_hole_idx;
- int second_child = 2 * p_hole_idx + 2;
+ inline void adjust_heap(int64_t p_first, int64_t p_hole_idx, int64_t p_len, T p_value, T *p_array) const {
+ int64_t top_index = p_hole_idx;
+ int64_t second_child = 2 * p_hole_idx + 2;
while (second_child < p_len) {
if (compare(p_array[p_first + second_child], p_array[p_first + (second_child - 1)])) {
@@ -127,18 +127,18 @@ public:
push_heap(p_first, p_hole_idx, top_index, p_value, p_array);
}
- inline void sort_heap(int p_first, int p_last, T *p_array) const {
+ inline void sort_heap(int64_t p_first, int64_t p_last, T *p_array) const {
while (p_last - p_first > 1) {
pop_heap(p_first, p_last--, p_array);
}
}
- inline void make_heap(int p_first, int p_last, T *p_array) const {
+ inline void make_heap(int64_t p_first, int64_t p_last, T *p_array) const {
if (p_last - p_first < 2) {
return;
}
- int len = p_last - p_first;
- int parent = (len - 2) / 2;
+ int64_t len = p_last - p_first;
+ int64_t parent = (len - 2) / 2;
while (true) {
adjust_heap(p_first, parent, len, p_array[p_first + parent], p_array);
@@ -149,9 +149,9 @@ public:
}
}
- inline void partial_sort(int p_first, int p_last, int p_middle, T *p_array) const {
+ inline void partial_sort(int64_t p_first, int64_t p_last, int64_t p_middle, T *p_array) const {
make_heap(p_first, p_middle, p_array);
- for (int i = p_middle; i < p_last; i++) {
+ for (int64_t i = p_middle; i < p_last; i++) {
if (compare(p_array[i], p_array[p_first])) {
pop_heap(p_first, p_middle, i, p_array[i], p_array);
}
@@ -159,18 +159,18 @@ public:
sort_heap(p_first, p_middle, p_array);
}
- inline void partial_select(int p_first, int p_last, int p_middle, T *p_array) const {
+ inline void partial_select(int64_t p_first, int64_t p_last, int64_t p_middle, T *p_array) const {
make_heap(p_first, p_middle, p_array);
- for (int i = p_middle; i < p_last; i++) {
+ for (int64_t i = p_middle; i < p_last; i++) {
if (compare(p_array[i], p_array[p_first])) {
pop_heap(p_first, p_middle, i, p_array[i], p_array);
}
}
}
- inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const {
- const int unmodified_first = p_first;
- const int unmodified_last = p_last;
+ inline int64_t partitioner(int64_t p_first, int64_t p_last, T p_pivot, T *p_array) const {
+ const int64_t unmodified_first = p_first;
+ const int64_t unmodified_last = p_last;
while (true) {
while (compare(p_array[p_first], p_pivot)) {
@@ -196,7 +196,7 @@ public:
}
}
- inline void introsort(int p_first, int p_last, T *p_array, int p_max_depth) const {
+ inline void introsort(int64_t p_first, int64_t p_last, T *p_array, int64_t p_max_depth) const {
while (p_last - p_first > INTROSORT_THRESHOLD) {
if (p_max_depth == 0) {
partial_sort(p_first, p_last, p_last, p_array);
@@ -205,7 +205,7 @@ public:
p_max_depth--;
- int cut = partitioner(
+ int64_t cut = partitioner(
p_first,
p_last,
median_of_3(
@@ -219,7 +219,7 @@ public:
}
}
- inline void introselect(int p_first, int p_nth, int p_last, T *p_array, int p_max_depth) const {
+ inline void introselect(int64_t p_first, int64_t p_nth, int64_t p_last, T *p_array, int64_t p_max_depth) const {
while (p_last - p_first > 3) {
if (p_max_depth == 0) {
partial_select(p_first, p_nth + 1, p_last, p_array);
@@ -229,7 +229,7 @@ public:
p_max_depth--;
- int cut = partitioner(
+ int64_t cut = partitioner(
p_first,
p_last,
median_of_3(
@@ -248,8 +248,8 @@ public:
insertion_sort(p_first, p_last, p_array);
}
- inline void unguarded_linear_insert(int p_last, T p_value, T *p_array) const {
- int next = p_last - 1;
+ inline void unguarded_linear_insert(int64_t p_last, T p_value, T *p_array) const {
+ int64_t next = p_last - 1;
while (compare(p_value, p_array[next])) {
if (Validate) {
ERR_BAD_COMPARE(next == 0);
@@ -261,10 +261,10 @@ public:
p_array[p_last] = p_value;
}
- inline void linear_insert(int p_first, int p_last, T *p_array) const {
+ inline void linear_insert(int64_t p_first, int64_t p_last, T *p_array) const {
T val = p_array[p_last];
if (compare(val, p_array[p_first])) {
- for (int i = p_last; i > p_first; i--) {
+ for (int64_t i = p_last; i > p_first; i--) {
p_array[i] = p_array[i - 1];
}
@@ -274,22 +274,22 @@ public:
}
}
- inline void insertion_sort(int p_first, int p_last, T *p_array) const {
+ inline void insertion_sort(int64_t p_first, int64_t p_last, T *p_array) const {
if (p_first == p_last) {
return;
}
- for (int i = p_first + 1; i != p_last; i++) {
+ for (int64_t i = p_first + 1; i != p_last; i++) {
linear_insert(p_first, i, p_array);
}
}
- inline void unguarded_insertion_sort(int p_first, int p_last, T *p_array) const {
- for (int i = p_first; i != p_last; i++) {
+ inline void unguarded_insertion_sort(int64_t p_first, int64_t p_last, T *p_array) const {
+ for (int64_t i = p_first; i != p_last; i++) {
unguarded_linear_insert(i, p_array[i], p_array);
}
}
- inline void final_insertion_sort(int p_first, int p_last, T *p_array) const {
+ inline void final_insertion_sort(int64_t p_first, int64_t p_last, T *p_array) const {
if (p_last - p_first > INTROSORT_THRESHOLD) {
insertion_sort(p_first, p_first + INTROSORT_THRESHOLD, p_array);
unguarded_insertion_sort(p_first + INTROSORT_THRESHOLD, p_last, p_array);
@@ -298,18 +298,18 @@ public:
}
}
- inline void sort_range(int p_first, int p_last, T *p_array) const {
+ inline void sort_range(int64_t p_first, int64_t p_last, T *p_array) const {
if (p_first != p_last) {
introsort(p_first, p_last, p_array, bitlog(p_last - p_first) * 2);
final_insertion_sort(p_first, p_last, p_array);
}
}
- inline void sort(T *p_array, int p_len) const {
+ inline void sort(T *p_array, int64_t p_len) const {
sort_range(0, p_len, p_array);
}
- inline void nth_element(int p_first, int p_last, int p_nth, T *p_array) const {
+ inline void nth_element(int64_t p_first, int64_t p_last, int64_t p_nth, T *p_array) const {
if (p_first == p_last || p_nth == p_last) {
return;
}
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 4cf5a88dd7..ce7bf4ef5c 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2889,7 +2889,7 @@
[/codeblocks]
[b]Note:[/b] The trailing colon is required for properly detecting built-in types.
</constant>
- <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="24" enum="PropertyHint" deprecated="This hint is not used anywhere and will be removed in the future.">
+ <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="24" enum="PropertyHint" deprecated="This hint is not used by the engine.">
</constant>
<constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="25" enum="PropertyHint">
Hints that an object is too big to be sent via the debugger.
@@ -2903,7 +2903,7 @@
<constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="28" enum="PropertyHint">
Hints that a [String] property is a path to a file. Editing it will show a file dialog for picking the path for the file to be saved at. The dialog has access to the entire filesystem. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. See also [member FileDialog.filters].
</constant>
- <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="29" enum="PropertyHint" deprecated="This hint is not used anywhere and will be removed in the future.">
+ <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="29" enum="PropertyHint" deprecated="This hint is not used by the engine.">
</constant>
<constant name="PROPERTY_HINT_INT_IS_POINTER" value="30" enum="PropertyHint">
Hints that an [int] property is a pointer. Used by GDExtension.
@@ -2974,7 +2974,7 @@
<constant name="PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED" value="16384" enum="PropertyUsageFlags" is_bitfield="true">
If this property is modified, all inspector fields will be refreshed.
</constant>
- <constant name="PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE" value="32768" enum="PropertyUsageFlags" is_bitfield="true" deprecated="This hint is not used anywhere and will be removed in the future.">
+ <constant name="PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE" value="32768" enum="PropertyUsageFlags" is_bitfield="true" deprecated="This flag is not used by the engine.">
</constant>
<constant name="PROPERTY_USAGE_CLASS_IS_ENUM" value="65536" enum="PropertyUsageFlags" is_bitfield="true">
The property is an enum, i.e. it only takes named integer constants from its associated enumeration.
@@ -3003,7 +3003,7 @@
<constant name="PROPERTY_USAGE_KEYING_INCREMENTS" value="16777216" enum="PropertyUsageFlags" is_bitfield="true">
Inserting an animation key frame of this property will automatically increment the value, allowing to easily keyframe multiple values in a row.
</constant>
- <constant name="PROPERTY_USAGE_DEFERRED_SET_RESOURCE" value="33554432" enum="PropertyUsageFlags" is_bitfield="true" deprecated="This hint is not used anywhere and will be removed in the future.">
+ <constant name="PROPERTY_USAGE_DEFERRED_SET_RESOURCE" value="33554432" enum="PropertyUsageFlags" is_bitfield="true" deprecated="This flag is not used by the engine.">
</constant>
<constant name="PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT" value="67108864" enum="PropertyUsageFlags" is_bitfield="true">
When this property is a [Resource] and base object is a [Node], a resource instance will be automatically created whenever the node is created in the editor.
diff --git a/doc/classes/AnimatedSprite2D.xml b/doc/classes/AnimatedSprite2D.xml
index 6eab269905..4b38773505 100644
--- a/doc/classes/AnimatedSprite2D.xml
+++ b/doc/classes/AnimatedSprite2D.xml
@@ -84,6 +84,7 @@
</member>
<member name="centered" type="bool" setter="set_centered" getter="is_centered" default="true">
If [code]true[/code], texture will be centered.
+ [b]Note:[/b] For games with a pixel art aesthetic, textures may appear deformed when centered. This is caused by their position being between pixels. To prevent this, set this property to [code]false[/code], or consider enabling [member ProjectSettings.rendering/2d/snap/snap_2d_vertices_to_pixel] and [member ProjectSettings.rendering/2d/snap/snap_2d_transforms_to_pixel].
</member>
<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false">
If [code]true[/code], texture is flipped horizontally.
diff --git a/doc/classes/AnimatedTexture.xml b/doc/classes/AnimatedTexture.xml
index aad16f6f3a..d443541e26 100644
--- a/doc/classes/AnimatedTexture.xml
+++ b/doc/classes/AnimatedTexture.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="AnimatedTexture" inherits="Texture2D" deprecated="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+<class name="AnimatedTexture" inherits="Texture2D" deprecated="This class does not work properly in current versions and may be removed in the future. There is currently no equivalent workaround." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Proxy texture for simple frame-based animations.
</brief_description>
diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml
index e521baffda..26d28f9c50 100644
--- a/doc/classes/AnimationMixer.xml
+++ b/doc/classes/AnimationMixer.xml
@@ -273,13 +273,13 @@
The number of possible simultaneous sounds for each of the assigned AudioStreamPlayers.
For example, if this value is [code]32[/code] and the animation has two audio tracks, the two [AudioStreamPlayer]s assigned can play simultaneously up to [code]32[/code] voices each.
</member>
- <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="1">
+ <member name="callback_mode_discrete" type="int" setter="set_callback_mode_discrete" getter="get_callback_mode_discrete" enum="AnimationMixer.AnimationCallbackModeDiscrete" default="0">
Ordinarily, tracks can be set to [constant Animation.UPDATE_DISCRETE] to update infrequently, usually when using nearest interpolation.
However, when blending with [constant Animation.UPDATE_CONTINUOUS] several results are considered. The [member callback_mode_discrete] specify it explicitly. See also [enum AnimationCallbackModeDiscrete].
To make the blended results look good, it is recommended to set this to [constant ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS] to update every frame during blending. Other values exist for compatibility and they are fine if there is no blending, but not so, may produce artifacts.
</member>
<member name="callback_mode_method" type="int" setter="set_callback_mode_method" getter="get_callback_mode_method" enum="AnimationMixer.AnimationCallbackModeMethod" default="0">
- The call mode to use for Call Method tracks.
+ The call mode used for "Call Method" tracks.
</member>
<member name="callback_mode_process" type="int" setter="set_callback_mode_process" getter="get_callback_mode_process" enum="AnimationMixer.AnimationCallbackModeProcess" default="1">
The process notification in which to update animations.
@@ -301,7 +301,7 @@
If the track has type [constant Animation.TYPE_POSITION_3D], [constant Animation.TYPE_ROTATION_3D] or [constant Animation.TYPE_SCALE_3D] the transformation will be canceled visually, and the animation will appear to stay in place. See also [method get_root_motion_position], [method get_root_motion_rotation], [method get_root_motion_scale] and [RootMotionView].
</member>
<member name="root_node" type="NodePath" setter="set_root_node" getter="get_root_node" default="NodePath(&quot;..&quot;)">
- The node from which node path references will travel.
+ The node which node path references will travel from.
</member>
</members>
<signals>
@@ -356,10 +356,10 @@
Make method calls immediately when reached in the animation.
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT" value="0" enum="AnimationCallbackModeDiscrete">
- An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values.
+ An [constant Animation.UPDATE_DISCRETE] track value takes precedence when blending [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer].
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE" value="1" enum="AnimationCallbackModeDiscrete">
- An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values. This is the default behavior for [AnimationPlayer].
+ An [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track value takes precedence when blending the [constant Animation.UPDATE_CONTINUOUS] or [constant Animation.UPDATE_CAPTURE] track values and the [constant Animation.UPDATE_DISCRETE] track values.
</constant>
<constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete">
Always treat the [constant Animation.UPDATE_DISCRETE] track value as [constant Animation.UPDATE_CONTINUOUS] with [constant Animation.INTERPOLATION_NEAREST]. This is the default behavior for [AnimationTree].
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 6236f96e63..e023f2d41a 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -47,6 +47,7 @@
<method name="get_method_call_mode" qualifiers="const" deprecated="Use [member AnimationMixer.callback_mode_method] instead.">
<return type="int" enum="AnimationPlayer.AnimationMethodCallMode" />
<description>
+ Returns the call mode used for "Call Method" tracks.
</description>
</method>
<method name="get_playing_speed" qualifiers="const">
@@ -59,6 +60,7 @@
<method name="get_process_callback" qualifiers="const" deprecated="Use [member AnimationMixer.callback_mode_process] instead.">
<return type="int" enum="AnimationPlayer.AnimationProcessCallback" />
<description>
+ Returns the process notification in which to update animations.
</description>
</method>
<method name="get_queue">
@@ -70,6 +72,7 @@
<method name="get_root" qualifiers="const" deprecated="Use [member AnimationMixer.root_node] instead.">
<return type="NodePath" />
<description>
+ Returns the node which node path references will travel from.
</description>
</method>
<method name="is_playing" qualifiers="const">
@@ -159,18 +162,21 @@
<return type="void" />
<param index="0" name="mode" type="int" enum="AnimationPlayer.AnimationMethodCallMode" />
<description>
+ Sets the call mode used for "Call Method" tracks.
</description>
</method>
<method name="set_process_callback" deprecated="Use [member AnimationMixer.callback_mode_process] instead.">
<return type="void" />
<param index="0" name="mode" type="int" enum="AnimationPlayer.AnimationProcessCallback" />
<description>
+ Sets the process notification in which to update animations.
</description>
</method>
<method name="set_root" deprecated="Use [member AnimationMixer.root_node] instead.">
<return type="void" />
<param index="0" name="path" type="NodePath" />
<description>
+ Sets the node which node path references will travel from.
</description>
</method>
<method name="stop">
diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml
index 778b5cf513..d240a4967e 100644
--- a/doc/classes/AnimationTree.xml
+++ b/doc/classes/AnimationTree.xml
@@ -15,12 +15,14 @@
<method name="get_process_callback" qualifiers="const" deprecated="Use [member AnimationMixer.callback_mode_process] instead.">
<return type="int" enum="AnimationTree.AnimationProcessCallback" />
<description>
+ Returns the process notification in which to update animations.
</description>
</method>
<method name="set_process_callback" deprecated="Use [member AnimationMixer.callback_mode_process] instead.">
<return type="void" />
<param index="0" name="mode" type="int" enum="AnimationTree.AnimationProcessCallback" />
<description>
+ Sets the process notification in which to update animations.
</description>
</method>
</methods>
diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml
index a6f85e7fe5..79283d6f85 100644
--- a/doc/classes/CPUParticles3D.xml
+++ b/doc/classes/CPUParticles3D.xml
@@ -309,7 +309,7 @@
<member name="tangential_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0">
Minimum tangent acceleration.
</member>
- <member name="visibility_aabb" type="AABB" setter="set_visibility_aabb" getter="get_visibility_aabb" default="AABB(-4, -4, -4, 8, 8, 8)">
+ <member name="visibility_aabb" type="AABB" setter="set_visibility_aabb" getter="get_visibility_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
The [AABB] that determines the node's region which needs to be visible on screen for the particle system to be active.
Grow the box if particles suddenly appear/disappear when the node enters/exits the screen. The [AABB] can be grown via code or with the [b]Particles → Generate AABB[/b] editor tool.
</member>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 6f4dc47fb9..0113131f25 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -411,6 +411,12 @@
Returns the canvas item RID used by [RenderingServer] for this item.
</description>
</method>
+ <method name="get_canvas_layer_node" qualifiers="const">
+ <return type="CanvasLayer" />
+ <description>
+ Returns the [CanvasLayer] that contains this node, or [code]null[/code] if the node is not in any [CanvasLayer].
+ </description>
+ </method>
<method name="get_canvas_transform" qualifiers="const">
<return type="Transform2D" />
<description>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 07f41192f7..a13bf0870d 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1158,12 +1158,12 @@
[b]Note:[/b] [member CanvasItem.z_index] doesn't affect which Control receives the notification.
See also [constant NOTIFICATION_MOUSE_EXIT_SELF].
</constant>
- <constant name="NOTIFICATION_MOUSE_ENTER_SELF" value="60" experimental="">
+ <constant name="NOTIFICATION_MOUSE_ENTER_SELF" value="60" experimental="The reason this notification is sent may change in the future.">
Sent when the mouse cursor enters the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not.
[b]Note:[/b] [member CanvasItem.z_index] doesn't affect which Control receives the notification.
See also [constant NOTIFICATION_MOUSE_ENTER].
</constant>
- <constant name="NOTIFICATION_MOUSE_EXIT_SELF" value="61" experimental="">
+ <constant name="NOTIFICATION_MOUSE_EXIT_SELF" value="61" experimental="The reason this notification is sent may change in the future.">
Sent when the mouse cursor leaves the control's visible area, that is not occluded behind other Controls or Windows, provided its [member mouse_filter] lets the event reach it and regardless if it's currently focused or not.
[b]Note:[/b] [member CanvasItem.z_index] doesn't affect which Control receives the notification.
See also [constant NOTIFICATION_MOUSE_EXIT].
diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml
index 535515165b..4fd288f16d 100644
--- a/doc/classes/EditorProperty.xml
+++ b/doc/classes/EditorProperty.xml
@@ -72,9 +72,6 @@
<member name="checked" type="bool" setter="set_checked" getter="is_checked" default="false">
Used by the inspector, set to [code]true[/code] when the property is checked.
</member>
- <member name="configuration_warning" type="String" setter="set_configuration_warning" getter="get_configuration_warning" default="&quot;&quot;">
- Used by the inspector, set to show a configuration warning on the property.
- </member>
<member name="deletable" type="bool" setter="set_deletable" getter="is_deletable" default="false">
Used by the inspector, set to [code]true[/code] when the property can be deleted by the user.
</member>
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index acd959d7f7..6913d1f75c 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -87,7 +87,18 @@
This is useful to simulate [url=https://en.wikipedia.org/wiki/Aerial_perspective]aerial perspective[/url] in large scenes with low density fog. However, it is not very useful for high-density fog, as the sky will shine through. When set to [code]1.0[/code], the fog color comes completely from the [Sky]. If set to [code]0.0[/code], aerial perspective is disabled.
</member>
<member name="fog_density" type="float" setter="set_fog_density" getter="get_fog_density" default="0.01">
- The [i]exponential[/i] fog density to use. Higher values result in a more dense fog. Fog rendering is exponential as in real life.
+ The fog density to be used. This is demonstrated in different ways depending on the [member fog_mode] mode chosen:
+ [b]Exponential Fog Mode:[/b] Higher values result in denser fog. The fog rendering is exponential like in real life.
+ [b]Depth Fog mode:[/b] The maximum intensity of the deep fog, effect will appear in the distance (relative to the camera). At [code]1.0[/code] the fog will fully obscure the scene, at [code]0.0[/code] the fog will not be visible.
+ </member>
+ <member name="fog_depth_begin" type="float" setter="set_fog_depth_begin" getter="get_fog_depth_begin" default="10.0">
+ The fog's depth starting distance from the camera. Only available when [member fog_mode] is set to [constant FOG_MODE_DEPTH].
+ </member>
+ <member name="fog_depth_curve" type="float" setter="set_fog_depth_curve" getter="get_fog_depth_curve" default="1.0">
+ The fog depth's intensity curve. A number of presets are available in the Inspector by right-clicking the curve. Only available when [member fog_mode] is set to [constant FOG_MODE_DEPTH].
+ </member>
+ <member name="fog_depth_end" type="float" setter="set_fog_depth_end" getter="get_fog_depth_end" default="100.0">
+ The fog's depth end distance from the camera. If this value is set to [code]0[/code], it will be equal to the current camera's [member Camera3D.far] value. Only available when [member fog_mode] is set to [constant FOG_MODE_DEPTH].
</member>
<member name="fog_enabled" type="bool" setter="set_fog_enabled" getter="is_fog_enabled" default="false">
If [code]true[/code], fog effects are enabled.
@@ -104,6 +115,9 @@
<member name="fog_light_energy" type="float" setter="set_fog_light_energy" getter="get_fog_light_energy" default="1.0">
The fog's brightness. Higher values result in brighter fog.
</member>
+ <member name="fog_mode" type="int" setter="set_fog_mode" getter="get_fog_mode" enum="Environment.FogMode" default="0">
+ The fog mode. See [enum FogMode] for possible values.
+ </member>
<member name="fog_sky_affect" type="float" setter="set_fog_sky_affect" getter="get_fog_sky_affect" default="1.0">
The factor to use when affecting the sky with non-volumetric fog. [code]1.0[/code] means that fog can fully obscure the sky. Lower values reduce the impact of fog on sky rendering, with [code]0.0[/code] not affecting sky rendering at all.
[b]Note:[/b] [member fog_sky_affect] has no visual effect if [member fog_aerial_perspective] is [code]1.0[/code].
@@ -412,6 +426,12 @@
<constant name="GLOW_BLEND_MODE_MIX" value="4" enum="GlowBlendMode">
Mixes the glow with the underlying color to avoid increasing brightness as much while still maintaining a glow effect.
</constant>
+ <constant name="FOG_MODE_EXPONENTIAL" value="0" enum="FogMode">
+ Use a physically-based fog model defined primarily by fog density.
+ </constant>
+ <constant name="FOG_MODE_DEPTH" value="1" enum="FogMode">
+ Use a simple fog model defined by start and end positions and a custom curve. While not physically accurate, this model can be useful when you need more artistic control.
+ </constant>
<constant name="SDFGI_Y_SCALE_50_PERCENT" value="0" enum="SDFGIYScale">
Use 50% scale for SDFGI on the Y (vertical) axis. SDFGI cells will be twice as short as they are wide. This allows providing increased GI detail and reduced light leaking with thin floors and ceilings. This is usually the best choice for scenes that don't feature much verticality.
</constant>
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 4befe2f8ec..25b6a48283 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -322,10 +322,10 @@
<constant name="RESPONSE_NOT_MODIFIED" value="304" enum="ResponseCode">
HTTP status code [code]304 Not Modified[/code]. A conditional GET or HEAD request has been received and would have resulted in a 200 OK response if it were not for the fact that the condition evaluated to [code]false[/code].
</constant>
- <constant name="RESPONSE_USE_PROXY" value="305" enum="ResponseCode" deprecated="">
+ <constant name="RESPONSE_USE_PROXY" value="305" enum="ResponseCode" deprecated="Many clients ignore this response code for security reasons. It is also deprecated by the HTTP standard.">
HTTP status code [code]305 Use Proxy[/code].
</constant>
- <constant name="RESPONSE_SWITCH_PROXY" value="306" enum="ResponseCode" deprecated="">
+ <constant name="RESPONSE_SWITCH_PROXY" value="306" enum="ResponseCode" deprecated="Many clients ignore this response code for security reasons. It is also deprecated by the HTTP standard.">
HTTP status code [code]306 Switch Proxy[/code].
</constant>
<constant name="RESPONSE_TEMPORARY_REDIRECT" value="307" enum="ResponseCode">
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index bb84813835..984d426c0a 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -383,6 +383,7 @@
<description>
Starts to vibrate the joypad. Joypads usually come with two rumble motors, a strong and a weak one. [param weak_magnitude] is the strength of the weak motor (between 0 and 1) and [param strong_magnitude] is the strength of the strong motor (between 0 and 1). [param duration] is the duration of the effect in seconds (a duration of 0 will try to play the vibration indefinitely). The vibration can be stopped early by calling [method stop_joy_vibration].
[b]Note:[/b] Not every hardware is compatible with long effect durations; it is recommended to restart an effect if it has to be played for more than a few seconds.
+ [b]Note:[/b] For macOS, vibration is only supported in macOS 11 and later.
</description>
</method>
<method name="stop_joy_vibration">
diff --git a/doc/classes/LightmapGIData.xml b/doc/classes/LightmapGIData.xml
index b1ba6c5d84..76824f84a0 100644
--- a/doc/classes/LightmapGIData.xml
+++ b/doc/classes/LightmapGIData.xml
@@ -54,7 +54,7 @@
</method>
</methods>
<members>
- <member name="light_texture" type="TextureLayered" setter="set_light_texture" getter="get_light_texture" deprecated="The lightmap atlas can now have multiple textures. See [member lightmap_textures].">
+ <member name="light_texture" type="TextureLayered" setter="set_light_texture" getter="get_light_texture" deprecated="The lightmap atlas can now contain multiple textures. See [member lightmap_textures].">
The lightmap atlas texture generated by the lightmapper.
</member>
<member name="lightmap_textures" type="TextureLayered[]" setter="set_lightmap_textures" getter="get_lightmap_textures" default="[]">
diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml
index cd6397137b..1ac79cd3da 100644
--- a/doc/classes/MultiMesh.xml
+++ b/doc/classes/MultiMesh.xml
@@ -90,13 +90,14 @@
<members>
<member name="buffer" type="PackedFloat32Array" setter="set_buffer" getter="get_buffer" default="PackedFloat32Array()">
</member>
- <member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array" deprecated="Use [method set_instance_color] instead.">
+ <member name="color_array" type="PackedColorArray" setter="_set_color_array" getter="_get_color_array" deprecated="Accessing this property is very slow. Use [method set_instance_color] and [method get_instance_color] instead.">
+ Array containing each [Color] used by all instances of this mesh.
</member>
<member name="custom_aabb" type="AABB" setter="set_custom_aabb" getter="get_custom_aabb" default="AABB(0, 0, 0, 0, 0, 0)">
Custom AABB for this MultiMesh resource. Setting this manually prevents costly runtime AABB recalculations.
</member>
- <member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array" deprecated="Use [method set_instance_custom_data] instead.">
- See [method set_instance_custom_data].
+ <member name="custom_data_array" type="PackedColorArray" setter="_set_custom_data_array" getter="_get_custom_data_array" deprecated="Accessing this property is very slow. Use [method set_instance_custom_data] and [method get_instance_custom_data] instead.">
+ Array containing each custom data value used by all instances of this mesh, as a [PackedColorArray].
</member>
<member name="instance_count" type="int" setter="set_instance_count" getter="get_instance_count" default="0">
Number of instances that will get drawn. This clears and (re)sizes the buffers. Setting data format or flags afterwards will have no effect.
@@ -106,9 +107,11 @@
[Mesh] resource to be instanced.
The looks of the individual instances can be modified using [method set_instance_color] and [method set_instance_custom_data].
</member>
- <member name="transform_2d_array" type="PackedVector2Array" setter="_set_transform_2d_array" getter="_get_transform_2d_array" deprecated="Use [method set_instance_transform_2d] instead.">
+ <member name="transform_2d_array" type="PackedVector2Array" setter="_set_transform_2d_array" getter="_get_transform_2d_array" deprecated="Accessing this property is very slow. Use [method set_instance_transform_2d] and [method get_instance_transform_2d] instead.">
+ Array containing each [Transform2D] value used by all instances of this mesh, as a [PackedVector2Array]. Each transform is divided into 3 [Vector2] values corresponding to the transforms' [code]x[/code], [code]y[/code], and [code]origin[/code].
</member>
- <member name="transform_array" type="PackedVector3Array" setter="_set_transform_array" getter="_get_transform_array" deprecated="Use [method set_instance_transform] instead.">
+ <member name="transform_array" type="PackedVector3Array" setter="_set_transform_array" getter="_get_transform_array" deprecated="Accessing this property is very slow. Use [method set_instance_transform] and [method get_instance_transform] instead.">
+ Array containing each [Transform3D] value used by all instances of this mesh, as a [PackedVector3Array]. Each transform is divided into 4 [Vector3] values corresponding to the transforms' [code]x[/code], [code]y[/code], [code]z[/code], and [code]origin[/code].
</member>
<member name="transform_format" type="int" setter="set_transform_format" getter="get_transform_format" enum="MultiMesh.TransformFormat" default="0">
Format of transform used to transform mesh, either 2D or 3D.
diff --git a/doc/classes/NavigationMeshGenerator.xml b/doc/classes/NavigationMeshGenerator.xml
index 35c9e13e58..c017a16dfa 100644
--- a/doc/classes/NavigationMeshGenerator.xml
+++ b/doc/classes/NavigationMeshGenerator.xml
@@ -14,12 +14,12 @@
<link title="Using NavigationMeshes">$DOCS_URL/tutorials/navigation/navigation_using_navigationmeshes.html</link>
</tutorials>
<methods>
- <method name="bake" deprecated="">
+ <method name="bake" deprecated="This method is deprecated due to core threading changes. To upgrade existing code, first create a [NavigationMeshSourceGeometryData3D] resource. Use this resource with [method parse_source_geometry_data] to parse the [SceneTree] for nodes that should contribute to the navigation mesh baking. The [SceneTree] parsing needs to happen on the main thread. After the parsing is finished use the resource with [method bake_from_source_geometry_data] to bake a navigation mesh.">
<return type="void" />
<param index="0" name="navigation_mesh" type="NavigationMesh" />
<param index="1" name="root_node" type="Node" />
<description>
- The bake function is deprecated due to core threading changes. To upgrade existing code, first create a [NavigationMeshSourceGeometryData3D] resource. Use this resource with [method parse_source_geometry_data] to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with [method bake_from_source_geometry_data] to bake a navigation mesh.
+ Bakes the [param navigation_mesh] with source geometry collected starting from the [param root_node].
</description>
</method>
<method name="bake_from_source_geometry_data">
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index 95a803b6e5..e88792cade 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -886,13 +886,12 @@
Queries a path in a given navigation map. Start and target position and other parameters are defined through [NavigationPathQueryParameters3D]. Updates the provided [NavigationPathQueryResult3D] result object with the path among other results requested by the query.
</description>
</method>
- <method name="region_bake_navigation_mesh" deprecated="This method is deprecated due to core threading changes.">
+ <method name="region_bake_navigation_mesh" deprecated="This method is deprecated due to core threading changes. To upgrade existing code, first create a [NavigationMeshSourceGeometryData3D] resource. Use this resource with [method parse_source_geometry_data] to parse the [SceneTree] for nodes that should contribute to the navigation mesh baking. The [SceneTree] parsing needs to happen on the main thread. After the parsing is finished use the resource with [method bake_from_source_geometry_data] to bake a navigation mesh.">
<return type="void" />
<param index="0" name="navigation_mesh" type="NavigationMesh" />
<param index="1" name="root_node" type="Node" />
<description>
Bakes the [param navigation_mesh] with bake source geometry collected starting from the [param root_node].
- [i]Deprecated.[/i] To upgrade existing code, first create a [NavigationMeshSourceGeometryData3D] resource. Use this resource with [method parse_source_geometry_data] to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with [method bake_from_source_geometry_data] to bake a navigation mesh.
</description>
</method>
<method name="region_create">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index a46bb593d4..29e144e4b9 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -37,13 +37,9 @@
</description>
</method>
<method name="_get_configuration_warnings" qualifiers="virtual const">
- <return type="Array" />
+ <return type="PackedStringArray" />
<description>
The elements in the array returned from this method are displayed as warnings in the Scene dock if the script that overrides it is a [code]tool[/code] script.
- Each array element must either be a [String] or a [Dictionary].
- A dictionary element must contain a key [code]message[/code] of type [String] which is shown in the user interface.
- The dictionary may optionally contain a key [code]property[/code] of type [NodePath], which also shows this warning in the inspector on the corresponding property.
- If a string is found in the returned array, it is converted to an equivalent dictionary with the [code]message[/code] field set.
Returning an empty array produces no warnings.
Call [method update_configuration_warnings] when the warnings need to be updated for this node.
[codeblock]
@@ -1068,8 +1064,7 @@
Notification received when the node is about to exit a [SceneTree]. See [method _exit_tree].
This notification is received [i]after[/i] the related [signal tree_exiting] signal.
</constant>
- <constant name="NOTIFICATION_MOVED_IN_PARENT" value="12" deprecated="Use [constant NOTIFICATION_CHILD_ORDER_CHANGED] instead.">
- This notification is no longer emitted.
+ <constant name="NOTIFICATION_MOVED_IN_PARENT" value="12" deprecated="This notification is no longer sent by the engine. Use [constant NOTIFICATION_CHILD_ORDER_CHANGED] instead.">
</constant>
<constant name="NOTIFICATION_READY" value="13">
Notification received when the node is ready. See [method _ready].
diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml
index 3e2d939e10..d0c3e64b7d 100644
--- a/doc/classes/ParticleProcessMaterial.xml
+++ b/doc/classes/ParticleProcessMaterial.xml
@@ -310,7 +310,7 @@
[b]Note:[/b] Animated velocities will not be affected by damping, use [member velocity_limit_curve] instead.
</member>
<member name="scale_curve" type="Texture2D" setter="set_param_texture" getter="get_param_texture">
- Each particle's scale will vary along this [CurveTexture]. If a [CurveXYZTexture] is supplied instead, the scale will be separated per-axis.
+ Each particle's scale will vary along this [CurveTexture] over its lifetime. If a [CurveXYZTexture] is supplied instead, the scale will be separated per-axis.
</member>
<member name="scale_max" type="float" setter="set_param_max" getter="get_param_max" default="1.0">
Maximum initial scale applied to each particle.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 2df34b0488..d8f8b73ca4 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2286,12 +2286,12 @@
[b]Note:[/b] This property is only read when the project starts. To change the 2D shadow atlas size at runtime, use [method RenderingServer.canvas_set_shadow_texture_size] instead.
</member>
<member name="rendering/2d/snap/snap_2d_transforms_to_pixel" type="bool" setter="" getter="" default="false">
- If [code]true[/code], [CanvasItem] nodes will internally snap to full pixels. Their position can still be sub-pixel, but the decimals will not have effect. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled.
+ If [code]true[/code], [CanvasItem] nodes will internally snap to full pixels. Useful for low-resolution pixel art games. Their position can still be sub-pixel, but the decimals will not have effect. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled.
[b]Note:[/b] This property is only read when the project starts. To toggle 2D transform snapping at runtime, use [method RenderingServer.viewport_set_snap_2d_transforms_to_pixel] on the root [Viewport] instead.
[b]Note:[/b] [Control] nodes are snapped to the nearest pixel by default. This is controlled by [member gui/common/snap_controls_to_pixels].
</member>
<member name="rendering/2d/snap/snap_2d_vertices_to_pixel" type="bool" setter="" getter="" default="false">
- If [code]true[/code], vertices of [CanvasItem] nodes will snap to full pixels. Only affects the final vertex positions, not the transforms. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled.
+ If [code]true[/code], vertices of [CanvasItem] nodes will snap to full pixels. Useful for low-resolution pixel art games. Only affects the final vertex positions, not the transforms. This can lead to a crisper appearance at the cost of less smooth movement, especially when [Camera2D] smoothing is enabled.
[b]Note:[/b] This property is only read when the project starts. To toggle 2D vertex snapping at runtime, use [method RenderingServer.viewport_set_snap_2d_vertices_to_pixel] on the root [Viewport] instead.
[b]Note:[/b] [Control] nodes are snapped to the nearest pixel by default. This is controlled by [member gui/common/snap_controls_to_pixels].
</member>
@@ -2766,6 +2766,7 @@
</member>
<member name="rendering/textures/canvas_textures/default_texture_filter" type="int" setter="" getter="" default="1">
The default texture filtering mode to use on [CanvasItem]s.
+ [b]Note:[/b] For pixel art aesthetics, see also [member rendering/2d/snap/snap_2d_vertices_to_pixel] and [member rendering/2d/snap/snap_2d_transforms_to_pixel].
</member>
<member name="rendering/textures/canvas_textures/default_texture_repeat" type="int" setter="" getter="" default="0">
The default texture repeating mode to use on [CanvasItem]s.
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index 8969d66b35..33cd831175 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/doc/classes/RenderingDevice.xml
@@ -19,6 +19,7 @@
<param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
+ This method does nothing.
</description>
</method>
<method name="buffer_clear">
@@ -194,6 +195,7 @@
<param index="0" name="name" type="String" />
<param index="1" name="color" type="Color" />
<description>
+ This method does nothing.
</description>
</method>
<method name="draw_list_begin">
@@ -254,6 +256,7 @@
<param index="9" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" />
<param index="10" name="storage_textures" type="RID[]" default="[]" />
<description>
+ This method does nothing and always returns an empty [PackedInt64Array].
</description>
</method>
<method name="draw_list_bind_index_array">
@@ -348,6 +351,7 @@
<return type="PackedInt64Array" />
<param index="0" name="splits" type="int" />
<description>
+ This method does nothing and always returns an empty [PackedInt64Array].
</description>
</method>
<method name="framebuffer_create">
@@ -438,6 +442,7 @@
<method name="full_barrier" deprecated="Barriers are automatically inserted by RenderingDevice.">
<return type="void" />
<description>
+ This method does nothing.
</description>
</method>
<method name="get_captured_timestamp_cpu_time" qualifiers="const">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 54a11af629..12b88d021a 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1145,6 +1145,7 @@
<param index="7" name="height_density" type="float" />
<param index="8" name="aerial_perspective" type="float" />
<param index="9" name="sky_affect" type="float" />
+ <param index="10" name="fog_mode" type="int" enum="RenderingServer.EnvironmentFogMode" default="0" />
<description>
Configures fog for the specified environment RID. See [code]fog_*[/code] properties in [Environment] for more information.
</description>
@@ -1560,7 +1561,7 @@
<return type="bool" />
<param index="0" name="feature" type="int" enum="RenderingServer.Features" />
<description>
- Always returns false.
+ This method does nothing and always returns [code]false[/code].
</description>
</method>
<method name="has_os_feature" qualifiers="const">
@@ -3390,7 +3391,7 @@
<return type="RID" />
<param index="0" name="base" type="RID" />
<description>
- This method does nothing when called and always returns a null [RID].
+ This method does nothing and always returns an invalid [RID].
</description>
</method>
<method name="texture_proxy_update" deprecated="ProxyTexture was removed in Godot 4.">
@@ -3398,7 +3399,7 @@
<param index="0" name="texture" type="RID" />
<param index="1" name="proxy_to" type="RID" />
<description>
- This method should not be used.
+ This method does nothing.
</description>
</method>
<method name="texture_rd_create">
@@ -4059,8 +4060,7 @@
<constant name="MAX_GLOW_LEVELS" value="7">
The maximum number of glow levels that can be used with the glow post-processing effect.
</constant>
- <constant name="MAX_CURSORS" value="8" deprecated="">
- This constant is unused internally.
+ <constant name="MAX_CURSORS" value="8" deprecated="This constant is not used by the engine.">
</constant>
<constant name="MAX_2D_DIRECTIONAL_LIGHTS" value="8">
The maximum number of directional lights that can be rendered at a given time in 2D.
@@ -4889,6 +4889,12 @@
<constant name="ENV_GLOW_BLEND_MODE_MIX" value="4" enum="EnvironmentGlowBlendMode">
Mixes the glow with the underlying color to avoid increasing brightness as much while still maintaining a glow effect.
</constant>
+ <constant name="ENV_FOG_MODE_EXPONENTIAL" value="0" enum="EnvironmentFogMode">
+ Use a physically-based fog model defined primarily by fog density.
+ </constant>
+ <constant name="ENV_FOG_MODE_DEPTH" value="1" enum="EnvironmentFogMode">
+ Use a simple fog model defined by start and end positions and a custom curve. While not physically accurate, this model can be useful when you need more artistic control.
+ </constant>
<constant name="ENV_TONE_MAPPER_LINEAR" value="0" enum="EnvironmentToneMapper">
Output color as they came in. This can cause bright lighting to look blown out, with noticeable clipping in the output colors.
</constant>
diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml
index ee6546dd14..a7df88f7c6 100644
--- a/doc/classes/Resource.xml
+++ b/doc/classes/Resource.xml
@@ -71,7 +71,7 @@
Returns the [RID] of this resource (or an empty RID). Many resources (such as [Texture2D], [Mesh], and so on) are high-level abstractions of resources stored in a specialized server ([DisplayServer], [RenderingServer], etc.), so this function will return the original [RID].
</description>
</method>
- <method name="setup_local_to_scene" deprecated="This method should only be called internally. Override [method _setup_local_to_scene] instead.">
+ <method name="setup_local_to_scene" deprecated="This method should only be called internally.">
<return type="void" />
<description>
Calls [method _setup_local_to_scene]. If [member resource_local_to_scene] is set to [code]true[/code], this method is automatically called from [method PackedScene.instantiate] by the newly duplicated resource within the scene instance.
diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml
index 360fc9b2a9..2c55f0dcd1 100644
--- a/doc/classes/Skeleton3D.xml
+++ b/doc/classes/Skeleton3D.xml
@@ -44,7 +44,7 @@
Returns the bone index that matches [param name] as its name.
</description>
</method>
- <method name="force_update_all_bone_transforms" deprecated="Do not use this method.">
+ <method name="force_update_all_bone_transforms" deprecated="This method should only be called internally.">
<return type="void" />
<description>
Force updates the bone transforms/poses for all bones in the skeleton.
diff --git a/doc/classes/Sprite2D.xml b/doc/classes/Sprite2D.xml
index f8622d8f38..e5c4c01d65 100644
--- a/doc/classes/Sprite2D.xml
+++ b/doc/classes/Sprite2D.xml
@@ -52,6 +52,7 @@
<members>
<member name="centered" type="bool" setter="set_centered" getter="is_centered" default="true">
If [code]true[/code], texture is centered.
+ [b]Note:[/b] For games with a pixel art aesthetic, textures may appear deformed when centered. This is caused by their position being between pixels. To prevent this, set this property to [code]false[/code], or consider enabling [member ProjectSettings.rendering/2d/snap/snap_2d_vertices_to_pixel] and [member ProjectSettings.rendering/2d/snap/snap_2d_transforms_to_pixel].
</member>
<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false">
If [code]true[/code], texture is flipped horizontally.
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index 6df5a4d18e..b4a88513ec 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -119,7 +119,7 @@
Removes the index array by expanding the vertex array.
</description>
</method>
- <method name="generate_lod" deprecated="Unused internally and fails to preserve normals or UVs. Consider using [method ImporterMesh.generate_lods] instead.">
+ <method name="generate_lod" deprecated="This method is unused internally, as it does not preserve normals or UVs. Consider using [method ImporterMesh.generate_lods] instead.">
<return type="PackedInt32Array" />
<param index="0" name="nd_threshold" type="float" />
<param index="1" name="target_index_count" type="int" default="3" />
diff --git a/doc/classes/Texture3D.xml b/doc/classes/Texture3D.xml
index 6f054a8e0d..2b1bc026c2 100644
--- a/doc/classes/Texture3D.xml
+++ b/doc/classes/Texture3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Texture3D" inherits="Texture" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Base class for 3-dimensionnal textures.
+ Base class for 3-dimensional textures.
</brief_description>
<description>
Base class for [ImageTexture3D] and [CompressedTexture3D]. Cannot be used directly, but contains all the functions necessary for accessing the derived resource types. [Texture3D] is the base class for all 3-dimensional texture types. See also [TextureLayered].
diff --git a/doc/classes/TextureRect.xml b/doc/classes/TextureRect.xml
index 73fbbb783e..d18c4a1eee 100644
--- a/doc/classes/TextureRect.xml
+++ b/doc/classes/TextureRect.xml
@@ -10,9 +10,8 @@
<link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link>
</tutorials>
<members>
- <member name="expand_mode" type="int" setter="set_expand_mode" getter="get_expand_mode" enum="TextureRect.ExpandMode" default="0" experimental="">
+ <member name="expand_mode" type="int" setter="set_expand_mode" getter="get_expand_mode" enum="TextureRect.ExpandMode" default="0" experimental="Using [constant EXPAND_FIT_WIDTH], [constant EXPAND_FIT_WIDTH_PROPORTIONAL], [constant EXPAND_FIT_HEIGHT], or [constant EXPAND_FIT_HEIGHT_PROPORTIONAL] may result in unstable behavior in some [Container] controls. This behavior may be re-evaluated and changed in the future.">
Defines how minimum size is determined based on the texture's size. See [enum ExpandMode] for options.
- [b]Note:[/b] Using [constant EXPAND_FIT_WIDTH], [constant EXPAND_FIT_WIDTH_PROPORTIONAL], [constant EXPAND_FIT_HEIGHT] or [constant EXPAND_FIT_HEIGHT_PROPORTIONAL] may result in unstable behavior in some containers. This functionality is being re-evaluated and will change in the future.
</member>
<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false">
If [code]true[/code], texture is flipped horizontally.
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index e1e16e86a7..1bb23211c9 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -80,6 +80,7 @@
<return type="void" />
<param index="0" name="layer" type="int" default="-1" />
<description>
+ Forces the TileMap and the layer [param layer] to update.
</description>
</method>
<method name="get_cell_alternative_tile" qualifiers="const">
@@ -167,7 +168,7 @@
<return type="RID" />
<param index="0" name="layer" type="int" />
<description>
- Returns the [NavigationServer2D] navigation map [RID] currently assigned to the specified TileMap [param layer].
+ Returns the [RID] of the [NavigationServer2D] navigation map assigned to the specified TileMap layer [param layer].
By default the TileMap uses the default [World2D] navigation map for the first TileMap layer. For each additional TileMap layer a new navigation map is created for the additional layer.
In order to make [NavigationAgent2D] switch between TileMap layer navigation maps use [method NavigationAgent2D.set_navigation_map] with the navigation map received from [method get_layer_navigation_map].
If [param layer] is negative, the layers are accessed from the last one.
@@ -195,11 +196,11 @@
Returns the number of layers in the TileMap.
</description>
</method>
- <method name="get_navigation_map" qualifiers="const" deprecated="">
+ <method name="get_navigation_map" qualifiers="const" deprecated="Use [method get_layer_navigation_map] instead.">
<return type="RID" />
<param index="0" name="layer" type="int" />
<description>
- See [method get_layer_navigation_map].
+ Returns the [RID] of the [NavigationServer2D] navigation map assigned to the specified TileMap layer [param layer].
</description>
</method>
<method name="get_neighbor_cell" qualifiers="const">
@@ -409,7 +410,7 @@
<param index="0" name="layer" type="int" />
<param index="1" name="map" type="RID" />
<description>
- Assigns a [NavigationServer2D] navigation map [RID] to the specified TileMap [param layer].
+ Assigns [param map] as a [NavigationServer2D] navigation map for the specified TileMap layer [param layer].
By default the TileMap uses the default [World2D] navigation map for the first TileMap layer. For each additional TileMap layer a new navigation map is created for the additional layer.
In order to make [NavigationAgent2D] switch between TileMap layer navigation maps use [method NavigationAgent2D.set_navigation_map] with the navigation map received from [method get_layer_navigation_map].
If [param layer] is negative, the layers are accessed from the last one.
@@ -444,12 +445,12 @@
If [param layer] is negative, the layers are accessed from the last one.
</description>
</method>
- <method name="set_navigation_map" deprecated="">
+ <method name="set_navigation_map" deprecated="Use [method set_layer_navigation_map] instead.">
<return type="void" />
<param index="0" name="layer" type="int" />
<param index="1" name="map" type="RID" />
<description>
- See [method set_layer_navigation_map].
+ Assigns [param map] as a [NavigationServer2D] navigation map for the specified TileMap layer [param layer].
</description>
</method>
<method name="set_pattern">
diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml
index b79a8cbdb4..a1173d4628 100644
--- a/doc/classes/TreeItem.xml
+++ b/doc/classes/TreeItem.xml
@@ -574,8 +574,8 @@
<param index="1" name="object" type="Object" />
<param index="2" name="callback" type="StringName" />
<description>
- Sets the given column's custom draw callback to [param callback] method on [param object].
- The [param callback] should accept two arguments: the [TreeItem] that is drawn and its position and size as a [Rect2].
+ Sets the given column's custom draw callback to the [param callback] method on [param object].
+ The method named [param callback] should accept two arguments: the [TreeItem] that is drawn and its position and size as a [Rect2].
</description>
</method>
<method name="set_custom_draw_callback">
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index b265f8ba11..ac16bebecb 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -237,6 +237,12 @@
<param index="0" name="parallel" type="bool" default="true" />
<description>
If [param parallel] is [code]true[/code], the [Tweener]s appended after this method will by default run simultaneously, as opposed to sequentially.
+ [b]Note:[/b] Just like with [method parallel], the tweener added right before this method will also be part of the parallel step.
+ [codeblock]
+ tween.tween_property(self, "position", Vector2(300, 0), 0.5)
+ tween.set_parallel()
+ tween.tween_property(self, "modulate", Color.GREEN, 0.5) # Runs together with the position tweener.
+ [/codeblock]
</description>
</method>
<method name="set_pause_mode">
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 711f6eadf8..930e437aa5 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -192,9 +192,8 @@
<param index="0" name="event" type="InputEvent" />
<param index="1" name="in_local_coords" type="bool" default="false" />
<description>
- Triggers the given [InputEvent] in this [Viewport]. This can be used to pass input events between viewports, or to locally apply inputs that were sent over the network or saved to a file.
+ Triggers the given [param event] in this [Viewport]. This can be used to pass an [InputEvent] between viewports, or to locally apply inputs that were sent over the network or saved to a file.
If [param in_local_coords] is [code]false[/code], the event's position is in the embedder's coordinates and will be converted to viewport coordinates. If [param in_local_coords] is [code]true[/code], the event's position is in viewport coordinates.
- While this method serves a similar purpose as [method Input.parse_input_event], it does not remap the specified [param event] based on project settings like [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse].
Calling this method will propagate calls to child nodes for following methods in the given order:
- [method Node._shortcut_input]
- [method Node._unhandled_key_input]
diff --git a/doc/classes/VisualShaderNodeCubemap.xml b/doc/classes/VisualShaderNodeCubemap.xml
index 8fc98e24cc..4e6cf2120a 100644
--- a/doc/classes/VisualShaderNodeCubemap.xml
+++ b/doc/classes/VisualShaderNodeCubemap.xml
@@ -33,7 +33,7 @@
No hints are added to the uniform declaration.
</constant>
<constant name="TYPE_COLOR" value="1" enum="TextureType">
- Adds [code]hint_albedo[/code] as hint to the uniform declaration for proper sRGB to linear conversion.
+ Adds [code]source_color[/code] as hint to the uniform declaration for proper sRGB to linear conversion.
</constant>
<constant name="TYPE_NORMAL_MAP" value="2" enum="TextureType">
Adds [code]hint_normal[/code] as hint to the uniform declaration, which internally converts the texture for proper usage as normal map.
diff --git a/doc/classes/VisualShaderNodeTexture.xml b/doc/classes/VisualShaderNodeTexture.xml
index 5b38683eba..eda09573ad 100644
--- a/doc/classes/VisualShaderNodeTexture.xml
+++ b/doc/classes/VisualShaderNodeTexture.xml
@@ -51,7 +51,7 @@
No hints are added to the uniform declaration.
</constant>
<constant name="TYPE_COLOR" value="1" enum="TextureType">
- Adds [code]hint_albedo[/code] as hint to the uniform declaration for proper sRGB to linear conversion.
+ Adds [code]source_color[/code] as hint to the uniform declaration for proper sRGB to linear conversion.
</constant>
<constant name="TYPE_NORMAL_MAP" value="2" enum="TextureType">
Adds [code]hint_normal[/code] as hint to the uniform declaration, which internally converts the texture for proper usage as normal map.
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 63caaefe07..97c7ffdd3c 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -360,7 +360,7 @@
<method name="move_to_foreground" deprecated="Use [method Window.grab_focus] instead.">
<return type="void" />
<description>
- Moves the [Window] on top of other windows and focuses it.
+ Causes the window to grab focus, allowing it to receive user input.
</description>
</method>
<method name="popup">
diff --git a/doc/classes/XRFaceModifier3D.xml b/doc/classes/XRFaceModifier3D.xml
new file mode 100644
index 0000000000..7a60e6db34
--- /dev/null
+++ b/doc/classes/XRFaceModifier3D.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="XRFaceModifier3D" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A node for driving standard face meshes from [XRFaceTracker] weights.
+ </brief_description>
+ <description>
+ This node applies weights from a [XRFaceTracker] to a mesh with supporting face blend shapes.
+ The [url=https://docs.vrcft.io/docs/tutorial-avatars/tutorial-avatars-extras/unified-blendshapes]Unified Expressions[/url] blend shapes are supported, as well as ARKit and SRanipal blend shapes.
+ The node attempts to identify blend shapes based on name matching. Blend shapes should match the names listed in the [url=https://docs.vrcft.io/docs/tutorial-avatars/tutorial-avatars-extras/compatibility/overview]Unified Expressions Compatibility[/url] chart.
+ </description>
+ <tutorials>
+ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link>
+ </tutorials>
+ <members>
+ <member name="face_tracker" type="StringName" setter="set_face_tracker" getter="get_face_tracker" default="&amp;&quot;/user/head&quot;">
+ The [XRFaceTracker] path.
+ </member>
+ <member name="target" type="NodePath" setter="set_target" getter="get_target" default="NodePath(&quot;&quot;)">
+ The [NodePath] of the face [MeshInstance3D].
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/XRFaceTracker.xml b/doc/classes/XRFaceTracker.xml
new file mode 100644
index 0000000000..0212cc8fff
--- /dev/null
+++ b/doc/classes/XRFaceTracker.xml
@@ -0,0 +1,469 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="XRFaceTracker" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ A tracked face.
+ </brief_description>
+ <description>
+ An instance of this object represents a tracked face and its corresponding blend shapes. The blend shapes come from the [url=https://docs.vrcft.io/docs/tutorial-avatars/tutorial-avatars-extras/unified-blendshapes]Unified Expressions[/url] standard, and contain extended details and visuals for each blend shape. Additionally the [url=https://docs.vrcft.io/docs/tutorial-avatars/tutorial-avatars-extras/compatibility/overview]Tracking Standard Comparison[/url] page documents the relationship between Unified Expressions and other standards.
+ As face trackers are turned on they are registered with the [XRServer].
+ </description>
+ <tutorials>
+ <link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link>
+ </tutorials>
+ <methods>
+ <method name="get_blend_shape" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="blend_shape" type="int" enum="XRFaceTracker.BlendShapeEntry" />
+ <description>
+ Returns the requested face blend shape weight.
+ </description>
+ </method>
+ <method name="set_blend_shape">
+ <return type="void" />
+ <param index="0" name="blend_shape" type="int" enum="XRFaceTracker.BlendShapeEntry" />
+ <param index="1" name="weight" type="float" />
+ <description>
+ Sets a face blend shape weight.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="blend_shapes" type="PackedFloat32Array" setter="set_blend_shapes" getter="get_blend_shapes" default="PackedFloat32Array()">
+ The array of face blend shape weights with indices corresponding to the [enum BlendShapeEntry] enum.
+ </member>
+ </members>
+ <constants>
+ <constant name="FT_EYE_LOOK_OUT_RIGHT" value="0" enum="BlendShapeEntry">
+ Right eye looks outwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_IN_RIGHT" value="1" enum="BlendShapeEntry">
+ Right eye looks inwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_UP_RIGHT" value="2" enum="BlendShapeEntry">
+ Right eye looks upwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_DOWN_RIGHT" value="3" enum="BlendShapeEntry">
+ Right eye looks downwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_OUT_LEFT" value="4" enum="BlendShapeEntry">
+ Left eye looks outwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_IN_LEFT" value="5" enum="BlendShapeEntry">
+ Left eye looks inwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_UP_LEFT" value="6" enum="BlendShapeEntry">
+ Left eye looks upwards.
+ </constant>
+ <constant name="FT_EYE_LOOK_DOWN_LEFT" value="7" enum="BlendShapeEntry">
+ Left eye looks downwards.
+ </constant>
+ <constant name="FT_EYE_CLOSED_RIGHT" value="8" enum="BlendShapeEntry">
+ Closes the right eyelid.
+ </constant>
+ <constant name="FT_EYE_CLOSED_LEFT" value="9" enum="BlendShapeEntry">
+ Closes the left eyelid.
+ </constant>
+ <constant name="FT_EYE_SQUINT_RIGHT" value="10" enum="BlendShapeEntry">
+ Squeezes the right eye socket muscles.
+ </constant>
+ <constant name="FT_EYE_SQUINT_LEFT" value="11" enum="BlendShapeEntry">
+ Squeezes the left eye socket muscles.
+ </constant>
+ <constant name="FT_EYE_WIDE_RIGHT" value="12" enum="BlendShapeEntry">
+ Right eyelid widens beyond relaxed.
+ </constant>
+ <constant name="FT_EYE_WIDE_LEFT" value="13" enum="BlendShapeEntry">
+ Left eyelid widens beyond relaxed.
+ </constant>
+ <constant name="FT_EYE_DILATION_RIGHT" value="14" enum="BlendShapeEntry">
+ Dilates the right eye pupil.
+ </constant>
+ <constant name="FT_EYE_DILATION_LEFT" value="15" enum="BlendShapeEntry">
+ Dilates the left eye pupil.
+ </constant>
+ <constant name="FT_EYE_CONSTRICT_RIGHT" value="16" enum="BlendShapeEntry">
+ Constricts the right eye pupil.
+ </constant>
+ <constant name="FT_EYE_CONSTRICT_LEFT" value="17" enum="BlendShapeEntry">
+ Constricts the left eye pupil.
+ </constant>
+ <constant name="FT_BROW_PINCH_RIGHT" value="18" enum="BlendShapeEntry">
+ Right eyebrow pinches in.
+ </constant>
+ <constant name="FT_BROW_PINCH_LEFT" value="19" enum="BlendShapeEntry">
+ Left eyebrow pinches in.
+ </constant>
+ <constant name="FT_BROW_LOWERER_RIGHT" value="20" enum="BlendShapeEntry">
+ Outer right eyebrow pulls down.
+ </constant>
+ <constant name="FT_BROW_LOWERER_LEFT" value="21" enum="BlendShapeEntry">
+ Outer left eyebrow pulls down.
+ </constant>
+ <constant name="FT_BROW_INNER_UP_RIGHT" value="22" enum="BlendShapeEntry">
+ Inner right eyebrow pulls up.
+ </constant>
+ <constant name="FT_BROW_INNER_UP_LEFT" value="23" enum="BlendShapeEntry">
+ Inner left eyebrow pulls up.
+ </constant>
+ <constant name="FT_BROW_OUTER_UP_RIGHT" value="24" enum="BlendShapeEntry">
+ Outer right eyebrow pulls up.
+ </constant>
+ <constant name="FT_BROW_OUTER_UP_LEFT" value="25" enum="BlendShapeEntry">
+ Outer left eyebrow pulls up.
+ </constant>
+ <constant name="FT_NOSE_SNEER_RIGHT" value="26" enum="BlendShapeEntry">
+ Right side face sneers.
+ </constant>
+ <constant name="FT_NOSE_SNEER_LEFT" value="27" enum="BlendShapeEntry">
+ Left side face sneers.
+ </constant>
+ <constant name="FT_NASAL_DILATION_RIGHT" value="28" enum="BlendShapeEntry">
+ Right side nose canal dilates.
+ </constant>
+ <constant name="FT_NASAL_DILATION_LEFT" value="29" enum="BlendShapeEntry">
+ Left side nose canal dilates.
+ </constant>
+ <constant name="FT_NASAL_CONSTRICT_RIGHT" value="30" enum="BlendShapeEntry">
+ Right side nose canal constricts.
+ </constant>
+ <constant name="FT_NASAL_CONSTRICT_LEFT" value="31" enum="BlendShapeEntry">
+ Left side nose canal constricts.
+ </constant>
+ <constant name="FT_CHEEK_SQUINT_RIGHT" value="32" enum="BlendShapeEntry">
+ Raises the right side cheek.
+ </constant>
+ <constant name="FT_CHEEK_SQUINT_LEFT" value="33" enum="BlendShapeEntry">
+ Raises the left side cheek.
+ </constant>
+ <constant name="FT_CHEEK_PUFF_RIGHT" value="34" enum="BlendShapeEntry">
+ Puffs the right side cheek.
+ </constant>
+ <constant name="FT_CHEEK_PUFF_LEFT" value="35" enum="BlendShapeEntry">
+ Puffs the left side cheek.
+ </constant>
+ <constant name="FT_CHEEK_SUCK_RIGHT" value="36" enum="BlendShapeEntry">
+ Sucks in the right side cheek.
+ </constant>
+ <constant name="FT_CHEEK_SUCK_LEFT" value="37" enum="BlendShapeEntry">
+ Sucks in the left side cheek.
+ </constant>
+ <constant name="FT_JAW_OPEN" value="38" enum="BlendShapeEntry">
+ Opens jawbone.
+ </constant>
+ <constant name="FT_MOUTH_CLOSED" value="39" enum="BlendShapeEntry">
+ Closes the mouth.
+ </constant>
+ <constant name="FT_JAW_RIGHT" value="40" enum="BlendShapeEntry">
+ Pushes jawbone right.
+ </constant>
+ <constant name="FT_JAW_LEFT" value="41" enum="BlendShapeEntry">
+ Pushes jawbone left.
+ </constant>
+ <constant name="FT_JAW_FORWARD" value="42" enum="BlendShapeEntry">
+ Pushes jawbone forward.
+ </constant>
+ <constant name="FT_JAW_BACKWARD" value="43" enum="BlendShapeEntry">
+ Pushes jawbone backward.
+ </constant>
+ <constant name="FT_JAW_CLENCH" value="44" enum="BlendShapeEntry">
+ Flexes jaw muscles.
+ </constant>
+ <constant name="FT_JAW_MANDIBLE_RAISE" value="45" enum="BlendShapeEntry">
+ Raises the jawbone.
+ </constant>
+ <constant name="FT_LIP_SUCK_UPPER_RIGHT" value="46" enum="BlendShapeEntry">
+ Upper right lip part tucks in the mouth.
+ </constant>
+ <constant name="FT_LIP_SUCK_UPPER_LEFT" value="47" enum="BlendShapeEntry">
+ Upper left lip part tucks in the mouth.
+ </constant>
+ <constant name="FT_LIP_SUCK_LOWER_RIGHT" value="48" enum="BlendShapeEntry">
+ Lower right lip part tucks in the mouth.
+ </constant>
+ <constant name="FT_LIP_SUCK_LOWER_LEFT" value="49" enum="BlendShapeEntry">
+ Lower left lip part tucks in the mouth.
+ </constant>
+ <constant name="FT_LIP_SUCK_CORNER_RIGHT" value="50" enum="BlendShapeEntry">
+ Right lip corner folds into the mouth.
+ </constant>
+ <constant name="FT_LIP_SUCK_CORNER_LEFT" value="51" enum="BlendShapeEntry">
+ Left lip corner folds into the mouth.
+ </constant>
+ <constant name="FT_LIP_FUNNEL_UPPER_RIGHT" value="52" enum="BlendShapeEntry">
+ Upper right lip part pushes into a funnel.
+ </constant>
+ <constant name="FT_LIP_FUNNEL_UPPER_LEFT" value="53" enum="BlendShapeEntry">
+ Upper left lip part pushes into a funnel.
+ </constant>
+ <constant name="FT_LIP_FUNNEL_LOWER_RIGHT" value="54" enum="BlendShapeEntry">
+ Lower right lip part pushes into a funnel.
+ </constant>
+ <constant name="FT_LIP_FUNNEL_LOWER_LEFT" value="55" enum="BlendShapeEntry">
+ Lower left lip part pushes into a funnel.
+ </constant>
+ <constant name="FT_LIP_PUCKER_UPPER_RIGHT" value="56" enum="BlendShapeEntry">
+ Upper right lip part pushes outwards.
+ </constant>
+ <constant name="FT_LIP_PUCKER_UPPER_LEFT" value="57" enum="BlendShapeEntry">
+ Upper left lip part pushes outwards.
+ </constant>
+ <constant name="FT_LIP_PUCKER_LOWER_RIGHT" value="58" enum="BlendShapeEntry">
+ Lower right lip part pushes outwards.
+ </constant>
+ <constant name="FT_LIP_PUCKER_LOWER_LEFT" value="59" enum="BlendShapeEntry">
+ Lower left lip part pushes outwards.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_UP_RIGHT" value="60" enum="BlendShapeEntry">
+ Upper right part of the lip pulls up.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_UP_LEFT" value="61" enum="BlendShapeEntry">
+ Upper left part of the lip pulls up.
+ </constant>
+ <constant name="FT_MOUTH_LOWER_DOWN_RIGHT" value="62" enum="BlendShapeEntry">
+ Lower right part of the lip pulls up.
+ </constant>
+ <constant name="FT_MOUTH_LOWER_DOWN_LEFT" value="63" enum="BlendShapeEntry">
+ Lower left part of the lip pulls up.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_DEEPEN_RIGHT" value="64" enum="BlendShapeEntry">
+ Upper right lip part pushes in the cheek.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_DEEPEN_LEFT" value="65" enum="BlendShapeEntry">
+ Upper left lip part pushes in the cheek.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_RIGHT" value="66" enum="BlendShapeEntry">
+ Moves upper lip right.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_LEFT" value="67" enum="BlendShapeEntry">
+ Moves upper lip left.
+ </constant>
+ <constant name="FT_MOUTH_LOWER_RIGHT" value="68" enum="BlendShapeEntry">
+ Moves lower lip right.
+ </constant>
+ <constant name="FT_MOUTH_LOWER_LEFT" value="69" enum="BlendShapeEntry">
+ Moves lower lip left.
+ </constant>
+ <constant name="FT_MOUTH_CORNER_PULL_RIGHT" value="70" enum="BlendShapeEntry">
+ Right lip corner pulls diagonally up and out.
+ </constant>
+ <constant name="FT_MOUTH_CORNER_PULL_LEFT" value="71" enum="BlendShapeEntry">
+ Left lip corner pulls diagonally up and out.
+ </constant>
+ <constant name="FT_MOUTH_CORNER_SLANT_RIGHT" value="72" enum="BlendShapeEntry">
+ Right corner lip slants up.
+ </constant>
+ <constant name="FT_MOUTH_CORNER_SLANT_LEFT" value="73" enum="BlendShapeEntry">
+ Left corner lip slants up.
+ </constant>
+ <constant name="FT_MOUTH_FROWN_RIGHT" value="74" enum="BlendShapeEntry">
+ Right corner lip pulls down.
+ </constant>
+ <constant name="FT_MOUTH_FROWN_LEFT" value="75" enum="BlendShapeEntry">
+ Left corner lip pulls down.
+ </constant>
+ <constant name="FT_MOUTH_STRETCH_RIGHT" value="76" enum="BlendShapeEntry">
+ Mouth corner lip pulls out and down.
+ </constant>
+ <constant name="FT_MOUTH_STRETCH_LEFT" value="77" enum="BlendShapeEntry">
+ Mouth corner lip pulls out and down.
+ </constant>
+ <constant name="FT_MOUTH_DIMPLE_RIGHT" value="78" enum="BlendShapeEntry">
+ Right lip corner is pushed backwards.
+ </constant>
+ <constant name="FT_MOUTH_DIMPLE_LEFT" value="79" enum="BlendShapeEntry">
+ Left lip corner is pushed backwards.
+ </constant>
+ <constant name="FT_MOUTH_RAISER_UPPER" value="80" enum="BlendShapeEntry">
+ Raises and slightly pushes out the upper mouth.
+ </constant>
+ <constant name="FT_MOUTH_RAISER_LOWER" value="81" enum="BlendShapeEntry">
+ Raises and slightly pushes out the lower mouth.
+ </constant>
+ <constant name="FT_MOUTH_PRESS_RIGHT" value="82" enum="BlendShapeEntry">
+ Right side lips press and flatten together vertically.
+ </constant>
+ <constant name="FT_MOUTH_PRESS_LEFT" value="83" enum="BlendShapeEntry">
+ Left side lips press and flatten together vertically.
+ </constant>
+ <constant name="FT_MOUTH_TIGHTENER_RIGHT" value="84" enum="BlendShapeEntry">
+ Right side lips squeeze together horizontally.
+ </constant>
+ <constant name="FT_MOUTH_TIGHTENER_LEFT" value="85" enum="BlendShapeEntry">
+ Left side lips squeeze together horizontally.
+ </constant>
+ <constant name="FT_TONGUE_OUT" value="86" enum="BlendShapeEntry">
+ Tongue visibly sticks out of the mouth.
+ </constant>
+ <constant name="FT_TONGUE_UP" value="87" enum="BlendShapeEntry">
+ Tongue points upwards.
+ </constant>
+ <constant name="FT_TONGUE_DOWN" value="88" enum="BlendShapeEntry">
+ Tongue points downwards.
+ </constant>
+ <constant name="FT_TONGUE_RIGHT" value="89" enum="BlendShapeEntry">
+ Tongue points right.
+ </constant>
+ <constant name="FT_TONGUE_LEFT" value="90" enum="BlendShapeEntry">
+ Tongue points left.
+ </constant>
+ <constant name="FT_TONGUE_ROLL" value="91" enum="BlendShapeEntry">
+ Sides of the tongue funnel, creating a roll.
+ </constant>
+ <constant name="FT_TONGUE_BLEND_DOWN" value="92" enum="BlendShapeEntry">
+ Tongue arches up then down inside the mouth.
+ </constant>
+ <constant name="FT_TONGUE_CURL_UP" value="93" enum="BlendShapeEntry">
+ Tongue arches down then up inside the mouth.
+ </constant>
+ <constant name="FT_TONGUE_SQUISH" value="94" enum="BlendShapeEntry">
+ Tongue squishes together and thickens.
+ </constant>
+ <constant name="FT_TONGUE_FLAT" value="95" enum="BlendShapeEntry">
+ Tongue flattens and thins out.
+ </constant>
+ <constant name="FT_TONGUE_TWIST_RIGHT" value="96" enum="BlendShapeEntry">
+ Tongue tip rotates clockwise, with the rest following gradually.
+ </constant>
+ <constant name="FT_TONGUE_TWIST_LEFT" value="97" enum="BlendShapeEntry">
+ Tongue tip rotates counter-clockwise, with the rest following gradually.
+ </constant>
+ <constant name="FT_SOFT_PALATE_CLOSE" value="98" enum="BlendShapeEntry">
+ Inner mouth throat closes.
+ </constant>
+ <constant name="FT_THROAT_SWALLOW" value="99" enum="BlendShapeEntry">
+ The Adam's apple visibly swallows.
+ </constant>
+ <constant name="FT_NECK_FLEX_RIGHT" value="100" enum="BlendShapeEntry">
+ Right side neck visibly flexes.
+ </constant>
+ <constant name="FT_NECK_FLEX_LEFT" value="101" enum="BlendShapeEntry">
+ Left side neck visibly flexes.
+ </constant>
+ <constant name="FT_EYE_CLOSED" value="102" enum="BlendShapeEntry">
+ Closes both eye lids.
+ </constant>
+ <constant name="FT_EYE_WIDE" value="103" enum="BlendShapeEntry">
+ Widens both eye lids.
+ </constant>
+ <constant name="FT_EYE_SQUINT" value="104" enum="BlendShapeEntry">
+ Squints both eye lids.
+ </constant>
+ <constant name="FT_EYE_DILATION" value="105" enum="BlendShapeEntry">
+ Dilates both pupils.
+ </constant>
+ <constant name="FT_EYE_CONSTRICT" value="106" enum="BlendShapeEntry">
+ Constricts both pupils.
+ </constant>
+ <constant name="FT_BROW_DOWN_RIGHT" value="107" enum="BlendShapeEntry">
+ Pulls the right eyebrow down and in.
+ </constant>
+ <constant name="FT_BROW_DOWN_LEFT" value="108" enum="BlendShapeEntry">
+ Pulls the left eyebrow down and in.
+ </constant>
+ <constant name="FT_BROW_DOWN" value="109" enum="BlendShapeEntry">
+ Pulls both eyebrows down and in.
+ </constant>
+ <constant name="FT_BROW_UP_RIGHT" value="110" enum="BlendShapeEntry">
+ Right brow appears worried.
+ </constant>
+ <constant name="FT_BROW_UP_LEFT" value="111" enum="BlendShapeEntry">
+ Left brow appears worried.
+ </constant>
+ <constant name="FT_BROW_UP" value="112" enum="BlendShapeEntry">
+ Both brows appear worried.
+ </constant>
+ <constant name="FT_NOSE_SNEER" value="113" enum="BlendShapeEntry">
+ Entire face sneers.
+ </constant>
+ <constant name="FT_NASAL_DILATION" value="114" enum="BlendShapeEntry">
+ Both nose canals dilate.
+ </constant>
+ <constant name="FT_NASAL_CONSTRICT" value="115" enum="BlendShapeEntry">
+ Both nose canals constrict.
+ </constant>
+ <constant name="FT_CHEEK_PUFF" value="116" enum="BlendShapeEntry">
+ Puffs both cheeks.
+ </constant>
+ <constant name="FT_CHEEK_SUCK" value="117" enum="BlendShapeEntry">
+ Sucks in both cheeks.
+ </constant>
+ <constant name="FT_CHEEK_SQUINT" value="118" enum="BlendShapeEntry">
+ Raises both cheeks.
+ </constant>
+ <constant name="FT_LIP_SUCK_UPPER" value="119" enum="BlendShapeEntry">
+ Tucks in the upper lips.
+ </constant>
+ <constant name="FT_LIP_SUCK_LOWER" value="120" enum="BlendShapeEntry">
+ Tucks in the lower lips.
+ </constant>
+ <constant name="FT_LIP_SUCK" value="121" enum="BlendShapeEntry">
+ Tucks in both lips.
+ </constant>
+ <constant name="FT_LIP_FUNNEL_UPPER" value="122" enum="BlendShapeEntry">
+ Funnels in the upper lips.
+ </constant>
+ <constant name="FT_LIP_FUNNEL_LOWER" value="123" enum="BlendShapeEntry">
+ Funnels in the lower lips.
+ </constant>
+ <constant name="FT_LIP_FUNNEL" value="124" enum="BlendShapeEntry">
+ Funnels in both lips.
+ </constant>
+ <constant name="FT_LIP_PUCKER_UPPER" value="125" enum="BlendShapeEntry">
+ Upper lip part pushes outwards.
+ </constant>
+ <constant name="FT_LIP_PUCKER_LOWER" value="126" enum="BlendShapeEntry">
+ Lower lip part pushes outwards.
+ </constant>
+ <constant name="FT_LIP_PUCKER" value="127" enum="BlendShapeEntry">
+ Lips push outwards.
+ </constant>
+ <constant name="FT_MOUTH_UPPER_UP" value="128" enum="BlendShapeEntry">
+ Raises the upper lips.
+ </constant>
+ <constant name="FT_MOUTH_LOWER_DOWN" value="129" enum="BlendShapeEntry">
+ Lowers the lower lips.
+ </constant>
+ <constant name="FT_MOUTH_OPEN" value="130" enum="BlendShapeEntry">
+ Mouth opens, revealing teeth.
+ </constant>
+ <constant name="FT_MOUTH_RIGHT" value="131" enum="BlendShapeEntry">
+ Moves mouth right.
+ </constant>
+ <constant name="FT_MOUTH_LEFT" value="132" enum="BlendShapeEntry">
+ Moves mouth left.
+ </constant>
+ <constant name="FT_MOUTH_SMILE_RIGHT" value="133" enum="BlendShapeEntry">
+ Right side of the mouth smiles.
+ </constant>
+ <constant name="FT_MOUTH_SMILE_LEFT" value="134" enum="BlendShapeEntry">
+ Left side of the mouth smiles.
+ </constant>
+ <constant name="FT_MOUTH_SMILE" value="135" enum="BlendShapeEntry">
+ Mouth expresses a smile.
+ </constant>
+ <constant name="FT_MOUTH_SAD_RIGHT" value="136" enum="BlendShapeEntry">
+ Right side of the mouth expresses sadness.
+ </constant>
+ <constant name="FT_MOUTH_SAD_LEFT" value="137" enum="BlendShapeEntry">
+ Left side of the mouth expresses sadness.
+ </constant>
+ <constant name="FT_MOUTH_SAD" value="138" enum="BlendShapeEntry">
+ Mouth expresses sadness.
+ </constant>
+ <constant name="FT_MOUTH_STRETCH" value="139" enum="BlendShapeEntry">
+ Mouth stretches.
+ </constant>
+ <constant name="FT_MOUTH_DIMPLE" value="140" enum="BlendShapeEntry">
+ Lip corners dimple.
+ </constant>
+ <constant name="FT_MOUTH_TIGHTENER" value="141" enum="BlendShapeEntry">
+ Mouth tightens.
+ </constant>
+ <constant name="FT_MOUTH_PRESS" value="142" enum="BlendShapeEntry">
+ Mouth presses together.
+ </constant>
+ <constant name="FT_MAX" value="143" enum="BlendShapeEntry">
+ Represents the size of the [enum BlendShapeEntry] enum.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml
index 5276e05379..ced641611f 100644
--- a/doc/classes/XRInterface.xml
+++ b/doc/classes/XRInterface.xml
@@ -99,19 +99,19 @@
<method name="is_initialized" qualifiers="const">
<return type="bool" />
<description>
- Is [code]true[/code] if this interface has been initialized.
+ Returns [code]true[/code] if this interface has been initialized.
</description>
</method>
<method name="is_passthrough_enabled" deprecated="Check if [member environment_blend_mode] is [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND], instead.">
<return type="bool" />
<description>
- Is [code]true[/code] if passthrough is enabled.
+ Returns [code]true[/code] if passthrough is enabled.
</description>
</method>
<method name="is_passthrough_supported" deprecated="Check that [constant XRInterface.XR_ENV_BLEND_MODE_ALPHA_BLEND] is supported using [method get_supported_environment_blend_modes], instead.">
<return type="bool" />
<description>
- Is [code]true[/code] if this interface supports passthrough.
+ Returns [code]true[/code] if this interface supports passthrough.
</description>
</method>
<method name="set_environment_blend_mode">
diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml
index 2f106af340..f98c1d66a4 100644
--- a/doc/classes/XRServer.xml
+++ b/doc/classes/XRServer.xml
@@ -10,6 +10,14 @@
<link title="XR documentation index">$DOCS_URL/tutorials/xr/index.html</link>
</tutorials>
<methods>
+ <method name="add_face_tracker">
+ <return type="void" />
+ <param index="0" name="tracker_name" type="StringName" />
+ <param index="1" name="face_tracker" type="XRFaceTracker" />
+ <description>
+ Registers a new [XRFaceTracker] that tracks the blend shapes of a face.
+ </description>
+ </method>
<method name="add_interface">
<return type="void" />
<param index="0" name="interface" type="XRInterface" />
@@ -50,6 +58,19 @@
Finds an interface by its [param name]. For example, if your project uses capabilities of an AR/VR platform, you can find the interface for that platform by name and initialize it.
</description>
</method>
+ <method name="get_face_tracker" qualifiers="const">
+ <return type="XRFaceTracker" />
+ <param index="0" name="tracker_name" type="StringName" />
+ <description>
+ Returns the [XRFaceTracker] with the given tracker name.
+ </description>
+ </method>
+ <method name="get_face_trackers" qualifiers="const">
+ <return type="Dictionary" />
+ <description>
+ Returns a dictionary of the registered face trackers. Each element of the dictionary is a tracker name mapping to the [XRFaceTracker] instance.
+ </description>
+ </method>
<method name="get_hmd_transform">
<return type="Transform3D" />
<description>
@@ -95,6 +116,13 @@
Returns a dictionary of trackers for [param tracker_types].
</description>
</method>
+ <method name="remove_face_tracker">
+ <return type="void" />
+ <param index="0" name="tracker_name" type="StringName" />
+ <description>
+ Removes a registered [XRFaceTracker].
+ </description>
+ </method>
<method name="remove_interface">
<return type="void" />
<param index="0" name="interface" type="XRInterface" />
@@ -123,6 +151,26 @@
</member>
</members>
<signals>
+ <signal name="face_tracker_added">
+ <param index="0" name="tracker_name" type="StringName" />
+ <param index="1" name="face_tracker" type="XRFaceTracker" />
+ <description>
+ Emitted when a new face tracker is added.
+ </description>
+ </signal>
+ <signal name="face_tracker_removed">
+ <param index="0" name="tracker_name" type="StringName" />
+ <description>
+ Emitted when a face tracker is removed.
+ </description>
+ </signal>
+ <signal name="face_tracker_updated">
+ <param index="0" name="tracker_name" type="StringName" />
+ <param index="1" name="face_tracker" type="XRFaceTracker" />
+ <description>
+ Emitted when an existing face tracker is updated.
+ </description>
+ </signal>
<signal name="interface_added">
<param index="0" name="interface_name" type="StringName" />
<description>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index b827f420f5..4e735039f7 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -66,6 +66,7 @@ BASE_STRINGS = [
"This method doesn't need an instance to be called, so it can be called directly using the class name.",
"This method describes a valid operator to use with this type as left-hand operand.",
"This value is an integer composed as a bitmask of the following flags.",
+ "No return value.",
"There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
"There is currently no description for this signal. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
"There is currently no description for this enum. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
@@ -454,7 +455,7 @@ class TypeName:
if self.enum is not None:
return make_enum(self.enum, self.is_bitfield, state)
elif self.type_name == "void":
- return "void"
+ return "|void|"
else:
return make_type(self.type_name, state)
@@ -1454,14 +1455,26 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
def make_type(klass: str, state: State) -> str:
if klass.find("*") != -1: # Pointer, ignore
- return klass
+ return f"``{klass}``"
+
link_type = klass
+ is_array = False
+
if link_type.endswith("[]"): # Typed array, strip [] to link to contained type.
link_type = link_type[:-2]
+ is_array = True
+
if link_type in state.classes:
- return f":ref:`{klass}<class_{link_type}>`"
- print_error(f'{state.current_class}.xml: Unresolved type "{klass}".', state)
- return klass
+ type_rst = f":ref:`{link_type}<class_{link_type}>`"
+ if is_array:
+ type_rst = f":ref:`Array<class_Array>`\\[{type_rst}\\]"
+ return type_rst
+
+ print_error(f'{state.current_class}.xml: Unresolved type "{link_type}".', state)
+ type_rst = f"``{link_type}``"
+ if is_array:
+ type_rst = f":ref:`Array<class_Array>`\\[{type_rst}\\]"
+ return type_rst
def make_enum(t: str, is_bitfield: bool, state: State) -> str:
@@ -1483,7 +1496,7 @@ def make_enum(t: str, is_bitfield: bool, state: State) -> str:
if is_bitfield:
if not state.classes[c].enums[e].is_bitfield:
print_error(f'{state.current_class}.xml: Enum "{t}" is not bitfield.', state)
- return f"|bitfield|\\<:ref:`{e}<enum_{c}_{e}>`\\>"
+ return f"|bitfield|\\[:ref:`{e}<enum_{c}_{e}>`\\]"
else:
return f":ref:`{e}<enum_{c}_{e}>`"
@@ -1513,36 +1526,36 @@ def make_method_signature(
out += f":ref:`{op_name}<class_{class_def.name}_{ref_type}_{sanitize_operator_name(definition.name, state)}"
for parameter in definition.parameters:
out += f"_{parameter.type_name.type_name}"
- out += f">` "
+ out += f">`"
elif ref_type == "method":
ref_type_qualifier = ""
if definition.name.startswith("_"):
ref_type_qualifier = "private_"
- out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type_qualifier}{ref_type}_{definition.name}>` "
+ out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type_qualifier}{ref_type}_{definition.name}>`"
else:
- out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type}_{definition.name}>` "
+ out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type}_{definition.name}>`"
else:
- out += f"**{definition.name}** "
+ out += f"**{definition.name}**"
- out += "**(**"
+ out += "\\ ("
for i, arg in enumerate(definition.parameters):
if i > 0:
out += ", "
else:
- out += " "
+ out += "\\ "
- out += f"{arg.type_name.to_rst(state)} {arg.name}"
+ out += f"{arg.name}\\: {arg.type_name.to_rst(state)}"
if arg.default_value is not None:
- out += f"={arg.default_value}"
+ out += f" = {arg.default_value}"
if qualifiers is not None and "vararg" in qualifiers:
if len(definition.parameters) > 0:
out += ", ..."
else:
- out += " ..."
+ out += "\\ ..."
- out += " **)**"
+ out += "\\ )"
if qualifiers is not None:
# Use substitutions for abbreviations. This is used to display tooltips on hover.
@@ -1629,6 +1642,7 @@ def make_footer() -> str:
)
operator_msg = translate("This method describes a valid operator to use with this type as left-hand operand.")
bitfield_msg = translate("This value is an integer composed as a bitmask of the following flags.")
+ void_msg = translate("No return value.")
return (
f".. |virtual| replace:: :abbr:`virtual ({virtual_msg})`\n"
@@ -1638,6 +1652,7 @@ def make_footer() -> str:
f".. |static| replace:: :abbr:`static ({static_msg})`\n"
f".. |operator| replace:: :abbr:`operator ({operator_msg})`\n"
f".. |bitfield| replace:: :abbr:`BitField ({bitfield_msg})`\n"
+ f".. |void| replace:: :abbr:`void ({void_msg})`\n"
)
diff --git a/drivers/alsamidi/midi_driver_alsamidi.cpp b/drivers/alsamidi/midi_driver_alsamidi.cpp
index 6b35987f70..b87be69cc5 100644
--- a/drivers/alsamidi/midi_driver_alsamidi.cpp
+++ b/drivers/alsamidi/midi_driver_alsamidi.cpp
@@ -82,13 +82,13 @@ size_t MIDIDriverALSAMidi::msg_expected_data(uint8_t status_byte) {
}
void MIDIDriverALSAMidi::InputConnection::parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver,
- uint64_t timestamp) {
+ uint64_t timestamp, int device_index) {
switch (msg_category(byte)) {
case MessageCategory::RealTime:
// Real-Time messages are single byte messages that can
// occur at any point.
// We pass them straight through.
- driver.receive_input_packet(timestamp, &byte, 1);
+ driver.receive_input_packet(device_index, timestamp, &byte, 1);
break;
case MessageCategory::Data:
@@ -100,7 +100,7 @@ void MIDIDriverALSAMidi::InputConnection::parse_byte(uint8_t byte, MIDIDriverALS
// Forward a complete message and reset relevant state.
if (received_data == expected_data) {
- driver.receive_input_packet(timestamp, buffer, received_data + 1);
+ driver.receive_input_packet(device_index, timestamp, buffer, received_data + 1);
received_data = 0;
if (msg_category(buffer[0]) != MessageCategory::Voice) {
@@ -130,13 +130,13 @@ void MIDIDriverALSAMidi::InputConnection::parse_byte(uint8_t byte, MIDIDriverALS
expected_data = msg_expected_data(byte);
skipping_sys_ex = false;
if (expected_data == 0) {
- driver.receive_input_packet(timestamp, &byte, 1);
+ driver.receive_input_packet(device_index, timestamp, &byte, 1);
}
break;
}
}
-int MIDIDriverALSAMidi::InputConnection::read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp) {
+int MIDIDriverALSAMidi::InputConnection::read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp, int device_index) {
int ret;
do {
uint8_t byte = 0;
@@ -147,7 +147,7 @@ int MIDIDriverALSAMidi::InputConnection::read_in(MIDIDriverALSAMidi &driver, uin
ERR_PRINT("snd_rawmidi_read error: " + String(snd_strerror(ret)));
}
} else {
- parse_byte(byte, driver, timestamp);
+ parse_byte(byte, driver, timestamp, device_index);
}
} while (ret > 0);
@@ -165,7 +165,7 @@ void MIDIDriverALSAMidi::thread_func(void *p_udata) {
size_t connection_count = md->connected_inputs.size();
for (size_t i = 0; i < connection_count; i++) {
- connections[i].read_in(*md, timestamp);
+ connections[i].read_in(*md, timestamp, (int)i);
}
md->unlock();
diff --git a/drivers/alsamidi/midi_driver_alsamidi.h b/drivers/alsamidi/midi_driver_alsamidi.h
index 80cc96310f..95ded3b1c9 100644
--- a/drivers/alsamidi/midi_driver_alsamidi.h
+++ b/drivers/alsamidi/midi_driver_alsamidi.h
@@ -58,7 +58,7 @@ class MIDIDriverALSAMidi : public MIDIDriver {
rawmidi_ptr{ midi_in } {}
// Read in and parse available data, forwarding any complete messages through the driver.
- int read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp);
+ int read_in(MIDIDriverALSAMidi &driver, uint64_t timestamp, int device_index);
snd_rawmidi_t *rawmidi_ptr = nullptr;
@@ -68,7 +68,7 @@ class MIDIDriverALSAMidi : public MIDIDriver {
size_t expected_data = 0;
size_t received_data = 0;
bool skipping_sys_ex = false;
- void parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver, uint64_t timestamp);
+ void parse_byte(uint8_t byte, MIDIDriverALSAMidi &driver, uint64_t timestamp, int device_index);
};
Vector<InputConnection> connected_inputs;
diff --git a/drivers/coremidi/midi_driver_coremidi.cpp b/drivers/coremidi/midi_driver_coremidi.cpp
index ed991b3d9d..87fc7612f7 100644
--- a/drivers/coremidi/midi_driver_coremidi.cpp
+++ b/drivers/coremidi/midi_driver_coremidi.cpp
@@ -39,8 +39,9 @@
void MIDIDriverCoreMidi::read(const MIDIPacketList *packet_list, void *read_proc_ref_con, void *src_conn_ref_con) {
MIDIPacket *packet = const_cast<MIDIPacket *>(packet_list->packet);
+ int *device_index = static_cast<int *>(src_conn_ref_con);
for (UInt32 i = 0; i < packet_list->numPackets; i++) {
- receive_input_packet(packet->timeStamp, packet->data, packet->length);
+ receive_input_packet(*device_index, packet->timeStamp, packet->data, packet->length);
packet = MIDIPacketNext(packet);
}
}
@@ -64,7 +65,7 @@ Error MIDIDriverCoreMidi::open() {
for (int i = 0; i < sources; i++) {
MIDIEndpointRef source = MIDIGetSource(i);
if (source) {
- MIDIPortConnectSource(port_in, source, (void *)this);
+ MIDIPortConnectSource(port_in, source, static_cast<void *>(&i));
connected_sources.insert(i, source);
}
}
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 6cca1fef7e..efd554eac9 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -1642,8 +1642,12 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da
}
scene_state.ubo.fog_enabled = environment_get_fog_enabled(p_render_data->environment);
+ scene_state.ubo.fog_mode = environment_get_fog_mode(p_render_data->environment);
scene_state.ubo.fog_density = environment_get_fog_density(p_render_data->environment);
scene_state.ubo.fog_height = environment_get_fog_height(p_render_data->environment);
+ scene_state.ubo.fog_depth_curve = environment_get_fog_depth_curve(p_render_data->environment);
+ scene_state.ubo.fog_depth_end = environment_get_fog_depth_end(p_render_data->environment) > 0.0 ? environment_get_fog_depth_end(p_render_data->environment) : scene_state.ubo.z_far;
+ scene_state.ubo.fog_depth_begin = MIN(environment_get_fog_depth_begin(p_render_data->environment), scene_state.ubo.fog_depth_end - 0.001);
scene_state.ubo.fog_height_density = environment_get_fog_height_density(p_render_data->environment);
scene_state.ubo.fog_aerial_perspective = environment_get_fog_aerial_perspective(p_render_data->environment);
@@ -2598,6 +2602,10 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
if (render_data.environment.is_null() || (render_data.environment.is_valid() && !environment_get_fog_enabled(render_data.environment))) {
spec_constant_base_flags |= SceneShaderGLES3::DISABLE_FOG;
}
+
+ if (render_data.environment.is_valid() && environment_get_fog_mode(render_data.environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) {
+ spec_constant_base_flags |= SceneShaderGLES3::USE_DEPTH_FOG;
+ }
}
// Render Opaque Objects.
RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe);
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 7880591dcb..ed59aba266 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -398,15 +398,21 @@ private:
float IBL_exposure_normalization;
uint32_t fog_enabled;
+ uint32_t fog_mode;
float fog_density;
float fog_height;
+
float fog_height_density;
+ float fog_depth_curve;
+ float pad;
+ float fog_depth_begin;
float fog_light_color[3];
+ float fog_depth_end;
+
float fog_sun_scatter;
float shadow_bias;
- float pad;
uint32_t camera_visible_layers;
bool pancake_shadows;
};
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index ffdac85c1e..667cbb3d90 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -13,6 +13,7 @@ DISABLE_LIGHT_DIRECTIONAL = false
DISABLE_LIGHT_OMNI = false
DISABLE_LIGHT_SPOT = false
DISABLE_FOG = false
+USE_DEPTH_FOG = false
USE_RADIANCE_MAP = true
USE_LIGHTMAP = false
USE_SH_LIGHTMAP = false
@@ -181,15 +182,21 @@ layout(std140) uniform SceneData { // ubo:2
float IBL_exposure_normalization;
bool fog_enabled;
+ uint fog_mode;
float fog_density;
float fog_height;
float fog_height_density;
+ float fog_depth_curve;
+ float pad;
+ float fog_depth_begin;
+
vec3 fog_light_color;
+ float fog_depth_end;
+
float fog_sun_scatter;
float shadow_bias;
- float pad;
uint camera_visible_layers;
bool pancake_shadows;
}
@@ -666,15 +673,21 @@ layout(std140) uniform SceneData { // ubo:2
float IBL_exposure_normalization;
bool fog_enabled;
+ uint fog_mode;
float fog_density;
float fog_height;
float fog_height_density;
+ float fog_depth_curve;
+ float pad;
+ float fog_depth_begin;
+
vec3 fog_light_color;
+ float fog_depth_end;
+
float fog_sun_scatter;
float shadow_bias;
- float pad;
uint camera_visible_layers;
bool pancake_shadows;
}
@@ -1250,7 +1263,14 @@ vec4 fog_process(vec3 vertex) {
}
#endif // !DISABLE_LIGHT_DIRECTIONAL
- float fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data.fog_density));
+ float fog_amount = 0.0;
+
+#ifdef USE_DEPTH_FOG
+ float fog_z = smoothstep(scene_data.fog_depth_begin, scene_data.fog_depth_end, length(vertex));
+ fog_amount = pow(fog_z, scene_data.fog_depth_curve) * scene_data.fog_density;
+#else
+ fog_amount = 1 - exp(min(0.0, -length(vertex) * scene_data.fog_density));
+#endif // USE_DEPTH_FOG
if (abs(scene_data.fog_height_density) >= 0.0001) {
float y = (scene_data.inv_view_matrix * vec4(vertex, 1.0)).y;
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 6c4bef10d5..8ab66e2bc6 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -1962,7 +1962,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr();
- if (multimesh->custom_aabb != AABB()) {
+ if (multimesh->custom_aabb == AABB()) {
_multimesh_re_create_aabb(multimesh, data, multimesh->instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
@@ -2113,7 +2113,7 @@ void MeshStorage::_update_dirty_multimeshes() {
if (multimesh->aabb_dirty && multimesh->mesh.is_valid()) {
multimesh->aabb_dirty = false;
- if (multimesh->custom_aabb != AABB()) {
+ if (multimesh->custom_aabb == AABB()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
diff --git a/drivers/winmidi/midi_driver_winmidi.cpp b/drivers/winmidi/midi_driver_winmidi.cpp
index cdbab489c4..07f0226c5d 100644
--- a/drivers/winmidi/midi_driver_winmidi.cpp
+++ b/drivers/winmidi/midi_driver_winmidi.cpp
@@ -36,7 +36,7 @@
void MIDIDriverWinMidi::read(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
if (wMsg == MIM_DATA) {
- receive_input_packet((uint64_t)dwParam2, (uint8_t *)&dwParam1, 3);
+ receive_input_packet((int)dwInstance, (uint64_t)dwParam2, (uint8_t *)&dwParam1, 3);
}
}
@@ -44,7 +44,7 @@ Error MIDIDriverWinMidi::open() {
for (UINT i = 0; i < midiInGetNumDevs(); i++) {
HMIDIIN midi_in;
- MMRESULT res = midiInOpen(&midi_in, i, (DWORD_PTR)read, (DWORD_PTR)this, CALLBACK_FUNCTION);
+ MMRESULT res = midiInOpen(&midi_in, i, (DWORD_PTR)read, (DWORD_PTR)i, CALLBACK_FUNCTION);
if (res == MMSYSERR_NOERROR) {
midiInStart(midi_in);
connected_sources.insert(i, midi_in);
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index e1ce08b83e..fd06bf0533 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -213,7 +213,9 @@ void AnimationBezierTrackEdit::_draw_line_clipped(const Vector2 &p_from, const V
void AnimationBezierTrackEdit::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
+ }
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -834,31 +836,42 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (p_event->is_pressed()) {
- if (ED_GET_SHORTCUT("animation_editor/duplicate_selected_keys")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/duplicate_selected_keys", p_event)) {
if (!read_only) {
duplicate_selected_keys(-1.0);
}
accept_event();
}
- if (ED_GET_SHORTCUT("animation_editor/copy_selected_keys")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/cut_selected_keys", p_event)) {
if (!read_only) {
- copy_selected_keys();
+ copy_selected_keys(true);
}
accept_event();
}
-
- if (ED_GET_SHORTCUT("animation_editor/paste_keys")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/copy_selected_keys", p_event)) {
+ if (!read_only) {
+ copy_selected_keys(false);
+ }
+ accept_event();
+ }
+ if (ED_IS_SHORTCUT("animation_editor/paste_keys", p_event)) {
if (!read_only) {
paste_keys(-1.0);
}
accept_event();
}
+ if (ED_IS_SHORTCUT("animation_editor/delete_selection", p_event)) {
+ if (!read_only) {
+ delete_selection();
+ }
+ accept_event();
+ }
}
Ref<InputEventKey> key_press = p_event;
if (key_press.is_valid() && key_press->is_pressed()) {
- if (ED_GET_SHORTCUT("animation_bezier_editor/focus")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_bezier_editor/focus", p_event)) {
SelectionSet focused_keys;
if (selection.is_empty()) {
for (int i = 0; i < edit_points.size(); ++i) {
@@ -927,7 +940,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
queue_redraw();
accept_event();
return;
- } else if (ED_GET_SHORTCUT("animation_bezier_editor/select_all_keys")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("animation_bezier_editor/select_all_keys", p_event)) {
for (int i = 0; i < edit_points.size(); ++i) {
selection.insert(IntPair(edit_points[i].track, edit_points[i].key));
}
@@ -935,7 +948,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
queue_redraw();
accept_event();
return;
- } else if (ED_GET_SHORTCUT("animation_bezier_editor/deselect_all_keys")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("animation_bezier_editor/deselect_all_keys", p_event)) {
selection.clear();
queue_redraw();
@@ -959,6 +972,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (selected || selection.size()) {
menu->add_separator();
menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE);
+ menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCut")), TTR("Cut Selected Key(s)"), MENU_KEY_CUT);
menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCopy")), TTR("Copy Selected Key(s)"), MENU_KEY_COPY);
}
@@ -1246,7 +1260,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (!read_only) {
- if (moving_selection) {
+ if (moving_selection && (abs(moving_selection_offset.x) > 0 || abs(moving_selection_offset.y) > 0)) {
//combit it
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
@@ -1503,7 +1517,7 @@ bool AnimationBezierTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p
}
set_animation_and_track(animation, pair.first, read_only);
- if (p_deselectable || !selection.has(pair)) {
+ if (!selection.has(pair)) {
selection.clear();
selection.insert(pair);
}
@@ -1592,8 +1606,11 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) {
case MENU_KEY_DELETE: {
delete_selection();
} break;
+ case MENU_KEY_CUT: {
+ copy_selected_keys(true);
+ } break;
case MENU_KEY_COPY: {
- copy_selected_keys();
+ copy_selected_keys(false);
} break;
case MENU_KEY_PASTE: {
paste_keys(time);
@@ -1673,7 +1690,7 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs) {
undo_redo->commit_action();
}
-void AnimationBezierTrackEdit::copy_selected_keys() {
+void AnimationBezierTrackEdit::copy_selected_keys(bool p_cut) {
if (selection.is_empty()) {
return;
}
@@ -1696,6 +1713,25 @@ void AnimationBezierTrackEdit::copy_selected_keys() {
keys.insert(sk, ki);
}
editor->_set_key_clipboard(selected_track, top_time, keys);
+
+ if (p_cut) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Animation Cut Keys"), UndoRedo::MERGE_DISABLE, animation.ptr());
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+ for (RBMap<AnimationTrackEditor::SelectedKey, AnimationTrackEditor::KeyInfo>::Element *E = keys.back(); E; E = E->prev()) {
+ int track_idx = E->key().track;
+ int key_idx = E->key().key;
+ float time = E->value().pos;
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track_idx, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track_idx, time, animation->track_get_key_value(track_idx, key_idx), animation->track_get_key_transition(track_idx, key_idx));
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, track_idx, time);
+ }
+ for (RBMap<AnimationTrackEditor::SelectedKey, AnimationTrackEditor::KeyInfo>::Element *E = keys.back(); E; E = E->prev()) {
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos);
+ }
+ undo_redo->commit_action();
+ }
}
void AnimationBezierTrackEdit::paste_keys(real_t p_ofs) {
diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h
index 109ba0d780..ec2b52221e 100644
--- a/editor/animation_bezier_editor.h
+++ b/editor/animation_bezier_editor.h
@@ -42,6 +42,7 @@ class AnimationBezierTrackEdit : public Control {
enum {
MENU_KEY_INSERT,
MENU_KEY_DUPLICATE,
+ MENU_KEY_CUT,
MENU_KEY_COPY,
MENU_KEY_PASTE,
MENU_KEY_DELETE,
@@ -212,7 +213,7 @@ public:
void update_play_position();
void duplicate_selected_keys(real_t p_ofs);
- void copy_selected_keys();
+ void copy_selected_keys(bool p_cut);
void paste_keys(real_t p_ofs);
void delete_selection();
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index cbfd233acc..1652c0b1f1 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1395,25 +1395,26 @@ void AnimationTimelineEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
- panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
add_track->set_icon(get_editor_theme_icon(SNAME("Add")));
loop->set_icon(get_editor_theme_icon(SNAME("Loop")));
time_icon->set_texture(get_editor_theme_icon(SNAME("Time")));
add_track->get_popup()->clear();
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyValue")), TTR("Property Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyXPosition")), TTR("3D Position Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyXRotation")), TTR("3D Rotation Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyXScale")), TTR("3D Scale Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyBlendShape")), TTR("Blend Shape Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyCall")), TTR("Call Method Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyBezier")), TTR("Bezier Curve Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyAudio")), TTR("Audio Playback Track"));
- add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyAnimation")), TTR("Animation Playback Track"));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyValue")), TTR("Property Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyXPosition")), TTR("3D Position Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyXRotation")), TTR("3D Rotation Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyXScale")), TTR("3D Scale Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyBlendShape")), TTR("Blend Shape Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyCall")), TTR("Call Method Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyBezier")), TTR("Bezier Curve Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyAudio")), TTR("Audio Playback Track..."));
+ add_track->get_popup()->add_icon_item(get_editor_theme_icon(SNAME("KeyAnimation")), TTR("Animation Playback Track..."));
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
+ }
} break;
case NOTIFICATION_RESIZED: {
@@ -2685,27 +2686,33 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
if (p_event->is_pressed()) {
- if (ED_GET_SHORTCUT("animation_editor/duplicate_selected_keys")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/duplicate_selected_keys", p_event)) {
if (!read_only) {
emit_signal(SNAME("duplicate_request"), -1.0);
}
accept_event();
}
- if (ED_GET_SHORTCUT("animation_editor/copy_selected_keys")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/cut_selected_keys", p_event)) {
+ if (!read_only) {
+ emit_signal(SNAME("cut_request"));
+ }
+ accept_event();
+ }
+ if (ED_IS_SHORTCUT("animation_editor/copy_selected_keys", p_event)) {
if (!read_only) {
emit_signal(SNAME("copy_request"));
}
accept_event();
}
- if (ED_GET_SHORTCUT("animation_editor/paste_keys")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/paste_keys", p_event)) {
if (!read_only) {
emit_signal(SNAME("paste_request"), -1.0);
}
accept_event();
}
- if (ED_GET_SHORTCUT("animation_editor/delete_selection")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("animation_editor/delete_selection", p_event)) {
if (!read_only) {
emit_signal(SNAME("delete_request"));
}
@@ -2847,10 +2854,11 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
bool selected = _try_select_at_ui_pos(pos, mb->is_command_or_control_pressed() || mb->is_shift_pressed(), false);
menu->clear();
- menu->add_icon_item(get_editor_theme_icon(SNAME("Key")), TTR("Insert Key"), MENU_KEY_INSERT);
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Key")), TTR("Insert Key..."), MENU_KEY_INSERT);
if (selected || editor->is_selection_active()) {
menu->add_separator();
menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Duplicate Key(s)"), MENU_KEY_DUPLICATE);
+ menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCut")), TTR("Cut Key(s)"), MENU_KEY_CUT);
menu->add_icon_item(get_editor_theme_icon(SNAME("ActionCopy")), TTR("Copy Key(s)"), MENU_KEY_COPY);
}
if (editor->is_key_clipboard_active()) {
@@ -3176,10 +3184,12 @@ void AnimationTrackEdit::_menu_selected(int p_index) {
case MENU_KEY_DUPLICATE: {
emit_signal(SNAME("duplicate_request"), insert_at_pos);
} break;
+ case MENU_KEY_CUT: {
+ emit_signal(SNAME("cut_request"));
+ } break;
case MENU_KEY_COPY: {
emit_signal(SNAME("copy_request"));
} break;
-
case MENU_KEY_PASTE: {
emit_signal(SNAME("paste_request"), insert_at_pos);
} break;
@@ -3258,6 +3268,7 @@ void AnimationTrackEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("duplicate_request", PropertyInfo(Variant::FLOAT, "offset")));
ADD_SIGNAL(MethodInfo("create_reset_request"));
ADD_SIGNAL(MethodInfo("copy_request"));
+ ADD_SIGNAL(MethodInfo("cut_request"));
ADD_SIGNAL(MethodInfo("paste_request", PropertyInfo(Variant::FLOAT, "offset")));
ADD_SIGNAL(MethodInfo("delete_request"));
}
@@ -4610,6 +4621,7 @@ void AnimationTrackEditor::_update_tracks() {
track_edit->connect("move_selection_cancel", callable_mp(this, &AnimationTrackEditor::_move_selection_cancel));
track_edit->connect("duplicate_request", callable_mp(this, &AnimationTrackEditor::_anim_duplicate_keys).bind(i), CONNECT_DEFERRED);
+ track_edit->connect("cut_request", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_CUT_KEYS), CONNECT_DEFERRED);
track_edit->connect("copy_request", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_COPY_KEYS), CONNECT_DEFERRED);
track_edit->connect("paste_request", callable_mp(this, &AnimationTrackEditor::_anim_paste_keys).bind(i), CONNECT_DEFERRED);
track_edit->connect("create_reset_request", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_ADD_RESET_KEY), CONNECT_DEFERRED);
@@ -4730,8 +4742,11 @@ MenuButton *AnimationTrackEditor::get_edit_menu() {
void AnimationTrackEditor::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
- } break;
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ break;
+ }
+ [[fallthrough]];
+ }
case NOTIFICATION_ENTER_TREE: {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
@@ -5724,10 +5739,11 @@ void AnimationTrackEditor::_anim_duplicate_keys(float p_ofs, int p_track) {
}
}
-void AnimationTrackEditor::_anim_copy_keys() {
+void AnimationTrackEditor::_anim_copy_keys(bool p_cut) {
if (is_selection_active() && animation.is_valid()) {
int top_track = 0x7FFFFFFF;
float top_time = 1e10;
+
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
const SelectedKey &sk = E->key();
@@ -5743,6 +5759,24 @@ void AnimationTrackEditor::_anim_copy_keys() {
ERR_FAIL_COND(top_track == 0x7FFFFFFF || top_time == 1e10);
_set_key_clipboard(top_track, top_time, selection);
+
+ if (p_cut) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Animation Cut Keys"), UndoRedo::MERGE_DISABLE, animation.ptr());
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+ for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+ int track_idx = E->key().track;
+ int key_idx = E->key().key;
+ float time = E->value().pos;
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track_idx, time);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track_idx, time, animation->track_get_key_value(track_idx, key_idx), animation->track_get_key_transition(track_idx, key_idx));
+ }
+ for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, E->value().pos);
+ }
+ undo_redo->commit_action();
+ }
}
}
@@ -6398,12 +6432,19 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
}
_anim_duplicate_keys(-1.0, -1.0);
} break;
+ case EDIT_CUT_KEYS: {
+ if (bezier_edit->is_visible()) {
+ bezier_edit->copy_selected_keys(true);
+ break;
+ }
+ _anim_copy_keys(true);
+ } break;
case EDIT_COPY_KEYS: {
if (bezier_edit->is_visible()) {
- bezier_edit->copy_selected_keys();
+ bezier_edit->copy_selected_keys(false);
break;
}
- _anim_copy_keys();
+ _anim_copy_keys(false);
} break;
case EDIT_PASTE_KEYS: {
_anim_paste_keys(-1.0, -1.0);
@@ -7144,18 +7185,19 @@ AnimationTrackEditor::AnimationTrackEditor() {
edit->set_flat(false);
edit->set_disabled(true);
edit->set_tooltip_text(TTR("Animation properties."));
- edit->get_popup()->add_item(TTR("Copy Tracks"), EDIT_COPY_TRACKS);
+ edit->get_popup()->add_item(TTR("Copy Tracks..."), EDIT_COPY_TRACKS);
edit->get_popup()->add_item(TTR("Paste Tracks"), EDIT_PASTE_TRACKS);
edit->get_popup()->add_separator();
- edit->get_popup()->add_item(TTR("Scale Selection"), EDIT_SCALE_SELECTION);
- edit->get_popup()->add_item(TTR("Scale From Cursor"), EDIT_SCALE_FROM_CURSOR);
+ edit->get_popup()->add_item(TTR("Scale Selection..."), EDIT_SCALE_SELECTION);
+ edit->get_popup()->add_item(TTR("Scale From Cursor..."), EDIT_SCALE_FROM_CURSOR);
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/set_start_offset", TTR("Set Start Offset (Audio)"), KeyModifierMask::CMD_OR_CTRL | Key::BRACKETLEFT), EDIT_SET_START_OFFSET);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/set_end_offset", TTR("Set End Offset (Audio)"), KeyModifierMask::CMD_OR_CTRL | Key::BRACKETRIGHT), EDIT_SET_END_OFFSET);
edit->get_popup()->add_separator();
- edit->get_popup()->add_item(TTR("Make Easing Selection"), EDIT_EASE_SELECTION);
+ edit->get_popup()->add_item(TTR("Make Easing Selection..."), EDIT_EASE_SELECTION);
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selected_keys", TTR("Duplicate Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::D), EDIT_DUPLICATE_SELECTED_KEYS);
+ edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/cut_selected_keys", TTR("Cut Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::X), EDIT_CUT_KEYS);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/copy_selected_keys", TTR("Copy Selected Keys"), KeyModifierMask::CMD_OR_CTRL | Key::C), EDIT_COPY_KEYS);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/paste_keys", TTR("Paste Keys"), KeyModifierMask::CMD_OR_CTRL | Key::V), EDIT_PASTE_KEYS);
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/add_reset_value", TTR("Add RESET Value(s)")));
@@ -7171,9 +7213,9 @@ AnimationTrackEditor::AnimationTrackEditor() {
edit->get_popup()->add_separator();
edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/apply_reset", TTR("Apply Reset")), EDIT_APPLY_RESET);
edit->get_popup()->add_separator();
- edit->get_popup()->add_item(TTR("Bake Animation"), EDIT_BAKE_ANIMATION);
- edit->get_popup()->add_item(TTR("Optimize Animation (no undo)"), EDIT_OPTIMIZE_ANIMATION);
- edit->get_popup()->add_item(TTR("Clean-Up Animation (no undo)"), EDIT_CLEAN_UP_ANIMATION);
+ edit->get_popup()->add_item(TTR("Bake Animation..."), EDIT_BAKE_ANIMATION);
+ edit->get_popup()->add_item(TTR("Optimize Animation (no undo)..."), EDIT_OPTIMIZE_ANIMATION);
+ edit->get_popup()->add_item(TTR("Clean-Up Animation (no undo)..."), EDIT_CLEAN_UP_ANIMATION);
edit->get_popup()->connect("id_pressed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed));
edit->get_popup()->connect("about_to_popup", callable_mp(this, &AnimationTrackEditor::_edit_menu_about_to_popup));
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 3f8dedb25c..0d6f93fefc 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -231,6 +231,7 @@ class AnimationTrackEdit : public Control {
MENU_LOOP_CLAMP,
MENU_KEY_INSERT,
MENU_KEY_DUPLICATE,
+ MENU_KEY_CUT,
MENU_KEY_COPY,
MENU_KEY_PASTE,
MENU_KEY_ADD_RESET,
@@ -580,7 +581,7 @@ class AnimationTrackEditor : public VBoxContainer {
void _anim_duplicate_keys(float p_ofs, int p_track);
- void _anim_copy_keys();
+ void _anim_copy_keys(bool p_cut);
bool _is_track_compatible(int p_target_track_idx, Variant::Type p_source_value_type, Animation::TrackType p_source_track_type);
@@ -651,6 +652,7 @@ public:
EDIT_COPY_TRACKS,
EDIT_COPY_TRACKS_CONFIRM,
EDIT_PASTE_TRACKS,
+ EDIT_CUT_KEYS,
EDIT_COPY_KEYS,
EDIT_PASTE_KEYS,
EDIT_SCALE_SELECTION,
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 76d0d40110..e7d2f2bda7 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -38,6 +38,7 @@
#include "editor/editor_string_names.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/resources/font.h"
void GotoLineDialog::popup_find_line(CodeEdit *p_edit) {
@@ -92,8 +93,13 @@ GotoLineDialog::GotoLineDialog() {
void FindReplaceBar::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_READY:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorThemeManager::is_generated_theme_outdated()) {
+ break;
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_READY: {
find_prev->set_icon(get_editor_theme_icon(SNAME("MoveUp")));
find_next->set_icon(get_editor_theme_icon(SNAME("MoveDown")));
hide_button->set_texture_normal(get_editor_theme_icon(SNAME("Close")));
@@ -1721,7 +1727,11 @@ void CodeTextEditor::_update_text_editor_theme() {
}
void CodeTextEditor::_on_settings_change() {
- _apply_settings_change();
+ if (EditorThemeManager::is_generated_theme_outdated() ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/completion")) {
+ _apply_settings_change();
+ }
}
void CodeTextEditor::_apply_settings_change() {
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 41a3437f20..9fef200611 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -1342,7 +1342,9 @@ void ConnectionsDock::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- update_tree();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editors")) {
+ update_tree();
+ }
} break;
}
}
diff --git a/editor/debugger/debug_adapter/debug_adapter_server.cpp b/editor/debugger/debug_adapter/debug_adapter_server.cpp
index 9cecc4bb08..a16c494299 100644
--- a/editor/debugger/debug_adapter/debug_adapter_server.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_server.cpp
@@ -62,9 +62,12 @@ void DebugAdapterServer::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("network/debug_adapter")) {
+ break;
+ }
protocol._request_timeout = EDITOR_GET("network/debug_adapter/request_timeout");
protocol._sync_breakpoints = EDITOR_GET("network/debug_adapter/sync_breakpoints");
- int port = (int)_EDITOR_GET("network/debug_adapter/remote_port");
+ int port = _EDITOR_GET("network/debug_adapter/remote_port");
if (port != remote_port) {
stop();
start();
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 6471bd449e..14997c52bb 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -43,6 +43,7 @@
#include "editor/plugins/editor_debugger_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/scene_tree_dock.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/tab_container.h"
#include "scene/resources/packed_scene.h"
@@ -312,7 +313,7 @@ void EditorDebuggerNode::stop(bool p_force) {
void EditorDebuggerNode::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (tabs->get_tab_count() > 1) {
+ if (tabs->get_tab_count() > 1 && EditorThemeManager::is_generated_theme_outdated()) {
add_theme_constant_override("margin_left", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_LEFT));
add_theme_constant_override("margin_right", -EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles))->get_margin(SIDE_RIGHT));
diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp
index 2809b873b1..1f978bf797 100644
--- a/editor/debugger/editor_profiler.cpp
+++ b/editor/debugger/editor_profiler.cpp
@@ -350,7 +350,7 @@ void EditorProfiler::_update_frame() {
category->set_custom_color(0, _get_color_from_signature(m.categories[i].signature));
}
- for (int j = m.categories[i].items.size() - 1; j >= 0; j--) {
+ for (int j = 0; j < m.categories[i].items.size(); j++) {
const Metric::Category::Item &it = m.categories[i].items[j];
if (it.internal == it.total && !display_internal_profiles->is_pressed() && m.categories[i].name == "Script Functions") {
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 9c0d474f49..b4ef0f8c4a 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -36,6 +36,8 @@
#include "core/version.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
+#include "scene/gui/item_list.h"
+#include "scene/resources/style_box.h"
// The metadata key used to store and retrieve the version text to copy to the clipboard.
const String EditorAbout::META_TEXT_TO_COPY = "text_to_copy";
@@ -52,13 +54,23 @@ void EditorAbout::_notification(int p_what) {
_tpl_text->add_theme_constant_override("line_separation", 4 * EDSCALE);
_tpl_text->end_bulk_theme_override();
- _license_text->begin_bulk_theme_override();
- _license_text->add_theme_font_override("normal_font", font);
- _license_text->add_theme_font_size_override("normal_font_size", font_size);
- _license_text->add_theme_constant_override("line_separation", 4 * EDSCALE);
- _license_text->end_bulk_theme_override();
+ license_text_label->begin_bulk_theme_override();
+ license_text_label->add_theme_font_override("normal_font", font);
+ license_text_label->add_theme_font_size_override("normal_font_size", font_size);
+ license_text_label->add_theme_constant_override("line_separation", 4 * EDSCALE);
+ license_text_label->end_bulk_theme_override();
_logo->set_texture(get_editor_theme_icon(SNAME("Logo")));
+
+ Ref<StyleBoxEmpty> empty_stylebox = memnew(StyleBoxEmpty);
+ for (ItemList *il : name_lists) {
+ for (int i = 0; i < il->get_item_count(); i++) {
+ if (il->get_item_metadata(i)) {
+ il->set_item_icon(i, get_theme_icon(SNAME("ExternalLink"), EditorStringName(EditorIcons)));
+ il->set_item_icon_modulate(i, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
+ }
+ }
+ }
} break;
}
}
@@ -73,15 +85,18 @@ void EditorAbout::_version_button_pressed() {
DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
}
-void EditorAbout::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_version_button_pressed"), &EditorAbout::_version_button_pressed);
+void EditorAbout::_item_with_website_selected(int p_id, ItemList *p_il) {
+ const String website = p_il->get_item_metadata(p_id);
+ if (!website.is_empty()) {
+ OS::get_singleton()->shell_open(website);
+ }
}
-TextureRect *EditorAbout::get_logo() const {
- return _logo;
+void EditorAbout::_item_list_resized(ItemList *p_il) {
+ p_il->set_fixed_column_width(p_il->get_size().x / 3.0 - 16 * EDSCALE * 2.5); // Weird. Should be 3.0 and that's it?.
}
-ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column) {
+ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_single_column_flags, const bool p_allow_website) {
ScrollContainer *sc = memnew(ScrollContainer);
sc->set_name(p_name);
sc->set_v_size_flags(Control::SIZE_EXPAND);
@@ -90,8 +105,10 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St
vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
sc->add_child(vbc);
+ Ref<StyleBoxEmpty> empty_stylebox = memnew(StyleBoxEmpty);
+
for (int i = 0; i < p_sections.size(); i++) {
- bool single_column = p_flag_single_column & 1 << i;
+ bool single_column = p_single_column_flags & (1 << i);
const char *const *names_ptr = p_src[i];
if (*names_ptr) {
Label *lbl = memnew(Label);
@@ -105,11 +122,47 @@ ScrollContainer *EditorAbout::_populate_list(const String &p_name, const List<St
il->set_same_column_width(true);
il->set_auto_height(true);
il->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ il->set_focus_mode(Control::FOCUS_NONE);
il->add_theme_constant_override("h_separation", 16 * EDSCALE);
- while (*names_ptr) {
- il->add_item(String::utf8(*names_ptr++), nullptr, false);
+ if (p_allow_website) {
+ il->set_focus_mode(Control::FOCUS_CLICK);
+ il->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
+ il->connect("item_activated", callable_mp(this, &EditorAbout::_item_with_website_selected).bind(il));
+ il->connect("resized", callable_mp(this, &EditorAbout::_item_list_resized).bind(il));
+ il->connect("focus_exited", callable_mp(il, &ItemList::deselect_all));
+
+ il->add_theme_style_override("focus", empty_stylebox);
+ il->add_theme_style_override("selected", empty_stylebox);
+
+ while (*names_ptr) {
+ const String name = String::utf8(*names_ptr++);
+ const String identifier = name.get_slice("<", 0);
+ const String website = name.get_slice_count("<") == 1 ? "" : name.get_slice("<", 1).trim_suffix(">");
+
+ const int name_item_id = il->add_item(identifier, nullptr, false);
+ il->set_item_tooltip_enabled(name_item_id, false);
+
+ if (!website.is_empty()) {
+ il->set_item_selectable(name_item_id, true);
+ il->set_item_metadata(name_item_id, website);
+ il->set_item_tooltip(name_item_id, website + "\n\n" + TTR("Double-click to open in browser."));
+ il->set_item_tooltip_enabled(name_item_id, true);
+ }
+
+ if (!*names_ptr && name.contains(" anonymous ")) {
+ il->set_item_disabled(name_item_id, true);
+ }
+ }
+ } else {
+ while (*names_ptr) {
+ il->add_item(String::utf8(*names_ptr++), nullptr, false);
+ }
}
- il->set_max_columns(il->get_item_count() < 4 || single_column ? 1 : 16);
+ il->set_max_columns(single_column ? 1 : 16);
+
+ name_lists.append(il);
+
vbc->add_child(il);
HSeparator *hs = memnew(HSeparator);
@@ -173,7 +226,7 @@ EditorAbout::EditorAbout() {
tc->set_theme_type_variation("TabContainerOdd");
vbc->add_child(tc);
- // Authors
+ // Authors.
List<String> dev_sections;
dev_sections.push_back(TTR("Project Founders"));
@@ -187,9 +240,9 @@ EditorAbout::EditorAbout() {
AUTHORS_PROJECT_MANAGERS,
AUTHORS_DEVELOPERS,
};
- tc->add_child(_populate_list(TTR("Authors"), dev_sections, dev_src, 1));
+ tc->add_child(_populate_list(TTR("Authors"), dev_sections, dev_src, 0b1)); // First section (Project Founders) is always one column.
- // Donors
+ // Donors.
List<String> donor_sections;
donor_sections.push_back(TTR("Patrons"));
@@ -210,19 +263,19 @@ EditorAbout::EditorAbout() {
DONORS_MEMBERS_PLATINUM,
DONORS_MEMBERS_GOLD,
};
- tc->add_child(_populate_list(TTR("Donors"), donor_sections, donor_src, 3));
+ tc->add_child(_populate_list(TTR("Donors"), donor_sections, donor_src, 0b1, true)); // First section (Patron) is one column.
- // License
+ // License.
- _license_text = memnew(RichTextLabel);
- _license_text->set_threaded(true);
- _license_text->set_name(TTR("License"));
- _license_text->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- _license_text->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- _license_text->set_text(String::utf8(GODOT_LICENSE_TEXT));
- tc->add_child(_license_text);
+ license_text_label = memnew(RichTextLabel);
+ license_text_label->set_threaded(true);
+ license_text_label->set_name(TTR("License"));
+ license_text_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ license_text_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ license_text_label->set_text(String::utf8(GODOT_LICENSE_TEXT));
+ tc->add_child(license_text_label);
- // Thirdparty License
+ // Thirdparty License.
VBoxContainer *license_thirdparty = memnew(VBoxContainer);
license_thirdparty->set_name(TTR("Third-party Licenses"));
diff --git a/editor/editor_about.h b/editor/editor_about.h
index 639dc6cc3f..fc3d6cedce 100644
--- a/editor/editor_about.h
+++ b/editor/editor_about.h
@@ -54,21 +54,21 @@ class EditorAbout : public AcceptDialog {
private:
void _license_tree_selected();
void _version_button_pressed();
- ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], const int p_flag_single_column = 0);
+ void _item_with_website_selected(int p_id, ItemList *p_il);
+ void _item_list_resized(ItemList *p_il);
+ ScrollContainer *_populate_list(const String &p_name, const List<String> &p_sections, const char *const *const p_src[], int p_single_column_flags = 0, bool p_allow_website = false);
LinkButton *version_btn = nullptr;
Tree *_tpl_tree = nullptr;
- RichTextLabel *_license_text = nullptr;
+ RichTextLabel *license_text_label = nullptr;
RichTextLabel *_tpl_text = nullptr;
TextureRect *_logo = nullptr;
+ Vector<ItemList *> name_lists;
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
- TextureRect *get_logo() const;
-
EditorAbout();
~EditorAbout();
};
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 56bada0c32..cc63618f1e 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -2818,8 +2818,13 @@ void EditorHelp::_notification(int p_what) {
_class_desc_resized(false);
} break;
- case NOTIFICATION_READY:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help")) {
+ break;
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_READY: {
_wait_for_thread();
_update_doc();
} break;
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 7ece509731..51df2716d7 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
bool EditorHelpSearch::_all_terms_in_name(const Vector<String> &p_terms, const String &p_name) const {
for (int i = 0; i < p_terms.size(); i++) {
@@ -214,7 +215,12 @@ void EditorHelpSearch::_notification(int p_what) {
connect("confirmed", callable_mp(this, &EditorHelpSearch::_confirmed));
} break;
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED:
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorThemeManager::is_generated_theme_outdated()) {
+ break;
+ }
+ [[fallthrough]];
+ }
case NOTIFICATION_THEME_CHANGED: {
const int icon_width = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
results_tree->add_theme_constant_override("icon_max_width", icon_width);
@@ -476,7 +482,8 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
// If the search term is empty, add any classes which are not script docs or which don't start with
// a double-quotation. This will ensure that only C++ classes and explicitly named classes will
// be added.
- match.name = (term.is_empty() && (!class_doc->is_script_doc || class_doc->name[0] != '\"')) || _match_string(term, class_doc->name) || _match_keywords(term, class_doc->keywords);
+ match.name = (term.is_empty() && (!class_doc->is_script_doc || class_doc->name[0] != '\"')) || _match_string(term, class_doc->name);
+ match.keyword = _match_keywords(term, class_doc->keywords);
}
// Match members only if the term is long enough, to avoid slow performance from building a large tree.
@@ -493,36 +500,56 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
}
if (search_flags & SEARCH_SIGNALS) {
for (int i = 0; i < class_doc->signals.size(); i++) {
- if (_all_terms_in_name(class_doc->signals[i].name) || _all_terms_in_keywords(class_doc->signals[i].keywords)) {
- match.signals.push_back(const_cast<DocData::MethodDoc *>(&class_doc->signals[i]));
+ MemberMatch<DocData::MethodDoc> signal;
+ signal.name = _all_terms_in_name(class_doc->signals[i].name);
+ signal.keyword = _match_keywords_in_all_terms(class_doc->signals[i].keywords);
+ if (signal.name || !signal.keyword.is_empty()) {
+ signal.doc = const_cast<DocData::MethodDoc *>(&class_doc->signals[i]);
+ match.signals.push_back(signal);
}
}
}
if (search_flags & SEARCH_CONSTANTS) {
for (int i = 0; i < class_doc->constants.size(); i++) {
- if (_all_terms_in_name(class_doc->constants[i].name) || _all_terms_in_keywords(class_doc->constants[i].keywords)) {
- match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc->constants[i]));
+ MemberMatch<DocData::ConstantDoc> constant;
+ constant.name = _all_terms_in_name(class_doc->constants[i].name);
+ constant.keyword = _match_keywords_in_all_terms(class_doc->constants[i].keywords);
+ if (constant.name || !constant.keyword.is_empty()) {
+ constant.doc = const_cast<DocData::ConstantDoc *>(&class_doc->constants[i]);
+ match.constants.push_back(constant);
}
}
}
if (search_flags & SEARCH_PROPERTIES) {
for (int i = 0; i < class_doc->properties.size(); i++) {
- if (_all_terms_in_name(class_doc->properties[i].name) || _all_terms_in_keywords(class_doc->properties[i].keywords)) {
- match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc->properties[i]));
+ MemberMatch<DocData::PropertyDoc> property;
+ property.name = _all_terms_in_name(class_doc->properties[i].name);
+ property.keyword = _match_keywords_in_all_terms(class_doc->properties[i].keywords);
+ if (property.name || !property.keyword.is_empty()) {
+ property.doc = const_cast<DocData::PropertyDoc *>(&class_doc->properties[i]);
+ match.properties.push_back(property);
}
}
}
if (search_flags & SEARCH_THEME_ITEMS) {
for (int i = 0; i < class_doc->theme_properties.size(); i++) {
- if (_all_terms_in_name(class_doc->theme_properties[i].name) || _all_terms_in_keywords(class_doc->theme_properties[i].keywords)) {
- match.theme_properties.push_back(const_cast<DocData::ThemeItemDoc *>(&class_doc->theme_properties[i]));
+ MemberMatch<DocData::ThemeItemDoc> theme_property;
+ theme_property.name = _all_terms_in_name(class_doc->theme_properties[i].name);
+ theme_property.keyword = _match_keywords_in_all_terms(class_doc->theme_properties[i].keywords);
+ if (theme_property.name || !theme_property.keyword.is_empty()) {
+ theme_property.doc = const_cast<DocData::ThemeItemDoc *>(&class_doc->theme_properties[i]);
+ match.theme_properties.push_back(theme_property);
}
}
}
if (search_flags & SEARCH_ANNOTATIONS) {
for (int i = 0; i < class_doc->annotations.size(); i++) {
- if (_match_string(term, class_doc->annotations[i].name) || _all_terms_in_keywords(class_doc->annotations[i].keywords)) {
- match.annotations.push_back(const_cast<DocData::MethodDoc *>(&class_doc->annotations[i]));
+ MemberMatch<DocData::MethodDoc> annotation;
+ annotation.name = _all_terms_in_name(class_doc->annotations[i].name);
+ annotation.keyword = _match_keywords_in_all_terms(class_doc->annotations[i].keywords);
+ if (annotation.name || !annotation.keyword.is_empty()) {
+ annotation.doc = const_cast<DocData::MethodDoc *>(&class_doc->annotations[i]);
+ match.annotations.push_back(annotation);
}
}
}
@@ -606,8 +633,8 @@ bool EditorHelpSearch::Runner::_phase_class_items() {
_create_class_hierarchy(match);
}
} else {
- if (match.name) {
- _create_class_item(root_item, match.doc, false);
+ if (match.name || !match.keyword.is_empty()) {
+ _create_class_item(root_item, match.doc, false, match.name ? String() : match.keyword);
}
}
@@ -636,14 +663,14 @@ bool EditorHelpSearch::Runner::_phase_member_items() {
TreeItem *parent_item = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
bool constructor_created = false;
for (int i = 0; i < match.methods.size(); i++) {
- String text = match.methods[i]->name;
+ String text = match.methods[i].doc->name;
if (!constructor_created) {
- if (match.doc->name == match.methods[i]->name) {
+ if (match.doc->name == match.methods[i].doc->name) {
text += " " + TTR("(constructors)");
constructor_created = true;
}
} else {
- if (match.doc->name == match.methods[i]->name) {
+ if (match.doc->name == match.methods[i].doc->name) {
continue;
}
}
@@ -662,8 +689,7 @@ bool EditorHelpSearch::Runner::_phase_member_items() {
_create_theme_property_item(parent_item, match.doc, match.theme_properties[i]);
}
for (int i = 0; i < match.annotations.size(); i++) {
- // Hide the redundant leading @ symbol.
- _create_annotation_item(parent_item, match.doc, match.annotations[i]->name.substr(1), match.annotations[i]);
+ _create_annotation_item(parent_item, match.doc, match.annotations[i]);
}
++iterator_match;
@@ -677,16 +703,20 @@ bool EditorHelpSearch::Runner::_phase_select_match() {
return true;
}
-void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<DocData::MethodDoc *> *r_match_methods) {
+void EditorHelpSearch::Runner::_match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<MemberMatch<DocData::MethodDoc>> *r_match_methods) {
// Constructors, Methods, Operators...
for (int i = 0; i < p_methods.size(); i++) {
String method_name = (search_flags & SEARCH_CASE_SENSITIVE) ? p_methods[i].name : p_methods[i].name.to_lower();
String keywords = (search_flags & SEARCH_CASE_SENSITIVE) ? p_methods[i].keywords : p_methods[i].keywords.to_lower();
- if (_all_terms_in_name(method_name) || _all_terms_in_keywords(keywords) ||
+ MemberMatch<DocData::MethodDoc> method;
+ method.name = _all_terms_in_name(method_name);
+ method.keyword = _match_keywords_in_all_terms(keywords);
+ if (method.name || !method.keyword.is_empty() ||
(term.begins_with(".") && method_name.begins_with(term.substr(1))) ||
(term.ends_with("(") && method_name.ends_with(term.left(term.length() - 1).strip_edges())) ||
(term.begins_with(".") && term.ends_with("(") && method_name == term.substr(1, term.length() - 2).strip_edges())) {
- r_match_methods->push_back(const_cast<DocData::MethodDoc *>(&p_methods[i]));
+ method.doc = const_cast<DocData::MethodDoc *>(&p_methods[i]);
+ r_match_methods->push_back(method);
}
}
}
@@ -700,13 +730,15 @@ bool EditorHelpSearch::Runner::_all_terms_in_name(const String &p_name) const {
return true;
}
-bool EditorHelpSearch::Runner::_all_terms_in_keywords(const String &p_keywords) const {
- for (const String &keyword : p_keywords.split(",")) {
- if (_all_terms_in_name(keyword.strip_edges())) {
- return true;
+String EditorHelpSearch::Runner::_match_keywords_in_all_terms(const String &p_keywords) const {
+ String matching_keyword;
+ for (int i = 0; i < terms.size(); i++) {
+ matching_keyword = _match_keywords(terms[i], p_keywords);
+ if (matching_keyword.is_empty()) {
+ return String();
}
}
- return false;
+ return matching_keyword;
}
bool EditorHelpSearch::Runner::_match_string(const String &p_term, const String &p_string) const {
@@ -717,13 +749,14 @@ bool EditorHelpSearch::Runner::_match_string(const String &p_term, const String
}
}
-bool EditorHelpSearch::Runner::_match_keywords(const String &p_term, const String &p_keywords) const {
- for (const String &keyword : p_keywords.split(",")) {
- if (_match_string(p_term, keyword.strip_edges())) {
- return true;
+String EditorHelpSearch::Runner::_match_keywords(const String &p_term, const String &p_keywords) const {
+ for (const String &k : p_keywords.split(",")) {
+ const String keyword = k.strip_edges();
+ if (_match_string(p_term, keyword)) {
+ return keyword;
}
}
- return false;
+ return String();
}
void EditorHelpSearch::Runner::_match_item(TreeItem *p_item, const String &p_text, bool p_is_keywords) {
@@ -766,9 +799,26 @@ String EditorHelpSearch::Runner::_build_method_tooltip(const DocData::ClassDoc *
}
}
tooltip += ")";
+ tooltip += _build_keywords_tooltip(p_doc->keywords);
return tooltip;
}
+String EditorHelpSearch::Runner::_build_keywords_tooltip(const String &p_keywords) const {
+ String tooltip;
+ if (p_keywords.is_empty()) {
+ return tooltip;
+ }
+
+ tooltip = "\n\n" + TTR("Keywords") + ": ";
+
+ for (const String &keyword : p_keywords.split(",")) {
+ tooltip += keyword.strip_edges().quote() + ", ";
+ }
+
+ // Remove trailing comma and space.
+ return tooltip.left(-2);
+}
+
TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_match) {
if (p_match.doc->name.is_empty()) {
return nullptr;
@@ -790,7 +840,7 @@ TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_
}
}
- TreeItem *class_item = _create_class_item(parent_item, p_match.doc, !p_match.name);
+ TreeItem *class_item = _create_class_item(parent_item, p_match.doc, !p_match.name && p_match.keyword.is_empty(), p_match.name ? String() : p_match.keyword);
class_items[p_match.doc->name] = class_item;
return class_item;
}
@@ -815,15 +865,15 @@ bool EditorHelpSearch::Runner::_find_or_create_item(TreeItem *p_parent, const St
}
}
-TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
+TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray, const String &p_matching_keyword) {
String tooltip = DTR(p_doc->brief_description.strip_edges());
+ tooltip += _build_keywords_tooltip(p_doc->keywords);
const String item_meta = "class_name:" + p_doc->name;
TreeItem *item = nullptr;
if (_find_or_create_item(p_parent, item_meta, item)) {
item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_doc->name));
- item->set_text(0, p_doc->name);
item->set_text(1, TTR("Class"));
item->set_tooltip_text(0, tooltip);
item->set_tooltip_text(1, tooltip);
@@ -845,6 +895,12 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
item->clear_custom_color(1);
}
+ if (p_matching_keyword.is_empty()) {
+ item->set_text(0, p_doc->name);
+ } else {
+ item->set_text(0, p_doc->name + " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword)));
+ }
+
_match_item(item, p_doc->name);
for (const String &keyword : p_doc->keywords.split(",")) {
_match_item(item, keyword.strip_edges(), true);
@@ -853,39 +909,44 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const
return item;
}
-TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
- String tooltip = _build_method_tooltip(p_class_doc, p_doc);
- return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_doc->name, p_text, TTRC("Method"), "method", tooltip, p_doc->keywords, p_doc->is_deprecated, p_doc->is_experimental);
+TreeItem *EditorHelpSearch::Runner::_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const MemberMatch<DocData::MethodDoc> &p_match) {
+ String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberMethod", p_match.doc->name, p_text, TTRC("Method"), "method", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
}
-TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) {
- String tooltip = _build_method_tooltip(p_class_doc, p_doc);
- return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_doc->name, p_doc->name, TTRC("Signal"), "signal", tooltip, p_doc->keywords, p_doc->is_deprecated, p_doc->is_experimental);
+TreeItem *EditorHelpSearch::Runner::_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
+ String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberSignal", p_match.doc->name, p_match.doc->name, TTRC("Signal"), "signal", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
}
-TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc) {
- String tooltip = _build_method_tooltip(p_class_doc, p_doc);
- return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_doc->name, p_text, TTRC("Annotation"), "annotation", tooltip, p_doc->keywords, p_doc->is_deprecated, p_doc->is_experimental);
+TreeItem *EditorHelpSearch::Runner::_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match) {
+ String tooltip = _build_method_tooltip(p_class_doc, p_match.doc);
+ // Hide the redundant leading @ symbol.
+ String text = p_match.doc->name.substr(1);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberAnnotation", p_match.doc->name, text, TTRC("Annotation"), "annotation", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
}
-TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc) {
- String tooltip = p_class_doc->name + "." + p_doc->name;
- return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_doc->name, p_doc->name, TTRC("Constant"), "constant", tooltip, p_doc->keywords, p_doc->is_deprecated, p_doc->is_experimental);
+TreeItem *EditorHelpSearch::Runner::_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ConstantDoc> &p_match) {
+ String tooltip = p_class_doc->name + "." + p_match.doc->name;
+ tooltip += _build_keywords_tooltip(p_match.doc->keywords);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberConstant", p_match.doc->name, p_match.doc->name, TTRC("Constant"), "constant", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
}
-TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc) {
- String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
- tooltip += "\n " + p_class_doc->name + "." + p_doc->setter + "(value) setter";
- tooltip += "\n " + p_class_doc->name + "." + p_doc->getter + "() getter";
- return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_doc->name, p_doc->name, TTRC("Property"), "property", tooltip, p_doc->keywords, p_doc->is_deprecated, p_doc->is_experimental);
+TreeItem *EditorHelpSearch::Runner::_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::PropertyDoc> &p_match) {
+ String tooltip = p_match.doc->type + " " + p_class_doc->name + "." + p_match.doc->name;
+ tooltip += "\n " + p_class_doc->name + "." + p_match.doc->setter + "(value) setter";
+ tooltip += "\n " + p_class_doc->name + "." + p_match.doc->getter + "() getter";
+ tooltip += _build_keywords_tooltip(p_match.doc->keywords);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberProperty", p_match.doc->name, p_match.doc->name, TTRC("Property"), "property", tooltip, p_match.doc->keywords, p_match.doc->is_deprecated, p_match.doc->is_experimental, p_match.name ? String() : p_match.keyword);
}
-TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc) {
- String tooltip = p_doc->type + " " + p_class_doc->name + "." + p_doc->name;
- return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_doc->name, p_doc->name, TTRC("Theme Property"), "theme_item", p_doc->keywords, tooltip, false, false);
+TreeItem *EditorHelpSearch::Runner::_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ThemeItemDoc> &p_match) {
+ String tooltip = p_match.doc->type + " " + p_class_doc->name + "." + p_match.doc->name;
+ tooltip += _build_keywords_tooltip(p_match.doc->keywords);
+ return _create_member_item(p_parent, p_class_doc->name, "MemberTheme", p_match.doc->name, p_match.doc->name, TTRC("Theme Property"), "theme_item", p_match.doc->keywords, tooltip, false, false, p_match.name ? String() : p_match.keyword);
}
-TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool is_deprecated, bool is_experimental) {
+TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword) {
const String item_meta = "class_" + p_metatype + ":" + p_class_name + ":" + p_name;
TreeItem *item = nullptr;
@@ -896,20 +957,25 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons
item->set_tooltip_text(1, p_tooltip);
item->set_metadata(0, item_meta);
- if (is_deprecated) {
+ if (p_is_deprecated) {
Ref<Texture2D> error_icon = ui_service->get_editor_theme_icon(SNAME("StatusError"));
item->add_button(0, error_icon, 0, false, TTR("This member is marked as deprecated."));
- } else if (is_experimental) {
+ } else if (p_is_experimental) {
Ref<Texture2D> warning_icon = ui_service->get_editor_theme_icon(SNAME("NodeWarning"));
item->add_button(0, warning_icon, 0, false, TTR("This member is marked as experimental."));
}
}
+ String text;
if (search_flags & SEARCH_SHOW_HIERARCHY) {
- item->set_text(0, p_text);
+ text = p_text;
} else {
- item->set_text(0, p_class_name + "." + p_text);
+ text = p_class_name + "." + p_text;
+ }
+ if (!p_matching_keyword.is_empty()) {
+ text += " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword));
}
+ item->set_text(0, text);
_match_item(item, p_name);
for (const String &keyword : p_keywords.split(",")) {
diff --git a/editor/editor_help_search.h b/editor/editor_help_search.h
index 1982da04e8..003b98adf0 100644
--- a/editor/editor_help_search.h
+++ b/editor/editor_help_search.h
@@ -117,20 +117,28 @@ class EditorHelpSearch::Runner : public RefCounted {
};
int phase = 0;
+ template <class T>
+ struct MemberMatch {
+ T *doc = nullptr;
+ bool name = false;
+ String keyword;
+ };
+
struct ClassMatch {
DocData::ClassDoc *doc = nullptr;
bool name = false;
- Vector<DocData::MethodDoc *> constructors;
- Vector<DocData::MethodDoc *> methods;
- Vector<DocData::MethodDoc *> operators;
- Vector<DocData::MethodDoc *> signals;
- Vector<DocData::ConstantDoc *> constants;
- Vector<DocData::PropertyDoc *> properties;
- Vector<DocData::ThemeItemDoc *> theme_properties;
- Vector<DocData::MethodDoc *> annotations;
+ String keyword;
+ Vector<MemberMatch<DocData::MethodDoc>> constructors;
+ Vector<MemberMatch<DocData::MethodDoc>> methods;
+ Vector<MemberMatch<DocData::MethodDoc>> operators;
+ Vector<MemberMatch<DocData::MethodDoc>> signals;
+ Vector<MemberMatch<DocData::ConstantDoc>> constants;
+ Vector<MemberMatch<DocData::PropertyDoc>> properties;
+ Vector<MemberMatch<DocData::ThemeItemDoc>> theme_properties;
+ Vector<MemberMatch<DocData::MethodDoc>> annotations;
bool required() {
- return name || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size() || annotations.size();
+ return name || !keyword.is_empty() || methods.size() || signals.size() || constants.size() || properties.size() || theme_properties.size() || annotations.size();
}
};
@@ -167,22 +175,23 @@ class EditorHelpSearch::Runner : public RefCounted {
bool _phase_select_match();
String _build_method_tooltip(const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc) const;
+ String _build_keywords_tooltip(const String &p_keywords) const;
- void _match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<DocData::MethodDoc *> *r_match_methods);
+ void _match_method_name_and_push_back(Vector<DocData::MethodDoc> &p_methods, Vector<MemberMatch<DocData::MethodDoc>> *r_match_methods);
bool _all_terms_in_name(const String &p_name) const;
- bool _all_terms_in_keywords(const String &p_name) const;
+ String _match_keywords_in_all_terms(const String &p_keywords) const;
bool _match_string(const String &p_term, const String &p_string) const;
- bool _match_keywords(const String &p_term, const String &p_keywords) const;
+ String _match_keywords(const String &p_term, const String &p_keywords) const;
void _match_item(TreeItem *p_item, const String &p_text, bool p_is_keywords = false);
TreeItem *_create_class_hierarchy(const ClassMatch &p_match);
- TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray);
- TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
- TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::MethodDoc *p_doc);
- TreeItem *_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const DocData::MethodDoc *p_doc);
- TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ConstantDoc *p_doc);
- TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::PropertyDoc *p_doc);
- TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const DocData::ThemeItemDoc *p_doc);
- TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool is_deprecated, bool is_experimental);
+ TreeItem *_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray, const String &p_matching_keyword);
+ TreeItem *_create_method_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const String &p_text, const MemberMatch<DocData::MethodDoc> &p_match);
+ TreeItem *_create_signal_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
+ TreeItem *_create_annotation_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::MethodDoc> &p_match);
+ TreeItem *_create_constant_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ConstantDoc> &p_match);
+ TreeItem *_create_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::PropertyDoc> &p_match);
+ TreeItem *_create_theme_property_item(TreeItem *p_parent, const DocData::ClassDoc *p_class_doc, const MemberMatch<DocData::ThemeItemDoc> &p_match);
+ TreeItem *_create_member_item(TreeItem *p_parent, const String &p_class_name, const String &p_icon, const String &p_name, const String &p_text, const String &p_type, const String &p_metatype, const String &p_tooltip, const String &p_keywords, bool p_is_deprecated, bool p_is_experimental, const String &p_matching_keyword);
public:
bool work(uint64_t slot = 100000);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 4e76d40b1d..3ee61f5203 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -43,6 +43,7 @@
#include "editor/multi_node_edit.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
#include "scene/property_utils.h"
@@ -214,22 +215,6 @@ void EditorProperty::_notification(int p_what) {
text_size -= close->get_width() + 4 * EDSCALE;
}
}
-
- if (!configuration_warning.is_empty() && !read_only) {
- Ref<Texture2D> warning;
-
- warning = get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"));
-
- rect.size.x -= warning->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree"));
-
- if (is_layout_rtl()) {
- rect.position.x += warning->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree"));
- }
-
- if (no_children) {
- text_size -= warning->get_width() + 4 * EDSCALE;
- }
- }
}
//set children
@@ -415,38 +400,6 @@ void EditorProperty::_notification(int p_what) {
} else {
delete_rect = Rect2();
}
-
- if (!configuration_warning.is_empty() && !read_only) {
- Ref<Texture2D> warning;
-
- StringName warning_icon;
- Node *node = Object::cast_to<Node>(object);
- if (node) {
- const int warning_num = node->get_configuration_warnings_of_property(property_path).size();
- warning_icon = Node::get_configuration_warning_icon(warning_num);
- } else {
- // This shouldn't happen, but let's not crash over an icon.
- warning_icon = "NodeWarning";
- }
- warning = get_theme_icon(warning_icon, SNAME("EditorIcons"));
-
- ofs -= warning->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree"));
-
- Color color2(1, 1, 1);
- if (configuration_warning_hover) {
- color2.r *= 1.2;
- color2.g *= 1.2;
- color2.b *= 1.2;
- }
- configuration_warning_rect = Rect2(ofs, ((size.height - warning->get_height()) / 2), warning->get_width(), warning->get_height());
- if (rtl) {
- draw_texture(warning, Vector2(size.width - configuration_warning_rect.position.x - warning->get_width(), configuration_warning_rect.position.y), color2);
- } else {
- draw_texture(warning, configuration_warning_rect.position, color2);
- }
- } else {
- configuration_warning_rect = Rect2();
- }
} break;
}
}
@@ -477,7 +430,7 @@ EditorInspector *EditorProperty::get_parent_inspector() const {
}
parent = parent->get_parent();
}
- ERR_FAIL_V_MSG(nullptr, "EditorProperty is outside inspector.");
+ return nullptr;
}
void EditorProperty::set_doc_path(const String &p_doc_path) {
@@ -722,12 +675,6 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
check_hover = new_check_hover;
queue_redraw();
}
-
- bool new_configuration_warning_hover = configuration_warning_rect.has_point(mpos) && !button_left;
- if (new_configuration_warning_hover != configuration_warning_hover) {
- configuration_warning_hover = new_configuration_warning_hover;
- queue_redraw();
- }
}
Ref<InputEventMouseButton> mb = p_event;
@@ -784,16 +731,6 @@ void EditorProperty::gui_input(const Ref<InputEvent> &p_event) {
queue_redraw();
emit_signal(SNAME("property_checked"), property, checked);
}
-
- if (configuration_warning_rect.has_point(mpos)) {
- if (warning_dialog == nullptr) {
- warning_dialog = memnew(AcceptDialog);
- add_child(warning_dialog);
- warning_dialog->set_title(TTR("Node Configuration Warning!"));
- }
- warning_dialog->set_text(configuration_warning);
- warning_dialog->popup_centered();
- }
} else if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
accept_event();
_update_popup();
@@ -919,16 +856,6 @@ float EditorProperty::get_name_split_ratio() const {
return split_ratio;
}
-void EditorProperty::set_configuration_warning(const String &p_configuration_warning) {
- configuration_warning = p_configuration_warning;
- queue_redraw();
- queue_sort();
-}
-
-String EditorProperty::get_configuration_warning() const {
- return configuration_warning;
-}
-
void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) {
object = p_object;
property = p_property;
@@ -985,15 +912,6 @@ void EditorProperty::_update_pin_flags() {
}
}
-void EditorProperty::_update_configuration_warnings() {
- Node *node = Object::cast_to<Node>(object);
- if (node) {
- const PackedStringArray warnings = node->get_configuration_warnings_as_strings(true, property_path);
- const String warning_lines = String("\n").join(warnings);
- set_configuration_warning(warning_lines);
- }
-}
-
Control *EditorProperty::make_custom_tooltip(const String &p_text) const {
EditorHelpBit *tooltip = nullptr;
@@ -1069,9 +987,6 @@ void EditorProperty::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_deletable", "deletable"), &EditorProperty::set_deletable);
ClassDB::bind_method(D_METHOD("is_deletable"), &EditorProperty::is_deletable);
- ClassDB::bind_method(D_METHOD("set_configuration_warning", "configuration_warning"), &EditorProperty::set_configuration_warning);
- ClassDB::bind_method(D_METHOD("get_configuration_warning"), &EditorProperty::get_configuration_warning);
-
ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property);
ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object);
@@ -1089,7 +1004,6 @@ void EditorProperty::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_warning"), "set_draw_warning", "is_draw_warning");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deletable"), "set_deletable", "is_deletable");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "configuration_warning"), "set_configuration_warning", "get_configuration_warning");
ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::STRING_NAME, "field"), PropertyInfo(Variant::BOOL, "changing")));
ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::PACKED_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
@@ -3409,7 +3323,6 @@ void EditorInspector::update_tree() {
ep->set_keying(keying);
ep->set_read_only(property_read_only || all_read_only);
ep->set_deletable(deletable_properties || p.name.begins_with("metadata/"));
- ep->_update_configuration_warnings();
}
current_vbox->add_child(editors[i].property_editor);
@@ -3535,9 +3448,6 @@ void EditorInspector::edit(Object *p_object) {
object = p_object;
- property_configuration_warnings.clear();
- _update_configuration_warnings();
-
if (object) {
update_scroll_request = 0; //reset
if (scroll_cache.has(object->get_instance_id())) { //if exists, set something else
@@ -4059,52 +3969,6 @@ void EditorInspector::_node_removed(Node *p_node) {
}
}
-void EditorInspector::_warning_changed(Node *p_node) {
- if (p_node == object) {
- // Only update the tree if the list of configuration warnings has changed.
- if (_update_configuration_warnings()) {
- update_tree_pending = true;
- }
- }
-}
-
-bool EditorInspector::_update_configuration_warnings() {
- Node *node = Object::cast_to<Node>(object);
- if (!node) {
- return false;
- }
-
- bool changed = false;
- LocalVector<int> found_warning_indices;
-
- // New and changed warnings.
- Vector<Dictionary> warnings = node->get_configuration_warnings_as_dicts();
- for (const Dictionary &warning : warnings) {
- if (!warning.has("property")) {
- continue;
- }
-
- int found_warning_index = property_configuration_warnings.find(warning);
- if (found_warning_index < 0) {
- found_warning_index = property_configuration_warnings.size();
- property_configuration_warnings.push_back(warning);
- changed = true;
- }
- found_warning_indices.push_back(found_warning_index);
- }
-
- // Removed warnings.
- for (uint32_t i = 0; i < property_configuration_warnings.size(); i++) {
- if (found_warning_indices.find(i) < 0) {
- property_configuration_warnings.remove_at(i);
- i--;
- changed = true;
- }
- }
-
- return changed;
-}
-
void EditorInspector::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
@@ -4116,7 +3980,6 @@ void EditorInspector::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (!sub_inspector) {
get_tree()->connect("node_removed", callable_mp(this, &EditorInspector::_node_removed));
- get_tree()->connect("node_configuration_warning_changed", callable_mp(this, &EditorInspector::_warning_changed));
}
} break;
@@ -4127,7 +3990,6 @@ void EditorInspector::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (!sub_inspector) {
get_tree()->disconnect("node_removed", callable_mp(this, &EditorInspector::_node_removed));
- get_tree()->disconnect("node_configuration_warning_changed", callable_mp(this, &EditorInspector::_warning_changed));
}
edit(nullptr);
} break;
@@ -4184,7 +4046,9 @@ void EditorInspector::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- _update_inspector_bg();
+ if (EditorThemeManager::is_generated_theme_outdated()) {
+ _update_inspector_bg();
+ }
bool needs_update = false;
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 4e937ae4ad..0e908b7a14 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -76,7 +76,6 @@ private:
String doc_path;
bool internal = false;
bool has_doc_tooltip = false;
- AcceptDialog *warning_dialog = nullptr;
int property_usage;
@@ -99,8 +98,6 @@ private:
bool check_hover = false;
Rect2 delete_rect;
bool delete_hover = false;
- Rect2 configuration_warning_rect;
- bool configuration_warning_hover = false;
bool can_revert = false;
bool can_pin = false;
@@ -124,15 +121,12 @@ private:
Control *bottom_editor = nullptr;
PopupMenu *menu = nullptr;
- String configuration_warning;
-
HashMap<StringName, Variant> cache;
GDVIRTUAL0(_update_property)
GDVIRTUAL1(_set_read_only, bool)
void _update_pin_flags();
- void _update_configuration_warnings();
protected:
void _notification(int p_what);
@@ -209,9 +203,6 @@ public:
void set_name_split_ratio(float p_ratio);
float get_name_split_ratio() const;
- void set_configuration_warning(const String &p_configuration_warning);
- String get_configuration_warning() const;
-
void set_object_and_property(Object *p_object, const StringName &p_property);
virtual Control *make_custom_tooltip(const String &p_text) const override;
@@ -514,7 +505,6 @@ class EditorInspector : public ScrollContainer {
int property_focusable;
int update_scroll_request;
- LocalVector<Dictionary> property_configuration_warnings;
HashMap<StringName, HashMap<StringName, String>> doc_path_cache;
HashSet<StringName> restart_request_props;
HashMap<String, String> custom_property_descriptions;
@@ -543,8 +533,6 @@ class EditorInspector : public ScrollContainer {
void _object_id_selected(const String &p_path, ObjectID p_id);
void _node_removed(Node *p_node);
- void _warning_changed(Node *p_node);
- bool _update_configuration_warnings();
HashMap<StringName, int> per_array_page;
void _page_change_request(int p_new_page, const StringName &p_array_prefix);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index dddd7345c8..04944a9143 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -47,6 +47,7 @@
#include "editor/editor_string_names.h"
#include "main/main.h"
#include "scene/3d/bone_attachment_3d.h"
+#include "scene/animation/animation_tree.h"
#include "scene/gui/color_picker.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/file_dialog.h"
@@ -514,8 +515,9 @@ void EditorNode::_update_theme(bool p_skip_creation) {
scene_root_parent->add_theme_style_override("panel", theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles)));
bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
- main_menu->add_theme_style_override("hover", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
+ main_menu->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
+ distraction_free->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons)));
help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)));
@@ -527,6 +529,11 @@ void EditorNode::_update_theme(bool p_skip_creation) {
bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
}
+ for (int i = 0; i < bottom_panel_items.size(); i++) {
+ bottom_panel_items.write[i].button->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
+ bottom_panel_items.write[i].button->add_theme_style_override("hover_pressed", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
+ }
+
for (int i = 0; i < main_editor_buttons.size(); i++) {
Button *tb = main_editor_buttons[i];
EditorPlugin *p_editor = editor_table[i];
@@ -760,36 +767,43 @@ void EditorNode::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- _update_vsync_mode();
- FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
- EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
- EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int());
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog")) {
+ FileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
+ EditorFileDialog::set_default_show_hidden_files(EDITOR_GET("filesystem/file_dialog/show_hidden_files"));
+ EditorFileDialog::set_default_display_mode((EditorFileDialog::DisplayMode)EDITOR_GET("filesystem/file_dialog/display_mode").operator int());
+ }
if (EditorThemeManager::is_generated_theme_outdated()) {
_update_theme();
+ _build_icon_type_cache();
+ recent_scenes->reset_size();
}
- scene_tabs->update_scene_tabs();
- recent_scenes->reset_size();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/scene_tabs")) {
+ scene_tabs->update_scene_tabs();
+ }
- _build_icon_type_cache();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("docks/filesystem")) {
+ HashSet<String> updated_textfile_extensions;
+ bool extensions_match = true;
+ const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
+ for (const String &E : textfile_ext) {
+ updated_textfile_extensions.insert(E);
+ if (extensions_match && !textfile_extensions.has(E)) {
+ extensions_match = false;
+ }
+ }
- HashSet<String> updated_textfile_extensions;
- bool extensions_match = true;
- const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
- for (const String &E : textfile_ext) {
- updated_textfile_extensions.insert(E);
- if (extensions_match && !textfile_extensions.has(E)) {
- extensions_match = false;
+ if (!extensions_match || updated_textfile_extensions.size() < textfile_extensions.size()) {
+ textfile_extensions = updated_textfile_extensions;
+ EditorFileSystem::get_singleton()->scan();
}
}
- if (!extensions_match || updated_textfile_extensions.size() < textfile_extensions.size()) {
- textfile_extensions = updated_textfile_extensions;
- EditorFileSystem::get_singleton()->scan();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor")) {
+ _update_update_spinner();
+ _update_vsync_mode();
}
-
- _update_update_spinner();
} break;
}
}
@@ -1739,7 +1753,14 @@ int EditorNode::_save_external_resources() {
static void _reset_animation_mixers(Node *p_node, List<Pair<AnimationMixer *, Ref<AnimatedValuesBackup>>> *r_anim_backups) {
for (int i = 0; i < p_node->get_child_count(); i++) {
AnimationMixer *mixer = Object::cast_to<AnimationMixer>(p_node->get_child(i));
- if (mixer && mixer->is_reset_on_save_enabled() && mixer->can_apply_reset()) {
+ if (mixer && mixer->is_active() && mixer->is_reset_on_save_enabled() && mixer->can_apply_reset()) {
+ AnimationTree *tree = Object::cast_to<AnimationTree>(p_node->get_child(i));
+ if (tree) {
+ AnimationPlayer *player = Object::cast_to<AnimationPlayer>(tree->get_node_or_null(tree->get_animation_player()));
+ if (player && player->is_active() && player->is_reset_on_save_enabled() && player->can_apply_reset()) {
+ continue; // Avoid to process reset/restore many times.
+ }
+ }
Ref<AnimatedValuesBackup> backup = mixer->apply_reset();
if (backup.is_valid()) {
Pair<AnimationMixer *, Ref<AnimatedValuesBackup>> pair;
@@ -3331,7 +3352,6 @@ void EditorNode::select_editor_by_name(const String &p_name) {
void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed) {
if (p_editor->has_main_screen()) {
Button *tb = memnew(Button);
- tb->set_flat(true);
tb->set_toggle_mode(true);
tb->set_theme_type_variation("MainScreenButton");
tb->set_name(p_editor->get_name());
@@ -5077,8 +5097,8 @@ void EditorNode::_update_layouts_menu() {
overridden_default_layout = -1;
editor_layouts->reset_size();
- editor_layouts->add_shortcut(ED_SHORTCUT("layout/save", TTR("Save Layout")), SETTINGS_LAYOUT_SAVE);
- editor_layouts->add_shortcut(ED_SHORTCUT("layout/delete", TTR("Delete Layout")), SETTINGS_LAYOUT_DELETE);
+ editor_layouts->add_shortcut(ED_SHORTCUT("layout/save", TTR("Save Layout...")), SETTINGS_LAYOUT_SAVE);
+ editor_layouts->add_shortcut(ED_SHORTCUT("layout/delete", TTR("Delete Layout...")), SETTINGS_LAYOUT_DELETE);
editor_layouts->add_separator();
editor_layouts->add_shortcut(ED_SHORTCUT("layout/default", TTR("Default")), SETTINGS_LAYOUT_DEFAULT);
@@ -5226,7 +5246,7 @@ void EditorNode::_scene_tab_closed(int p_tab) {
Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item, bool p_at_front) {
Button *tb = memnew(Button);
- tb->set_flat(true);
+ tb->set_theme_type_variation("FlatMenuButton");
tb->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_switch_by_control).bind(p_item));
tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorNode::_bottom_panel_drag_hover).bind(tb, p_item), Callable());
tb->set_text(p_text);
@@ -6671,7 +6691,7 @@ EditorNode::EditorNode() {
scene_tabs->connect("tab_closed", callable_mp(this, &EditorNode::_scene_tab_closed));
distraction_free = memnew(Button);
- distraction_free->set_flat(true);
+ distraction_free->set_theme_type_variation("FlatMenuButton");
ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F11);
ED_SHORTCUT_OVERRIDE("editor/distraction_free_mode", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::D);
distraction_free->set_shortcut(ED_GET_SHORTCUT("editor/distraction_free_mode"));
@@ -6711,9 +6731,7 @@ EditorNode::EditorNode() {
main_menu = memnew(MenuBar);
title_bar->add_child(main_menu);
-
- main_menu->add_theme_style_override("hover", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
- main_menu->set_flat(true);
+ main_menu->set_theme_type_variation("FlatMenuButton");
main_menu->set_start_index(0); // Main menu, add to the start of global menu.
main_menu->set_prefer_global_menu(global_menu);
main_menu->set_switch_on_hover(true);
@@ -6967,7 +6985,7 @@ EditorNode::EditorNode() {
help_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
- ED_SHORTCUT_AND_COMMAND("editor/editor_help", TTR("Search Help"), Key::F1);
+ ED_SHORTCUT_AND_COMMAND("editor/editor_help", TTR("Search Help..."), Key::F1);
ED_SHORTCUT_OVERRIDE("editor/editor_help", "macos", KeyModifierMask::ALT | Key::SPACE);
help_menu->add_icon_shortcut(theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)), ED_GET_SHORTCUT("editor/editor_help"), HELP_SEARCH);
help_menu->add_separator();
@@ -6983,7 +7001,7 @@ EditorNode::EditorNode() {
help_menu->add_separator();
if (!global_menu || !OS::get_singleton()->has_feature("macos")) {
// On macOS "Quit" and "About" options are in the "app" menu.
- help_menu->add_icon_shortcut(theme->get_icon(SNAME("Godot"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/about", TTR("About Godot")), HELP_ABOUT);
+ help_menu->add_icon_shortcut(theme->get_icon(SNAME("Godot"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/about", TTR("About Godot...")), HELP_ABOUT);
}
help_menu->add_icon_shortcut(theme->get_icon(SNAME("Heart"), EditorStringName(EditorIcons)), ED_SHORTCUT_AND_COMMAND("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT);
@@ -7179,7 +7197,8 @@ EditorNode::EditorNode() {
bottom_panel_raise = memnew(Button);
bottom_panel_hb->add_child(bottom_panel_raise);
bottom_panel_raise->hide();
- bottom_panel_raise->set_flat(true);
+ bottom_panel_raise->set_flat(false);
+ bottom_panel_raise->set_theme_type_variation("FlatMenuButton");
bottom_panel_raise->set_toggle_mode(true);
bottom_panel_raise->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
bottom_panel_raise->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_raise_toggled));
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index d22e60d7b4..7d0024b1da 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -36,6 +36,7 @@
#include "core/io/file_access.h"
#include "core/os/main_loop.h"
#include "editor/editor_node.h"
+#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/tree.h"
@@ -62,44 +63,33 @@ void EditorPluginSettings::update_plugins() {
plugins.sort();
for (int i = 0; i < plugins.size(); i++) {
- Ref<ConfigFile> cf;
- cf.instantiate();
+ Ref<ConfigFile> cfg;
+ cfg.instantiate();
const String &path = plugins[i];
- Error err2 = cf->load(path);
+ Error err = cfg->load(path);
- if (err2 != OK) {
- WARN_PRINT("Can't load plugin config: " + path);
+ if (err != OK) {
+ WARN_PRINT("Can't load plugin config at: " + path);
} else {
- bool key_missing = false;
-
- if (!cf->has_section_key("plugin", "name")) {
- WARN_PRINT("Plugin config misses \"plugin/name\" key: " + path);
- key_missing = true;
- }
- if (!cf->has_section_key("plugin", "author")) {
- WARN_PRINT("Plugin config misses \"plugin/author\" key: " + path);
- key_missing = true;
- }
- if (!cf->has_section_key("plugin", "version")) {
- WARN_PRINT("Plugin config misses \"plugin/version\" key: " + path);
- key_missing = true;
- }
- if (!cf->has_section_key("plugin", "description")) {
- WARN_PRINT("Plugin config misses \"plugin/description\" key: " + path);
- key_missing = true;
- }
- if (!cf->has_section_key("plugin", "script")) {
- WARN_PRINT("Plugin config misses \"plugin/script\" key: " + path);
- key_missing = true;
+ Vector<String> missing_keys;
+ for (const String required_key : { "name", "author", "version", "description", "script" }) {
+ if (!cfg->has_section_key("plugin", required_key)) {
+ missing_keys.append("\"plugin/" + required_key + "\"");
+ }
}
- if (!key_missing) {
- String name = cf->get_value("plugin", "name");
- String author = cf->get_value("plugin", "author");
- String version = cf->get_value("plugin", "version");
- String description = cf->get_value("plugin", "description");
- String scr = cf->get_value("plugin", "script");
+ if (!missing_keys.is_empty()) {
+ WARN_PRINT(vformat("Plugin config at \"%s\" is missing the following keys: %s", path, String(",").join(missing_keys)));
+ } else {
+ String name = cfg->get_value("plugin", "name");
+ String author = cfg->get_value("plugin", "author");
+ String version = cfg->get_value("plugin", "version");
+ String description = cfg->get_value("plugin", "description");
+ String scr = cfg->get_value("plugin", "script");
+
+ bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(path);
+ Color disabled_color = get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor));
const PackedInt32Array boundaries = TS->string_get_word_breaks(description, "", 80);
String wrapped_description;
@@ -111,19 +101,22 @@ void EditorPluginSettings::update_plugins() {
}
TreeItem *item = plugin_list->create_item(root);
- item->set_text(0, name);
- item->set_tooltip_text(0, TTR("Name:") + " " + name + "\n" + TTR("Path:") + " " + path + "\n" + TTR("Main Script:") + " " + scr + "\n" + TTR("Description:") + " " + wrapped_description);
- item->set_metadata(0, path);
- item->set_text(1, version);
- item->set_metadata(1, scr);
- item->set_text(2, author);
- item->set_metadata(2, description);
- item->set_cell_mode(3, TreeItem::CELL_MODE_CHECK);
- item->set_text(3, TTR("Enable"));
- bool is_active = EditorNode::get_singleton()->is_addon_plugin_enabled(path);
- item->set_checked(3, is_active);
- item->set_editable(3, true);
- item->add_button(4, get_editor_theme_icon(SNAME("Edit")), BUTTON_PLUGIN_EDIT, false, TTR("Edit Plugin"));
+ item->set_text(COLUMN_NAME, name);
+ if (!is_enabled) {
+ item->set_custom_color(COLUMN_NAME, disabled_color);
+ }
+ item->set_tooltip_text(COLUMN_NAME, vformat(TTR("Name: %s\nPath: %s\nMain Script: %s\n\n%s"), name, path, scr, wrapped_description));
+ item->set_metadata(COLUMN_NAME, path);
+ item->set_text(COLUMN_VERSION, version);
+ item->set_custom_font(COLUMN_VERSION, get_theme_font("source", EditorStringName(EditorFonts)));
+ item->set_metadata(COLUMN_VERSION, scr);
+ item->set_text(COLUMN_AUTHOR, author);
+ item->set_metadata(COLUMN_AUTHOR, description);
+ item->set_cell_mode(COLUMN_STATUS, TreeItem::CELL_MODE_CHECK);
+ item->set_text(COLUMN_STATUS, TTR("On"));
+ item->set_checked(COLUMN_STATUS, is_enabled);
+ item->set_editable(COLUMN_STATUS, true);
+ item->add_button(COLUMN_EDIT, get_editor_theme_icon(SNAME("Edit")), BUTTON_PLUGIN_EDIT, false, TTR("Edit Plugin"));
}
}
}
@@ -138,18 +131,19 @@ void EditorPluginSettings::_plugin_activity_changed() {
TreeItem *ti = plugin_list->get_edited();
ERR_FAIL_NULL(ti);
- bool active = ti->is_checked(3);
- String name = ti->get_metadata(0);
+ bool checked = ti->is_checked(COLUMN_STATUS);
+ String name = ti->get_metadata(COLUMN_NAME);
- EditorNode::get_singleton()->set_addon_plugin_enabled(name, active, true);
+ EditorNode::get_singleton()->set_addon_plugin_enabled(name, checked, true);
- bool is_active = EditorNode::get_singleton()->is_addon_plugin_enabled(name);
+ bool is_enabled = EditorNode::get_singleton()->is_addon_plugin_enabled(name);
- if (is_active != active) {
+ if (is_enabled != checked) {
updating = true;
- ti->set_checked(3, is_active);
+ ti->set_checked(COLUMN_STATUS, is_enabled);
updating = false;
}
+ ti->set_custom_color(COLUMN_NAME, is_enabled ? Color() : get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
}
void EditorPluginSettings::_create_clicked() {
@@ -166,8 +160,8 @@ void EditorPluginSettings::_cell_button_pressed(Object *p_item, int p_column, in
return;
}
if (p_id == BUTTON_PLUGIN_EDIT) {
- if (p_column == 4) {
- String dir = item->get_metadata(0);
+ if (p_column == COLUMN_EDIT) {
+ String dir = item->get_metadata(COLUMN_NAME);
plugin_config_dialog->config(dir);
plugin_config_dialog->popup_centered();
}
@@ -212,38 +206,47 @@ EditorPluginSettings::EditorPluginSettings() {
add_child(plugin_config_dialog);
HBoxContainer *title_hb = memnew(HBoxContainer);
- Label *l = memnew(Label(TTR("Installed Plugins:")));
- l->set_theme_type_variation("HeaderSmall");
- title_hb->add_child(l);
+ Label *label = memnew(Label(TTR("Installed Plugins:")));
+ label->set_theme_type_variation("HeaderSmall");
+ title_hb->add_child(label);
title_hb->add_spacer();
- Button *create_plugin = memnew(Button(TTR("Create New Plugin")));
- create_plugin->connect("pressed", callable_mp(this, &EditorPluginSettings::_create_clicked));
- title_hb->add_child(create_plugin);
+ Button *create_plugin_button = memnew(Button(TTR("Create New Plugin")));
+ create_plugin_button->connect("pressed", callable_mp(this, &EditorPluginSettings::_create_clicked));
+ title_hb->add_child(create_plugin_button);
add_child(title_hb);
plugin_list = memnew(Tree);
plugin_list->set_v_size_flags(SIZE_EXPAND_FILL);
- plugin_list->set_columns(5);
+ plugin_list->set_columns(COLUMN_MAX);
plugin_list->set_column_titles_visible(true);
- plugin_list->set_column_title(0, TTR("Name"));
- plugin_list->set_column_title(1, TTR("Version"));
- plugin_list->set_column_title(2, TTR("Author"));
- plugin_list->set_column_title(3, TTR("Status"));
- plugin_list->set_column_title(4, TTR("Edit"));
- plugin_list->set_column_expand(0, true);
- plugin_list->set_column_clip_content(0, true);
- plugin_list->set_column_expand(1, false);
- plugin_list->set_column_clip_content(1, true);
- plugin_list->set_column_expand(2, false);
- plugin_list->set_column_clip_content(2, true);
- plugin_list->set_column_expand(3, false);
- plugin_list->set_column_clip_content(3, true);
- plugin_list->set_column_expand(4, false);
- plugin_list->set_column_clip_content(4, true);
- plugin_list->set_column_custom_minimum_width(1, 100 * EDSCALE);
- plugin_list->set_column_custom_minimum_width(2, 250 * EDSCALE);
- plugin_list->set_column_custom_minimum_width(3, 80 * EDSCALE);
- plugin_list->set_column_custom_minimum_width(4, 40 * EDSCALE);
+ plugin_list->set_column_title(COLUMN_STATUS, TTR("Enabled"));
+ plugin_list->set_column_title(COLUMN_NAME, TTR("Name"));
+ plugin_list->set_column_title(COLUMN_VERSION, TTR("Version"));
+ plugin_list->set_column_title(COLUMN_AUTHOR, TTR("Author"));
+ plugin_list->set_column_title(COLUMN_EDIT, TTR("Edit"));
+ plugin_list->set_column_title_alignment(COLUMN_STATUS, HORIZONTAL_ALIGNMENT_LEFT);
+ plugin_list->set_column_title_alignment(COLUMN_NAME, HORIZONTAL_ALIGNMENT_LEFT);
+ plugin_list->set_column_title_alignment(COLUMN_VERSION, HORIZONTAL_ALIGNMENT_LEFT);
+ plugin_list->set_column_title_alignment(COLUMN_AUTHOR, HORIZONTAL_ALIGNMENT_LEFT);
+ plugin_list->set_column_title_alignment(COLUMN_EDIT, HORIZONTAL_ALIGNMENT_LEFT);
+ plugin_list->set_column_expand(COLUMN_PADDING_LEFT, false);
+ plugin_list->set_column_expand(COLUMN_STATUS, false);
+ plugin_list->set_column_expand(COLUMN_NAME, true);
+ plugin_list->set_column_expand(COLUMN_VERSION, false);
+ plugin_list->set_column_expand(COLUMN_AUTHOR, false);
+ plugin_list->set_column_expand(COLUMN_EDIT, false);
+ plugin_list->set_column_expand(COLUMN_PADDING_RIGHT, false);
+ plugin_list->set_column_clip_content(COLUMN_STATUS, true);
+ plugin_list->set_column_clip_content(COLUMN_NAME, true);
+ plugin_list->set_column_clip_content(COLUMN_VERSION, true);
+ plugin_list->set_column_clip_content(COLUMN_AUTHOR, true);
+ plugin_list->set_column_clip_content(COLUMN_EDIT, true);
+ plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_LEFT, 10 * EDSCALE);
+ plugin_list->set_column_custom_minimum_width(COLUMN_STATUS, 80 * EDSCALE);
+ plugin_list->set_column_custom_minimum_width(COLUMN_VERSION, 100 * EDSCALE);
+ plugin_list->set_column_custom_minimum_width(COLUMN_AUTHOR, 250 * EDSCALE);
+ plugin_list->set_column_custom_minimum_width(COLUMN_EDIT, 40 * EDSCALE);
+ plugin_list->set_column_custom_minimum_width(COLUMN_PADDING_RIGHT, 10 * EDSCALE);
plugin_list->set_hide_root(true);
plugin_list->connect("item_edited", callable_mp(this, &EditorPluginSettings::_plugin_activity_changed), CONNECT_DEFERRED);
diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h
index 96ddad2a43..8db8ffd78c 100644
--- a/editor/editor_plugin_settings.h
+++ b/editor/editor_plugin_settings.h
@@ -43,6 +43,17 @@ class EditorPluginSettings : public VBoxContainer {
BUTTON_PLUGIN_EDIT
};
+ enum {
+ COLUMN_PADDING_LEFT,
+ COLUMN_STATUS,
+ COLUMN_NAME,
+ COLUMN_VERSION,
+ COLUMN_AUTHOR,
+ COLUMN_EDIT,
+ COLUMN_PADDING_RIGHT,
+ COLUMN_MAX,
+ };
+
PluginConfigDialog *plugin_config_dialog = nullptr;
Tree *plugin_list = nullptr;
bool updating = false;
diff --git a/editor/editor_properties_vector.cpp b/editor/editor_properties_vector.cpp
index 82cd703508..453675bebe 100644
--- a/editor/editor_properties_vector.cpp
+++ b/editor/editor_properties_vector.cpp
@@ -105,8 +105,6 @@ void EditorPropertyVectorN::_update_ratio() {
if (spin_sliders[base_slider_idx]->get_value() != 0) {
ratio_write[i] = spin_sliders[secondary_slider_idx]->get_value() / spin_sliders[base_slider_idx]->get_value();
- } else {
- ratio_write[i] = 0;
}
}
}
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 74db32df19..95436427ad 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -194,10 +194,11 @@ void EditorResourcePicker::_update_menu_items() {
set_create_options(edit_menu);
// Add an option to load a resource from a file using the QuickOpen dialog.
- edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Load")), TTR("Quick Load"), OBJ_MENU_QUICKLOAD);
+ edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Load")), TTR("Quick Load..."), OBJ_MENU_QUICKLOAD);
+ edit_menu->set_item_tooltip(-1, TTR("Opens a quick menu to select from a list of allowed Resource files."));
// Add an option to load a resource from a file using the regular file dialog.
- edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Load")), TTR("Load"), OBJ_MENU_LOAD);
+ edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Load")), TTR("Load..."), OBJ_MENU_LOAD);
}
// Add options for changing existing value of the resource.
@@ -1054,11 +1055,11 @@ void EditorScriptPicker::set_create_options(Object *p_menu_node) {
return;
}
- menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script"), OBJ_MENU_NEW_SCRIPT);
+ menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT);
if (script_owner) {
Ref<Script> scr = script_owner->get_script();
if (scr.is_valid()) {
- menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptExtend")), TTR("Extend Script"), OBJ_MENU_EXTEND_SCRIPT);
+ menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptExtend")), TTR("Extend Script..."), OBJ_MENU_EXTEND_SCRIPT);
}
}
menu_node->add_separator();
@@ -1110,7 +1111,7 @@ void EditorShaderPicker::set_create_options(Object *p_menu_node) {
return;
}
- menu_node->add_icon_item(get_editor_theme_icon(SNAME("Shader")), TTR("New Shader"), OBJ_MENU_NEW_SHADER);
+ menu_node->add_icon_item(get_editor_theme_icon(SNAME("Shader")), TTR("New Shader..."), OBJ_MENU_NEW_SHADER);
menu_node->add_separator();
}
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index fd6b083c12..a380ebd4ad 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -157,6 +157,8 @@ bool EditorRunNative::is_deploy_debug_remote_enabled() const {
EditorRunNative::EditorRunNative() {
remote_debug = memnew(MenuButton);
+ remote_debug->set_flat(false);
+ remote_debug->set_theme_type_variation("RunBarButton");
remote_debug->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::start_run_native));
remote_debug->set_tooltip_text(TTR("Remote Debug"));
remote_debug->set_disabled(true);
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index e50b92366b..13e3c41e3b 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -44,6 +44,7 @@
#include "editor/event_listener_line_edit.h"
#include "editor/input_event_configuration_dialog.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/margin_container.h"
void EditorSettingsDialog::ok_pressed() {
@@ -145,7 +146,9 @@ void EditorSettingsDialog::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- _update_icons();
+ if (EditorThemeManager::is_generated_theme_outdated()) {
+ _update_icons();
+ }
bool update_shortcuts_tab =
EditorSettings::get_singleton()->check_changed_settings_in_group("shortcuts") ||
diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp
index 7572a14483..1f76c94655 100644
--- a/editor/editor_vcs_interface.cpp
+++ b/editor/editor_vcs_interface.cpp
@@ -367,6 +367,7 @@ void EditorVCSInterface::create_vcs_metadata_files(VCSMetadata p_vcs_metadata_ty
} else {
f->store_line("# Godot 4+ specific ignores");
f->store_line(".godot/");
+ f->store_line("android/");
}
f = FileAccess::open(p_dir.path_join(".gitattributes"), FileAccess::WRITE);
if (f.is_null()) {
diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp
index 2e34685d75..0c1cce9f3e 100644
--- a/editor/export/editor_export.cpp
+++ b/editor/export/editor_export.cpp
@@ -47,6 +47,7 @@ void EditorExport::_save() {
config->set_value(section, "name", preset->get_name());
config->set_value(section, "platform", preset->get_platform()->get_name());
config->set_value(section, "runnable", preset->is_runnable());
+ config->set_value(section, "advanced_options", preset->are_advanced_options_enabled());
config->set_value(section, "dedicated_server", preset->is_dedicated_server());
config->set_value(section, "custom_features", preset->get_custom_features());
@@ -234,6 +235,7 @@ void EditorExport::load_config() {
}
preset->set_name(config->get_value(section, "name"));
+ preset->set_advanced_options_enabled(config->get_value(section, "advanced_options", false));
preset->set_runnable(config->get_value(section, "runnable"));
preset->set_dedicated_server(config->get_value(section, "dedicated_server", false));
diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp
index 6fc2228bee..e2e3e9d154 100644
--- a/editor/export/editor_export_preset.cpp
+++ b/editor/export/editor_export_preset.cpp
@@ -229,6 +229,19 @@ bool EditorExportPreset::is_runnable() const {
return runnable;
}
+void EditorExportPreset::set_advanced_options_enabled(bool p_enabled) {
+ if (advanced_options_enabled == p_enabled) {
+ return;
+ }
+ advanced_options_enabled = p_enabled;
+ EditorExport::singleton->save_presets();
+ notify_property_list_changed();
+}
+
+bool EditorExportPreset::are_advanced_options_enabled() const {
+ return advanced_options_enabled;
+}
+
void EditorExportPreset::set_dedicated_server(bool p_enable) {
dedicated_server = p_enable;
EditorExport::singleton->save_presets();
diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h
index 03ff75b467..c6a8808af1 100644
--- a/editor/export/editor_export_preset.h
+++ b/editor/export/editor_export_preset.h
@@ -71,6 +71,7 @@ private:
HashSet<String> selected_files;
HashMap<String, FileExportMode> customized_files;
bool runnable = false;
+ bool advanced_options_enabled = false;
bool dedicated_server = false;
friend class EditorExport;
@@ -128,6 +129,9 @@ public:
void set_runnable(bool p_enable);
bool is_runnable() const;
+ void set_advanced_options_enabled(bool p_enabled);
+ bool are_advanced_options_enabled() const;
+
void set_dedicated_server(bool p_enable);
bool is_dedicated_server() const;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 5de56b9d90..7088d4e2ab 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -240,6 +240,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
name->set_text("");
name->set_editable(false);
export_path->hide();
+ advanced_options->set_disabled(true);
runnable->set_disabled(true);
parameters->edit(nullptr);
presets->deselect_all();
@@ -274,6 +275,8 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_path->setup(extension_vector, false, true);
export_path->update_property();
+ advanced_options->set_disabled(false);
+ advanced_options->set_pressed(current->are_advanced_options_enabled());
runnable->set_disabled(false);
runnable->set_pressed(current->is_runnable());
if (parameters->get_edited_object() != current.ptr()) {
@@ -449,6 +452,18 @@ void ProjectExportDialog::_update_parameters(const String &p_edited_property) {
_update_current_preset();
}
+void ProjectExportDialog::_advanced_options_pressed() {
+ if (updating) {
+ return;
+ }
+
+ Ref<EditorExportPreset> current = get_current_preset();
+ ERR_FAIL_COND(current.is_null());
+
+ current->set_advanced_options_enabled(advanced_options->is_pressed());
+ _update_presets();
+}
+
void ProjectExportDialog::_runnable_pressed() {
if (updating) {
return;
@@ -637,6 +652,7 @@ void ProjectExportDialog::_duplicate_preset() {
if (make_runnable) {
preset->set_runnable(make_runnable);
}
+ preset->set_advanced_options_enabled(current->are_advanced_options_enabled());
preset->set_dedicated_server(current->is_dedicated_server());
preset->set_export_filter(current->get_export_filter());
preset->set_include_filter(current->get_include_filter());
@@ -1236,11 +1252,22 @@ ProjectExportDialog::ProjectExportDialog() {
name = memnew(LineEdit);
settings_vb->add_margin_child(TTR("Name:"), name);
name->connect("text_changed", callable_mp(this, &ProjectExportDialog::_name_changed));
+
runnable = memnew(CheckButton);
runnable->set_text(TTR("Runnable"));
runnable->set_tooltip_text(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable."));
runnable->connect("pressed", callable_mp(this, &ProjectExportDialog::_runnable_pressed));
- settings_vb->add_child(runnable);
+
+ advanced_options = memnew(CheckButton);
+ advanced_options->set_text(TTR("Advanced Options"));
+ advanced_options->set_tooltip_text(TTR("If checked, the advanced options will be shown."));
+ advanced_options->connect("pressed", callable_mp(this, &ProjectExportDialog::_advanced_options_pressed));
+
+ HBoxContainer *preset_configs_container = memnew(HBoxContainer);
+ preset_configs_container->add_spacer(true);
+ preset_configs_container->add_child(advanced_options);
+ preset_configs_container->add_child(runnable);
+ settings_vb->add_child(preset_configs_container);
export_path = memnew(EditorPropertyPath);
settings_vb->add_child(export_path);
@@ -1413,6 +1440,7 @@ ProjectExportDialog::ProjectExportDialog() {
// Disable by default.
name->set_editable(false);
export_path->hide();
+ advanced_options->set_disabled(true);
runnable->set_disabled(true);
duplicate_preset->set_disabled(true);
delete_preset->set_disabled(true);
diff --git a/editor/export/project_export.h b/editor/export/project_export.h
index 0fe7ecc2a8..bcab05cebb 100644
--- a/editor/export/project_export.h
+++ b/editor/export/project_export.h
@@ -81,6 +81,7 @@ class ProjectExportDialog : public ConfirmationDialog {
EditorPropertyPath *export_path = nullptr;
EditorInspector *parameters = nullptr;
CheckButton *runnable = nullptr;
+ CheckButton *advanced_options = nullptr;
Button *button_export = nullptr;
bool updating = false;
@@ -119,6 +120,7 @@ class ProjectExportDialog : public ConfirmationDialog {
bool exporting = false;
+ void _advanced_options_pressed();
void _runnable_pressed();
void _update_parameters(const String &p_edited_property);
void _name_changed(const String &p_string);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index ac83460542..c4022bd9a5 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -618,8 +618,10 @@ void FileSystemDock::_notification(int p_what) {
_update_tree(get_uncollapsed_paths());
}
- // Change full tree mode.
- _update_display_mode();
+ if (EditorThemeManager::is_generated_theme_outdated()) {
+ // Change full tree mode.
+ _update_display_mode();
+ }
} break;
}
}
@@ -2066,13 +2068,18 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
fpath = p_selected[0];
}
- String file = ProjectSettings::get_singleton()->globalize_path(fpath);
+ const String file = ProjectSettings::get_singleton()->globalize_path(fpath);
+ const String extension = file.get_extension();
- String resource_type = ResourceLoader::get_resource_type(fpath);
+ const String resource_type = ResourceLoader::get_resource_type(fpath);
String external_program;
- if (resource_type == "CompressedTexture2D" || resource_type == "Image") {
- if (file.get_extension() == "svg" || file.get_extension() == "svgz") {
+ if (ClassDB::is_parent_class(resource_type, "Script") || extension == "tres" || extension == "tscn") {
+ external_program = EDITOR_GET("text_editor/external/exec_path");
+ } else if (extension == "res" || extension == "scn") {
+ // Binary resources have no meaningful editor outside Godot, so just fallback to something default.
+ } else if (resource_type == "CompressedTexture2D" || resource_type == "Image") {
+ if (extension == "svg" || extension == "svgz") {
external_program = EDITOR_GET("filesystem/external_programs/vector_image_editor");
} else {
external_program = EDITOR_GET("filesystem/external_programs/raster_image_editor");
@@ -2080,12 +2087,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
} else if (ClassDB::is_parent_class(resource_type, "AudioStream")) {
external_program = EDITOR_GET("filesystem/external_programs/audio_editor");
} else if (resource_type == "PackedScene") {
- // Ignore non-model scenes.
- if (file.get_extension() != "tscn" && file.get_extension() != "scn" && file.get_extension() != "res") {
- external_program = EDITOR_GET("filesystem/external_programs/3d_model_editor");
- }
- } else if (ClassDB::is_parent_class(resource_type, "Script")) {
- external_program = EDITOR_GET("text_editor/external/exec_path");
+ external_program = EDITOR_GET("filesystem/external_programs/3d_model_editor");
}
if (external_program.is_empty()) {
@@ -3605,7 +3607,8 @@ Dictionary FileSystemDock::get_assigned_folder_colors() const {
MenuButton *FileSystemDock::_create_file_menu_button() {
MenuButton *button = memnew(MenuButton);
- button->set_flat(true);
+ button->set_flat(false);
+ button->set_theme_type_variation("FlatMenuButton");
button->set_tooltip_text(TTR("Sort Files"));
PopupMenu *p = button->get_popup();
@@ -3819,7 +3822,7 @@ FileSystemDock::FileSystemDock() {
button_toggle_display_mode->connect("pressed", callable_mp(this, &FileSystemDock::_change_split_mode));
button_toggle_display_mode->set_focus_mode(FOCUS_NONE);
button_toggle_display_mode->set_tooltip_text(TTR("Change Split Mode"));
- button_toggle_display_mode->set_flat(true);
+ button_toggle_display_mode->set_theme_type_variation("FlatMenuButton");
toolbar_hbc->add_child(button_toggle_display_mode);
button_dock_placement = memnew(Button);
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index c57c22404d..2dada25728 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -135,6 +135,9 @@ void EditorFileDialog::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog")) {
+ break;
+ }
bool is_showing_hidden = EDITOR_GET("filesystem/file_dialog/show_hidden_files");
if (show_hidden_files != is_showing_hidden) {
set_show_hidden_files(is_showing_hidden);
diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp
index a79dced69e..a44dfc9858 100644
--- a/editor/gui/editor_run_bar.cpp
+++ b/editor/gui/editor_run_bar.cpp
@@ -372,7 +372,7 @@ EditorRunBar::EditorRunBar() {
play_button = memnew(Button);
main_hbox->add_child(play_button);
- play_button->set_flat(true);
+ play_button->set_theme_type_variation("RunBarButton");
play_button->set_toggle_mode(true);
play_button->set_focus_mode(Control::FOCUS_NONE);
play_button->set_tooltip_text(TTR("Run the project's default scene."));
@@ -384,7 +384,7 @@ EditorRunBar::EditorRunBar() {
pause_button = memnew(Button);
main_hbox->add_child(pause_button);
- pause_button->set_flat(true);
+ pause_button->set_theme_type_variation("RunBarButton");
pause_button->set_toggle_mode(true);
pause_button->set_focus_mode(Control::FOCUS_NONE);
pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
@@ -396,7 +396,7 @@ EditorRunBar::EditorRunBar() {
stop_button = memnew(Button);
main_hbox->add_child(stop_button);
- stop_button->set_flat(true);
+ stop_button->set_theme_type_variation("RunBarButton");
stop_button->set_focus_mode(Control::FOCUS_NONE);
stop_button->set_tooltip_text(TTR("Stop the currently running project."));
stop_button->set_disabled(true);
@@ -412,7 +412,7 @@ EditorRunBar::EditorRunBar() {
play_scene_button = memnew(Button);
main_hbox->add_child(play_scene_button);
- play_scene_button->set_flat(true);
+ play_scene_button->set_theme_type_variation("RunBarButton");
play_scene_button->set_toggle_mode(true);
play_scene_button->set_focus_mode(Control::FOCUS_NONE);
play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
@@ -424,7 +424,7 @@ EditorRunBar::EditorRunBar() {
play_custom_scene_button = memnew(Button);
main_hbox->add_child(play_custom_scene_button);
- play_custom_scene_button->set_flat(true);
+ play_custom_scene_button->set_theme_type_variation("RunBarButton");
play_custom_scene_button->set_toggle_mode(true);
play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
@@ -439,7 +439,7 @@ EditorRunBar::EditorRunBar() {
write_movie_button = memnew(Button);
write_movie_panel->add_child(write_movie_button);
- write_movie_button->set_flat(true);
+ write_movie_button->set_theme_type_variation("RunBarButton");
write_movie_button->set_toggle_mode(true);
write_movie_button->set_pressed(false);
write_movie_button->set_focus_mode(Control::FOCUS_NONE);
diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp
index 476b06f52b..fdc0c589cc 100644
--- a/editor/gui/editor_scene_tabs.cpp
+++ b/editor/gui/editor_scene_tabs.cpp
@@ -60,8 +60,10 @@ void EditorSceneTabs::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int());
- scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE);
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/scene_tabs")) {
+ scene_tabs->set_tab_close_display_policy((TabBar::CloseButtonDisplayPolicy)EDITOR_GET("interface/scene_tabs/display_close_button").operator int());
+ scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE);
+ }
} break;
}
}
diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp
index 48e1c56e83..69a129e73c 100644
--- a/editor/gui/editor_toaster.cpp
+++ b/editor/gui/editor_toaster.cpp
@@ -543,7 +543,7 @@ EditorToaster::EditorToaster() {
main_button->set_tooltip_text(TTR("No notifications."));
main_button->set_modulate(Color(0.5, 0.5, 0.5));
main_button->set_disabled(true);
- main_button->set_flat(true);
+ main_button->set_theme_type_variation("FlatMenuButton");
main_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled).bind(true));
main_button->connect("pressed", callable_mp(this, &EditorToaster::_repop_old));
main_button->connect("draw", callable_mp(this, &EditorToaster::_draw_button));
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 7fb52251ce..14af49aabf 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -132,13 +132,32 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
}
undo_redo->commit_action();
} else if (p_id == BUTTON_WARNING) {
- const PackedStringArray warnings = n->get_configuration_warnings_as_strings(true);
+ const PackedStringArray warnings = n->get_configuration_warnings();
+
if (warnings.is_empty()) {
return;
}
- const String warning_lines = String("\n").join(warnings);
- warning->set_text(warning_lines);
+ // Improve looks on tooltip, extra spacing on non-bullet point newlines.
+ const String bullet_point = U"• ";
+ String all_warnings;
+ for (const String &w : warnings) {
+ all_warnings += "\n" + bullet_point + w;
+ }
+
+ // Limit the line width while keeping some padding.
+ // It is not efficient, but it does not have to be.
+ const PackedInt32Array boundaries = TS->string_get_word_breaks(all_warnings, "", 80);
+ PackedStringArray lines;
+ for (int i = 0; i < boundaries.size(); i += 2) {
+ const int start = boundaries[i];
+ const int end = boundaries[i + 1];
+ const String line = all_warnings.substr(start, end - start);
+ lines.append(line);
+ }
+ all_warnings = String("\n").join(lines).indent(" ").replace(U" •", U"\n•").substr(2); // We don't want the first two newlines.
+
+ warning->set_text(all_warnings);
warning->popup_centered();
} else if (p_id == BUTTON_SIGNALS) {
@@ -275,12 +294,29 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
if (can_rename) { //should be can edit..
- const PackedStringArray warnings = p_node->get_configuration_warnings_as_strings(false);
+ const PackedStringArray warnings = p_node->get_configuration_warnings();
const int num_warnings = warnings.size();
if (num_warnings > 0) {
- const StringName warning_icon = Node::get_configuration_warning_icon(num_warnings);
- const String warning_lines = String("\n\n").join(warnings);
- item->add_button(0, get_editor_theme_icon(warning_icon), BUTTON_WARNING, false, TTR("Node configuration warning:") + "\n\n" + warning_lines);
+ String warning_icon;
+ if (num_warnings == 1) {
+ warning_icon = SNAME("NodeWarning");
+ } else if (num_warnings <= 3) {
+ warning_icon = vformat("NodeWarnings%d", num_warnings);
+ } else {
+ warning_icon = SNAME("NodeWarnings4Plus");
+ }
+
+ // Improve looks on tooltip, extra spacing on non-bullet point newlines.
+ const String bullet_point = U"• ";
+ String all_warnings;
+ for (const String &w : warnings) {
+ all_warnings += "\n\n" + bullet_point + w.replace("\n", "\n ");
+ }
+ if (num_warnings == 1) {
+ all_warnings.remove_at(0); // With only one warning, two newlines do not look great.
+ }
+
+ item->add_button(0, get_editor_theme_icon(warning_icon), BUTTON_WARNING, false, TTR("Node configuration warning:") + all_warnings);
}
if (p_node->is_unique_name_in_owner()) {
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
index 5d45292222..d56b426c86 100644
--- a/editor/import/resource_importer_csv_translation.cpp
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -101,9 +101,12 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const
for (int i = 1; i < line.size(); i++) {
String locale = TranslationServer::get_singleton()->standardize_locale(line[i]);
- if (locale.is_empty()) {
+ if (line[i].left(1) == "_") {
skipped_locales.insert(i);
- ERR_CONTINUE_MSG(true, vformat("Error importing CSV translation: Invalid locale format '%s', should be 'language_Script_COUNTRY_VARIANT@extra'.", line[i]));
+ continue;
+ } else if (locale.is_empty()) {
+ skipped_locales.insert(i);
+ ERR_CONTINUE_MSG(true, vformat("Error importing CSV translation: Invalid locale format '%s', should be 'language_Script_COUNTRY_VARIANT@extra'. This column will be ignored.", line[i]));
}
locales.push_back(locale);
@@ -117,7 +120,7 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const
line = f->get_csv_line(delimiter);
String key = line[0];
if (!key.is_empty()) {
- ERR_CONTINUE_MSG(line.size() != locales.size() + 1, vformat("Error importing CSV translation: expected %d locale(s), but the '%s' key has %d locale(s).", locales.size(), key, line.size() - 1));
+ ERR_CONTINUE_MSG(line.size() != locales.size() + (int)skipped_locales.size() + 1, vformat("Error importing CSV translation: expected %d locale(s), but the '%s' key has %d locale(s).", locales.size(), key, line.size() - 1));
for (int i = 1; i < line.size(); i++) {
if (skipped_locales.has(i)) {
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 0ceece263c..47572a991c 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -37,6 +37,7 @@
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
class ImportDockParameters : public Object {
GDCLASS(ImportDockParameters, Object);
@@ -656,7 +657,9 @@ void ImportDock::_replace_resource_in_object(Object *p_object, const Ref<Resourc
void ImportDock::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- imported->add_theme_style_override("normal", get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")));
+ if (EditorThemeManager::is_generated_theme_outdated()) {
+ imported->add_theme_style_override("normal", get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")));
+ }
} break;
case NOTIFICATION_ENTER_TREE: {
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index d5be2bd5a9..8a7eb80281 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -646,20 +646,22 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
add_child(general_options_hb);
resource_new_button = memnew(Button);
- resource_new_button->set_flat(true);
+ resource_new_button->set_theme_type_variation("FlatMenuButton");
resource_new_button->set_tooltip_text(TTR("Create a new resource in memory and edit it."));
general_options_hb->add_child(resource_new_button);
resource_new_button->connect("pressed", callable_mp(this, &InspectorDock::_new_resource));
resource_new_button->set_focus_mode(Control::FOCUS_NONE);
resource_load_button = memnew(Button);
- resource_load_button->set_flat(true);
+ resource_load_button->set_theme_type_variation("FlatMenuButton");
resource_load_button->set_tooltip_text(TTR("Load an existing resource from disk and edit it."));
general_options_hb->add_child(resource_load_button);
resource_load_button->connect("pressed", callable_mp(this, &InspectorDock::_open_resource_selector));
resource_load_button->set_focus_mode(Control::FOCUS_NONE);
resource_save_button = memnew(MenuButton);
+ resource_save_button->set_flat(false);
+ resource_save_button->set_theme_type_variation("FlatMenuButton");
resource_save_button->set_tooltip_text(TTR("Save the currently edited resource."));
general_options_hb->add_child(resource_save_button);
resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE);
@@ -669,6 +671,8 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
resource_save_button->set_disabled(true);
resource_extra_button = memnew(MenuButton);
+ resource_extra_button->set_flat(false);
+ resource_extra_button->set_theme_type_variation("FlatMenuButton");
resource_extra_button->set_tooltip_text(TTR("Extra resource options."));
general_options_hb->add_child(resource_extra_button);
resource_extra_button->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_resource_extra_popup));
@@ -698,6 +702,8 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward));
history_menu = memnew(MenuButton);
+ history_menu->set_flat(false);
+ history_menu->set_theme_type_variation("FlatMenuButton");
history_menu->set_tooltip_text(TTR("History of recently edited objects."));
general_options_hb->add_child(history_menu);
history_menu->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_history));
@@ -710,7 +716,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
subresource_hb->add_child(object_selector);
open_docs_button = memnew(Button);
- open_docs_button->set_flat(true);
+ open_docs_button->set_theme_type_variation("FlatMenuButton");
open_docs_button->set_disabled(true);
open_docs_button->set_tooltip_text(TTR("Open documentation for this object."));
open_docs_button->set_shortcut(ED_SHORTCUT("property_editor/open_help", TTR("Open Documentation")));
@@ -732,6 +738,8 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) {
property_tools_hb->add_child(search);
object_menu = memnew(MenuButton);
+ object_menu->set_flat(false);
+ object_menu->set_theme_type_variation("FlatMenuButton");
object_menu->set_shortcut_context(this);
property_tools_hb->add_child(object_menu);
object_menu->set_tooltip_text(TTR("Manage object properties."));
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index 5c2c059c98..8391eddc59 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -100,7 +100,8 @@ void PluginConfigDialog::_on_canceled() {
void PluginConfigDialog::_on_required_text_changed() {
int lang_idx = script_option_edit->get_selected();
- String ext = ScriptServer::get_language(lang_idx)->get_extension();
+ ScriptLanguage *language = ScriptServer::get_language(lang_idx);
+ String ext = language->get_extension();
if (name_edit->get_text().is_empty()) {
validation_panel->set_message(MSG_ID_PLUGIN, TTR("Plugin name cannot be blank."), EditorValidationPanel::MSG_ERROR);
@@ -120,6 +121,15 @@ void PluginConfigDialog::_on_required_text_changed() {
} else {
validation_panel->set_message(MSG_ID_SUBFOLDER, "", EditorValidationPanel::MSG_OK);
}
+ if (active_edit->is_visible()) {
+ if (language->get_name() == "C#") {
+ active_edit->set_pressed(false);
+ active_edit->set_disabled(true);
+ validation_panel->set_message(MSG_ID_ACTIVE, TTR("C# doesn't support activating the plugin on creation because the project must be built first."), EditorValidationPanel::MSG_WARNING);
+ } else {
+ active_edit->set_disabled(false);
+ }
+ }
}
String PluginConfigDialog::_get_subfolder() {
@@ -290,7 +300,6 @@ PluginConfigDialog::PluginConfigDialog() {
grid->add_child(script_edit);
// Activate now checkbox
- // TODO Make this option work better with languages like C#. Right now, it does not work because the C# project must be compiled first.
Label *active_lb = memnew(Label);
active_lb->set_text(TTR("Activate now?"));
active_lb->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
@@ -311,6 +320,7 @@ PluginConfigDialog::PluginConfigDialog() {
validation_panel->add_line(MSG_ID_PLUGIN, TTR("Plugin name is valid."));
validation_panel->add_line(MSG_ID_SCRIPT, TTR("Script extension is valid."));
validation_panel->add_line(MSG_ID_SUBFOLDER, TTR("Subfolder name is valid."));
+ validation_panel->add_line(MSG_ID_ACTIVE, "");
validation_panel->set_update_callback(callable_mp(this, &PluginConfigDialog::_on_required_text_changed));
validation_panel->set_accept_button(get_ok_button());
diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h
index c6befbab85..2fdf6368a0 100644
--- a/editor/plugin_config_dialog.h
+++ b/editor/plugin_config_dialog.h
@@ -48,6 +48,7 @@ class PluginConfigDialog : public ConfirmationDialog {
MSG_ID_PLUGIN,
MSG_ID_SUBFOLDER,
MSG_ID_SCRIPT,
+ MSG_ID_ACTIVE,
};
LineEdit *name_edit = nullptr;
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 78b4647975..22813124d0 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -940,7 +940,9 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- _update_editor_settings();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ _update_editor_settings();
+ }
} break;
case NOTIFICATION_THEME_CHANGED: {
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 3f9f609dba..15b62b51e7 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -44,6 +44,7 @@
#include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning.
#include "editor/scene_tree_dock.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/animation/animation_tree.h"
#include "scene/gui/separator.h"
#include "scene/main/window.h"
@@ -129,7 +130,9 @@ void AnimationPlayerEditor::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("panel"), SNAME("Panel")));
+ if (EditorThemeManager::is_generated_theme_outdated()) {
+ add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("panel"), SNAME("Panel")));
+ }
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
@@ -1867,7 +1870,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
tool_anim->set_flat(false);
tool_anim->set_tooltip_text(TTR("Animation Tools"));
tool_anim->set_text(TTR("Animation"));
- tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM);
+ tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New...")), TOOL_NEW_ANIM);
tool_anim->get_popup()->add_separator();
tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/animation_libraries", TTR("Manage Animations...")), TOOL_ANIM_LIBRARY);
tool_anim->get_popup()->add_separator();
@@ -1908,6 +1911,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug
hb->add_child(onion_toggle);
onion_skinning = memnew(MenuButton);
+ onion_skinning->set_flat(false);
+ onion_skinning->set_theme_type_variation("FlatMenuButton");
onion_skinning->set_tooltip_text(TTR("Onion Skinning Options"));
onion_skinning->get_popup()->add_separator(TTR("Directions"));
// TRANSLATORS: Opposite of "Future", refers to a direction in animation onion skinning.
@@ -2157,6 +2162,7 @@ void AnimationPlayerEditorPlugin::_update_dummy_player(AnimationMixer *p_mixer)
Node *parent = p_mixer->get_parent();
ERR_FAIL_NULL(parent);
dummy_player = memnew(AnimationPlayer);
+ dummy_player->set_active(false); // Inactive as default, it will be activated if the AnimationPlayerEditor visibility is changed.
parent->add_child(dummy_player);
}
player = dummy_player;
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index dca20e3dbc..459c5e8b31 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -679,6 +679,11 @@ void EditorAssetLibrary::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("asset_library") &&
+ !EditorSettings::get_singleton()->check_changed_settings_in_group("netweork")) {
+ break;
+ }
+
_update_repository_options();
setup_http_request(request);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 132b85b090..7c766e6195 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -1285,25 +1285,25 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
if (k->is_pressed()) {
- if (ED_GET_SHORTCUT("canvas_item_editor/zoom_3.125_percent")->matches_event(p_event)) {
+ if (ED_IS_SHORTCUT("canvas_item_editor/zoom_3.125_percent", p_event)) {
_shortcut_zoom_set(1.0 / 32.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_6.25_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_6.25_percent", p_event)) {
_shortcut_zoom_set(1.0 / 16.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_12.5_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_12.5_percent", p_event)) {
_shortcut_zoom_set(1.0 / 8.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_25_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_25_percent", p_event)) {
_shortcut_zoom_set(1.0 / 4.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_50_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_50_percent", p_event)) {
_shortcut_zoom_set(1.0 / 2.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_100_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_100_percent", p_event)) {
_shortcut_zoom_set(1.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_200_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_200_percent", p_event)) {
_shortcut_zoom_set(2.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_400_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_400_percent", p_event)) {
_shortcut_zoom_set(4.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_800_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_800_percent", p_event)) {
_shortcut_zoom_set(8.0);
- } else if (ED_GET_SHORTCUT("canvas_item_editor/zoom_1600_percent")->matches_event(p_event)) {
+ } else if (ED_IS_SHORTCUT("canvas_item_editor/zoom_1600_percent", p_event)) {
_shortcut_zoom_set(16.0);
}
}
@@ -4010,6 +4010,10 @@ void CanvasItemEditor::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorThemeManager::is_generated_theme_outdated() &&
+ !EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ break;
+ }
_update_editor_settings();
} break;
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index a2feea8488..3191858657 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -537,7 +537,7 @@ void CollisionShape2DEditor::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/polygon_editor/point_grab_radius")) {
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/polygon_editor")) {
grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
}
} break;
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index ea32b659d7..ee9a2e99d9 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -118,8 +118,13 @@ void CurveEdit::_notification(int p_what) {
queue_redraw();
}
} break;
- case NOTIFICATION_THEME_CHANGED:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen")) {
+ break;
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
float gizmo_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
point_radius = Math::round(BASE_POINT_RADIUS * get_theme_default_base_scale() * gizmo_scale);
hover_radius = Math::round(BASE_HOVER_RADIUS * get_theme_default_base_scale() * gizmo_scale);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index f0cb2aa3a5..a007411d29 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2666,7 +2666,11 @@ void Node3DEditorPlugin::edited_scene_changed() {
}
void Node3DEditorViewport::_project_settings_changed() {
- //update shadow atlas if changed
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("rendering")) {
+ return;
+ }
+
+ // Update shadow atlas if changed.
int shadowmap_size = GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/atlas_size");
bool shadowmap_16_bits = GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/atlas_16_bits");
int atlas_q0 = GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/atlas_quadrant_0_subdiv");
@@ -7697,8 +7701,10 @@ void Node3DEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
// Update grid color by rebuilding grid.
- _finish_grid();
- _init_grid();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/3d")) {
+ _finish_grid();
+ _init_grid();
+ }
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index e56fc94a55..707cf13cad 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -75,8 +75,13 @@ int Polygon2DEditor::_get_polygon_count() const {
void Polygon2DEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ break;
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_ENTER_TREE: {
uv_panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
} break;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index e6a1b76c78..d1d858b4db 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -2779,6 +2779,11 @@ void ScriptEditor::_save_layout() {
}
void ScriptEditor::_editor_settings_changed() {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor") &&
+ !EditorSettings::get_singleton()->check_changed_settings_in_group("docks/filesystem")) {
+ return;
+ }
+
textfile_extensions.clear();
const Vector<String> textfile_ext = ((String)(EDITOR_GET("docks/filesystem/textfile_extensions"))).split(",", false);
for (const String &E : textfile_ext) {
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 8625e468fa..f8e2bd0915 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -625,13 +625,13 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("File"));
file_menu->set_shortcut_context(main_split);
- file_menu->get_popup()->add_item(TTR("New Shader"), FILE_NEW);
- file_menu->get_popup()->add_item(TTR("New Shader Include"), FILE_NEW_INCLUDE);
+ file_menu->get_popup()->add_item(TTR("New Shader..."), FILE_NEW);
+ file_menu->get_popup()->add_item(TTR("New Shader Include..."), FILE_NEW_INCLUDE);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_item(TTR("Load Shader File"), FILE_OPEN);
- file_menu->get_popup()->add_item(TTR("Load Shader Include File"), FILE_OPEN_INCLUDE);
+ file_menu->get_popup()->add_item(TTR("Load Shader File..."), FILE_OPEN);
+ file_menu->get_popup()->add_item(TTR("Load Shader Include File..."), FILE_OPEN_INCLUDE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/save", TTR("Save File"), KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::S), FILE_SAVE);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/save_as", TTR("Save File As")), FILE_SAVE_AS);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("shader_editor/save_as", TTR("Save File As...")), FILE_SAVE_AS);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_item(TTR("Open File in Inspector"), FILE_INSPECT);
file_menu->get_popup()->add_separator();
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index ea200419f6..17b48118ac 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -740,6 +740,9 @@ void TextShaderEditor::_notification(int p_what) {
}
void TextShaderEditor::_editor_settings_changed() {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor")) {
+ return;
+ }
shader_editor->update_editor_settings();
shader_editor->get_text_editor()->add_theme_constant_override("line_spacing", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index a3d578b437..6c7a3f776e 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -804,14 +804,19 @@ void TextureRegionEditor::_update_autoslice() {
void TextureRegionEditor::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ break;
+ }
+ [[fallthrough]];
+ }
+
+ case NOTIFICATION_READY: {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
} break;
case NOTIFICATION_ENTER_TREE: {
get_tree()->connect("node_removed", callable_mp(this, &TextureRegionEditor::_node_removed));
- panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
-
hb_grid->set_visible(snap_mode == SNAP_GRID);
if (snap_mode == SNAP_AUTOSLICE && is_visible() && autoslice_is_dirty) {
_update_autoslice();
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 721186ef82..8c2cb68413 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -575,8 +575,13 @@ void TileAtlasView::_update_theme_item_cache() {
void TileAtlasView::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ break;
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_ENTER_TREE: {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
} break;
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index e2f6cb0f38..d24e7faeaf 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -3650,7 +3650,9 @@ void TileMapLayerEditor::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- toggle_grid_button->set_pressed(EDITOR_GET("editors/tiles_editor/display_grid"));
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/tiles_editor")) {
+ toggle_grid_button->set_pressed_no_signal(EDITOR_GET("editors/tiles_editor/display_grid"));
+ }
} break;
}
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 7efa978a78..176e8b7fee 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2673,11 +2673,9 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view->connect("transform_changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_view_transform_changed).unbind(2));
right_panel->add_child(tile_atlas_view);
- tile_create_help = memnew(HBoxContainer);
+ tile_create_help = memnew(VBoxContainer);
tile_atlas_view->add_child(tile_create_help);
tile_create_help->set_mouse_filter(MOUSE_FILTER_IGNORE);
- tile_create_help->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_LEFT, Control::PRESET_MODE_MINSIZE, 30 * EDSCALE);
- tile_create_help->add_theme_constant_override("separation", 30 * EDSCALE);
Label *help_label = memnew(Label(TTR("Hold Ctrl to create multiple tiles.")));
tile_create_help->add_child(help_label);
@@ -2685,6 +2683,11 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
help_label = memnew(Label(TTR("Hold Shift to create big tiles.")));
tile_create_help->add_child(help_label);
+ tile_create_help->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_LEFT, Control::PRESET_MODE_MINSIZE);
+ Vector2 pos = tile_create_help->get_position();
+ pos.y -= 8 * EDSCALE;
+ tile_create_help->set_position(pos);
+
base_tile_popup_menu = memnew(PopupMenu);
base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE);
base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index e1e6a3113c..b98705baf1 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -160,7 +160,7 @@ private:
// -- Atlas view --
TileAtlasView *tile_atlas_view = nullptr;
- HBoxContainer *tile_create_help = nullptr;
+ VBoxContainer *tile_create_help = nullptr;
// Dragging
enum DragType {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index c69e85454f..4750179473 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -4307,11 +4307,15 @@ void VisualShaderEditor::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
- graph->set_warped_panning(bool(EDITOR_GET("editors/panning/warped_mouse_panning")));
- graph->set_minimap_opacity(EDITOR_GET("editors/visual_editors/minimap_opacity"));
- graph->set_connection_lines_curvature(EDITOR_GET("editors/visual_editors/lines_curvature"));
- _update_graph();
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/panning")) {
+ graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning")));
+ graph->set_warped_panning(bool(EDITOR_GET("editors/panning/warped_mouse_panning")));
+ }
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("editors/visual_editors")) {
+ graph->set_minimap_opacity(EDITOR_GET("editors/visual_editors/minimap_opacity"));
+ graph->set_connection_lines_curvature(EDITOR_GET("editors/visual_editors/lines_curvature"));
+ _update_graph();
+ }
} break;
case NOTIFICATION_ENTER_TREE: {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index ea3d00a437..023d9e9f00 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1475,7 +1475,9 @@ void SceneTreeDock::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- scene_tree->set_auto_expand_selected(EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), false);
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("docks/scene_tree")) {
+ scene_tree->set_auto_expand_selected(EDITOR_GET("docks/scene_tree/auto_expand_to_selected"), false);
+ }
} break;
case NOTIFICATION_THEME_CHANGED: {
@@ -4162,7 +4164,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
ED_SHORTCUT("scene_tree/rename", TTR("Rename"), Key::F2);
ED_SHORTCUT_OVERRIDE("scene_tree/rename", "macos", Key::ENTER);
- ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename"), KeyModifierMask::SHIFT | Key::F2);
+ ED_SHORTCUT("scene_tree/batch_rename", TTR("Batch Rename..."), KeyModifierMask::SHIFT | Key::F2);
ED_SHORTCUT_OVERRIDE("scene_tree/batch_rename", "macos", KeyModifierMask::SHIFT | Key::ENTER);
ED_SHORTCUT("scene_tree/add_child_node", TTR("Add Child Node..."), KeyModifierMask::CMD_OR_CTRL | Key::A);
@@ -4179,10 +4181,10 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KeyModifierMask::CMD_OR_CTRL | Key::UP);
ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KeyModifierMask::CMD_OR_CTRL | Key::DOWN);
ED_SHORTCUT("scene_tree/duplicate", TTR("Duplicate"), KeyModifierMask::CMD_OR_CTRL | Key::D);
- ED_SHORTCUT("scene_tree/reparent", TTR("Reparent"));
- ED_SHORTCUT("scene_tree/reparent_to_new_node", TTR("Reparent to New Node"));
+ ED_SHORTCUT("scene_tree/reparent", TTR("Reparent..."));
+ ED_SHORTCUT("scene_tree/reparent_to_new_node", TTR("Reparent to New Node..."));
ED_SHORTCUT("scene_tree/make_root", TTR("Make Scene Root"));
- ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene"));
+ ED_SHORTCUT("scene_tree/save_branch_as_scene", TTR("Save Branch as Scene..."));
ED_SHORTCUT("scene_tree/copy_node_path", TTR("Copy Node Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
ED_SHORTCUT("scene_tree/show_in_file_system", TTR("Show in FileSystem"));
ED_SHORTCUT("scene_tree/toggle_unique_name", TTR("Toggle Access as Unique Name"));
@@ -4190,14 +4192,14 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
ED_SHORTCUT("scene_tree/delete", TTR("Delete"), Key::KEY_DELETE);
button_add = memnew(Button);
- button_add->set_flat(true);
+ button_add->set_theme_type_variation("FlatMenuButton");
button_add->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_NEW, false));
button_add->set_tooltip_text(TTR("Add/Create a New Node."));
button_add->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node"));
filter_hbc->add_child(button_add);
button_instance = memnew(Button);
- button_instance->set_flat(true);
+ button_instance->set_theme_type_variation("FlatMenuButton");
button_instance->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_INSTANTIATE, false));
button_instance->set_tooltip_text(TTR("Instantiate a scene file as a Node. Creates an inherited scene if no root node exists."));
button_instance->set_shortcut(ED_GET_SHORTCUT("scene_tree/instantiate_scene"));
@@ -4217,11 +4219,12 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
_append_filter_options_to(filter->get_menu());
filter_quick_menu = memnew(PopupMenu);
+ filter_quick_menu->set_theme_type_variation("FlatMenuButton");
filter_quick_menu->connect("id_pressed", callable_mp(this, &SceneTreeDock::_filter_option_selected));
filter->add_child(filter_quick_menu);
button_create_script = memnew(Button);
- button_create_script->set_flat(true);
+ button_create_script->set_theme_type_variation("FlatMenuButton");
button_create_script->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_ATTACH_SCRIPT, false));
button_create_script->set_tooltip_text(TTR("Attach a new or existing script to the selected node."));
button_create_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script"));
@@ -4229,7 +4232,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
button_create_script->hide();
button_detach_script = memnew(Button);
- button_detach_script->set_flat(true);
+ button_detach_script->set_theme_type_variation("FlatMenuButton");
button_detach_script->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_DETACH_SCRIPT, false));
button_detach_script->set_tooltip_text(TTR("Detach the script from the selected node."));
button_detach_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/detach_script"));
@@ -4237,7 +4240,8 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
button_detach_script->hide();
button_tree_menu = memnew(MenuButton);
- button_tree_menu->set_flat(true);
+ button_tree_menu->set_flat(false);
+ button_tree_menu->set_theme_type_variation("FlatMenuButton");
button_tree_menu->set_tooltip_text(TTR("Extra scene options."));
button_tree_menu->connect("about_to_popup", callable_mp(this, &SceneTreeDock::_update_tree_menu));
filter_hbc->add_child(button_tree_menu);
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 479a3ebc87..052b19478c 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -681,6 +681,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
p_theme->set_color("icon_normal_color", "Button", p_config.icon_normal_color);
p_theme->set_color("icon_hover_color", "Button", p_config.icon_hover_color);
p_theme->set_color("icon_focus_color", "Button", p_config.icon_focus_color);
+ p_theme->set_color("icon_hover_pressed_color", "Button", p_config.icon_pressed_color);
p_theme->set_color("icon_pressed_color", "Button", p_config.icon_pressed_color);
p_theme->set_color("icon_disabled_color", "Button", p_config.icon_disabled_color);
@@ -1679,7 +1680,19 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
p_theme->set_stylebox("BottomPanel", EditorStringName(EditorStyles), style_bottom_panel);
// Main menu.
+ Ref<StyleBoxFlat> menu_transparent_style = p_config.button_style->duplicate();
+ menu_transparent_style->set_bg_color(Color(1, 1, 1, 0));
+ menu_transparent_style->set_border_width_all(0);
+ p_theme->set_stylebox("MenuTransparent", EditorStringName(EditorStyles), menu_transparent_style);
p_theme->set_stylebox("MenuHover", EditorStringName(EditorStyles), p_config.button_style_hover);
+ p_theme->set_stylebox("normal", "MainScreenButton", menu_transparent_style);
+ p_theme->set_stylebox("pressed", "MainScreenButton", menu_transparent_style);
+ p_theme->set_stylebox("hover_pressed", "MainScreenButton", p_config.button_style_hover);
+
+ // Run bar.
+ p_theme->set_type_variation("RunBarButton", "FlatMenuButton");
+ p_theme->set_stylebox("disabled", "RunBarButton", menu_transparent_style);
+ p_theme->set_stylebox("pressed", "RunBarButton", menu_transparent_style);
}
// Editor GUI widgets.
@@ -1744,12 +1757,12 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
style_flat_button_pressed->set_bg_color(flat_pressed_color);
p_theme->set_stylebox("normal", "FlatButton", style_flat_button);
- p_theme->set_stylebox("hover", "FlatButton", style_flat_button);
+ p_theme->set_stylebox("hover", "FlatButton", p_config.button_style_hover);
p_theme->set_stylebox("pressed", "FlatButton", style_flat_button_pressed);
p_theme->set_stylebox("disabled", "FlatButton", style_flat_button);
p_theme->set_stylebox("normal", "FlatMenuButton", style_flat_button);
- p_theme->set_stylebox("hover", "FlatMenuButton", style_flat_button);
+ p_theme->set_stylebox("hover", "FlatMenuButton", p_config.button_style_hover);
p_theme->set_stylebox("pressed", "FlatMenuButton", style_flat_button_pressed);
p_theme->set_stylebox("disabled", "FlatMenuButton", style_flat_button);
@@ -2293,6 +2306,10 @@ void EditorThemeManager::_populate_text_editor_styles(const Ref<EditorTheme> &p_
/* clang-format on */
}
+void EditorThemeManager::_reset_dirty_flag() {
+ outdated_cache_dirty = true;
+}
+
// Public interface for theme generation.
Ref<EditorTheme> EditorThemeManager::generate_theme(const Ref<EditorTheme> &p_old_theme) {
@@ -2323,18 +2340,26 @@ bool EditorThemeManager::is_generated_theme_outdated() {
// Note that the editor scale is purposefully omitted because it cannot be changed
// without a restart, so there is no point regenerating the theme.
- // TODO: We can use this information more intelligently to do partial theme updates and speed things up.
- return EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/font") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/main_font") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/code_font") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/increase_scrollbar_touch_area") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("docks/property_editor/subresource_hue_tint") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") ||
- EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size");
+ if (outdated_cache_dirty) {
+ // TODO: We can use this information more intelligently to do partial theme updates and speed things up.
+ outdated_cache = EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/font") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/main_font") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/code_font") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/increase_scrollbar_touch_area") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/scale_gizmo_handles") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("docks/property_editor/subresource_hue_tint") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") ||
+ EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size");
+
+ // The outdated flag is relevant at the moment of changing editor settings.
+ callable_mp_static(&EditorThemeManager::_reset_dirty_flag).call_deferred();
+ outdated_cache_dirty = false;
+ }
+
+ return outdated_cache;
}
bool EditorThemeManager::is_dark_theme() {
diff --git a/editor/themes/editor_theme_manager.h b/editor/themes/editor_theme_manager.h
index 0b30a9c853..de088a1011 100644
--- a/editor/themes/editor_theme_manager.h
+++ b/editor/themes/editor_theme_manager.h
@@ -36,6 +36,8 @@
class EditorThemeManager {
static int benchmark_run;
+ static inline bool outdated_cache = false;
+ static inline bool outdated_cache_dirty = true;
static String get_benchmark_key();
@@ -155,6 +157,8 @@ class EditorThemeManager {
static void _generate_text_editor_defaults(ThemeConfiguration &p_config);
static void _populate_text_editor_styles(const Ref<EditorTheme> &p_theme, ThemeConfiguration &p_config);
+ static void _reset_dirty_flag();
+
public:
static Ref<EditorTheme> generate_theme(const Ref<EditorTheme> &p_old_theme = nullptr);
static bool is_generated_theme_outdated();
diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected
index 2b7a39944a..b801066204 100644
--- a/misc/extension_api_validation/4.2-stable.expected
+++ b/misc/extension_api_validation/4.2-stable.expected
@@ -84,13 +84,6 @@ Validate extension JSON: Error: Field 'classes/Sky/properties/sky_material': typ
Property hints reordered to improve editor usability. The types allowed are still the same as before. No adjustments should be necessary.
-GH-68420
---------
-Validate extension JSON: Error: Field 'classes/Node/methods/_get_configuration_warnings/return_value': type changed value in new API, from "PackedStringArray" to "Array".
-
-Allow configuration warnings to refer to a property. Compatibility method registered.
-
-
GH-86907
--------
@@ -147,3 +140,10 @@ GH-86661
Validate extension JSON: Error: Field 'classes/Animation/methods/track_find_key/arguments': size changed value in new API, from 3 to 4.
Added optional argument to track_find_key to avoid finding keys out of the animation range. Compatibility method registered.
+
+
+GH-84792
+--------
+Validate extension JSON: Error: Field 'classes/RenderingServer/methods/environment_set_fog/arguments': size changed value in new API, from 10 to 11.
+
+Added fog mode argument. Compatibility method registered.
diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub
index 14669847bc..80bfd7e858 100644
--- a/modules/basis_universal/SCsub
+++ b/modules/basis_universal/SCsub
@@ -28,7 +28,6 @@ encoder_sources = [
"basisu_resample_filters.cpp",
"basisu_ssim.cpp",
"basisu_uastc_enc.cpp",
- "jpgd.cpp",
"pvpngreader.cpp",
]
encoder_sources = [thirdparty_dir + "encoder/" + file for file in encoder_sources]
@@ -36,9 +35,11 @@ transcoder_sources = [thirdparty_dir + "transcoder/basisu_transcoder.cpp"]
# Treat Basis headers as system headers to avoid raising warnings. Not supported on MSVC.
if not env.msvc:
- env_basisu.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
+ env_basisu.Append(
+ CPPFLAGS=["-isystem", Dir(thirdparty_dir).path, "-isystem", Dir("#thirdparty/jpeg-compressor").path]
+ )
else:
- env_basisu.Prepend(CPPPATH=[thirdparty_dir])
+ env_basisu.Prepend(CPPPATH=[thirdparty_dir, "#thirdparty/jpeg-compressor"])
if env["builtin_zstd"]:
env_basisu.Prepend(CPPPATH=["#thirdparty/zstd"])
diff --git a/modules/basis_universal/config.py b/modules/basis_universal/config.py
index d22f9454ed..6a67f2c77c 100644
--- a/modules/basis_universal/config.py
+++ b/modules/basis_universal/config.py
@@ -1,4 +1,5 @@
def can_build(env, platform):
+ env.module_add_dependencies("basis_universal", ["jpg"])
return True
diff --git a/modules/basis_universal/image_compress_basisu.cpp b/modules/basis_universal/image_compress_basisu.cpp
new file mode 100644
index 0000000000..41de62b20b
--- /dev/null
+++ b/modules/basis_universal/image_compress_basisu.cpp
@@ -0,0 +1,251 @@
+/**************************************************************************/
+/* image_compress_basisu.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 "image_compress_basisu.h"
+
+#include "servers/rendering_server.h"
+
+#include <transcoder/basisu_transcoder.h>
+#ifdef TOOLS_ENABLED
+#include <encoder/basisu_comp.h>
+#endif
+
+void basis_universal_init() {
+#ifdef TOOLS_ENABLED
+ basisu::basisu_encoder_init();
+#endif
+
+ basist::basisu_transcoder_init();
+}
+
+#ifdef TOOLS_ENABLED
+Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
+ Ref<Image> image = p_image->duplicate();
+ image->convert(Image::FORMAT_RGBA8);
+
+ basisu::basis_compressor_params params;
+
+ params.m_uastc = true;
+ params.m_quality_level = basisu::BASISU_QUALITY_MIN;
+ params.m_pack_uastc_flags &= ~basisu::cPackUASTCLevelMask;
+ params.m_pack_uastc_flags |= basisu::cPackUASTCLevelFastest;
+
+ params.m_rdo_uastc = 0.0f;
+ params.m_rdo_uastc_quality_scalar = 0.0f;
+ params.m_rdo_uastc_dict_size = 1024;
+
+ params.m_mip_fast = true;
+ params.m_multithreading = true;
+ params.m_check_for_alpha = false;
+
+ basisu::job_pool job_pool(OS::get_singleton()->get_processor_count());
+ params.m_pJob_pool = &job_pool;
+
+ BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
+ switch (p_channels) {
+ case Image::USED_CHANNELS_L: {
+ decompress_format = BASIS_DECOMPRESS_RGB;
+ } break;
+ case Image::USED_CHANNELS_LA: {
+ params.m_force_alpha = true;
+ decompress_format = BASIS_DECOMPRESS_RGBA;
+ } break;
+ case Image::USED_CHANNELS_R: {
+ decompress_format = BASIS_DECOMPRESS_RGB;
+ } break;
+ case Image::USED_CHANNELS_RG: {
+ // Currently RG textures are compressed as DXT5/ETC2_RGBA8 with a RA -> RG swizzle,
+ // as BasisUniversal didn't use to support ETC2_RG11 transcoding.
+ params.m_force_alpha = true;
+ image->convert_rg_to_ra_rgba8();
+ decompress_format = BASIS_DECOMPRESS_RG_AS_RA;
+ } break;
+ case Image::USED_CHANNELS_RGB: {
+ decompress_format = BASIS_DECOMPRESS_RGB;
+ } break;
+ case Image::USED_CHANNELS_RGBA: {
+ params.m_force_alpha = true;
+ decompress_format = BASIS_DECOMPRESS_RGBA;
+ } break;
+ }
+
+ {
+ // Encode the image with mipmaps.
+ Vector<uint8_t> image_data = image->get_data();
+ basisu::vector<basisu::image> basisu_mipmaps;
+
+ for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
+ int ofs, size, width, height;
+ image->get_mipmap_offset_size_and_dimensions(i, ofs, size, width, height);
+
+ basisu::image basisu_image(width, height);
+ memcpy(basisu_image.get_ptr(), image_data.ptr() + ofs, size);
+
+ if (i == 0) {
+ params.m_source_images.push_back(basisu_image);
+ } else {
+ basisu_mipmaps.push_back(basisu_image);
+ }
+ }
+
+ params.m_source_mipmap_images.push_back(basisu_mipmaps);
+ }
+
+ // Encode the image data.
+ Vector<uint8_t> basisu_data;
+
+ basisu::basis_compressor compressor;
+ compressor.init(params);
+
+ int basisu_err = compressor.process();
+ ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, basisu_data);
+
+ const basisu::uint8_vec &basisu_out = compressor.get_output_basis_file();
+ basisu_data.resize(basisu_out.size() + 4);
+
+ // Copy the encoded data to the buffer.
+ {
+ uint8_t *w = basisu_data.ptrw();
+ *(uint32_t *)w = decompress_format;
+
+ memcpy(w + 4, basisu_out.get_ptr(), basisu_out.size());
+ }
+
+ return basisu_data;
+}
+#endif // TOOLS_ENABLED
+
+Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
+ Ref<Image> image;
+ ERR_FAIL_NULL_V_MSG(p_data, image, "Cannot unpack invalid BasisUniversal data.");
+
+ const uint8_t *src_ptr = p_data;
+ int src_size = p_size;
+
+ basist::transcoder_texture_format basisu_format = basist::transcoder_texture_format::cTFTotalTextureFormats;
+ Image::Format image_format = Image::FORMAT_MAX;
+
+ // Get supported compression formats.
+ bool bptc_supported = RS::get_singleton()->has_os_feature("bptc");
+ bool s3tc_supported = RS::get_singleton()->has_os_feature("s3tc");
+ bool etc2_supported = RS::get_singleton()->has_os_feature("etc2");
+
+ switch (*(uint32_t *)(src_ptr)) {
+ case BASIS_DECOMPRESS_RG: {
+ // RGTC transcoding is currently performed with RG_AS_RA, fail.
+ ERR_FAIL_V(image);
+ } break;
+ case BASIS_DECOMPRESS_RGB: {
+ if (bptc_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFBC7_M6_OPAQUE_ONLY;
+ image_format = Image::FORMAT_BPTC_RGBA;
+ } else if (s3tc_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFBC1;
+ image_format = Image::FORMAT_DXT1;
+ } else if (etc2_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFETC1;
+ image_format = Image::FORMAT_ETC2_RGB8;
+ } else {
+ // No supported VRAM compression formats, decompress.
+ basisu_format = basist::transcoder_texture_format::cTFRGBA32;
+ image_format = Image::FORMAT_RGBA8;
+ }
+
+ } break;
+ case BASIS_DECOMPRESS_RGBA: {
+ if (bptc_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFBC7_M5;
+ image_format = Image::FORMAT_BPTC_RGBA;
+ } else if (s3tc_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFBC3;
+ image_format = Image::FORMAT_DXT5;
+ } else if (etc2_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFETC2;
+ image_format = Image::FORMAT_ETC2_RGBA8;
+ } else {
+ // No supported VRAM compression formats, decompress.
+ basisu_format = basist::transcoder_texture_format::cTFRGBA32;
+ image_format = Image::FORMAT_RGBA8;
+ }
+ } break;
+ case BASIS_DECOMPRESS_RG_AS_RA: {
+ if (s3tc_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFBC3;
+ image_format = Image::FORMAT_DXT5_RA_AS_RG;
+ } else if (etc2_supported) {
+ basisu_format = basist::transcoder_texture_format::cTFETC2;
+ image_format = Image::FORMAT_ETC2_RA_AS_RG;
+ } else {
+ // No supported VRAM compression formats, decompress.
+ basisu_format = basist::transcoder_texture_format::cTFRGBA32;
+ image_format = Image::FORMAT_RGBA8;
+ }
+ } break;
+ }
+
+ src_ptr += 4;
+ src_size -= 4;
+
+ basist::basisu_transcoder transcoder;
+ ERR_FAIL_COND_V(!transcoder.validate_header(src_ptr, src_size), image);
+
+ transcoder.start_transcoding(src_ptr, src_size);
+
+ basist::basisu_image_info basisu_info;
+ transcoder.get_image_info(src_ptr, src_size, basisu_info, 0);
+
+ Vector<uint8_t> out_data;
+ out_data.resize(Image::get_image_data_size(basisu_info.m_width, basisu_info.m_height, image_format, basisu_info.m_total_levels > 1));
+
+ uint8_t *dst = out_data.ptrw();
+ memset(dst, 0, out_data.size());
+
+ uint32_t mip_count = Image::get_image_required_mipmaps(basisu_info.m_orig_width, basisu_info.m_orig_height, image_format);
+ for (uint32_t i = 0; i <= mip_count; i++) {
+ basist::basisu_image_level_info basisu_level;
+ transcoder.get_image_level_info(src_ptr, src_size, basisu_level, 0, i);
+
+ int ofs = Image::get_image_mipmap_offset(basisu_info.m_width, basisu_info.m_height, image_format, i);
+ bool result = transcoder.transcode_image_level(src_ptr, src_size, 0, i, dst + ofs, basisu_level.m_total_blocks, basisu_format);
+
+ if (!result) {
+ print_line(vformat("BasisUniversal cannot unpack level %d.", i));
+ break;
+ }
+ }
+
+ image = Image::create_from_data(basisu_info.m_width, basisu_info.m_height, basisu_info.m_total_levels > 1, image_format, out_data);
+
+ return image;
+}
+
+Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
+ return basis_universal_unpacker_ptr(p_buffer.ptr(), p_buffer.size());
+}
diff --git a/scene/main/node.compat.inc b/modules/basis_universal/image_compress_basisu.h
index 7e957e5a14..ac5d62ae73 100644
--- a/scene/main/node.compat.inc
+++ b/modules/basis_universal/image_compress_basisu.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* node.compat.inc */
+/* image_compress_basisu.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,25 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef DISABLE_DEPRECATED
+#ifndef IMAGE_COMPRESS_BASISU_H
+#define IMAGE_COMPRESS_BASISU_H
-PackedStringArray Node::get_configuration_warnings_bind_compat_68420() const {
- return PackedStringArray(get_configuration_warnings());
-}
+#include "core/io/image.h"
-void Node::_bind_compatibility_methods() {
- ClassDB::bind_compatibility_method(D_METHOD("get_configuration_warnings"), &Node::get_configuration_warnings_bind_compat_68420);
-}
+enum BasisDecompressFormat {
+ BASIS_DECOMPRESS_RG,
+ BASIS_DECOMPRESS_RGB,
+ BASIS_DECOMPRESS_RGBA,
+ BASIS_DECOMPRESS_RG_AS_RA,
+};
-#endif // DISABLE_DEPRECATED
+void basis_universal_init();
+
+#ifdef TOOLS_ENABLED
+Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels);
+#endif
+
+Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size);
+Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer);
+
+#endif // IMAGE_COMPRESS_BASISU_H
diff --git a/modules/basis_universal/patches/external-jpgd.patch b/modules/basis_universal/patches/external-jpgd.patch
new file mode 100644
index 0000000000..7a805d00cb
--- /dev/null
+++ b/modules/basis_universal/patches/external-jpgd.patch
@@ -0,0 +1,13 @@
+diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
+index c431ceaf12..e87dd636a2 100644
+--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
++++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
+@@ -409,7 +409,7 @@ namespace basisu
+ bool load_jpg(const char *pFilename, image& img)
+ {
+ int width = 0, height = 0, actual_comps = 0;
+- uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagLinearChromaFiltering);
++ uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagBoxChromaFiltering);
+ if (!pImage_data)
+ return false;
+
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index c9ea67cb09..06a3fb76cb 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -30,262 +30,19 @@
#include "register_types.h"
-#include "core/os/os.h"
-#include "servers/rendering_server.h"
-
-#include <transcoder/basisu_transcoder.h>
-
-#ifdef TOOLS_ENABLED
-#include <encoder/basisu_comp.h>
-#endif
-
-enum BasisDecompressFormat {
- BASIS_DECOMPRESS_RG,
- BASIS_DECOMPRESS_RGB,
- BASIS_DECOMPRESS_RGBA,
- BASIS_DECOMPRESS_RG_AS_RA
-};
-
-//workaround for lack of ETC2 RG
-#define USE_RG_AS_RGBA
-
-#ifdef TOOLS_ENABLED
-static Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
- Vector<uint8_t> budata;
- {
- basisu::basis_compressor_params params;
- Ref<Image> image = p_image->duplicate();
- if (image->get_format() != Image::FORMAT_RGBA8) {
- image->convert(Image::FORMAT_RGBA8);
- }
-
- params.m_uastc = true;
- params.m_quality_level = basisu::BASISU_QUALITY_MIN;
-
- params.m_pack_uastc_flags &= ~basisu::cPackUASTCLevelMask;
-
- static const uint32_t s_level_flags[basisu::TOTAL_PACK_UASTC_LEVELS] = { basisu::cPackUASTCLevelFastest, basisu::cPackUASTCLevelFaster, basisu::cPackUASTCLevelDefault, basisu::cPackUASTCLevelSlower, basisu::cPackUASTCLevelVerySlow };
- params.m_pack_uastc_flags |= s_level_flags[0];
- params.m_rdo_uastc = 0.0f;
- params.m_rdo_uastc_quality_scalar = 0.0f;
- params.m_rdo_uastc_dict_size = 1024;
-
- params.m_mip_fast = true;
- params.m_multithreading = true;
-
- basisu::job_pool jpool(OS::get_singleton()->get_processor_count());
- params.m_pJob_pool = &jpool;
-
- BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
- params.m_check_for_alpha = false;
-
- switch (p_channels) {
- case Image::USED_CHANNELS_L: {
- decompress_format = BASIS_DECOMPRESS_RGB;
- } break;
- case Image::USED_CHANNELS_LA: {
- params.m_force_alpha = true;
- decompress_format = BASIS_DECOMPRESS_RGBA;
- } break;
- case Image::USED_CHANNELS_R: {
- decompress_format = BASIS_DECOMPRESS_RGB;
- } break;
- case Image::USED_CHANNELS_RG: {
-#ifdef USE_RG_AS_RGBA
- params.m_force_alpha = true;
- image->convert_rg_to_ra_rgba8();
- decompress_format = BASIS_DECOMPRESS_RG_AS_RA;
-#else
- params.m_seperate_rg_to_color_alpha = true;
- decompress_format = BASIS_DECOMPRESS_RG;
-#endif
- } break;
- case Image::USED_CHANNELS_RGB: {
- decompress_format = BASIS_DECOMPRESS_RGB;
- } break;
- case Image::USED_CHANNELS_RGBA: {
- params.m_force_alpha = true;
- decompress_format = BASIS_DECOMPRESS_RGBA;
- } break;
- }
-
- if (!image->has_mipmaps()) {
- basisu::image buimg(image->get_width(), image->get_height());
- Vector<uint8_t> vec = image->get_data();
- const uint8_t *r = vec.ptr();
- memcpy(buimg.get_ptr(), r, vec.size());
- params.m_source_images.push_back(buimg);
- } else {
- {
- Ref<Image> base_image = image->get_image_from_mipmap(0);
- Vector<uint8_t> image_vec = base_image->get_data();
- basisu::image buimg_image(base_image->get_width(), base_image->get_height());
- const uint8_t *r = image_vec.ptr();
- memcpy(buimg_image.get_ptr(), r, image_vec.size());
- params.m_source_images.push_back(buimg_image);
- }
- basisu::vector<basisu::image> images;
- for (int32_t mip_map_i = 1; mip_map_i <= image->get_mipmap_count(); mip_map_i++) {
- Ref<Image> mip_map = image->get_image_from_mipmap(mip_map_i);
- Vector<uint8_t> mip_map_vec = mip_map->get_data();
- basisu::image buimg_mipmap(mip_map->get_width(), mip_map->get_height());
- const uint8_t *r = mip_map_vec.ptr();
- memcpy(buimg_mipmap.get_ptr(), r, mip_map_vec.size());
- images.push_back(buimg_mipmap);
- }
- params.m_source_mipmap_images.push_back(images);
- }
-
- basisu::basis_compressor c;
- c.init(params);
-
- int buerr = c.process();
- ERR_FAIL_COND_V(buerr != basisu::basis_compressor::cECSuccess, budata);
-
- const basisu::uint8_vec &buvec = c.get_output_basis_file();
- budata.resize(buvec.size() + 4);
-
- {
- uint8_t *w = budata.ptrw();
- uint32_t *decf = (uint32_t *)w;
- *decf = decompress_format;
- memcpy(w + 4, &buvec[0], buvec.size());
- }
- }
-
- return budata;
-}
-#endif // TOOLS_ENABLED
-
-static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
- Ref<Image> image;
-
- const uint8_t *ptr = p_data;
- int size = p_size;
- ERR_FAIL_NULL_V_MSG(p_data, image, "Cannot unpack invalid basis universal data.");
-
- basist::transcoder_texture_format format = basist::transcoder_texture_format::cTFTotalTextureFormats;
- Image::Format imgfmt = Image::FORMAT_MAX;
-
- switch (*(uint32_t *)(ptr)) {
- case BASIS_DECOMPRESS_RG: {
- if (RS::get_singleton()->has_os_feature("rgtc")) {
- format = basist::transcoder_texture_format::cTFBC5; // get this from renderer
- imgfmt = Image::FORMAT_RGTC_RG;
- } else if (RS::get_singleton()->has_os_feature("etc2")) {
- //unfortunately, basis universal does not support
- //
- ERR_FAIL_V(image); //unimplemented here
- //format = basist::transcoder_texture_format::cTFETC1; // get this from renderer
- //imgfmt = Image::FORMAT_RGTC_RG;
- } else {
- // FIXME: There wasn't anything here, but then imgformat is used uninitialized.
- ERR_FAIL_V(image);
- }
- } break;
- case BASIS_DECOMPRESS_RGB: {
- if (RS::get_singleton()->has_os_feature("bptc")) {
- format = basist::transcoder_texture_format::cTFBC7_M6_OPAQUE_ONLY; // get this from renderer
- imgfmt = Image::FORMAT_BPTC_RGBA;
- } else if (RS::get_singleton()->has_os_feature("s3tc")) {
- format = basist::transcoder_texture_format::cTFBC1; // get this from renderer
- imgfmt = Image::FORMAT_DXT1;
- } else if (RS::get_singleton()->has_os_feature("etc2")) {
- format = basist::transcoder_texture_format::cTFETC1; // get this from renderer
- imgfmt = Image::FORMAT_ETC2_RGB8;
- } else {
- format = basist::transcoder_texture_format::cTFBGR565; // get this from renderer
- imgfmt = Image::FORMAT_RGB565;
- }
-
- } break;
- case BASIS_DECOMPRESS_RGBA: {
- if (RS::get_singleton()->has_os_feature("bptc")) {
- format = basist::transcoder_texture_format::cTFBC7_M5; // get this from renderer
- imgfmt = Image::FORMAT_BPTC_RGBA;
- } else if (RS::get_singleton()->has_os_feature("s3tc")) {
- format = basist::transcoder_texture_format::cTFBC3; // get this from renderer
- imgfmt = Image::FORMAT_DXT5;
- } else if (RS::get_singleton()->has_os_feature("etc2")) {
- format = basist::transcoder_texture_format::cTFETC2; // get this from renderer
- imgfmt = Image::FORMAT_ETC2_RGBA8;
- } else {
- //opengl most likely
- format = basist::transcoder_texture_format::cTFRGBA4444; // get this from renderer
- imgfmt = Image::FORMAT_RGBA4444;
- }
- } break;
- case BASIS_DECOMPRESS_RG_AS_RA: {
- if (RS::get_singleton()->has_os_feature("s3tc")) {
- format = basist::transcoder_texture_format::cTFBC3; // get this from renderer
- imgfmt = Image::FORMAT_DXT5_RA_AS_RG;
- } else if (RS::get_singleton()->has_os_feature("etc2")) {
- format = basist::transcoder_texture_format::cTFETC2; // get this from renderer
- imgfmt = Image::FORMAT_ETC2_RA_AS_RG;
- } else {
- //opengl most likely, bad for normal maps, nothing to do about this.
- format = basist::transcoder_texture_format::cTFRGBA32;
- imgfmt = Image::FORMAT_RGBA8;
- }
- } break;
- }
-
- ptr += 4;
- size -= 4;
-
- basist::basisu_transcoder tr;
-
- ERR_FAIL_COND_V(!tr.validate_header(ptr, size), image);
-
- tr.start_transcoding(ptr, size);
-
- basist::basisu_image_info info;
- tr.get_image_info(ptr, size, info, 0);
- Vector<uint8_t> gpudata;
- gpudata.resize(Image::get_image_data_size(info.m_width, info.m_height, imgfmt, info.m_total_levels > 1));
-
- uint8_t *w = gpudata.ptrw();
- uint8_t *dst = w;
- for (int i = 0; i < gpudata.size(); i++) {
- dst[i] = 0x00;
- }
- uint32_t mip_count = Image::get_image_required_mipmaps(info.m_orig_width, info.m_orig_height, imgfmt);
- for (uint32_t level_i = 0; level_i <= mip_count; level_i++) {
- basist::basisu_image_level_info level;
- tr.get_image_level_info(ptr, size, level, 0, level_i);
- int ofs = Image::get_image_mipmap_offset(info.m_width, info.m_height, imgfmt, level_i);
- bool ret = tr.transcode_image_level(ptr, size, 0, level_i, dst + ofs, level.m_total_blocks, format);
- if (!ret) {
- print_line(vformat("Basis universal cannot unpack level %d.", level_i));
- break;
- };
- }
-
- image = Image::create_from_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata);
-
- return image;
-}
-
-static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
- Ref<Image> image;
-
- const uint8_t *r = p_buffer.ptr();
- int size = p_buffer.size();
- return basis_universal_unpacker_ptr(r, size);
-}
+#include "image_compress_basisu.h"
void initialize_basis_universal_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
+ basis_universal_init();
+
#ifdef TOOLS_ENABLED
- using namespace basisu;
- using namespace basist;
- basisu_encoder_init();
Image::basis_universal_packer = basis_universal_packer;
#endif
- basist::basisu_transcoder_init();
+
Image::basis_universal_unpacker = basis_universal_unpacker;
Image::basis_universal_unpacker_ptr = basis_universal_unpacker_ptr;
}
@@ -298,6 +55,7 @@ void uninitialize_basis_universal_module(ModuleInitializationLevel p_level) {
#ifdef TOOLS_ENABLED
Image::basis_universal_packer = nullptr;
#endif
+
Image::basis_universal_unpacker = nullptr;
Image::basis_universal_unpacker_ptr = nullptr;
}
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 0e48e484c7..20d7fdb601 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -95,7 +95,7 @@ Variant GDScriptNativeClass::_new() {
}
Object *GDScriptNativeClass::instantiate() {
- return ClassDB::instantiate(name);
+ return ClassDB::instantiate_no_placeholders(name);
}
Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 053be7eec2..9ba41352f2 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -63,6 +63,10 @@ void GDScriptLanguageServer::_notification(int p_what) {
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("network/language_server")) {
+ break;
+ }
+
String remote_host = String(_EDITOR_GET("network/language_server/remote_host"));
int remote_port = (GDScriptLanguageServer::port_override > -1) ? GDScriptLanguageServer::port_override : (int)_EDITOR_GET("network/language_server/remote_port");
bool remote_use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index c6b68e9d34..d53bf7f7ec 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -1501,6 +1501,9 @@ GridMapEditor::~GridMapEditor() {
void GridMapEditorPlugin::_notification(int p_what) {
switch (p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/grid_map")) {
+ break;
+ }
switch ((int)EDITOR_GET("editors/grid_map/editor_side")) {
case 0: { // Left.
Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor);
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 859d77b262..3d087c9e27 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -1,8 +1,3 @@
-# Prior to .NET Core, we supported these: ["windows", "macos", "linuxbsd", "android", "web", "ios"]
-# Eventually support for each them should be added back.
-supported_platforms = ["windows", "macos", "linuxbsd", "android", "ios"]
-
-
def can_build(env, platform):
if env["arch"].startswith("rv"):
return False
@@ -14,9 +9,10 @@ def can_build(env, platform):
def configure(env):
- platform = env["platform"]
+ # Check if the platform has marked mono as supported.
+ supported = env.get("supported", [])
- if platform not in supported_platforms:
+ if not "mono" in supported:
raise RuntimeError("This module does not currently support building for this platform")
env.add_module_version_string("mono")
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
index 2cd1a08c0e..457d8daa8e 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
@@ -2,17 +2,6 @@
namespace Godot.SourceGenerators.Sample
{
- partial class Generic<T> : GodotObject
- {
- private int _field;
- }
-
- // Generic again but different generic parameters
- partial class Generic<T, R> : GodotObject
- {
- private int _field;
- }
-
// Generic again but without generic parameters
partial class Generic : GodotObject
{
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs
new file mode 100644
index 0000000000..9c4f8ee1e1
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS0169
+
+namespace Godot.SourceGenerators.Sample
+{
+ partial class Generic1T<T> : GodotObject
+ {
+ private int _field;
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs
new file mode 100644
index 0000000000..80551a0b42
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs
@@ -0,0 +1,10 @@
+#pragma warning disable CS0169
+
+namespace Godot.SourceGenerators.Sample
+{
+ // Generic again but different generic parameters
+ partial class Generic2T<T, R> : GodotObject
+ {
+ private int _field;
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs
index d75922481c..84e319352d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/CSharpSourceGeneratorVerifier.cs
@@ -61,15 +61,15 @@ where TSourceGenerator : ISourceGenerator, new()
build_property.GodotProjectDir = {Constants.ExecutingAssemblyPath}
"""));
- verifier.TestState.Sources.AddRange(sources.Select(source =>
- {
- return (source, SourceText.From(File.ReadAllText(Path.Combine(Constants.SourceFolderPath, source))));
- }));
+ verifier.TestState.Sources.AddRange(sources.Select(source => (
+ source,
+ SourceText.From(File.ReadAllText(Path.Combine(Constants.SourceFolderPath, source)))
+ )));
- verifier.TestState.GeneratedSources.AddRange(generatedSources.Select(generatedSource =>
- {
- return (FullGeneratedSourceName(generatedSource), SourceText.From(File.ReadAllText(Path.Combine(Constants.GeneratedSourceFolderPath, generatedSource)), Encoding.UTF8));
- }));
+ verifier.TestState.GeneratedSources.AddRange(generatedSources.Select(generatedSource => (
+ FullGeneratedSourceName(generatedSource),
+ SourceText.From(File.ReadAllText(Path.Combine(Constants.GeneratedSourceFolderPath, generatedSource)), Encoding.UTF8)
+ )));
return verifier;
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs
index b7ad4217aa..4f6b50cf02 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -47,9 +48,33 @@ public class ScriptPathAttributeGeneratorTests
{
var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier(
new string[] { "Generic.cs" },
- new string[] { "Generic_ScriptPath.generated.cs" }
+ new string[] { "Generic(Of T)_ScriptPath.generated.cs" }
);
- verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic" }));
+ verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic<>" }));
+ await verifier.RunAsync();
+ }
+
+ [Fact]
+ public async void GenericMultipleClassesSameName()
+ {
+ var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier(
+ Array.Empty<string>(),
+ new string[] { "Generic(Of T)_ScriptPath.generated.cs" }
+ );
+ verifier.TestState.Sources.Add(("Generic.cs", File.ReadAllText(Path.Combine(Constants.SourceFolderPath, "Generic.GD0003.cs"))));
+ verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic<>", "global::Generic<,>", "global::Generic" }));
+ await verifier.RunAsync();
+ }
+
+ [Fact]
+ public async void NamespaceMultipleClassesSameName()
+ {
+ var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier(
+ Array.Empty<string>(),
+ new string[] { "NamespaceA.SameName_ScriptPath.generated.cs" }
+ );
+ verifier.TestState.Sources.Add(("SameName.cs", File.ReadAllText(Path.Combine(Constants.SourceFolderPath, "SameName.GD0003.cs"))));
+ verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::NamespaceA.SameName", "global::NamespaceB.SameName" }));
await verifier.RunAsync();
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic(Of T)_ScriptPath.generated.cs
index 72c48595a2..355b01f753 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic(Of T)_ScriptPath.generated.cs
@@ -1,5 +1,5 @@
using Godot;
[ScriptPathAttribute("res://Generic.cs")]
-partial class Generic
+partial class Generic<T>
{
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs
new file mode 100644
index 0000000000..cad9f2a46b
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs
@@ -0,0 +1,9 @@
+using Godot;
+namespace NamespaceA {
+
+[ScriptPathAttribute("res://SameName.cs")]
+partial class SameName
+{
+}
+
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs
index 160c5d193d..51dc359157 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/EventSignals.cs
@@ -4,4 +4,15 @@ public partial class EventSignals : GodotObject
{
[Signal]
public delegate void MySignalEventHandler(string str, int num);
+
+ private struct MyStruct { }
+
+ [Signal]
+ private delegate void {|GD0201:MyInvalidSignal|}();
+
+ [Signal]
+ private delegate void MyInvalidParameterTypeSignalEventHandler(MyStruct {|GD0202:myStruct|});
+
+ [Signal]
+ private delegate MyStruct {|GD0203:MyInvalidReturnTypeSignalEventHandler|}();
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs
new file mode 100644
index 0000000000..15c1e03801
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs
@@ -0,0 +1,18 @@
+using Godot;
+
+partial class Generic<T> : GodotObject
+{
+ private int _field;
+}
+
+// Generic again but different generic parameters
+partial class {|GD0003:Generic|}<T, R> : GodotObject
+{
+ private int _field;
+}
+
+// Generic again but without generic parameters
+partial class {|GD0003:Generic|} : GodotObject
+{
+ private int _field;
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs
index 84d1ede065..5a83e21e96 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs
@@ -4,15 +4,3 @@ partial class Generic<T> : GodotObject
{
private int _field;
}
-
-// Generic again but different generic parameters
-partial class Generic<T, R> : GodotObject
-{
- private int _field;
-}
-
-// Generic again but without generic parameters
-partial class Generic : GodotObject
-{
- private int _field;
-}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs
index 6e6d3a6f39..1908703a71 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0401.cs
@@ -15,8 +15,8 @@ public partial class CustomGlobalClass2 : Node
}
// This raises a GD0401 diagnostic error: global classes must inherit from GodotObject
-{|GD0401:[GlobalClass]
-public partial class CustomGlobalClass3
+[GlobalClass]
+public partial class {|GD0401:CustomGlobalClass3|}
{
-}|}
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs
index 1c0a169841..4f7885cf37 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/GlobalClass.GD0402.cs
@@ -8,8 +8,8 @@ public partial class CustomGlobalClass : GodotObject
}
// This raises a GD0402 diagnostic error: global classes can't have any generic type parameter
-{|GD0402:[GlobalClass]
-public partial class CustomGlobalClass<T> : GodotObject
+[GlobalClass]
+public partial class {|GD0402:CustomGlobalClass|}<T> : GodotObject
{
-}|}
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs
new file mode 100644
index 0000000000..3f4f79fc49
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs
@@ -0,0 +1,18 @@
+using Godot;
+
+namespace NamespaceA
+{
+ partial class SameName : GodotObject
+ {
+ private int _field;
+ }
+}
+
+// SameName again but different namespace
+namespace NamespaceB
+{
+ partial class {|GD0003:SameName|} : GodotObject
+ {
+ private int _field;
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 1113629fef..6cd5ddb42f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -1,7 +1,5 @@
-using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Diagnostics;
namespace Godot.SourceGenerators
{
@@ -67,430 +65,164 @@ namespace Godot.SourceGenerators
outerTypeDeclSyntax.SyntaxTree.FilePath));
}
- public static void ReportExportedMemberIsStatic(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
- bool isField = exportedMemberSymbol is IFieldSymbol;
-
- string message = $"Attempted to export static {(isField ? "field" : "property")}: " +
- $"'{exportedMemberSymbol.ToDisplayString()}'";
-
- string description = $"{message}. Only instance fields and properties can be exported." +
- " Remove the 'static' modifier or the '[Export]' attribute.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0101",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0101")),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportExportedMemberTypeNotSupported(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
- bool isField = exportedMemberSymbol is IFieldSymbol;
-
- string message = $"The type of the exported {(isField ? "field" : "property")} " +
- $"is not supported: '{exportedMemberSymbol.ToDisplayString()}'";
-
- string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0102",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0102")),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportExportedMemberIsReadOnly(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
- bool isField = exportedMemberSymbol is IFieldSymbol;
-
- string message = $"The exported {(isField ? "field" : "property")} " +
- $"is read-only: '{exportedMemberSymbol.ToDisplayString()}'";
-
- string description = isField ?
- $"{message}. Exported fields cannot be read-only." :
- $"{message}. Exported properties must be writable.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0103",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0103")),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportExportedMemberIsWriteOnly(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
-
- string message = $"The exported property is write-only: '{exportedMemberSymbol.ToDisplayString()}'";
-
- string description = $"{message}. Exported properties must be readable.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0104",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0104")),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportExportedMemberIsIndexer(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
-
- string message = $"Attempted to export indexer property: " +
- $"'{exportedMemberSymbol.ToDisplayString()}'";
-
- string description = $"{message}. Indexer properties can't be exported." +
- " Remove the '[Export]' attribute.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0105",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0105")),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportExportedMemberIsExplicitInterfaceImplementation(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
-
- string message = $"Attempted to export explicit interface property implementation: " +
- $"'{exportedMemberSymbol.ToDisplayString()}'";
-
- string description = $"{message}. Explicit interface implementations can't be exported." +
- " Remove the '[Export]' attribute.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0106",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0106")),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportOnlyNodesShouldExportNodes(
- GeneratorExecutionContext context,
- ISymbol exportedMemberSymbol
- )
- {
- var locations = exportedMemberSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
- bool isField = exportedMemberSymbol is IFieldSymbol;
-
- string message = $"Types not derived from Node should not export Node {(isField ? "fields" : "properties")}";
-
- string description = $"{message}. Node export is only supported in Node-derived classes.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0107",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description),
- location,
- location?.SourceTree?.FilePath));
- }
-
- public static void ReportSignalDelegateMissingSuffix(
- GeneratorExecutionContext context,
- INamedTypeSymbol delegateSymbol)
- {
- var locations = delegateSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
-
- string message = "The name of the delegate must end with 'EventHandler': " +
- delegateSymbol.ToDisplayString() +
- $". Did you mean '{delegateSymbol.Name}EventHandler'?";
+ public static readonly DiagnosticDescriptor MultipleClassesInGodotScriptRule =
+ new DiagnosticDescriptor(id: "GD0003",
+ title: "Found multiple classes with the same name in the same script file",
+ messageFormat: "Found multiple classes with the name '{0}' in the same script file",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "Found multiple classes with the same name in the same script file. A script file must only contain one class with a name that matches the file name.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0003"));
- string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
+ public static readonly DiagnosticDescriptor ExportedMemberIsStaticRule =
+ new DiagnosticDescriptor(id: "GD0101",
+ title: "The exported member is static",
+ messageFormat: "The exported member '{0}' is static",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The exported member is static. Only instance fields and properties can be exported. Remove the 'static' modifier, or the '[Export]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0101"));
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0201",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0201")),
- location,
- location?.SourceTree?.FilePath));
- }
+ public static readonly DiagnosticDescriptor ExportedMemberTypeIsNotSupportedRule =
+ new DiagnosticDescriptor(id: "GD0102",
+ title: "The type of the exported member is not supported",
+ messageFormat: "The type of the exported member '{0}' is not supported",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The type of the exported member is not supported. Use a supported type, or remove the '[Export]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0102"));
- public static void ReportSignalParameterTypeNotSupported(
- GeneratorExecutionContext context,
- IParameterSymbol parameterSymbol)
- {
- var locations = parameterSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ public static readonly DiagnosticDescriptor ExportedMemberIsReadOnlyRule =
+ new DiagnosticDescriptor(id: "GD0103",
+ title: "The exported member is read-only",
+ messageFormat: "The exported member '{0}' is read-only",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The exported member is read-only. Exported member must be writable.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0103"));
- string message = "The parameter of the delegate signature of the signal " +
- $"is not supported: '{parameterSymbol.ToDisplayString()}'";
+ public static readonly DiagnosticDescriptor ExportedPropertyIsWriteOnlyRule =
+ new DiagnosticDescriptor(id: "GD0104",
+ title: "The exported property is write-only",
+ messageFormat: "The exported property '{0}' is write-only",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The exported property is write-only. Exported properties must be readable.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0104"));
- string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
+ public static readonly DiagnosticDescriptor ExportedMemberIsIndexerRule =
+ new DiagnosticDescriptor(id: "GD0105",
+ title: "The exported property is an indexer",
+ messageFormat: "The exported property '{0}' is an indexer",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The exported property is an indexer. Remove the '[Export]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0105"));
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0202",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0202")),
- location,
- location?.SourceTree?.FilePath));
- }
+ public static readonly DiagnosticDescriptor ExportedMemberIsExplicitInterfaceImplementationRule =
+ new DiagnosticDescriptor(id: "GD0106",
+ title: "The exported property is an explicit interface implementation",
+ messageFormat: "The exported property '{0}' is an explicit interface implementation",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The exported property is an explicit interface implementation. Remove the '[Export]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0106"));
- public static void ReportSignalDelegateSignatureMustReturnVoid(
- GeneratorExecutionContext context,
- INamedTypeSymbol delegateSymbol)
- {
- var locations = delegateSymbol.Locations;
- var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
+ public static readonly DiagnosticDescriptor OnlyNodesShouldExportNodesRule =
+ new DiagnosticDescriptor(id: "GD0107",
+ title: "Types not derived from Node should not export Node members",
+ messageFormat: "Types not derived from Node should not export Node members",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "Types not derived from Node should not export Node members. Node export is only supported in Node-derived classes.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0107"));
- string message = "The delegate signature of the signal " +
- $"must return void: '{delegateSymbol.ToDisplayString()}'";
+ public static readonly DiagnosticDescriptor SignalDelegateMissingSuffixRule =
+ new DiagnosticDescriptor(id: "GD0201",
+ title: "The name of the delegate must end with 'EventHandler'",
+ messageFormat: "The name of the delegate '{0}' must end with 'EventHandler'",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The name of the delegate must end with 'EventHandler'. Rename the delegate accordingly, or remove the '[Signal]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0201"));
- string description = $"{message}. Return void or remove the '[Signal]' attribute.";
+ public static readonly DiagnosticDescriptor SignalParameterTypeNotSupportedRule =
+ new DiagnosticDescriptor(id: "GD0202",
+ title: "The parameter of the delegate signature of the signal is not supported",
+ messageFormat: "The parameter of the delegate signature of the signal '{0}' is not supported",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The parameter of the delegate signature of the signal is not supported. Use supported types only, or remove the '[Signal]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0202"));
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0203",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0203")),
- location,
- location?.SourceTree?.FilePath));
- }
+ public static readonly DiagnosticDescriptor SignalDelegateSignatureMustReturnVoidRule =
+ new DiagnosticDescriptor(id: "GD0203",
+ title: "The delegate signature of the signal must return void",
+ messageFormat: "The delegate signature of the signal '{0}' must return void",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The delegate signature of the signal must return void. Return void, or remove the '[Signal]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0203"));
public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
new DiagnosticDescriptor(id: "GD0301",
title: "The generic type argument must be a Variant compatible type",
- messageFormat: "The generic type argument must be a Variant compatible type: {0}",
+ messageFormat: "The generic type argument '{0}' must be a Variant compatible type",
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
"The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.",
helpLinkUri: string.Format(_helpLinkFormat, "GD0301"));
- public static void ReportGenericTypeArgumentMustBeVariant(
- SyntaxNodeAnalysisContext context,
- SyntaxNode typeArgumentSyntax,
- ISymbol typeArgumentSymbol)
- {
- string message = "The generic type argument " +
- $"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
-
- string description = $"{message}. Use a Variant compatible type as the generic type argument.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0301",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0301")),
- typeArgumentSyntax.GetLocation(),
- typeArgumentSyntax.SyntaxTree.FilePath));
- }
-
public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
new DiagnosticDescriptor(id: "GD0302",
- title: "The generic type parameter must be annotated with the MustBeVariant attribute",
- messageFormat: "The generic type argument must be a Variant type: {0}",
+ title: "The generic type parameter must be annotated with the '[MustBeVariant]' attribute",
+ messageFormat: "The generic type parameter '{0}' must be annotated with the '[MustBeVariant]' attribute",
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.",
+ "The generic type parameter must be annotated with the '[MustBeVariant]' attribute. Add the '[MustBeVariant]' attribute to the generic type parameter.",
helpLinkUri: string.Format(_helpLinkFormat, "GD0302"));
- public static void ReportGenericTypeParameterMustBeVariantAnnotated(
- SyntaxNodeAnalysisContext context,
- SyntaxNode typeArgumentSyntax,
- ISymbol typeArgumentSymbol)
- {
- string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
-
- string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0302",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0302")),
- typeArgumentSyntax.GetLocation(),
- typeArgumentSyntax.SyntaxTree.FilePath));
- }
-
public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
new DiagnosticDescriptor(id: "GD0303",
- title: "The generic type parameter must be annotated with the MustBeVariant attribute",
- messageFormat: "The generic type argument must be a Variant type: {0}",
+ title: "The parent symbol of a type argument that must be Variant compatible was not handled",
+ messageFormat: "The parent symbol '{0}' of a type argument that must be Variant compatible was not handled",
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.",
+ "The parent symbol of a type argument that must be Variant compatible was not handled. This is an issue in the engine, and should be reported.",
helpLinkUri: string.Format(_helpLinkFormat, "GD0303"));
- public static void ReportTypeArgumentParentSymbolUnhandled(
- SyntaxNodeAnalysisContext context,
- SyntaxNode typeArgumentSyntax,
- ISymbol parentSymbol)
- {
- string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
- "that must be Variant compatible was not handled.";
-
- string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0303",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0303")),
- typeArgumentSyntax.GetLocation(),
- typeArgumentSyntax.SyntaxTree.FilePath));
- }
-
public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule =
new DiagnosticDescriptor(id: "GD0401",
- title: "The class must derive from GodotObject or a derived class",
- messageFormat: "The class '{0}' must derive from GodotObject or a derived class",
+ title: $"The class must derive from {GodotClasses.GodotObject} or a derived class",
+ messageFormat: $"The class '{{0}}' must derive from {GodotClasses.GodotObject} or a derived class",
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.",
+ $"The class must derive from {GodotClasses.GodotObject} or a derived class. Change the base type, or remove the '[GlobalClass]' attribute.",
helpLinkUri: string.Format(_helpLinkFormat, "GD0401"));
- public static void ReportGlobalClassMustDeriveFromGodotObject(
- SyntaxNodeAnalysisContext context,
- SyntaxNode classSyntax,
- ISymbol typeSymbol)
- {
- string message = $"The class '{typeSymbol.ToDisplayString()}' must derive from GodotObject or a derived class";
-
- string description = $"{message}. Change the base class or remove the '[GlobalClass]' attribute.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0401",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0401")),
- classSyntax.GetLocation(),
- classSyntax.SyntaxTree.FilePath));
- }
-
public static readonly DiagnosticDescriptor GlobalClassMustNotBeGenericRule =
new DiagnosticDescriptor(id: "GD0402",
- title: "The class must not contain generic arguments",
- messageFormat: "The class '{0}' must not contain generic arguments",
+ title: "The class must not be generic",
+ messageFormat: "The class '{0}' must not be generic",
category: "Usage",
DiagnosticSeverity.Error,
isEnabledByDefault: true,
- "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.",
- helpLinkUri: string.Format(_helpLinkFormat, "GD0401"));
-
- public static void ReportGlobalClassMustNotBeGeneric(
- SyntaxNodeAnalysisContext context,
- SyntaxNode classSyntax,
- ISymbol typeSymbol)
- {
- string message = $"The class '{typeSymbol.ToDisplayString()}' must not contain generic arguments";
-
- string description = $"{message}. Remove the generic arguments or the '[GlobalClass]' attribute.";
-
- context.ReportDiagnostic(Diagnostic.Create(
- new DiagnosticDescriptor(id: "GD0402",
- title: message,
- messageFormat: message,
- category: "Usage",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true,
- description,
- helpLinkUri: string.Format(_helpLinkFormat, "GD0402")),
- classSyntax.GetLocation(),
- classSyntax.SyntaxTree.FilePath));
- }
+ "The class must not be generic. Make the class non-generic, or remove the '[GlobalClass]' attribute.",
+ helpLinkUri: string.Format(_helpLinkFormat, "GD0402"));
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index c7fd45238d..35db0d6f10 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -329,6 +329,11 @@ namespace Godot.SourceGenerators
}
}
+ public static Location? FirstLocationWithSourceTreeOrDefault(this IEnumerable<Location> locations)
+ {
+ return locations.FirstOrDefault(location => location.SourceTree != null) ?? locations.FirstOrDefault();
+ }
+
public static string Path(this Location location)
=> location.SourceTree?.GetLineSpan(location.SourceSpan).Path
?? location.GetLineSpan().Path;
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
index bcb35dae8a..77530ea049 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
@@ -9,7 +9,7 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace Godot.SourceGenerators
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
- public class GlobalClassAnalyzer : DiagnosticAnalyzer
+ public sealed class GlobalClassAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(
@@ -33,10 +33,22 @@ namespace Godot.SourceGenerators
return;
if (typeSymbol.IsGenericType)
- Common.ReportGlobalClassMustNotBeGeneric(context, typeClassDecl, typeSymbol);
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.GlobalClassMustNotBeGenericRule,
+ typeSymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
+ typeSymbol.ToDisplayString()
+ ));
+ }
if (!typeSymbol.InheritsFrom("GodotSharp", GodotClasses.GodotObject))
- Common.ReportGlobalClassMustDeriveFromGodotObject(context, typeClassDecl, typeSymbol);
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.GlobalClassMustDeriveFromGodotObjectRule,
+ typeSymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
+ typeSymbol.ToDisplayString()
+ ));
+ }
}
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
index b4f78fd218..95eaca4d3d 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MustBeVariantAnalyzer.cs
@@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.Diagnostics;
namespace Godot.SourceGenerators
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
- public class MustBeVariantAnalyzer : DiagnosticAnalyzer
+ public sealed class MustBeVariantAnalyzer : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
=> ImmutableArray.Create(
@@ -62,7 +62,11 @@ namespace Godot.SourceGenerators
{
if (!typeParamSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false))
{
- Common.ReportGenericTypeParameterMustBeVariantAnnotated(context, typeSyntax, typeSymbol);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.GenericTypeParameterMustBeVariantAnnotatedRule,
+ typeSyntax.GetLocation(),
+ typeSymbol.ToDisplayString()
+ ));
}
continue;
}
@@ -71,8 +75,11 @@ namespace Godot.SourceGenerators
if (marshalType is null)
{
- Common.ReportGenericTypeArgumentMustBeVariant(context, typeSyntax, typeSymbol);
- continue;
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.GenericTypeArgumentMustBeVariantRule,
+ typeSyntax.GetLocation(),
+ typeSymbol.ToDisplayString()
+ ));
}
}
}
@@ -106,8 +113,15 @@ namespace Godot.SourceGenerators
/// <param name="parentSymbol">The symbol retrieved for the parent node syntax.</param>
/// <param name="typeArgumentSyntax">The type node syntax of the argument type to check.</param>
/// <param name="typeArgumentSymbol">The symbol retrieved for the type node syntax.</param>
+ /// <param name="typeArgumentIndex"></param>
/// <returns><see langword="true"/> if the type must be variant and must be analyzed.</returns>
- private bool ShouldCheckTypeArgument(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntax, ISymbol parentSymbol, TypeSyntax typeArgumentSyntax, ITypeSymbol typeArgumentSymbol, int typeArgumentIndex)
+ private bool ShouldCheckTypeArgument(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode parentSyntax,
+ ISymbol parentSymbol,
+ TypeSyntax typeArgumentSyntax,
+ ITypeSymbol typeArgumentSymbol,
+ int typeArgumentIndex)
{
ITypeParameterSymbol? typeParamSymbol = parentSymbol switch
{
@@ -120,18 +134,24 @@ namespace Godot.SourceGenerators
INamedTypeSymbol { TypeParameters.Length: > 0 } typeSymbol
=> typeSymbol.TypeParameters[typeArgumentIndex],
+
_
=> null
};
- if (typeParamSymbol == null)
+ if (typeParamSymbol != null)
{
- Common.ReportTypeArgumentParentSymbolUnhandled(context, typeArgumentSyntax, parentSymbol);
- return false;
+ return typeParamSymbol.GetAttributes()
+ .Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false);
}
- return typeParamSymbol.GetAttributes()
- .Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.TypeArgumentParentSymbolUnhandledRule,
+ typeArgumentSyntax.GetLocation(),
+ parentSymbol.ToDisplayString()
+ ));
+
+ return false;
}
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index 625a6f9921..6dc541cc59 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -58,9 +58,10 @@ namespace Godot.SourceGenerators
.GroupBy<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol), INamedTypeSymbol>(x => x.symbol, SymbolEqualityComparer.Default)
.ToDictionary<IGrouping<INamedTypeSymbol, (ClassDeclarationSyntax cds, INamedTypeSymbol symbol)>, INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>>(g => g.Key, g => g.Select(x => x.cds), SymbolEqualityComparer.Default);
+ var usedPaths = new HashSet<string>();
foreach (var godotClass in godotClasses)
{
- VisitGodotScriptClass(context, godotProjectDir,
+ VisitGodotScriptClass(context, godotProjectDir, usedPaths,
symbol: godotClass.Key,
classDeclarations: godotClass.Value);
}
@@ -74,6 +75,7 @@ namespace Godot.SourceGenerators
private static void VisitGodotScriptClass(
GeneratorExecutionContext context,
string godotProjectDir,
+ HashSet<string> usedPaths,
INamedTypeSymbol symbol,
IEnumerable<ClassDeclarationSyntax> classDeclarations
)
@@ -93,8 +95,19 @@ namespace Godot.SourceGenerators
if (attributes.Length != 0)
attributes.Append("\n");
+ string scriptPath = RelativeToDir(cds.SyntaxTree.FilePath, godotProjectDir);
+ if (!usedPaths.Add(scriptPath))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.MultipleClassesInGodotScriptRule,
+ cds.Identifier.GetLocation(),
+ symbol.Name
+ ));
+ return;
+ }
+
attributes.Append(@"[ScriptPathAttribute(""res://");
- attributes.Append(RelativeToDir(cds.SyntaxTree.FilePath, godotProjectDir));
+ attributes.Append(scriptPath);
attributes.Append(@""")]");
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index 6e034c6e72..9c883ffbf0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -443,14 +443,22 @@ namespace Godot.SourceGenerators
if (propertySymbol.GetMethod == null)
{
// This should never happen, as we filtered WriteOnly properties, but just in case.
- Common.ReportExportedMemberIsWriteOnly(context, propertySymbol);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedPropertyIsWriteOnlyRule,
+ propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
+ propertySymbol.ToDisplayString()
+ ));
return null;
}
if (propertySymbol.SetMethod == null || propertySymbol.SetMethod.IsInitOnly)
{
// This should never happen, as we filtered ReadOnly properties, but just in case.
- Common.ReportExportedMemberIsReadOnly(context, propertySymbol);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsReadOnlyRule,
+ propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
+ propertySymbol.ToDisplayString()
+ ));
return null;
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index 253e24f092..27e7632ff7 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -131,33 +131,55 @@ namespace Godot.SourceGenerators
{
if (property.IsStatic)
{
- Common.ReportExportedMemberIsStatic(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsStaticRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault(),
+ property.ToDisplayString()
+ ));
continue;
}
if (property.IsIndexer)
{
- Common.ReportExportedMemberIsIndexer(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsIndexerRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault(),
+ property.ToDisplayString()
+ ));
continue;
}
- // TODO: We should still restore read-only properties after reloading assembly. Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
- // Ignore properties without a getter, without a setter or with an init-only setter. Godot properties must be both readable and writable.
+ // TODO: We should still restore read-only properties after reloading assembly.
+ // Two possible ways: reflection or turn RestoreGodotObjectData into a constructor overload.
+ // Ignore properties without a getter, without a setter or with an init-only setter.
+ // Godot properties must be both readable and writable.
if (property.IsWriteOnly)
{
- Common.ReportExportedMemberIsWriteOnly(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedPropertyIsWriteOnlyRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault(),
+ property.ToDisplayString()
+ ));
continue;
}
if (property.IsReadOnly || property.SetMethod!.IsInitOnly)
{
- Common.ReportExportedMemberIsReadOnly(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsReadOnlyRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault(),
+ property.ToDisplayString()
+ ));
continue;
}
if (property.ExplicitInterfaceImplementations.Length > 0)
{
- Common.ReportExportedMemberIsExplicitInterfaceImplementation(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsExplicitInterfaceImplementationRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault(),
+ property.ToDisplayString()
+ ));
continue;
}
@@ -166,7 +188,11 @@ namespace Godot.SourceGenerators
if (marshalType == null)
{
- Common.ReportExportedMemberTypeNotSupported(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberTypeIsNotSupportedRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault(),
+ property.ToDisplayString()
+ ));
continue;
}
@@ -175,7 +201,10 @@ namespace Godot.SourceGenerators
if (!symbol.InheritsFrom("GodotSharp", "Godot.Node") &&
propertyType.InheritsFrom("GodotSharp", "Godot.Node"))
{
- Common.ReportOnlyNodesShouldExportNodes(context, property);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.OnlyNodesShouldExportNodesRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault()
+ ));
}
}
@@ -194,7 +223,7 @@ namespace Godot.SourceGenerators
else
{
var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors
- .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault();
+ .FirstOrDefault(a => a.Keyword.IsKind(SyntaxKind.GetKeyword));
if (propertyGet != null)
{
if (propertyGet.ExpressionBody != null)
@@ -253,7 +282,11 @@ namespace Godot.SourceGenerators
{
if (field.IsStatic)
{
- Common.ReportExportedMemberIsStatic(context, field);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsStaticRule,
+ field.Locations.FirstLocationWithSourceTreeOrDefault(),
+ field.ToDisplayString()
+ ));
continue;
}
@@ -261,7 +294,11 @@ namespace Godot.SourceGenerators
// Ignore properties without a getter or without a setter. Godot properties must be both readable and writable.
if (field.IsReadOnly)
{
- Common.ReportExportedMemberIsReadOnly(context, field);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberIsReadOnlyRule,
+ field.Locations.FirstLocationWithSourceTreeOrDefault(),
+ field.ToDisplayString()
+ ));
continue;
}
@@ -270,7 +307,11 @@ namespace Godot.SourceGenerators
if (marshalType == null)
{
- Common.ReportExportedMemberTypeNotSupported(context, field);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.ExportedMemberTypeIsNotSupportedRule,
+ field.Locations.FirstLocationWithSourceTreeOrDefault(),
+ field.ToDisplayString()
+ ));
continue;
}
@@ -279,7 +320,10 @@ namespace Godot.SourceGenerators
if (!symbol.InheritsFrom("GodotSharp", "Godot.Node") &&
fieldType.InheritsFrom("GodotSharp", "Godot.Node"))
{
- Common.ReportOnlyNodesShouldExportNodes(context, field);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.OnlyNodesShouldExportNodesRule,
+ field.Locations.FirstLocationWithSourceTreeOrDefault()
+ ));
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 5246cc5780..323af85d3b 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -136,7 +136,11 @@ namespace Godot.SourceGenerators
{
if (!signalDelegateSymbol.Name.EndsWith(SignalDelegateSuffix))
{
- Common.ReportSignalDelegateMissingSuffix(context, signalDelegateSymbol);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.SignalDelegateMissingSuffixRule,
+ signalDelegateSymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
+ signalDelegateSymbol.ToDisplayString()
+ ));
continue;
}
@@ -154,21 +158,32 @@ namespace Godot.SourceGenerators
{
if (parameter.RefKind != RefKind.None)
{
- Common.ReportSignalParameterTypeNotSupported(context, parameter);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.SignalParameterTypeNotSupportedRule,
+ parameter.Locations.FirstLocationWithSourceTreeOrDefault(),
+ parameter.ToDisplayString()
+ ));
continue;
}
var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(parameter.Type, typeCache);
-
if (marshalType == null)
{
- Common.ReportSignalParameterTypeNotSupported(context, parameter);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.SignalParameterTypeNotSupportedRule,
+ parameter.Locations.FirstLocationWithSourceTreeOrDefault(),
+ parameter.ToDisplayString()
+ ));
}
}
if (!methodSymbol.ReturnsVoid)
{
- Common.ReportSignalDelegateSignatureMustReturnVoid(context, signalDelegateSymbol);
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.SignalDelegateSignatureMustReturnVoidRule,
+ signalDelegateSymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
+ signalDelegateSymbol.ToDisplayString()
+ ));
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
index c335e7b09f..09908c85a1 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj
@@ -5,10 +5,13 @@
<TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
+ <RuntimeIdentifier>win-x86</RuntimeIdentifier>
+ <SelfContained>False</SelfContained>
</PropertyGroup>
<PropertyGroup Condition="Exists('$(SolutionDir)/../../../../bin/GodotSharp/Api/Debug/GodotSharp.dll') And ('$(GodotPlatform)' == 'windows' Or ('$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT'))">
<OutputPath>$(SolutionDir)/../../../../bin/GodotSharp/Tools</OutputPath>
<AppendTargetFrameworkToOutputPath>False</AppendTargetFrameworkToOutputPath>
+ <AppendRuntimeIdentifierToOutputPath>False</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EnvDTE" Version="17.8.37221" />
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index ef9437066e..b4adc94c64 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -506,7 +506,7 @@ namespace GodotTools
_toolBarBuildButton = new Button
{
- Flat = true,
+ Flat = false,
Icon = EditorInterface.Singleton.GetEditorTheme().GetIcon("BuildCSharp", "EditorIcons"),
FocusMode = Control.FocusModeEnum.None,
Shortcut = EditorDefShortcut("mono/build_solution", "Build Project".TTR(), (Key)KeyModifierMask.MaskAlt | Key.B),
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index ff14ee7b5d..35b3f5a710 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -29,7 +29,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
- <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.8" />
+ <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.9" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<Reference Include="GodotSharp">
diff --git a/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs
index b86a2b8b24..8aeb19e08b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs
@@ -28,6 +28,15 @@ namespace GodotTools.Inspector
continue;
string scriptPath = script.ResourcePath;
+
+ if (string.IsNullOrEmpty(scriptPath))
+ {
+ // Generic types used empty paths in older versions of Godot
+ // so we assume your project is out of sync.
+ AddCustomControl(new InspectorOutOfSyncWarning());
+ break;
+ }
+
if (scriptPath.StartsWith("csharp://"))
{
// This is a virtual path used by generic types, extract the real path.
diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
index 8b9203d316..a4f7fad2f0 100644
--- a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
+++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml
@@ -27,7 +27,7 @@
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the given [param path] is configured for synchronization.
+ Returns [code]true[/code] if the given [param path] is configured for synchronization.
</description>
</method>
<method name="property_get_index" qualifiers="const">
@@ -48,21 +48,21 @@
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the property identified by the given [param path] is configured to be synchronized on spawn.
+ Returns [code]true[/code] if the property identified by the given [param path] is configured to be synchronized on spawn.
</description>
</method>
<method name="property_get_sync" deprecated="Use [method property_get_replication_mode] instead.">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the property identified by the given [param path] is configured to be synchronized on process.
+ Returns [code]true[/code] if the property identified by the given [param path] is configured to be synchronized on process.
</description>
</method>
<method name="property_get_watch" deprecated="Use [method property_get_replication_mode] instead.">
<return type="bool" />
<param index="0" name="path" type="NodePath" />
<description>
- Returns whether the property identified by the given [param path] is configured to be reliably synchronized when changes are detected on process.
+ Returns [code]true[/code] if the property identified by the given [param path] is configured to be reliably synchronized when changes are detected on process.
</description>
</method>
<method name="property_set_replication_mode">
diff --git a/modules/multiplayer/editor/replication_editor.cpp b/modules/multiplayer/editor/replication_editor.cpp
index 2e3df732e2..51974e7767 100644
--- a/modules/multiplayer/editor/replication_editor.cpp
+++ b/modules/multiplayer/editor/replication_editor.cpp
@@ -40,6 +40,7 @@
#include "editor/inspector_dock.h"
#include "editor/property_selector.h"
#include "editor/themes/editor_scale.h"
+#include "editor/themes/editor_theme_manager.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/separator.h"
#include "scene/gui/tree.h"
@@ -362,8 +363,13 @@ void ReplicationEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_da
void ReplicationEditor::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ if (!EditorThemeManager::is_generated_theme_outdated()) {
+ break;
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_ENTER_TREE: {
add_theme_style_override("panel", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("panel"), SNAME("Panel")));
add_pick_button->set_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons)));
pin->set_icon(get_theme_icon(SNAME("Pin"), EditorStringName(EditorIcons)));
diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp
index fecefc7e71..264a2e9c8e 100644
--- a/modules/multiplayer/multiplayer_spawner.cpp
+++ b/modules/multiplayer/multiplayer_spawner.cpp
@@ -87,8 +87,8 @@ void MultiplayerSpawner::_get_property_list(List<PropertyInfo> *p_list) const {
}
#endif
-Array MultiplayerSpawner::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray MultiplayerSpawner::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (spawn_path.is_empty() || !has_node(spawn_path)) {
warnings.push_back(RTR("A valid NodePath must be set in the \"Spawn Path\" property in order for MultiplayerSpawner to be able to spawn Nodes."));
diff --git a/modules/multiplayer/multiplayer_spawner.h b/modules/multiplayer/multiplayer_spawner.h
index 6cd2946df7..0e94b781ea 100644
--- a/modules/multiplayer/multiplayer_spawner.h
+++ b/modules/multiplayer/multiplayer_spawner.h
@@ -91,7 +91,7 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
#endif
public:
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
Node *get_spawn_node() const {
return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr;
diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp
index 16df9da78e..02e3a11964 100644
--- a/modules/multiplayer/multiplayer_synchronizer.cpp
+++ b/modules/multiplayer/multiplayer_synchronizer.cpp
@@ -143,8 +143,8 @@ bool MultiplayerSynchronizer::update_inbound_sync_time(uint16_t p_network_time)
return true;
}
-Array MultiplayerSynchronizer::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray MultiplayerSynchronizer::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (root_path.is_empty() || !has_node(root_path)) {
warnings.push_back(RTR("A valid NodePath must be set in the \"Root Path\" property in order for MultiplayerSynchronizer to be able to synchronize properties."));
diff --git a/modules/multiplayer/multiplayer_synchronizer.h b/modules/multiplayer/multiplayer_synchronizer.h
index f5f3993f0d..192d7a5920 100644
--- a/modules/multiplayer/multiplayer_synchronizer.h
+++ b/modules/multiplayer/multiplayer_synchronizer.h
@@ -91,7 +91,7 @@ public:
bool update_outbound_sync_time(uint64_t p_usec);
bool update_inbound_sync_time(uint16_t p_network_time);
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_replication_interval(double p_interval);
double get_replication_interval() const;
diff --git a/modules/noise/SCsub b/modules/noise/SCsub
index 1430aa0c4e..f309fd2dd4 100644
--- a/modules/noise/SCsub
+++ b/modules/noise/SCsub
@@ -5,23 +5,9 @@ Import("env_modules")
env_noise = env_modules.Clone()
-# Thirdparty source files
-
-thirdparty_obj = []
-
thirdparty_dir = "#thirdparty/noise/"
-thirdparty_sources = [
- # Add C++ source files for noise modules here
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
env_noise.Prepend(CPPPATH=[thirdparty_dir])
-env_thirdparty = env_noise.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
-env.modules_sources += thirdparty_obj
-
# Godot source files
module_obj = []
@@ -29,6 +15,3 @@ module_obj = []
env_noise.add_source_files(module_obj, "*.cpp")
env_noise.add_source_files(module_obj, "editor/*.cpp")
env.modules_sources += module_obj
-
-# Needed to force rebuilding the module files when the thirdparty library is updated.
-env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/openxr/doc_classes/OpenXRAPIExtension.xml b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
index 2f1f472a7e..f737f3b642 100644
--- a/modules/openxr/doc_classes/OpenXRAPIExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRAPIExtension.xml
@@ -30,6 +30,13 @@
Returns an error string for the given [url=https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrResult.html]XrResult[/url].
</description>
</method>
+ <method name="get_hand_tracker">
+ <return type="int" />
+ <param index="0" name="hand_index" type="int" />
+ <description>
+ Returns the corresponding [code]XRHandTrackerEXT[/code] handle for the given hand index value.
+ </description>
+ </method>
<method name="get_instance">
<return type="int" />
<description>
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 36c3faaf75..ccf97662e2 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -57,6 +57,7 @@
#include "extensions/openxr_fb_display_refresh_rate_extension.h"
#include "extensions/openxr_fb_foveation_extension.h"
#include "extensions/openxr_fb_update_swapchain_extension.h"
+#include "extensions/openxr_hand_tracking_extension.h"
#ifdef ANDROID_ENABLED
#define OPENXR_LOADER_NAME "libopenxr_loader.so"
@@ -1536,6 +1537,12 @@ void OpenXRAPI::cleanup_extension_wrappers() {
registered_extension_wrappers.clear();
}
+XrHandTrackerEXT OpenXRAPI::get_hand_tracker(int p_hand_index) {
+ ERR_FAIL_INDEX_V(p_hand_index, OpenXRHandTrackingExtension::HandTrackedHands::OPENXR_MAX_TRACKED_HANDS, XR_NULL_HANDLE);
+ OpenXRHandTrackingExtension::HandTrackedHands hand = static_cast<OpenXRHandTrackingExtension::HandTrackedHands>(p_hand_index);
+ return OpenXRHandTrackingExtension::get_singleton()->get_hand_tracker(hand)->hand_tracker;
+}
+
Size2 OpenXRAPI::get_recommended_target_size() {
ERR_FAIL_NULL_V(view_configuration_views, Size2());
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index d39e6e0b2e..d3e6eb01ce 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -359,6 +359,8 @@ public:
XrTime get_next_frame_time() { return frame_state.predictedDisplayTime + frame_state.predictedDisplayPeriod; }
bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; }
+ XrHandTrackerEXT get_hand_tracker(int p_hand_index);
+
Size2 get_recommended_target_size();
XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
diff --git a/modules/openxr/openxr_api_extension.cpp b/modules/openxr/openxr_api_extension.cpp
index d9e282e218..fae0fc13d3 100644
--- a/modules/openxr/openxr_api_extension.cpp
+++ b/modules/openxr/openxr_api_extension.cpp
@@ -51,6 +51,8 @@ void OpenXRAPIExtension::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_next_frame_time"), &OpenXRAPIExtension::get_next_frame_time);
ClassDB::bind_method(D_METHOD("can_render"), &OpenXRAPIExtension::can_render);
+ ClassDB::bind_method(D_METHOD("get_hand_tracker", "hand_index"), &OpenXRAPIExtension::get_hand_tracker);
+
ClassDB::bind_method(D_METHOD("register_composition_layer_provider", "extension"), &OpenXRAPIExtension::register_composition_layer_provider);
ClassDB::bind_method(D_METHOD("unregister_composition_layer_provider", "extension"), &OpenXRAPIExtension::unregister_composition_layer_provider);
@@ -138,6 +140,11 @@ bool OpenXRAPIExtension::can_render() {
return OpenXRAPI::get_singleton()->can_render();
}
+uint64_t OpenXRAPIExtension::get_hand_tracker(int p_hand_index) {
+ ERR_FAIL_NULL_V(OpenXRAPI::get_singleton(), 0);
+ return (uint64_t)OpenXRAPI::get_singleton()->get_hand_tracker(p_hand_index);
+}
+
void OpenXRAPIExtension::register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension) {
ERR_FAIL_NULL(OpenXRAPI::get_singleton());
OpenXRAPI::get_singleton()->register_composition_layer_provider(p_extension);
diff --git a/modules/openxr/openxr_api_extension.h b/modules/openxr/openxr_api_extension.h
index 82344c1d06..576e497798 100644
--- a/modules/openxr/openxr_api_extension.h
+++ b/modules/openxr/openxr_api_extension.h
@@ -72,6 +72,8 @@ public:
int64_t get_next_frame_time();
bool can_render();
+ uint64_t get_hand_tracker(int p_hand_index);
+
void register_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension);
void unregister_composition_layer_provider(OpenXRExtensionWrapperExtension *p_extension);
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index d49578d2a9..4a1037431a 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -334,7 +334,7 @@ String RegEx::sub(const String &p_subject, const String &p_replacement, bool p_a
return String();
}
- return String(output.ptr(), olength);
+ return String(output.ptr(), olength) + p_subject.substr(length);
}
bool RegEx::is_valid() const {
diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h
index 0b401da831..1c305f70f7 100644
--- a/modules/regex/tests/test_regex.h
+++ b/modules/regex/tests/test_regex.h
@@ -133,6 +133,18 @@ TEST_CASE("[RegEx] Substitution") {
RegEx re4("(a)(b){0}(c)");
REQUIRE(re4.is_valid());
CHECK(re4.sub(s4, "${1}.${3}.", true) == "a.c.a.c.a.c.");
+
+ const String s5 = "aaaa";
+
+ RegEx re5("a");
+ REQUIRE(re5.is_valid());
+ CHECK(re5.sub(s5, "b", true, 0, 2) == "bbaa");
+ CHECK(re5.sub(s5, "b", true, 1, 3) == "abba");
+ CHECK(re5.sub(s5, "b", true, 0, 0) == "aaaa");
+ CHECK(re5.sub(s5, "b", true, 1, 1) == "aaaa");
+ CHECK(re5.sub(s5, "cc", true, 0, 2) == "ccccaa");
+ CHECK(re5.sub(s5, "cc", true, 1, 3) == "acccca");
+ CHECK(re5.sub(s5, "", true, 0, 2) == "aa");
}
TEST_CASE("[RegEx] Substitution with empty input and/or replacement") {
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 98b3ecdeff..fea8ec3287 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -69,6 +69,7 @@ def get_flags():
return [
("arch", "arm64"), # Default for convenience.
("target", "template_debug"),
+ ("supported", ["mono"]),
]
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index d0db7b2e6c..28ab8e3335 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -830,8 +830,9 @@ bool EditorExportPlatformAndroid::_uses_vulkan() {
void EditorExportPlatformAndroid::_notification(int p_what) {
#ifndef ANDROID_ENABLED
if (p_what == NOTIFICATION_POSTINITIALIZE) {
- ERR_FAIL_NULL(EditorExport::get_singleton());
- EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformAndroid::_update_preset_status));
+ if (EditorExport::get_singleton()) {
+ EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformAndroid::_update_preset_status));
+ }
}
#endif
}
@@ -1911,13 +1912,22 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
}
bool EditorExportPlatformAndroid::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const {
+ if (p_preset == nullptr) {
+ return true;
+ }
+
+ bool advanced_options_enabled = p_preset->are_advanced_options_enabled();
+ if (p_option == "graphics/opengl_debug" ||
+ p_option == "command_line/extra_args" ||
+ p_option == "permissions/custom_permissions") {
+ return advanced_options_enabled;
+ }
if (p_option == "gradle_build/gradle_build_directory" || p_option == "gradle_build/android_source_template") {
- // @todo These are experimental options - keep them hidden for now.
- //return (bool)p_preset->get("gradle_build/use_gradle_build");
- return false;
- } else if (p_option == "custom_template/debug" || p_option == "custom_template/release") {
+ return advanced_options_enabled && bool(p_preset->get("gradle_build/use_gradle_build"));
+ }
+ if (p_option == "custom_template/debug" || p_option == "custom_template/release") {
// The APK templates are ignored if Gradle build is enabled.
- return !p_preset->get("gradle_build/use_gradle_build");
+ return advanced_options_enabled && !bool(p_preset->get("gradle_build/use_gradle_build"));
}
return true;
}
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 9f929ddf0a..4d6e3ae9ba 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -51,6 +51,7 @@ def get_flags():
("arch", "arm64"), # Default for convenience.
("target", "template_debug"),
("use_volk", False),
+ ("supported", ["mono"]),
]
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index ea2b23cfb9..91d75b0629 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -127,7 +127,9 @@ String EditorExportPlatformIOS::get_export_option_warning(const EditorExportPres
void EditorExportPlatformIOS::_notification(int p_what) {
#ifdef MACOS_ENABLED
if (p_what == NOTIFICATION_POSTINITIALIZE) {
- EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformIOS::_update_preset_status));
+ if (EditorExport::get_singleton()) {
+ EditorExport::get_singleton()->connect_presets_runnable_updated(callable_mp(this, &EditorExportPlatformIOS::_update_preset_status));
+ }
}
#endif
}
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 8fbd8b10b1..4856076436 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -67,6 +67,7 @@ def get_doc_path():
def get_flags():
return [
("arch", detect_arch()),
+ ("supported", ["mono"]),
]
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 85846335f7..f29275c910 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -34,6 +34,7 @@
#include "core/io/dir_access.h"
#include "main/main.h"
#include "servers/display_server.h"
+#include "servers/rendering_server.h"
#ifdef X11_ENABLED
#include "x11/display_server_x11.h"
diff --git a/platform/macos/SCsub b/platform/macos/SCsub
index 08783ee14a..355772fcd2 100644
--- a/platform/macos/SCsub
+++ b/platform/macos/SCsub
@@ -115,7 +115,7 @@ files = [
"godot_open_save_delegate.mm",
"dir_access_macos.mm",
"tts_macos.mm",
- "joypad_macos.cpp",
+ "joypad_macos.mm",
"rendering_context_driver_vulkan_macos.mm",
"gl_manager_macos_angle.mm",
"gl_manager_macos_legacy.mm",
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index cfbe9a8ee7..ed9e59ae05 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -56,6 +56,7 @@ def get_flags():
return [
("arch", detect_arch()),
("use_volk", False),
+ ("supported", ["mono"]),
]
@@ -207,7 +208,9 @@ def configure(env: "SConsEnvironment"):
"-framework",
"IOKit",
"-framework",
- "ForceFeedback",
+ "GameController",
+ "-framework",
+ "CoreHaptics",
"-framework",
"CoreVideo",
"-framework",
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index ce4c7b2e05..d8e546f571 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -2559,7 +2559,7 @@ bool DisplayServerMacOS::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, N
}
// Confine mouse position to the window, and update delta.
- NSRect frame = [p_wd.window_object frame];
+ NSRect frame = [p_wd.window_view frame];
NSPoint conf_pos = r_mpos;
conf_pos.x = CLAMP(conf_pos.x + r_delta.x, 0.f, frame.size.width);
conf_pos.y = CLAMP(conf_pos.y - r_delta.y, 0.f, frame.size.height);
diff --git a/platform/macos/joypad_macos.cpp b/platform/macos/joypad_macos.cpp
deleted file mode 100644
index 1fcd636a4b..0000000000
--- a/platform/macos/joypad_macos.cpp
+++ /dev/null
@@ -1,619 +0,0 @@
-/**************************************************************************/
-/* joypad_macos.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 "joypad_macos.h"
-
-#include <machine/endian.h>
-
-#define GODOT_JOY_LOOP_RUN_MODE CFSTR("GodotJoypad")
-
-static JoypadMacOS *self = nullptr;
-
-joypad::joypad() {
- ff_constant_force.lMagnitude = 10000;
- ff_effect.dwDuration = 0;
- ff_effect.dwSamplePeriod = 0;
- ff_effect.dwGain = 10000;
- ff_effect.dwFlags = FFEFF_OBJECTOFFSETS;
- ff_effect.dwTriggerButton = FFEB_NOTRIGGER;
- ff_effect.dwStartDelay = 0;
- ff_effect.dwTriggerRepeatInterval = 0;
- ff_effect.lpEnvelope = nullptr;
- ff_effect.cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
- ff_effect.lpvTypeSpecificParams = &ff_constant_force;
- ff_effect.dwSize = sizeof(ff_effect);
-}
-
-void joypad::free() {
- if (device_ref) {
- IOHIDDeviceUnscheduleFromRunLoop(device_ref, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE);
- }
- if (ff_device) {
- FFDeviceReleaseEffect(ff_device, ff_object);
- FFReleaseDevice(ff_device);
- ff_device = nullptr;
- memfree(ff_axes);
- memfree(ff_directions);
- }
-}
-
-bool joypad::has_element(IOHIDElementCookie p_cookie, Vector<rec_element> *p_list) const {
- for (int i = 0; i < p_list->size(); i++) {
- if (p_cookie == p_list->get(i).cookie) {
- return true;
- }
- }
- return false;
-}
-
-int joypad::get_hid_element_state(rec_element *p_element) const {
- int value = 0;
- if (p_element && p_element->ref) {
- IOHIDValueRef valueRef;
- if (IOHIDDeviceGetValue(device_ref, p_element->ref, &valueRef) == kIOReturnSuccess) {
- value = (SInt32)IOHIDValueGetIntegerValue(valueRef);
-
- // Record min and max for auto calibration.
- if (value < p_element->min) {
- p_element->min = value;
- }
- if (value > p_element->max) {
- p_element->max = value;
- }
- }
- }
- return value;
-}
-
-void joypad::add_hid_element(IOHIDElementRef p_element) {
- const CFTypeID elementTypeID = p_element ? CFGetTypeID(p_element) : 0;
-
- if (p_element && (elementTypeID == IOHIDElementGetTypeID())) {
- const IOHIDElementCookie cookie = IOHIDElementGetCookie(p_element);
- const uint32_t usagePage = IOHIDElementGetUsagePage(p_element);
- const uint32_t usage = IOHIDElementGetUsage(p_element);
- Vector<rec_element> *list = nullptr;
-
- switch (IOHIDElementGetType(p_element)) {
- case kIOHIDElementTypeInput_Misc:
- case kIOHIDElementTypeInput_Button:
- case kIOHIDElementTypeInput_Axis: {
- switch (usagePage) {
- case kHIDPage_GenericDesktop:
- switch (usage) {
- case kHIDUsage_GD_X:
- case kHIDUsage_GD_Y:
- case kHIDUsage_GD_Z:
- case kHIDUsage_GD_Rx:
- case kHIDUsage_GD_Ry:
- case kHIDUsage_GD_Rz:
- case kHIDUsage_GD_Slider:
- case kHIDUsage_GD_Dial:
- case kHIDUsage_GD_Wheel:
- if (!has_element(cookie, &axis_elements)) {
- list = &axis_elements;
- }
- break;
-
- case kHIDUsage_GD_Hatswitch:
- if (!has_element(cookie, &hat_elements)) {
- list = &hat_elements;
- }
- break;
- case kHIDUsage_GD_DPadUp:
- case kHIDUsage_GD_DPadDown:
- case kHIDUsage_GD_DPadRight:
- case kHIDUsage_GD_DPadLeft:
- case kHIDUsage_GD_Start:
- case kHIDUsage_GD_Select:
- if (!has_element(cookie, &button_elements)) {
- list = &button_elements;
- }
- break;
- }
- break;
-
- case kHIDPage_Simulation:
- switch (usage) {
- case kHIDUsage_Sim_Rudder:
- case kHIDUsage_Sim_Throttle:
- case kHIDUsage_Sim_Accelerator:
- case kHIDUsage_Sim_Brake:
- if (!has_element(cookie, &axis_elements)) {
- list = &axis_elements;
- }
- break;
-
- default:
- break;
- }
- break;
-
- case kHIDPage_Button:
- case kHIDPage_Consumer:
- if (!has_element(cookie, &button_elements)) {
- list = &button_elements;
- }
- break;
-
- default:
- break;
- }
- } break;
-
- case kIOHIDElementTypeCollection: {
- CFArrayRef array = IOHIDElementGetChildren(p_element);
- if (array) {
- add_hid_elements(array);
- }
- } break;
-
- default:
- break;
- }
-
- if (list) { // Add to list.
- rec_element element;
-
- element.ref = p_element;
- element.usage = usage;
-
- element.min = (SInt32)IOHIDElementGetLogicalMin(p_element);
- element.max = (SInt32)IOHIDElementGetLogicalMax(p_element);
- element.cookie = IOHIDElementGetCookie(p_element);
- list->push_back(element);
- list->sort_custom<rec_element::Comparator>();
- }
- }
-}
-
-static void hid_element_added(const void *p_value, void *p_parameter) {
- joypad *joy = static_cast<joypad *>(p_parameter);
- joy->add_hid_element((IOHIDElementRef)p_value);
-}
-
-void joypad::add_hid_elements(CFArrayRef p_array) {
- CFRange range = { 0, CFArrayGetCount(p_array) };
- CFArrayApplyFunction(p_array, range, hid_element_added, this);
-}
-
-static void joypad_removed_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject) {
- self->_device_removed(res, ioHIDDeviceObject);
-}
-
-static void joypad_added_callback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef ioHIDDeviceObject) {
- self->_device_added(res, ioHIDDeviceObject);
-}
-
-static bool is_joypad(IOHIDDeviceRef p_device_ref) {
- int usage_page = 0;
- int usage = 0;
- CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDPrimaryUsagePageKey));
- if (refCF) {
- CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &usage_page);
- }
- if (usage_page != kHIDPage_GenericDesktop) {
- return false;
- }
-
- refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDPrimaryUsageKey));
- if (refCF) {
- CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &usage);
- }
- if ((usage != kHIDUsage_GD_Joystick &&
- usage != kHIDUsage_GD_GamePad &&
- usage != kHIDUsage_GD_MultiAxisController)) {
- return false;
- }
- return true;
-}
-
-void JoypadMacOS::_device_added(IOReturn p_res, IOHIDDeviceRef p_device) {
- if (p_res != kIOReturnSuccess || have_device(p_device)) {
- return;
- }
-
- joypad new_joypad;
- if (is_joypad(p_device)) {
- configure_joypad(p_device, &new_joypad);
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
- if (IOHIDDeviceGetService) {
-#endif
- const io_service_t ioservice = IOHIDDeviceGetService(p_device);
- if ((ioservice) && (FFIsForceFeedback(ioservice) == FF_OK) && new_joypad.config_force_feedback(ioservice)) {
- new_joypad.ffservice = ioservice;
- }
-#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
- }
-#endif
- device_list.push_back(new_joypad);
- }
- IOHIDDeviceScheduleWithRunLoop(p_device, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE);
-}
-
-void JoypadMacOS::_device_removed(IOReturn p_res, IOHIDDeviceRef p_device) {
- int device = get_joy_ref(p_device);
- ERR_FAIL_COND(device == -1);
-
- input->joy_connection_changed(device_list[device].id, false, "");
- device_list.write[device].free();
- device_list.remove_at(device);
-}
-
-static String _hex_str(uint8_t p_byte) {
- static const char *dict = "0123456789abcdef";
- char ret[3];
- ret[2] = 0;
-
- ret[0] = dict[p_byte >> 4];
- ret[1] = dict[p_byte & 0xF];
-
- return ret;
-}
-
-bool JoypadMacOS::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
- p_joy->device_ref = p_device_ref;
- // Get device name.
- String name;
- char c_name[256];
- CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductKey));
- if (!refCF) {
- refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDManufacturerKey));
- }
- if ((!refCF) || (!CFStringGetCString((CFStringRef)refCF, c_name, sizeof(c_name), kCFStringEncodingUTF8))) {
- name = "Unidentified Joypad";
- } else {
- name = c_name;
- }
-
- int id = input->get_unused_joy_id();
- ERR_FAIL_COND_V(id == -1, false);
- p_joy->id = id;
- int vendor = 0;
- refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVendorIDKey));
- if (refCF) {
- CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &vendor);
- }
-
- int product_id = 0;
- refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductIDKey));
- if (refCF) {
- CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id);
- }
-
- int version = 0;
- refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey));
- if (refCF) {
- CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version);
- }
-
- if (vendor && product_id) {
- char uid[128];
- snprintf(uid, 128, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version));
- input->joy_connection_changed(id, true, name, uid);
- } else {
- // Bluetooth device.
- String guid = "05000000";
- for (int i = 0; i < 12; i++) {
- if (i < name.size()) {
- guid += _hex_str(name[i]);
- } else {
- guid += "00";
- }
- }
- input->joy_connection_changed(id, true, name, guid);
- }
-
- CFArrayRef array = IOHIDDeviceCopyMatchingElements(p_device_ref, nullptr, kIOHIDOptionsTypeNone);
- if (array) {
- p_joy->add_hid_elements(array);
- CFRelease(array);
- }
- // Xbox controller hat values start at 1 rather than 0.
- p_joy->offset_hat = vendor == 0x45e &&
- (product_id == 0x0b05 ||
- product_id == 0x02e0 ||
- product_id == 0x02fd ||
- product_id == 0x0b13);
-
- return true;
-}
-
-#define FF_ERR() \
- { \
- if (ret != FF_OK) { \
- FFReleaseDevice(ff_device); \
- ff_device = nullptr; \
- return false; \
- } \
- }
-bool joypad::config_force_feedback(io_service_t p_service) {
- HRESULT ret = FFCreateDevice(p_service, &ff_device);
- ERR_FAIL_COND_V(ret != FF_OK, false);
-
- ret = FFDeviceSendForceFeedbackCommand(ff_device, FFSFFC_RESET);
- FF_ERR();
-
- ret = FFDeviceSendForceFeedbackCommand(ff_device, FFSFFC_SETACTUATORSON);
- FF_ERR();
-
- if (check_ff_features()) {
- ret = FFDeviceCreateEffect(ff_device, kFFEffectType_ConstantForce_ID, &ff_effect, &ff_object);
- FF_ERR();
- return true;
- }
- FFReleaseDevice(ff_device);
- ff_device = nullptr;
- return false;
-}
-#undef FF_ERR
-
-#define TEST_FF(ff) (features.supportedEffects & (ff))
-bool joypad::check_ff_features() {
- FFCAPABILITIES features;
- HRESULT ret = FFDeviceGetForceFeedbackCapabilities(ff_device, &features);
- if (ret == FF_OK && (features.supportedEffects & FFCAP_ET_CONSTANTFORCE)) {
- uint32_t val;
- ret = FFDeviceGetForceFeedbackProperty(ff_device, FFPROP_FFGAIN, &val, sizeof(val));
- if (ret != FF_OK) {
- return false;
- }
- int num_axes = features.numFfAxes;
- ff_axes = (DWORD *)memalloc(sizeof(DWORD) * num_axes);
- ff_directions = (LONG *)memalloc(sizeof(LONG) * num_axes);
-
- for (int i = 0; i < num_axes; i++) {
- ff_axes[i] = features.ffAxes[i];
- ff_directions[i] = 0;
- }
-
- ff_effect.cAxes = num_axes;
- ff_effect.rgdwAxes = ff_axes;
- ff_effect.rglDirection = ff_directions;
- return true;
- }
- return false;
-}
-
-static BitField<HatMask> process_hat_value(int p_min, int p_max, int p_value, bool p_offset_hat) {
- int range = (p_max - p_min + 1);
- int value = p_value - p_min;
- BitField<HatMask> hat_value;
- if (range == 4) {
- value *= 2;
- }
- if (p_offset_hat) {
- value -= 1;
- }
-
- switch (value) {
- case 0:
- hat_value.set_flag(HatMask::UP);
- break;
- case 1:
- hat_value.set_flag(HatMask::UP);
- hat_value.set_flag(HatMask::RIGHT);
- break;
- case 2:
- hat_value.set_flag(HatMask::RIGHT);
- break;
- case 3:
- hat_value.set_flag(HatMask::DOWN);
- hat_value.set_flag(HatMask::RIGHT);
- break;
- case 4:
- hat_value.set_flag(HatMask::DOWN);
- break;
- case 5:
- hat_value.set_flag(HatMask::DOWN);
- hat_value.set_flag(HatMask::LEFT);
- break;
- case 6:
- hat_value.set_flag(HatMask::LEFT);
- break;
- case 7:
- hat_value.set_flag(HatMask::UP);
- hat_value.set_flag(HatMask::LEFT);
- break;
- default:
- break;
- }
- return hat_value;
-}
-
-void JoypadMacOS::poll_joypads() const {
- while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
- // No-op. Pending callbacks will fire.
- }
-}
-
-static float axis_correct(int p_value, int p_min, int p_max) {
- // Convert to a value between -1.0f and 1.0f.
- return 2.0f * (p_value - p_min) / (p_max - p_min) - 1.0f;
-}
-
-void JoypadMacOS::process_joypads() {
- poll_joypads();
-
- for (int i = 0; i < device_list.size(); i++) {
- joypad &joy = device_list.write[i];
-
- for (int j = 0; j < joy.axis_elements.size(); j++) {
- rec_element &elem = joy.axis_elements.write[j];
- int value = joy.get_hid_element_state(&elem);
- input->joy_axis(joy.id, (JoyAxis)j, axis_correct(value, elem.min, elem.max));
- }
- for (int j = 0; j < joy.button_elements.size(); j++) {
- int value = joy.get_hid_element_state(&joy.button_elements.write[j]);
- input->joy_button(joy.id, (JoyButton)j, (value >= 1));
- }
- for (int j = 0; j < joy.hat_elements.size(); j++) {
- rec_element &elem = joy.hat_elements.write[j];
- int value = joy.get_hid_element_state(&elem);
- BitField<HatMask> hat_value = process_hat_value(elem.min, elem.max, value, joy.offset_hat);
- input->joy_hat(joy.id, hat_value);
- }
-
- if (joy.ffservice) {
- uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id);
- if (timestamp > joy.ff_timestamp) {
- Vector2 strength = input->get_joy_vibration_strength(joy.id);
- float duration = input->get_joy_vibration_duration(joy.id);
- if (strength.x == 0 && strength.y == 0) {
- joypad_vibration_stop(joy.id, timestamp);
- } else {
- float gain = MAX(strength.x, strength.y);
- joypad_vibration_start(joy.id, gain, duration, timestamp);
- }
- }
- }
- }
-}
-
-void JoypadMacOS::joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp) {
- joypad *joy = &device_list.write[get_joy_index(p_id)];
- joy->ff_timestamp = p_timestamp;
- joy->ff_effect.dwDuration = p_duration * FF_SECONDS;
- joy->ff_effect.dwGain = p_magnitude * FF_FFNOMINALMAX;
- FFEffectSetParameters(joy->ff_object, &joy->ff_effect, FFEP_DURATION | FFEP_GAIN);
- FFEffectStart(joy->ff_object, 1, 0);
-}
-
-void JoypadMacOS::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
- joypad *joy = &device_list.write[get_joy_index(p_id)];
- joy->ff_timestamp = p_timestamp;
- FFEffectStop(joy->ff_object);
-}
-
-int JoypadMacOS::get_joy_index(int p_id) const {
- for (int i = 0; i < device_list.size(); i++) {
- if (device_list[i].id == p_id) {
- return i;
- }
- }
- return -1;
-}
-
-int JoypadMacOS::get_joy_ref(IOHIDDeviceRef p_device) const {
- for (int i = 0; i < device_list.size(); i++) {
- if (device_list[i].device_ref == p_device) {
- return i;
- }
- }
- return -1;
-}
-
-bool JoypadMacOS::have_device(IOHIDDeviceRef p_device) const {
- for (int i = 0; i < device_list.size(); i++) {
- if (device_list[i].device_ref == p_device) {
- return true;
- }
- }
- return false;
-}
-
-static CFDictionaryRef create_match_dictionary(const UInt32 page, const UInt32 usage, int *okay) {
- CFDictionaryRef retval = nullptr;
- CFNumberRef pageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
- CFNumberRef usageNumRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
-
- if (pageNumRef && usageNumRef) {
- const void *keys[2] = { (void *)CFSTR(kIOHIDDeviceUsagePageKey), (void *)CFSTR(kIOHIDDeviceUsageKey) };
- const void *vals[2] = { (void *)pageNumRef, (void *)usageNumRef };
- retval = CFDictionaryCreate(kCFAllocatorDefault, keys, vals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- }
-
- if (pageNumRef) {
- CFRelease(pageNumRef);
- }
- if (usageNumRef) {
- CFRelease(usageNumRef);
- }
-
- if (!retval) {
- *okay = 0;
- }
-
- return retval;
-}
-
-void JoypadMacOS::config_hid_manager(CFArrayRef p_matching_array) const {
- CFRunLoopRef runloop = CFRunLoopGetCurrent();
- IOReturn ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
- ERR_FAIL_COND(ret != kIOReturnSuccess);
-
- IOHIDManagerSetDeviceMatchingMultiple(hid_manager, p_matching_array);
- IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, joypad_added_callback, nullptr);
- IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, joypad_removed_callback, nullptr);
- IOHIDManagerScheduleWithRunLoop(hid_manager, runloop, GODOT_JOY_LOOP_RUN_MODE);
-
- while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
- // No-op. Callback fires once per existing device.
- }
-}
-
-JoypadMacOS::JoypadMacOS(Input *in) {
- self = this;
- input = in;
-
- int okay = 1;
- const void *vals[] = {
- (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick, &okay),
- (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad, &okay),
- (void *)create_match_dictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController, &okay),
- };
- const size_t n_elements = sizeof(vals) / sizeof(vals[0]);
- CFArrayRef array = okay ? CFArrayCreate(kCFAllocatorDefault, vals, n_elements, &kCFTypeArrayCallBacks) : nullptr;
-
- for (size_t i = 0; i < n_elements; i++) {
- if (vals[i]) {
- CFRelease((CFTypeRef)vals[i]);
- }
- }
-
- if (array) {
- hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
- if (hid_manager) {
- config_hid_manager(array);
- }
- CFRelease(array);
- }
-}
-
-JoypadMacOS::~JoypadMacOS() {
- for (int i = 0; i < device_list.size(); i++) {
- device_list.write[i].free();
- }
-
- IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), GODOT_JOY_LOOP_RUN_MODE);
- IOHIDManagerClose(hid_manager, kIOHIDOptionsTypeNone);
- CFRelease(hid_manager);
- hid_manager = nullptr;
-}
diff --git a/platform/macos/joypad_macos.h b/platform/macos/joypad_macos.h
index 6a2af11b51..b37a1b24f3 100644
--- a/platform/macos/joypad_macos.h
+++ b/platform/macos/joypad_macos.h
@@ -28,93 +28,62 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef JOYPAD_MACOS_H
-#define JOYPAD_MACOS_H
-
#include "core/input/input.h"
-#import <ForceFeedback/ForceFeedback.h>
-#import <ForceFeedback/ForceFeedbackConstants.h>
-#import <IOKit/hid/IOHIDLib.h>
-#import <Kernel/IOKit/hidsystem/IOHIDUsageTables.h>
-
-struct rec_element {
- IOHIDElementRef ref;
- IOHIDElementCookie cookie;
+#define Key _QKey
+#import <CoreHaptics/CoreHaptics.h>
+#import <GameController/GameController.h>
+#undef Key
- uint32_t usage = 0;
+@interface JoypadMacOSObserver : NSObject
- int min = 0;
- int max = 0;
+- (void)startObserving;
+- (void)startProcessing;
+- (void)finishObserving;
- struct Comparator {
- bool operator()(const rec_element p_a, const rec_element p_b) const { return p_a.usage < p_b.usage; }
- };
-};
+@end
-struct joypad {
- IOHIDDeviceRef device_ref = nullptr;
+API_AVAILABLE(macosx(11))
+@interface RumbleMotor : NSObject
+@property(strong, nonatomic) CHHapticEngine *engine;
+@property(strong, nonatomic) id<CHHapticPatternPlayer> player;
+@end
- Vector<rec_element> axis_elements;
- Vector<rec_element> button_elements;
- Vector<rec_element> hat_elements;
+API_AVAILABLE(macosx(11))
+@interface RumbleContext : NSObject
+// High frequency motor, it's usually the right engine.
+@property(strong, nonatomic) RumbleMotor *weak_motor;
+// Low frequency motor, it's usually the left engine.
+@property(strong, nonatomic) RumbleMotor *strong_motor;
+@end
- int id = 0;
- bool offset_hat = false;
+// Controller support for macOS begins with macOS 10.9+,
+// however haptics (vibrations) are only supported in macOS 11+.
+@interface Joypad : NSObject
- io_service_t ffservice = 0; // Interface for force feedback, 0 = no ff.
- FFCONSTANTFORCE ff_constant_force;
- FFDeviceObjectReference ff_device = nullptr;
- FFEffectObjectReference ff_object = nullptr;
- uint64_t ff_timestamp = 0;
- LONG *ff_directions = nullptr;
- FFEFFECT ff_effect;
- DWORD *ff_axes = nullptr;
+@property(assign, nonatomic) BOOL force_feedback;
+@property(assign, nonatomic) NSInteger ff_effect_timestamp;
+@property(strong, nonatomic) GCController *controller;
+@property(strong, nonatomic) RumbleContext *rumble_context API_AVAILABLE(macosx(11));
- void add_hid_elements(CFArrayRef p_array);
- void add_hid_element(IOHIDElementRef p_element);
+- (instancetype)init;
+- (instancetype)init:(GCController *)controller;
- bool has_element(IOHIDElementCookie p_cookie, Vector<rec_element> *p_list) const;
- bool config_force_feedback(io_service_t p_service);
- bool check_ff_features();
-
- int get_hid_element_state(rec_element *p_element) const;
-
- void free();
- joypad();
-};
+@end
class JoypadMacOS {
- enum {
- JOYPADS_MAX = 16,
- };
-
private:
- Input *input = nullptr;
- IOHIDManagerRef hid_manager;
-
- Vector<joypad> device_list;
-
- bool have_device(IOHIDDeviceRef p_device) const;
- bool configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy);
-
- int get_joy_index(int p_id) const;
- int get_joy_ref(IOHIDDeviceRef p_device) const;
-
- void poll_joypads() const;
- void config_hid_manager(CFArrayRef p_matching_array) const;
-
- void joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp);
- void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
+ JoypadMacOSObserver *observer;
public:
- void process_joypads();
+ JoypadMacOS();
+ ~JoypadMacOS();
- void _device_added(IOReturn p_res, IOHIDDeviceRef p_device);
- void _device_removed(IOReturn p_res, IOHIDDeviceRef p_device);
+ API_AVAILABLE(macosx(11))
+ void joypad_vibration_start(Joypad *p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
+ API_AVAILABLE(macosx(11))
+ void joypad_vibration_stop(Joypad *p_joypad, uint64_t p_timestamp);
- JoypadMacOS(Input *in);
- ~JoypadMacOS();
+ void start_processing();
+ void process_joypads();
};
-
-#endif // JOYPAD_MACOS_H
diff --git a/platform/macos/joypad_macos.mm b/platform/macos/joypad_macos.mm
new file mode 100644
index 0000000000..2420bd73fb
--- /dev/null
+++ b/platform/macos/joypad_macos.mm
@@ -0,0 +1,608 @@
+/**************************************************************************/
+/* joypad_macos.mm */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+#import "joypad_macos.h"
+
+#include <Foundation/Foundation.h>
+
+#import "os_macos.h"
+
+#include "core/config/project_settings.h"
+#include "core/os/keyboard.h"
+#include "core/string/ustring.h"
+#include "main/main.h"
+
+@implementation RumbleMotor
+
+- (instancetype)initWithController:(GCController *)controller locality:(GCHapticsLocality)locality {
+ self = [super init];
+ self.engine = [controller.haptics createEngineWithLocality:locality];
+ self.player = nil;
+ return self;
+}
+
+- (void)execute_pattern:(CHHapticPattern *)pattern {
+ NSError *error;
+ id<CHHapticPatternPlayer> player = [self.engine createPlayerWithPattern:pattern error:&error];
+
+ // When all players have stopped for an engine, stop the engine.
+ [self.engine notifyWhenPlayersFinished:^CHHapticEngineFinishedAction(NSError *_Nullable error) {
+ return CHHapticEngineFinishedActionStopEngine;
+ }];
+
+ self.player = player;
+
+ // Starts the engine and returns if an error was encountered.
+ if (![self.engine startAndReturnError:&error]) {
+ print_verbose("Couldn't start controller haptic engine");
+ return;
+ }
+ if (![self.player startAtTime:0 error:&error]) {
+ print_verbose("Couldn't execute controller haptic pattern");
+ }
+}
+
+- (void)stop {
+ NSError *error;
+ [self.player stopAtTime:0 error:&error];
+ self.player = nil;
+}
+
+@end
+
+@implementation RumbleContext
+
+- (instancetype)init {
+ self = [super init];
+ self.weak_motor = nil;
+ self.strong_motor = nil;
+ return self;
+}
+
+- (bool)hasMotors {
+ return self.weak_motor != nil && self.strong_motor != nil;
+}
+- (bool)hasActivePlayers {
+ if (![self hasMotors]) {
+ return NO;
+ }
+ return self.weak_motor.player != nil && self.strong_motor.player != nil;
+}
+
+@end
+
+@implementation Joypad
+
+- (instancetype)init {
+ self = [super init];
+ return self;
+}
+- (instancetype)init:(GCController *)controller {
+ self = [super init];
+ self.controller = controller;
+
+ if (@available(macOS 11, *)) {
+ // Haptics within the controller is only available in macOS 11+.
+ self.rumble_context = [[RumbleContext alloc] init];
+
+ // Create Weak and Strong motors for controller.
+ self.rumble_context.weak_motor = [[RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityRightHandle];
+ self.rumble_context.strong_motor = [[RumbleMotor alloc] initWithController:controller locality:GCHapticsLocalityLeftHandle];
+
+ // If the rumble motors aren't available, disable force feedback.
+ if (![self.rumble_context hasMotors]) {
+ self.force_feedback = NO;
+ } else {
+ self.force_feedback = YES;
+ }
+ } else {
+ self.force_feedback = NO;
+ }
+
+ self.ff_effect_timestamp = 0;
+
+ return self;
+}
+
+@end
+
+JoypadMacOS::JoypadMacOS() {
+ observer = [[JoypadMacOSObserver alloc] init];
+ [observer startObserving];
+}
+
+JoypadMacOS::~JoypadMacOS() {
+ if (observer) {
+ [observer finishObserving];
+ observer = nil;
+ }
+}
+
+void JoypadMacOS::start_processing() {
+ if (observer) {
+ [observer startProcessing];
+ }
+ process_joypads();
+}
+
+API_AVAILABLE(macosx(10.15))
+CHHapticPattern *get_vibration_pattern(float p_magnitude, float p_duration) {
+ // Creates a vibration pattern with an intensity and duration.
+ NSDictionary *hapticDict = @{
+ CHHapticPatternKeyPattern : @[
+ @{
+ CHHapticPatternKeyEvent : @{
+ CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous,
+ CHHapticPatternKeyTime : @(CHHapticTimeImmediate),
+ CHHapticPatternKeyEventDuration : [NSNumber numberWithFloat:p_duration],
+
+ CHHapticPatternKeyEventParameters : @[
+ @{
+ CHHapticPatternKeyParameterID : CHHapticEventParameterIDHapticIntensity,
+ CHHapticPatternKeyParameterValue : [NSNumber numberWithFloat:p_magnitude]
+ },
+ ],
+ },
+ },
+ ],
+ };
+ NSError *error;
+ CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error];
+ return pattern;
+}
+
+void JoypadMacOS::joypad_vibration_start(Joypad *p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
+ if (!p_joypad.force_feedback || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
+ return;
+ }
+
+ // If there is active vibration players, stop them.
+ if ([p_joypad.rumble_context hasActivePlayers]) {
+ joypad_vibration_stop(p_joypad, p_timestamp);
+ }
+
+ // Gets the default vibration pattern and creates a player for each motor.
+ CHHapticPattern *weak_pattern = get_vibration_pattern(p_weak_magnitude, p_duration);
+ CHHapticPattern *strong_pattern = get_vibration_pattern(p_strong_magnitude, p_duration);
+
+ RumbleMotor *weak_motor = p_joypad.rumble_context.weak_motor;
+ RumbleMotor *strong_motor = p_joypad.rumble_context.strong_motor;
+
+ [weak_motor execute_pattern:weak_pattern];
+ [strong_motor execute_pattern:strong_pattern];
+
+ p_joypad.ff_effect_timestamp = p_timestamp;
+}
+
+void JoypadMacOS::joypad_vibration_stop(Joypad *p_joypad, uint64_t p_timestamp) {
+ if (!p_joypad.force_feedback) {
+ return;
+ }
+ // If there is no active vibration players, exit.
+ if (![p_joypad.rumble_context hasActivePlayers]) {
+ return;
+ }
+
+ RumbleMotor *weak_motor = p_joypad.rumble_context.weak_motor;
+ RumbleMotor *strong_motor = p_joypad.rumble_context.strong_motor;
+
+ [weak_motor stop];
+ [strong_motor stop];
+
+ p_joypad.ff_effect_timestamp = p_timestamp;
+}
+
+@interface JoypadMacOSObserver ()
+
+@property(assign, nonatomic) BOOL isObserving;
+@property(assign, nonatomic) BOOL isProcessing;
+@property(strong, nonatomic) NSMutableDictionary<NSNumber *, Joypad *> *connectedJoypads;
+@property(strong, nonatomic) NSMutableArray<Joypad *> *joypadsQueue;
+
+@end
+
+@implementation JoypadMacOSObserver
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.isObserving = NO;
+ self.isProcessing = NO;
+}
+
+- (void)startProcessing {
+ self.isProcessing = YES;
+
+ for (GCController *controller in self.joypadsQueue) {
+ [self addMacOSJoypad:controller];
+ }
+
+ [self.joypadsQueue removeAllObjects];
+}
+
+- (void)startObserving {
+ if (self.isObserving) {
+ return;
+ }
+
+ self.isObserving = YES;
+
+ self.connectedJoypads = [NSMutableDictionary dictionary];
+ self.joypadsQueue = [NSMutableArray array];
+
+ // Get told when controllers connect, this will be called right away for
+ // already connected controllers.
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasConnected:)
+ name:GCControllerDidConnectNotification
+ object:nil];
+
+ // Get told when controllers disconnect.
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(controllerWasDisconnected:)
+ name:GCControllerDidDisconnectNotification
+ object:nil];
+}
+
+- (void)finishObserving {
+ if (self.isObserving) {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ }
+
+ self.isObserving = NO;
+ self.isProcessing = NO;
+
+ self.connectedJoypads = nil;
+ self.joypadsQueue = nil;
+}
+
+- (void)dealloc {
+ [self finishObserving];
+}
+
+- (NSArray<NSNumber *> *)getAllKeysForController:(GCController *)controller {
+ NSArray *keys = [self.connectedJoypads allKeys];
+ NSMutableArray *final_keys = [NSMutableArray array];
+
+ for (NSNumber *key in keys) {
+ Joypad *joypad = [self.connectedJoypads objectForKey:key];
+ if (joypad.controller == controller) {
+ [final_keys addObject:key];
+ }
+ }
+
+ return final_keys;
+}
+
+- (int)getJoyIdForController:(GCController *)controller {
+ NSArray *keys = [self getAllKeysForController:controller];
+
+ for (NSNumber *key in keys) {
+ int joy_id = [key intValue];
+ return joy_id;
+ }
+
+ return -1;
+}
+
+- (void)addMacOSJoypad:(GCController *)controller {
+ // Get a new id for our controller.
+ int joy_id = Input::get_singleton()->get_unused_joy_id();
+
+ if (joy_id == -1) {
+ print_verbose("Couldn't retrieve new joy ID.");
+ return;
+ }
+
+ // Assign our player index.
+ if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+ controller.playerIndex = [self getFreePlayerIndex];
+ }
+
+ // Tell Godot about our new controller.
+ Input::get_singleton()->joy_connection_changed(joy_id, true, String::utf8([controller.vendorName UTF8String]));
+
+ Joypad *joypad = [[Joypad alloc] init:controller];
+
+ // Add it to our dictionary, this will retain our controllers.
+ [self.connectedJoypads setObject:joypad forKey:[NSNumber numberWithInt:joy_id]];
+
+ // Set our input handler.
+ [self setControllerInputHandler:controller];
+}
+
+- (void)controllerWasConnected:(NSNotification *)notification {
+ // Get our controller.
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ print_verbose("Couldn't retrieve new controller.");
+ return;
+ }
+
+ if ([[self getAllKeysForController:controller] count] > 0) {
+ print_verbose("Controller is already registered.");
+ } else if (!self.isProcessing) {
+ Joypad *joypad = [[Joypad alloc] init:controller];
+ [self.joypadsQueue addObject:joypad];
+ } else {
+ [self addMacOSJoypad:controller];
+ }
+}
+
+- (void)controllerWasDisconnected:(NSNotification *)notification {
+ // Find our joystick, there should be only one in our dictionary.
+ GCController *controller = (GCController *)notification.object;
+
+ if (!controller) {
+ return;
+ }
+
+ NSArray *keys = [self getAllKeysForController:controller];
+ for (NSNumber *key in keys) {
+ // Tell Godot this joystick is no longer there.
+ int joy_id = [key intValue];
+ Input::get_singleton()->joy_connection_changed(joy_id, false, "");
+
+ // And remove it from our dictionary.
+ [self.connectedJoypads removeObjectForKey:key];
+ }
+}
+
+- (GCControllerPlayerIndex)getFreePlayerIndex {
+ bool have_player_1 = false;
+ bool have_player_2 = false;
+ bool have_player_3 = false;
+ bool have_player_4 = false;
+
+ if (self.connectedJoypads == nil) {
+ NSArray *keys = [self.connectedJoypads allKeys];
+ for (NSNumber *key in keys) {
+ Joypad *joypad = [self.connectedJoypads objectForKey:key];
+ GCController *controller = joypad.controller;
+ if (controller.playerIndex == GCControllerPlayerIndex1) {
+ have_player_1 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex2) {
+ have_player_2 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex3) {
+ have_player_3 = true;
+ } else if (controller.playerIndex == GCControllerPlayerIndex4) {
+ have_player_4 = true;
+ }
+ }
+ }
+
+ if (!have_player_1) {
+ return GCControllerPlayerIndex1;
+ } else if (!have_player_2) {
+ return GCControllerPlayerIndex2;
+ } else if (!have_player_3) {
+ return GCControllerPlayerIndex3;
+ } else if (!have_player_4) {
+ return GCControllerPlayerIndex4;
+ } else {
+ return GCControllerPlayerIndexUnset;
+ }
+}
+
+- (void)setControllerInputHandler:(GCController *)controller {
+ // Hook in the callback handler for the correct gamepad profile.
+ // This is a bit of a weird design choice on Apples part.
+ // You need to select the most capable gamepad profile for the
+ // gamepad attached.
+ if (controller.extendedGamepad != nil) {
+ // The extended gamepad profile has all the input you could possibly find on
+ // a gamepad but will only be active if your gamepad actually has all of
+ // these...
+ _weakify(self);
+ _weakify(controller);
+
+ controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
+ _strongify(self);
+ _strongify(controller);
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonB) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::B,
+ gamepad.buttonB.isPressed);
+ } else if (element == gamepad.buttonX) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.buttonY) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::Y,
+ gamepad.buttonY.isPressed);
+ } else if (element == gamepad.leftShoulder) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::LEFT_SHOULDER,
+ gamepad.leftShoulder.isPressed);
+ } else if (element == gamepad.rightShoulder) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::RIGHT_SHOULDER,
+ gamepad.rightShoulder.isPressed);
+ } else if (element == gamepad.dpad) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_UP,
+ gamepad.dpad.up.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_DOWN,
+ gamepad.dpad.down.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_LEFT,
+ gamepad.dpad.left.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_RIGHT,
+ gamepad.dpad.right.isPressed);
+ }
+
+ if (element == gamepad.leftThumbstick) {
+ float value = gamepad.leftThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_X, value);
+ value = -gamepad.leftThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::LEFT_Y, value);
+ } else if (element == gamepad.rightThumbstick) {
+ float value = gamepad.rightThumbstick.xAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_X, value);
+ value = -gamepad.rightThumbstick.yAxis.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::RIGHT_Y, value);
+ } else if (element == gamepad.leftTrigger) {
+ float value = gamepad.leftTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_LEFT, value);
+ } else if (element == gamepad.rightTrigger) {
+ float value = gamepad.rightTrigger.value;
+ Input::get_singleton()->joy_axis(joy_id, JoyAxis::TRIGGER_RIGHT, value);
+ }
+
+ if (@available(macOS 10.14.1, *)) {
+ if (element == gamepad.leftThumbstickButton) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::LEFT_STICK,
+ gamepad.leftThumbstickButton.isPressed);
+ } else if (element == gamepad.rightThumbstickButton) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::RIGHT_STICK,
+ gamepad.rightThumbstickButton.isPressed);
+ }
+ }
+
+ if (@available(macOS 10.15, *)) {
+ if (element == gamepad.buttonOptions) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::BACK,
+ gamepad.buttonOptions.isPressed);
+ } else if (element == gamepad.buttonMenu) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::START,
+ gamepad.buttonMenu.isPressed);
+ }
+ }
+
+ if (@available(macOS 11, *)) {
+ if (element == gamepad.buttonHome) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::GUIDE,
+ gamepad.buttonHome.isPressed);
+ }
+ if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
+ GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
+ if (element == xboxGamepad.paddleButton1) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE1,
+ xboxGamepad.paddleButton1.isPressed);
+ } else if (element == xboxGamepad.paddleButton2) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE2,
+ xboxGamepad.paddleButton2.isPressed);
+ } else if (element == xboxGamepad.paddleButton3) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE3,
+ xboxGamepad.paddleButton3.isPressed);
+ } else if (element == xboxGamepad.paddleButton4) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::PADDLE4,
+ xboxGamepad.paddleButton4.isPressed);
+ }
+ }
+ }
+
+ if (@available(macOS 12, *)) {
+ if ([gamepad isKindOfClass:[GCXboxGamepad class]]) {
+ GCXboxGamepad *xboxGamepad = (GCXboxGamepad *)gamepad;
+ if (element == xboxGamepad.buttonShare) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::MISC1,
+ xboxGamepad.buttonShare.isPressed);
+ }
+ }
+ }
+ };
+ } else if (controller.microGamepad != nil) {
+ // Micro gamepads were added in macOS 10.11 and feature just 2 buttons and a d-pad.
+ _weakify(self);
+ _weakify(controller);
+
+ controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) {
+ _strongify(self);
+ _strongify(controller);
+
+ int joy_id = [self getJoyIdForController:controller];
+
+ if (element == gamepad.buttonA) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::A,
+ gamepad.buttonA.isPressed);
+ } else if (element == gamepad.buttonX) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::X,
+ gamepad.buttonX.isPressed);
+ } else if (element == gamepad.dpad) {
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_UP,
+ gamepad.dpad.up.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_DOWN,
+ gamepad.dpad.down.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_LEFT,
+ gamepad.dpad.left.isPressed);
+ Input::get_singleton()->joy_button(joy_id, JoyButton::DPAD_RIGHT,
+ gamepad.dpad.right.isPressed);
+ }
+ };
+ }
+
+ // TODO: Need to add support for controller.motion which gives us access to
+ // the orientation of the device (if supported).
+}
+
+@end
+
+void JoypadMacOS::process_joypads() {
+ if (@available(macOS 11, *)) {
+ // Process vibrations in macOS 11+.
+ NSArray *keys = [observer.connectedJoypads allKeys];
+
+ for (NSNumber *key in keys) {
+ int id = key.intValue;
+ Joypad *joypad = [observer.connectedJoypads objectForKey:key];
+
+ if (joypad.force_feedback) {
+ Input *input = Input::get_singleton();
+ uint64_t timestamp = input->get_joy_vibration_timestamp(id);
+
+ if (timestamp > (unsigned)joypad.ff_effect_timestamp) {
+ Vector2 strength = input->get_joy_vibration_strength(id);
+ float duration = input->get_joy_vibration_duration(id);
+ if (duration == 0) {
+ duration = GCHapticDurationInfinite;
+ }
+
+ if (strength.x == 0 && strength.y == 0) {
+ joypad_vibration_stop(joypad, timestamp);
+ } else {
+ joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h
index 9b20e51f47..8088c3431c 100644
--- a/platform/macos/os_macos.h
+++ b/platform/macos/os_macos.h
@@ -32,7 +32,7 @@
#define OS_MACOS_H
#include "crash_handler_macos.h"
-#include "joypad_macos.h"
+#import "joypad_macos.h"
#include "core/input/input.h"
#import "drivers/coreaudio/audio_driver_coreaudio.h"
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index 934767db74..29f433a51c 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -139,7 +139,7 @@ void OS_MacOS::finalize() {
}
void OS_MacOS::initialize_joypads() {
- joypad_macos = memnew(JoypadMacOS(Input::get_singleton()));
+ joypad_macos = memnew(JoypadMacOS());
}
void OS_MacOS::set_main_loop(MainLoop *p_main_loop) {
@@ -769,7 +769,7 @@ void OS_MacOS::run() {
if (DisplayServer::get_singleton()) {
DisplayServer::get_singleton()->process_events(); // Get rid of pending events.
}
- joypad_macos->process_joypads();
+ joypad_macos->start_processing();
if (Main::iteration()) {
quit = true;
diff --git a/platform/macos/platform_config.h b/platform/macos/platform_config.h
index 1a571b689a..01b0a12b5d 100644
--- a/platform/macos/platform_config.h
+++ b/platform/macos/platform_config.h
@@ -31,3 +31,10 @@
#include <alloca.h>
#define PTHREAD_RENAME_SELF
+
+#define _weakify(var) __weak typeof(var) GDWeak_##var = var;
+#define _strongify(var) \
+ _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wshadow\"") \
+ __strong typeof(var) var = GDWeak_##var; \
+ _Pragma("clang diagnostic pop")
diff --git a/platform/web/detect.py b/platform/web/detect.py
index 7d9b1de6b7..e692c79a20 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -253,6 +253,9 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-fvisibility=hidden"])
env.extra_suffix = ".dlink" + env.extra_suffix
+ # WASM_BIGINT is needed since emscripten ≥ 3.1.41
+ needs_wasm_bigint = cc_semver >= (3, 1, 41)
+
# Run the main application in a web worker
if env["proxy_to_pthread"]:
env.Append(LINKFLAGS=["-s", "PROXY_TO_PTHREAD=1"])
@@ -261,6 +264,9 @@ def configure(env: "SConsEnvironment"):
# https://github.com/emscripten-core/emscripten/issues/18034#issuecomment-1277561925
env.Append(LINKFLAGS=["-s", "TEXTDECODER=0"])
# BigInt support to pass object pointers between contexts
+ needs_wasm_bigint = True
+
+ if needs_wasm_bigint:
env.Append(LINKFLAGS=["-s", "WASM_BIGINT"])
# Reduce code size by generating less support code (e.g. skip NodeJS support).
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index f536c1ac27..4585884859 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -248,6 +248,7 @@ def get_flags():
return [
("arch", arch),
+ ("supported", ["mono"]),
]
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index d319c8ae4e..699d09c0d3 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -569,8 +569,8 @@ StringName AnimatedSprite2D::get_animation() const {
return animation;
}
-Array AnimatedSprite2D::get_configuration_warnings() const {
- Array warnings = Node2D::get_configuration_warnings();
+PackedStringArray AnimatedSprite2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (frames.is_null()) {
warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite2D to display frames."));
}
diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h
index 1f70f1af81..ac53bd26ee 100644
--- a/scene/2d/animated_sprite_2d.h
+++ b/scene/2d/animated_sprite_2d.h
@@ -125,7 +125,7 @@ public:
void set_flip_v(bool p_flip);
bool is_flipped_v() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
AnimatedSprite2D();
diff --git a/scene/2d/canvas_modulate.cpp b/scene/2d/canvas_modulate.cpp
index f87880afd7..2c5c6a1a16 100644
--- a/scene/2d/canvas_modulate.cpp
+++ b/scene/2d/canvas_modulate.cpp
@@ -113,8 +113,8 @@ Color CanvasModulate::get_color() const {
return color;
}
-Array CanvasModulate::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CanvasModulate::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_in_canvas && is_visible_in_tree()) {
List<Node *> nodes;
diff --git a/scene/2d/canvas_modulate.h b/scene/2d/canvas_modulate.h
index 05b126e889..08ded52e23 100644
--- a/scene/2d/canvas_modulate.h
+++ b/scene/2d/canvas_modulate.h
@@ -54,7 +54,7 @@ public:
void set_color(const Color &p_color);
Color get_color() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
CanvasModulate();
~CanvasModulate();
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index e36eda573c..4e5852984b 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -582,8 +582,8 @@ void CollisionObject2D::_update_pickable() {
}
}
-Array CollisionObject2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CollisionObject2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (shapes.is_empty()) {
warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape2D or CollisionPolygon2D as a child to define its shape."));
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
index 25ae9af8d5..780793f289 100644
--- a/scene/2d/collision_object_2d.h
+++ b/scene/2d/collision_object_2d.h
@@ -166,7 +166,7 @@ public:
void set_pickable(bool p_enabled);
bool is_pickable() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
_FORCE_INLINE_ RID get_rid() const { return rid; }
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index cfbdf65776..d2f71eca9d 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -232,8 +232,8 @@ bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, doubl
}
#endif
-Array CollisionPolygon2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CollisionPolygon2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject2D>(get_parent())) {
warnings.push_back(RTR("CollisionPolygon2D only serves to provide a collision shape to a CollisionObject2D derived node. Please only use it as a child of Area2D, StaticBody2D, RigidBody2D, CharacterBody2D, etc. to give them a shape."));
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index cbedc718d8..f1ee30babe 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -77,7 +77,7 @@ public:
void set_polygon(const Vector<Point2> &p_polygon);
Vector<Point2> get_polygon() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_disabled(bool p_disabled);
bool is_disabled() const;
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 2c6e675527..ee413c7bc2 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -173,8 +173,8 @@ bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double
return shape->_edit_is_selected_on_click(p_point, p_tolerance);
}
-Array CollisionShape2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CollisionShape2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
CollisionObject2D *col_object = Object::cast_to<CollisionObject2D>(get_parent());
if (col_object == nullptr) {
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index 18f16269c0..3e13dd698c 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -80,7 +80,7 @@ public:
void set_debug_color(const Color &p_color);
Color get_debug_color() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
CollisionShape2D();
};
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index c06b172b38..e04e6d7dce 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -246,8 +246,8 @@ bool CPUParticles2D::get_fractional_delta() const {
return fractional_delta;
}
-Array CPUParticles2D::get_configuration_warnings() const {
- Array warnings = Node2D::get_configuration_warnings();
+PackedStringArray CPUParticles2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr());
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 7108764bbe..3f858c3277 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -282,7 +282,7 @@ public:
void set_gravity(const Vector2 &p_gravity);
Vector2 get_gravity() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void restart();
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index d34e339448..bc39513c03 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -327,8 +327,8 @@ float GPUParticles2D::get_interp_to_end() const {
return interp_to_end_factor;
}
-Array GPUParticles2D::get_configuration_warnings() const {
- Array warnings = Node2D::get_configuration_warnings();
+PackedStringArray GPUParticles2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (process_material.is_null()) {
warnings.push_back(RTR("A material to process the particles is not assigned, so no behavior is imprinted."));
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index ba10e54251..58996b0327 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -161,7 +161,7 @@ public:
void set_amount_ratio(float p_ratio);
float get_amount_ratio() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_sub_emitter(const NodePath &p_path);
NodePath get_sub_emitter() const;
diff --git a/scene/2d/joint_2d.cpp b/scene/2d/joint_2d.cpp
index dd9a721240..41b121a482 100644
--- a/scene/2d/joint_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -212,8 +212,8 @@ bool Joint2D::get_exclude_nodes_from_collision() const {
return exclude_from_collision;
}
-Array Joint2D::get_configuration_warnings() const {
- Array warnings = Node2D::get_configuration_warnings();
+PackedStringArray Joint2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (!warning.is_empty()) {
warnings.push_back(warning);
diff --git a/scene/2d/joint_2d.h b/scene/2d/joint_2d.h
index 6a3777d3f1..5ff75a77a1 100644
--- a/scene/2d/joint_2d.h
+++ b/scene/2d/joint_2d.h
@@ -62,7 +62,7 @@ protected:
_FORCE_INLINE_ bool is_configured() const { return configured; }
public:
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
void set_node_a(const NodePath &p_node_a);
NodePath get_node_a() const;
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index a8aecd8257..c03786caef 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -401,14 +401,11 @@ Vector2 PointLight2D::get_texture_offset() const {
return texture_offset;
}
-Array PointLight2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray PointLight2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!texture.is_valid()) {
- Dictionary texture_warning;
- texture_warning["message"] = RTR("A texture with the shape of the light must be supplied.");
- texture_warning["property"] = "texture";
- warnings.push_back(texture_warning);
+ warnings.push_back(RTR("A texture with the shape of the light must be supplied to the \"Texture\" property."));
}
return warnings;
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 79b49b4c69..3c1171deae 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -174,7 +174,7 @@ public:
void set_texture_scale(real_t p_scale);
real_t get_texture_scale() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
PointLight2D();
};
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index f5da96d4f9..61f5d5cd46 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -247,8 +247,8 @@ int LightOccluder2D::get_occluder_light_mask() const {
return mask;
}
-Array LightOccluder2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray LightOccluder2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!occluder_polygon.is_valid()) {
warnings.push_back(RTR("An occluder polygon must be set (or drawn) for this occluder to take effect."));
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index b8add2e498..dd3130394e 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -105,7 +105,7 @@ public:
void set_as_sdf_collision(bool p_enable);
bool is_set_as_sdf_collision() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
LightOccluder2D();
~LightOccluder2D();
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 3b222e27c4..fee774fe2e 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -624,8 +624,8 @@ void NavigationAgent2D::_avoidance_done(Vector3 p_new_velocity) {
emit_signal(SNAME("velocity_computed"), safe_velocity);
}
-Array NavigationAgent2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray NavigationAgent2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node2D>(get_parent())) {
warnings.push_back(RTR("The NavigationAgent2D can be used only under a Node2D inheriting parent node."));
diff --git a/scene/2d/navigation_agent_2d.h b/scene/2d/navigation_agent_2d.h
index 7a8d008e90..0e46086a81 100644
--- a/scene/2d/navigation_agent_2d.h
+++ b/scene/2d/navigation_agent_2d.h
@@ -200,7 +200,7 @@ public:
void _avoidance_done(Vector3 p_new_velocity);
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const;
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index be224eb08b..04ba550888 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -337,8 +337,8 @@ void NavigationLink2D::set_travel_cost(real_t p_travel_cost) {
NavigationServer2D::get_singleton()->link_set_travel_cost(link, travel_cost);
}
-Array NavigationLink2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray NavigationLink2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (start_position.is_equal_approx(end_position)) {
warnings.push_back(RTR("NavigationLink2D start position should be different than the end position to be useful."));
diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h
index 7d8b375b6c..2929691c04 100644
--- a/scene/2d/navigation_link_2d.h
+++ b/scene/2d/navigation_link_2d.h
@@ -93,7 +93,7 @@ public:
void set_travel_cost(real_t p_travel_cost);
real_t get_travel_cost() const { return travel_cost; }
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
NavigationLink2D();
~NavigationLink2D();
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 134ca065ce..4a434fa3f8 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -283,8 +283,8 @@ void NavigationRegion2D::_navigation_debug_changed() {
}
#endif // DEBUG_ENABLED
-Array NavigationRegion2D::get_configuration_warnings() const {
- Array warnings = Node2D::get_configuration_warnings();
+PackedStringArray NavigationRegion2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!navigation_polygon.is_valid()) {
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 9e9a21d9f3..e9387376cb 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -114,7 +114,7 @@ public:
void set_avoidance_layer_value(int p_layer_number, bool p_value);
bool get_avoidance_layer_value(int p_layer_number) const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void bake_navigation_polygon(bool p_on_thread);
void _bake_finished(Ref<NavigationPolygon> p_navigation_polygon);
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 44cfc1deb2..3dd0d7b61c 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -137,8 +137,8 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s
_update_mirroring();
}
-Array ParallaxLayer::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray ParallaxLayer::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Object::cast_to<ParallaxBackground>(get_parent())) {
warnings.push_back(RTR("ParallaxLayer node only works when set as child of a ParallaxBackground node."));
diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h
index dbf9386198..22fa0dd51c 100644
--- a/scene/2d/parallax_layer.h
+++ b/scene/2d/parallax_layer.h
@@ -59,7 +59,7 @@ public:
void set_base_offset_and_scale(const Point2 &p_offset, real_t p_scale);
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
ParallaxLayer();
};
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 4fce49dc57..282d14da5d 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -287,8 +287,8 @@ void PathFollow2D::_validate_property(PropertyInfo &p_property) const {
}
}
-Array PathFollow2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray PathFollow2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path2D>(get_parent())) {
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 616903c788..bfd5cde5e9 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -106,7 +106,7 @@ public:
void set_cubic_interpolation_enabled(bool p_enabled);
bool is_cubic_interpolation_enabled() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
PathFollow2D() {}
};
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp
index 12a3e6da21..64cf56fa85 100644
--- a/scene/2d/physical_bone_2d.cpp
+++ b/scene/2d/physical_bone_2d.cpp
@@ -106,8 +106,8 @@ void PhysicalBone2D::_find_joint_child() {
}
}
-Array PhysicalBone2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray PhysicalBone2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!parent_skeleton) {
warnings.push_back(RTR("A PhysicalBone2D only works with a Skeleton2D or another PhysicalBone2D as a parent node!"));
diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h
index 1930ce1aba..e585f2c0ed 100644
--- a/scene/2d/physical_bone_2d.h
+++ b/scene/2d/physical_bone_2d.h
@@ -79,7 +79,7 @@ public:
void set_follow_bone_when_simulating(bool p_follow);
bool get_follow_bone_when_simulating() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
PhysicalBone2D();
~PhysicalBone2D();
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 2e0b020232..92af3fa3f0 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -928,10 +928,10 @@ void RigidBody2D::_notification(int p_what) {
#endif
}
-Array RigidBody2D::get_configuration_warnings() const {
+PackedStringArray RigidBody2D::get_configuration_warnings() const {
Transform2D t = get_transform();
- Array warnings = CollisionObject2D::get_configuration_warnings();
+ PackedStringArray warnings = CollisionObject2D::get_configuration_warnings();
if (ABS(t.columns[0].length() - 1.0) > 0.05 || ABS(t.columns[1].length() - 1.0) > 0.05) {
warnings.push_back(RTR("Size changes to RigidBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 44ff1ba822..88161e284a 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -313,7 +313,7 @@ public:
TypedArray<Node2D> get_colliding_bodies() const; //function for script
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
RigidBody2D();
~RigidBody2D();
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
index b3926a4638..5ea5098475 100644
--- a/scene/2d/remote_transform_2d.cpp
+++ b/scene/2d/remote_transform_2d.cpp
@@ -200,8 +200,8 @@ void RemoteTransform2D::force_update_cache() {
_update_cache();
}
-Array RemoteTransform2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray RemoteTransform2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
warnings.push_back(RTR("Path property must point to a valid Node2D node to work."));
diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h
index fe7289380a..997fd8fc69 100644
--- a/scene/2d/remote_transform_2d.h
+++ b/scene/2d/remote_transform_2d.h
@@ -70,7 +70,7 @@ public:
void force_update_cache();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
RemoteTransform2D();
};
diff --git a/scene/2d/shape_cast_2d.cpp b/scene/2d/shape_cast_2d.cpp
index 332e5eeb3e..90d80d7549 100644
--- a/scene/2d/shape_cast_2d.cpp
+++ b/scene/2d/shape_cast_2d.cpp
@@ -402,8 +402,8 @@ Array ShapeCast2D::_get_collision_result() const {
return ret;
}
-Array ShapeCast2D::get_configuration_warnings() const {
- Array warnings = Node2D::get_configuration_warnings();
+PackedStringArray ShapeCast2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node2D::get_configuration_warnings();
if (shape.is_null()) {
warnings.push_back(RTR("This node cannot interact with other objects unless a Shape2D is assigned."));
diff --git a/scene/2d/shape_cast_2d.h b/scene/2d/shape_cast_2d.h
index da7c58bacc..a577c351fd 100644
--- a/scene/2d/shape_cast_2d.h
+++ b/scene/2d/shape_cast_2d.h
@@ -118,7 +118,7 @@ public:
void remove_exception(const CollisionObject2D *p_node);
void clear_exceptions();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
ShapeCast2D();
};
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 432b8e914e..69e0414855 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -415,8 +415,8 @@ int Bone2D::get_index_in_skeleton() const {
return skeleton_index;
}
-Array Bone2D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray Bone2D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!skeleton) {
if (parent_bone) {
warnings.push_back(RTR("This Bone2D chain should end at a Skeleton2D node."));
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 1128ebb7c6..6a36a31552 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -78,7 +78,7 @@ public:
void apply_rest();
Transform2D get_skeleton_rest() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_autocalculate_length_and_angle(bool p_autocalculate);
bool get_autocalculate_length_and_angle() const;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index b8ca480e3b..48fe2afccc 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -846,8 +846,8 @@ TypedArray<Vector2i> TileMap::get_surrounding_cells(const Vector2i &p_coords) {
return tile_set->get_surrounding_cells(p_coords);
}
-Array TileMap::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray TileMap::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
// Retrieve the set of Z index values with a Y-sorted layer.
RBSet<int> y_sorted_z_index;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 6edf348caf..16750625e5 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -216,7 +216,7 @@ public:
GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *);
// Configuration warnings.
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
TileMap();
~TileMap();
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 3cab128be5..5683fb7306 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -87,8 +87,8 @@ void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
-Array BoneAttachment3D::get_configuration_warnings() const {
- Array warnings = Node3D::get_configuration_warnings();
+PackedStringArray BoneAttachment3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (use_external_skeleton) {
if (external_skeleton_node_cache.is_null()) {
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index 13fdccab3c..1bf44c2756 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -71,8 +71,7 @@ public:
virtual void notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Dictionary p_rename_map);
#endif // TOOLS_ENABLED
-public:
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
void set_bone_name(const String &p_name);
String get_bone_name() const;
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index 32799cacfe..0cfe0f8cb7 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -728,8 +728,8 @@ bool CollisionObject3D::get_capture_input_on_drag() const {
return capture_input_on_drag;
}
-Array CollisionObject3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CollisionObject3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (shapes.is_empty()) {
warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h
index dc3c9269e4..b51423f021 100644
--- a/scene/3d/collision_object_3d.h
+++ b/scene/3d/collision_object_3d.h
@@ -173,7 +173,7 @@ public:
_FORCE_INLINE_ RID get_rid() const { return rid; }
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
CollisionObject3D();
~CollisionObject3D();
diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp
index 29cec43011..9c1a7181aa 100644
--- a/scene/3d/collision_polygon_3d.cpp
+++ b/scene/3d/collision_polygon_3d.cpp
@@ -168,8 +168,8 @@ void CollisionPolygon3D::set_margin(real_t p_margin) {
}
}
-Array CollisionPolygon3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CollisionPolygon3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape."));
diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h
index 715ea721cf..61290a7947 100644
--- a/scene/3d/collision_polygon_3d.h
+++ b/scene/3d/collision_polygon_3d.h
@@ -74,7 +74,7 @@ public:
real_t get_margin() const;
void set_margin(real_t p_margin);
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
CollisionPolygon3D();
};
diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index 30692660f6..61941a0e53 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -118,8 +118,8 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) {
}
#endif
-Array CollisionShape3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray CollisionShape3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
CollisionObject3D *col_object = Object::cast_to<CollisionObject3D>(get_parent());
if (col_object == nullptr) {
diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h
index f66f88648e..bc0e70f8ac 100644
--- a/scene/3d/collision_shape_3d.h
+++ b/scene/3d/collision_shape_3d.h
@@ -64,7 +64,7 @@ public:
void set_disabled(bool p_disabled);
bool is_disabled() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
CollisionShape3D();
~CollisionShape3D();
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 32d68f7f78..8725ff19c9 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -203,8 +203,8 @@ bool CPUParticles3D::get_fractional_delta() const {
return fractional_delta;
}
-Array CPUParticles3D::get_configuration_warnings() const {
- Array warnings = GeometryInstance3D::get_configuration_warnings();
+PackedStringArray CPUParticles3D::get_configuration_warnings() const {
+ PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings();
bool mesh_found = false;
bool anim_material_found = false;
@@ -1684,7 +1684,6 @@ CPUParticles3D::CPUParticles3D() {
set_emitting(true);
set_amount(8);
- set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8)));
set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0);
set_param_min(PARAM_ANGULAR_VELOCITY, 0);
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index e9b75d9140..82ea4bbef3 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -305,7 +305,7 @@ public:
void set_gravity(const Vector3 &p_gravity);
Vector3 get_gravity() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void restart();
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index acd52da8b0..6878df21d8 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -162,8 +162,8 @@ void Decal::_validate_property(PropertyInfo &p_property) const {
}
}
-Array Decal::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray Decal::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("Decals are only available when using the Forward+ or Mobile rendering backends."));
diff --git a/scene/3d/decal.h b/scene/3d/decal.h
index 3bc6664afd..171b52815a 100644
--- a/scene/3d/decal.h
+++ b/scene/3d/decal.h
@@ -69,7 +69,7 @@ protected:
#endif // DISABLE_DEPRECATED
public:
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
void set_size(const Vector3 &p_size);
Vector3 get_size() const;
diff --git a/scene/3d/fog_volume.cpp b/scene/3d/fog_volume.cpp
index 1c17a733e9..12ca1888c4 100644
--- a/scene/3d/fog_volume.cpp
+++ b/scene/3d/fog_volume.cpp
@@ -117,8 +117,8 @@ AABB FogVolume::get_aabb() const {
return AABB();
}
-Array FogVolume::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray FogVolume::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
Ref<Environment> environment = get_viewport()->find_world_3d()->get_environment();
diff --git a/scene/3d/fog_volume.h b/scene/3d/fog_volume.h
index a185cdbd94..f7e861e3d0 100644
--- a/scene/3d/fog_volume.h
+++ b/scene/3d/fog_volume.h
@@ -66,7 +66,7 @@ public:
Ref<Material> get_material() const;
virtual AABB get_aabb() const override;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
FogVolume();
~FogVolume();
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index 6670c2c5f6..16813b9017 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -296,8 +296,8 @@ bool GPUParticles3D::get_interpolate() const {
return interpolate;
}
-Array GPUParticles3D::get_configuration_warnings() const {
- Array warnings = GeometryInstance3D::get_configuration_warnings();
+PackedStringArray GPUParticles3D::get_configuration_warnings() const {
+ PackedStringArray warnings = GeometryInstance3D::get_configuration_warnings();
bool meshes_found = false;
bool anim_material_found = false;
diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h
index 1697749d9b..0c9f2c1378 100644
--- a/scene/3d/gpu_particles_3d.h
+++ b/scene/3d/gpu_particles_3d.h
@@ -164,7 +164,7 @@ public:
void set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh);
Ref<Mesh> get_draw_pass_mesh(int p_pass) const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_sub_emitter(const NodePath &p_path);
NodePath get_sub_emitter() const;
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index f5d5795a27..cbc75801b0 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -527,8 +527,8 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() {
return ret;
}
-Array GPUParticlesCollisionSDF3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray GPUParticlesCollisionSDF3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (bake_mask == 0) {
warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property."));
diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h
index a4d8b15f9f..1649320069 100644
--- a/scene/3d/gpu_particles_collision_3d.h
+++ b/scene/3d/gpu_particles_collision_3d.h
@@ -170,7 +170,7 @@ protected:
#endif // DISABLE_DEPRECATED
public:
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
void set_thickness(float p_thickness);
float get_thickness() const;
diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp
index d76838d434..1e4e50182c 100644
--- a/scene/3d/joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -198,8 +198,8 @@ bool Joint3D::get_exclude_nodes_from_collision() const {
return exclude_from_collision;
}
-Array Joint3D::get_configuration_warnings() const {
- Array warnings = Node3D::get_configuration_warnings();
+PackedStringArray Joint3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (!warning.is_empty()) {
warnings.push_back(warning);
diff --git a/scene/3d/joint_3d.h b/scene/3d/joint_3d.h
index bf2519db12..527aed4079 100644
--- a/scene/3d/joint_3d.h
+++ b/scene/3d/joint_3d.h
@@ -63,7 +63,7 @@ protected:
_FORCE_INLINE_ bool is_configured() const { return configured; }
public:
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
void set_node_a(const NodePath &p_node_a);
NodePath get_node_a() const;
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index bc74ff2c0f..7b70986adc 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -169,8 +169,8 @@ AABB Light3D::get_aabb() const {
return AABB();
}
-Array Light3D::get_configuration_warnings() const {
- Array warnings = VisualInstance3D::get_configuration_warnings();
+PackedStringArray Light3D::get_configuration_warnings() const {
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (!get_scale().is_equal_approx(Vector3(1, 1, 1))) {
warnings.push_back(RTR("A light's scale does not affect the visual size of the light."));
@@ -596,8 +596,8 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const {
return shadow_mode;
}
-Array OmniLight3D::get_configuration_warnings() const {
- Array warnings = Light3D::get_configuration_warnings();
+PackedStringArray OmniLight3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Light3D::get_configuration_warnings();
if (!has_shadow() && get_projector().is_valid()) {
warnings.push_back(RTR("Projector texture only works with shadows active."));
@@ -628,8 +628,8 @@ OmniLight3D::OmniLight3D() :
set_shadow_mode(SHADOW_CUBE);
}
-Array SpotLight3D::get_configuration_warnings() const {
- Array warnings = Light3D::get_configuration_warnings();
+PackedStringArray SpotLight3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Light3D::get_configuration_warnings();
if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) {
warnings.push_back(RTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."));
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index b242493c28..d6eca8d8b6 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -147,7 +147,7 @@ public:
Color get_correlated_color() const;
virtual AABB get_aabb() const override;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
Light3D();
~Light3D();
@@ -217,7 +217,7 @@ public:
void set_shadow_mode(ShadowMode p_mode);
ShadowMode get_shadow_mode() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
OmniLight3D();
};
@@ -231,7 +231,7 @@ protected:
static void _bind_methods();
public:
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
SpotLight3D();
};
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index 008a09f141..86ff6d15dd 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -1528,8 +1528,8 @@ Ref<CameraAttributes> LightmapGI::get_camera_attributes() const {
return camera_attributes;
}
-Array LightmapGI::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray LightmapGI::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("Lightmap can only be baked from a device that supports the RD backends. Lightmap baking may fail."));
diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h
index 8d96c1e909..765e4a731d 100644
--- a/scene/3d/lightmap_gi.h
+++ b/scene/3d/lightmap_gi.h
@@ -302,7 +302,7 @@ public:
BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr);
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
LightmapGI();
};
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index ff0869b4ea..eb52d4540e 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -690,8 +690,8 @@ void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) {
emit_signal(SNAME("velocity_computed"), safe_velocity);
}
-Array NavigationAgent3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray NavigationAgent3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node3D>(get_parent())) {
warnings.push_back(RTR("The NavigationAgent3D can be used only under a Node3D inheriting parent node."));
diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h
index 5f7b249b7f..4eaed83149 100644
--- a/scene/3d/navigation_agent_3d.h
+++ b/scene/3d/navigation_agent_3d.h
@@ -221,7 +221,7 @@ public:
void _avoidance_done(Vector3 p_new_velocity);
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_avoidance_layers(uint32_t p_layers);
uint32_t get_avoidance_layers() const;
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index c535393273..dc776ebea2 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -458,8 +458,8 @@ void NavigationLink3D::set_travel_cost(real_t p_travel_cost) {
NavigationServer3D::get_singleton()->link_set_travel_cost(link, travel_cost);
}
-Array NavigationLink3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray NavigationLink3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (start_position.is_equal_approx(end_position)) {
warnings.push_back(RTR("NavigationLink3D start position should be different than the end position to be useful."));
diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h
index 771d470711..1867082811 100644
--- a/scene/3d/navigation_link_3d.h
+++ b/scene/3d/navigation_link_3d.h
@@ -99,7 +99,7 @@ public:
void set_travel_cost(real_t p_travel_cost);
real_t get_travel_cost() const { return travel_cost; }
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
};
#endif // NAVIGATION_LINK_3D_H
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 5d258d394a..d8a63c60a2 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -270,8 +270,8 @@ bool NavigationRegion3D::is_baking() const {
return NavigationServer3D::get_singleton()->is_baking_navigation_mesh(navigation_mesh);
}
-Array NavigationRegion3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray NavigationRegion3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!navigation_mesh.is_valid()) {
diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h
index b6dde0bec8..82468627de 100644
--- a/scene/3d/navigation_region_3d.h
+++ b/scene/3d/navigation_region_3d.h
@@ -108,7 +108,7 @@ public:
void _bake_finished(Ref<NavigationMesh> p_navigation_mesh);
bool is_baking() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
NavigationRegion3D();
~NavigationRegion3D();
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 0f56bfad52..a76488e479 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -693,8 +693,8 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node,
return BAKE_ERROR_OK;
}
-Array OccluderInstance3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray OccluderInstance3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) {
warnings.push_back(RTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling."));
diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h
index cf20655c2c..f607877e8f 100644
--- a/scene/3d/occluder_instance_3d.h
+++ b/scene/3d/occluder_instance_3d.h
@@ -181,7 +181,7 @@ protected:
static void _bind_methods();
public:
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
enum BakeError {
BAKE_ERROR_OK,
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 12e0123242..1f8f7cd54c 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -317,8 +317,8 @@ void PathFollow3D::_validate_property(PropertyInfo &p_property) const {
}
}
-Array PathFollow3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray PathFollow3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_visible_in_tree() && is_inside_tree()) {
if (!Object::cast_to<Path3D>(get_parent())) {
diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h
index 82fbf60bb6..0c9111bb8e 100644
--- a/scene/3d/path_3d.h
+++ b/scene/3d/path_3d.h
@@ -122,7 +122,7 @@ public:
void set_cubic_interpolation_enabled(bool p_enabled);
bool is_cubic_interpolation_enabled() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void update_transform(bool p_immediate = false);
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 2cbd484870..67a7f76d7d 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -995,8 +995,8 @@ TypedArray<Node3D> RigidBody3D::get_colliding_bodies() const {
return ret;
}
-Array RigidBody3D::get_configuration_warnings() const {
- Array warnings = CollisionObject3D::get_configuration_warnings();
+PackedStringArray RigidBody3D::get_configuration_warnings() const {
+ PackedStringArray warnings = CollisionObject3D::get_configuration_warnings();
Vector3 scale = get_transform().get_basis().get_scale();
if (ABS(scale.x - 1.0) > 0.05 || ABS(scale.y - 1.0) > 0.05 || ABS(scale.z - 1.0) > 0.05) {
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index b360954263..b84b7c4f02 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -329,7 +329,7 @@ public:
void set_constant_torque(const Vector3 &p_torque);
Vector3 get_constant_torque() const;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
RigidBody3D();
~RigidBody3D();
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index 89368a7309..b4dd6d09be 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -190,8 +190,8 @@ AABB ReflectionProbe::get_aabb() const {
return aabb;
}
-Array ReflectionProbe::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray ReflectionProbe::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("ReflectionProbes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h
index 2561b2a922..425fbb5bc2 100644
--- a/scene/3d/reflection_probe.h
+++ b/scene/3d/reflection_probe.h
@@ -122,7 +122,7 @@ public:
virtual AABB get_aabb() const override;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
ReflectionProbe();
~ReflectionProbe();
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index bb41015219..8d6e717132 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -200,8 +200,8 @@ void RemoteTransform3D::force_update_cache() {
_update_cache();
}
-Array RemoteTransform3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray RemoteTransform3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) {
warnings.push_back(RTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."));
diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h
index 40b59a5a5e..3841821dae 100644
--- a/scene/3d/remote_transform_3d.h
+++ b/scene/3d/remote_transform_3d.h
@@ -70,7 +70,7 @@ public:
void force_update_cache();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
RemoteTransform3D();
};
diff --git a/scene/3d/shape_cast_3d.cpp b/scene/3d/shape_cast_3d.cpp
index ba4ce834da..4d9eeada06 100644
--- a/scene/3d/shape_cast_3d.cpp
+++ b/scene/3d/shape_cast_3d.cpp
@@ -170,8 +170,8 @@ void ShapeCast3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color"), "set_debug_shape_custom_color", "get_debug_shape_custom_color");
}
-Array ShapeCast3D::get_configuration_warnings() const {
- Array warnings = Node3D::get_configuration_warnings();
+PackedStringArray ShapeCast3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node3D::get_configuration_warnings();
if (shape.is_null()) {
warnings.push_back(RTR("This node cannot interact with other objects unless a Shape3D is assigned."));
diff --git a/scene/3d/shape_cast_3d.h b/scene/3d/shape_cast_3d.h
index d690a17a78..043e35090f 100644
--- a/scene/3d/shape_cast_3d.h
+++ b/scene/3d/shape_cast_3d.h
@@ -140,7 +140,7 @@ public:
void remove_exception(const CollisionObject3D *p_node);
void clear_exceptions();
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
};
#endif // SHAPE_CAST_3D_H
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index ee30f60c72..cae1b2ce9c 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -95,17 +95,32 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
} else if (what == "scale") {
set_bone_pose_scale(which, p_value);
#ifndef DISABLE_DEPRECATED
- } else if (what == "pose") {
+ } else if (what == "pose" || what == "bound_children") {
// Kept for compatibility from 3.x to 4.x.
WARN_DEPRECATED_MSG("Skeleton uses old pose format, which is deprecated (and loads slower). Consider re-importing or re-saving the scene." +
(is_inside_tree() ? vformat(" Path: \"%s\"", get_path()) : String()));
- // Old Skeleton poses were relative to rest, new ones are absolute, so we need to recompute the pose.
- // Skeleton3D nodes were always written with rest before pose, so this *SHOULD* work...
- Transform3D rest = get_bone_rest(which);
- Transform3D pose = rest * (Transform3D)p_value;
- set_bone_pose_position(which, pose.origin);
- set_bone_pose_rotation(which, pose.basis.get_rotation_quaternion());
- set_bone_pose_scale(which, pose.basis.get_scale());
+ if (what == "pose") {
+ // Old Skeleton poses were relative to rest, new ones are absolute, so we need to recompute the pose.
+ // Skeleton3D nodes were always written with rest before pose, so this *SHOULD* work...
+ Transform3D rest = get_bone_rest(which);
+ Transform3D pose = rest * (Transform3D)p_value;
+ set_bone_pose_position(which, pose.origin);
+ set_bone_pose_rotation(which, pose.basis.get_rotation_quaternion());
+ set_bone_pose_scale(which, pose.basis.get_scale());
+ } else { // bound_children
+ // This handles the case where the pose was set to the rest position; the pose property would == Transform() and would not be saved to the scene by default.
+ // However, the bound_children property was always saved regardless of value, and it was always saved after both pose and rest.
+ // We don't do anything else with bound_children, as it's not present on Skeleton3D.
+ Vector3 pos = get_bone_pose_position(which);
+ Quaternion rot = get_bone_pose_rotation(which);
+ Vector3 scale = get_bone_pose_scale(which);
+ Transform3D rest = get_bone_rest(which);
+ if (rest != Transform3D() && pos == Vector3() && rot == Quaternion() && scale == Vector3(1, 1, 1)) {
+ set_bone_pose_position(which, rest.origin);
+ set_bone_pose_rotation(which, rest.basis.get_rotation_quaternion());
+ set_bone_pose_scale(which, rest.basis.get_scale());
+ }
+ }
#endif
} else {
return false;
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index 540e70866a..bd03a97a36 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -376,8 +376,8 @@ void SoftBody3D::_bind_methods() {
BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE);
}
-Array SoftBody3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray SoftBody3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (mesh.is_null()) {
warnings.push_back(RTR("This body will be ignored until you set a mesh."));
diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h
index b5d31d8440..ab30f7e654 100644
--- a/scene/3d/soft_body_3d.h
+++ b/scene/3d/soft_body_3d.h
@@ -126,7 +126,7 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
public:
RID get_physics_rid() const { return physics_rid; }
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 234baac61a..8e76e75d0e 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -1430,8 +1430,8 @@ StringName AnimatedSprite3D::get_animation() const {
return animation;
}
-Array AnimatedSprite3D::get_configuration_warnings() const {
- Array warnings = SpriteBase3D::get_configuration_warnings();
+PackedStringArray AnimatedSprite3D::get_configuration_warnings() const {
+ PackedStringArray warnings = SpriteBase3D::get_configuration_warnings();
if (frames.is_null()) {
warnings.push_back(RTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."));
}
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index c83ed88b2d..cbc7192ddf 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -285,7 +285,7 @@ public:
virtual Rect2 get_item_rect() const override;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
AnimatedSprite3D();
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index b309205697..c23032d3b9 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -105,8 +105,8 @@ void VehicleWheel3D::_notification(int p_what) {
}
}
-Array VehicleWheel3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray VehicleWheel3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Object::cast_to<VehicleBody3D>(get_parent())) {
warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h
index 8127340acc..ce913f152d 100644
--- a/scene/3d/vehicle_body_3d.h
+++ b/scene/3d/vehicle_body_3d.h
@@ -147,7 +147,7 @@ public:
void set_steering(real_t p_steering);
real_t get_steering() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
VehicleWheel3D();
};
diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp
index c10ddec17e..be86872a59 100644
--- a/scene/3d/visible_on_screen_notifier_3d.cpp
+++ b/scene/3d/visible_on_screen_notifier_3d.cpp
@@ -79,8 +79,8 @@ void VisibleOnScreenNotifier3D::_notification(int p_what) {
}
}
-Array VisibleOnScreenNotifier3D::get_configuration_warnings() const {
- Array warnings = VisualInstance3D::get_configuration_warnings();
+PackedStringArray VisibleOnScreenNotifier3D::get_configuration_warnings() const {
+ PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("VisibleOnScreenNotifier3D nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
diff --git a/scene/3d/visible_on_screen_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h
index 10e41ceec6..85156c256e 100644
--- a/scene/3d/visible_on_screen_notifier_3d.h
+++ b/scene/3d/visible_on_screen_notifier_3d.h
@@ -57,7 +57,7 @@ public:
virtual AABB get_aabb() const override;
bool is_on_screen() const;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
VisibleOnScreenNotifier3D();
~VisibleOnScreenNotifier3D();
diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp
index 47a9a581b8..503c39ae3e 100644
--- a/scene/3d/visual_instance_3d.cpp
+++ b/scene/3d/visual_instance_3d.cpp
@@ -425,8 +425,8 @@ bool GeometryInstance3D::is_ignoring_occlusion_culling() {
return ignore_occlusion_culling;
}
-Array GeometryInstance3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray GeometryInstance3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) {
warnings.push_back(RTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance."));
diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h
index c088e189ce..59ede26ac1 100644
--- a/scene/3d/visual_instance_3d.h
+++ b/scene/3d/visual_instance_3d.h
@@ -194,7 +194,7 @@ public:
void set_ignore_occlusion_culling(bool p_enabled);
bool is_ignoring_occlusion_culling();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
GeometryInstance3D();
virtual ~GeometryInstance3D();
};
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index 011aecc724..eb8569fa30 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -518,8 +518,8 @@ AABB VoxelGI::get_aabb() const {
return AABB(-size / 2, size);
}
-Array VoxelGI::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray VoxelGI::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
warnings.push_back(RTR("VoxelGI nodes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
diff --git a/scene/3d/voxel_gi.h b/scene/3d/voxel_gi.h
index 316cb26b77..7d7787f721 100644
--- a/scene/3d/voxel_gi.h
+++ b/scene/3d/voxel_gi.h
@@ -163,7 +163,7 @@ public:
virtual AABB get_aabb() const override;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
VoxelGI();
~VoxelGI();
diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp
index 8db310ce30..4687c84734 100644
--- a/scene/3d/world_environment.cpp
+++ b/scene/3d/world_environment.cpp
@@ -135,8 +135,8 @@ Ref<CameraAttributes> WorldEnvironment::get_camera_attributes() const {
return camera_attributes;
}
-Array WorldEnvironment::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray WorldEnvironment::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!environment.is_valid() && !camera_attributes.is_valid()) {
warnings.push_back(RTR("To have any visible effect, WorldEnvironment requires its \"Environment\" property to contain an Environment, its \"Camera Attributes\" property to contain a CameraAttributes resource, or both."));
diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h
index 57d95f1f0e..2809d2550a 100644
--- a/scene/3d/world_environment.h
+++ b/scene/3d/world_environment.h
@@ -55,7 +55,7 @@ public:
void set_camera_attributes(const Ref<CameraAttributes> &p_camera_attributes);
Ref<CameraAttributes> get_camera_attributes() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
WorldEnvironment();
};
diff --git a/scene/3d/xr_face_modifier_3d.cpp b/scene/3d/xr_face_modifier_3d.cpp
new file mode 100644
index 0000000000..be92a587b0
--- /dev/null
+++ b/scene/3d/xr_face_modifier_3d.cpp
@@ -0,0 +1,615 @@
+/**************************************************************************/
+/* xr_face_modifier_3d.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 "xr_face_modifier_3d.h"
+
+#include "servers/xr/xr_face_tracker.h"
+#include "servers/xr_server.h"
+
+// This method takes the name of a mesh blend shape and returns the
+// corresponding XRFaceTracker blend shape. If no match is
+// found then the function returns -1.
+static int find_face_blend_shape(const StringName &p_name) {
+ // Entry for blend shape name table.
+ struct blend_map_entry {
+ int blend;
+ const char *name[4];
+ };
+
+ // Table of blend shape names.
+ //
+ // This table consists of the XRFaceTracker blend shape and
+ // the corresponding names (lowercase and no underscore) of:
+ // - The Unified Expression blend shape name.
+ // - The ARKit blend shape name (if present and different).
+ // - The SRanipal blend shape name (if present and different).
+ // - The Meta blend shape name (if present and different).
+ static constexpr blend_map_entry blend_map[] = {
+ { XRFaceTracker::FT_EYE_LOOK_OUT_RIGHT,
+ { "eyelookoutright", "eyerightright", "eyeslookoutr" } },
+ { XRFaceTracker::FT_EYE_LOOK_IN_RIGHT,
+ { "eyelookinright", "eyerightleft", "eyeslookinr" } },
+ { XRFaceTracker::FT_EYE_LOOK_UP_RIGHT,
+ { "eyelookupright", "eyerightlookup", "eyeslookupr" } },
+ { XRFaceTracker::FT_EYE_LOOK_DOWN_RIGHT,
+ { "eyelookdownright", "eyerightlookdown", "eyeslookdownr" } },
+ { XRFaceTracker::FT_EYE_LOOK_OUT_LEFT,
+ { "eyelookoutleft", "eyeleftleft", "eyeslookoutl" } },
+ { XRFaceTracker::FT_EYE_LOOK_IN_LEFT,
+ { "eyelookinleft", "eyeleftright", "eyeslookinl" } },
+ { XRFaceTracker::FT_EYE_LOOK_UP_LEFT,
+ { "eyelookupleft", "eyeleftlookup", "eyeslookupl" } },
+ { XRFaceTracker::FT_EYE_LOOK_DOWN_LEFT,
+ { "eyelookdownleft", "eyeleftlookdown", "eyeslookdownl" } },
+ { XRFaceTracker::FT_EYE_CLOSED_RIGHT,
+ { "eyeclosedright", "eyeblinkright", "eyerightblink", "eyesclosedr" } },
+ { XRFaceTracker::FT_EYE_CLOSED_LEFT,
+ { "eyeclosedleft", "eyeblinkleft", "eyeleftblink", "eyesclosedl" } },
+ { XRFaceTracker::FT_EYE_SQUINT_RIGHT,
+ { "eyesquintright", "eyessquintr" } },
+ { XRFaceTracker::FT_EYE_SQUINT_LEFT,
+ { "eyesquintleft", "eyessquintl" } },
+ { XRFaceTracker::FT_EYE_WIDE_RIGHT,
+ { "eyewideright", "eyerightwide", "eyeswidenr" } },
+ { XRFaceTracker::FT_EYE_WIDE_LEFT,
+ { "eyewideleft", "eyeleftwide", "eyeswidenl" } },
+ { XRFaceTracker::FT_EYE_DILATION_RIGHT,
+ { "eyedilationright", "eyerightdilation" } },
+ { XRFaceTracker::FT_EYE_DILATION_LEFT,
+ { "eyedilationleft", "eyeleftdilation" } },
+ { XRFaceTracker::FT_EYE_CONSTRICT_RIGHT,
+ { "eyeconstrictright", "eyerightconstrict" } },
+ { XRFaceTracker::FT_EYE_CONSTRICT_LEFT,
+ { "eyeconstrictleft", "eyeleftconstrict" } },
+ { XRFaceTracker::FT_BROW_PINCH_RIGHT,
+ { "browpinchright" } },
+ { XRFaceTracker::FT_BROW_PINCH_LEFT,
+ { "browpinchleft" } },
+ { XRFaceTracker::FT_BROW_LOWERER_RIGHT,
+ { "browlowererright" } },
+ { XRFaceTracker::FT_BROW_LOWERER_LEFT,
+ { "browlowererleft" } },
+ { XRFaceTracker::FT_BROW_INNER_UP_RIGHT,
+ { "browinnerupright", "innerbrowraiserr" } },
+ { XRFaceTracker::FT_BROW_INNER_UP_LEFT,
+ { "browinnerupleft", "innerbrowraiserl" } },
+ { XRFaceTracker::FT_BROW_OUTER_UP_RIGHT,
+ { "browouterupright", "outerbrowraiserr" } },
+ { XRFaceTracker::FT_BROW_OUTER_UP_LEFT,
+ { "browouterupleft", "outerbrowraiserl" } },
+ { XRFaceTracker::FT_NOSE_SNEER_RIGHT,
+ { "nosesneerright", "nosewrinklerr" } },
+ { XRFaceTracker::FT_NOSE_SNEER_LEFT,
+ { "nosesneerleft", "nosewrinklerl" } },
+ { XRFaceTracker::FT_NASAL_DILATION_RIGHT,
+ { "nasaldilationright" } },
+ { XRFaceTracker::FT_NASAL_DILATION_LEFT,
+ { "nasaldilationleft" } },
+ { XRFaceTracker::FT_NASAL_CONSTRICT_RIGHT,
+ { "nasalconstrictright" } },
+ { XRFaceTracker::FT_NASAL_CONSTRICT_LEFT,
+ { "nasalconstrictleft" } },
+ { XRFaceTracker::FT_CHEEK_SQUINT_RIGHT,
+ { "cheeksquintright", "cheekraiserr" } },
+ { XRFaceTracker::FT_CHEEK_SQUINT_LEFT,
+ { "cheeksquintleft", "cheekraiserl" } },
+ { XRFaceTracker::FT_CHEEK_PUFF_RIGHT,
+ { "cheekpuffright", "cheekpuffr" } },
+ { XRFaceTracker::FT_CHEEK_PUFF_LEFT,
+ { "cheekpuffleft", "cheekpuffl" } },
+ { XRFaceTracker::FT_CHEEK_SUCK_RIGHT,
+ { "cheeksuckright", "cheeksuckr" } },
+ { XRFaceTracker::FT_CHEEK_SUCK_LEFT,
+ { "cheeksuckleft", "cheeksuckl" } },
+ { XRFaceTracker::FT_JAW_OPEN,
+ { "jawopen", "jawdrop" } },
+ { XRFaceTracker::FT_MOUTH_CLOSED,
+ { "mouthclosed", "mouthclose", "mouthapeshape", "lipstoward" } },
+ { XRFaceTracker::FT_JAW_RIGHT,
+ { "jawright", "jawsidewaysright" } },
+ { XRFaceTracker::FT_JAW_LEFT,
+ { "jawleft", "jawsidewaysleft" } },
+ { XRFaceTracker::FT_JAW_FORWARD,
+ { "jawforward", "jawthrust" } },
+ { XRFaceTracker::FT_JAW_BACKWARD,
+ { "jawbackward" } },
+ { XRFaceTracker::FT_JAW_CLENCH,
+ { "jawclench" } },
+ { XRFaceTracker::FT_JAW_MANDIBLE_RAISE,
+ { "jawmandibleraise" } },
+ { XRFaceTracker::FT_LIP_SUCK_UPPER_RIGHT,
+ { "lipsuckupperright", "lipsuckrt" } },
+ { XRFaceTracker::FT_LIP_SUCK_UPPER_LEFT,
+ { "lipsuckupperleft", "lipsucklt" } },
+ { XRFaceTracker::FT_LIP_SUCK_LOWER_RIGHT,
+ { "lipsucklowerright", "lipsuckrb" } },
+ { XRFaceTracker::FT_LIP_SUCK_LOWER_LEFT,
+ { "lipsucklowerleft", "lipsucklb" } },
+ { XRFaceTracker::FT_LIP_SUCK_CORNER_RIGHT,
+ { "lipsuckcornerright" } },
+ { XRFaceTracker::FT_LIP_SUCK_CORNER_LEFT,
+ { "lipsuckcornerleft" } },
+ { XRFaceTracker::FT_LIP_FUNNEL_UPPER_RIGHT,
+ { "lipfunnelupperright", "lipfunnelerrt" } },
+ { XRFaceTracker::FT_LIP_FUNNEL_UPPER_LEFT,
+ { "lipfunnelupperleft", "lipfunnelerlt" } },
+ { XRFaceTracker::FT_LIP_FUNNEL_LOWER_RIGHT,
+ { "lipfunnellowerright", "lipsuckrb" } },
+ { XRFaceTracker::FT_LIP_FUNNEL_LOWER_LEFT,
+ { "lipfunnellowerleft", "lipsucklb" } },
+ { XRFaceTracker::FT_LIP_PUCKER_UPPER_RIGHT,
+ { "lippuckerupperright" } },
+ { XRFaceTracker::FT_LIP_PUCKER_UPPER_LEFT,
+ { "lippuckerupperleft" } },
+ { XRFaceTracker::FT_LIP_PUCKER_LOWER_RIGHT,
+ { "lippuckerlowerright" } },
+ { XRFaceTracker::FT_LIP_PUCKER_LOWER_LEFT,
+ { "lippuckerlowerleft" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_UP_RIGHT,
+ { "mouthupperupright", "upperlipraiserr" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_UP_LEFT,
+ { "mouthupperupleft", "upperlipraiserl" } },
+ { XRFaceTracker::FT_MOUTH_LOWER_DOWN_RIGHT,
+ { "mouthlowerdownright", "mouthlowerupright", "lowerlipdepressorr" } },
+ { XRFaceTracker::FT_MOUTH_LOWER_DOWN_LEFT,
+ { "mouthlowerdownleft", "mouthlowerupleft", "lowerlipdepressorl" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_DEEPEN_RIGHT,
+ { "mouthupperdeepenright" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_DEEPEN_LEFT,
+ { "mouthupperdeepenleft" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_RIGHT,
+ { "mouthupperright" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_LEFT,
+ { "mouthupperleft" } },
+ { XRFaceTracker::FT_MOUTH_LOWER_RIGHT,
+ { "mouthlowerright" } },
+ { XRFaceTracker::FT_MOUTH_LOWER_LEFT,
+ { "mouthlowerleft" } },
+ { XRFaceTracker::FT_MOUTH_CORNER_PULL_RIGHT,
+ { "mouthcornerpullright" } },
+ { XRFaceTracker::FT_MOUTH_CORNER_PULL_LEFT,
+ { "mouthcornerpullleft" } },
+ { XRFaceTracker::FT_MOUTH_CORNER_SLANT_RIGHT,
+ { "mouthcornerslantright" } },
+ { XRFaceTracker::FT_MOUTH_CORNER_SLANT_LEFT,
+ { "mouthcornerslantleft" } },
+ { XRFaceTracker::FT_MOUTH_FROWN_RIGHT,
+ { "mouthfrownright", "lipcornerdepressorr" } },
+ { XRFaceTracker::FT_MOUTH_FROWN_LEFT,
+ { "mouthfrownleft", "lipcornerdepressorl" } },
+ { XRFaceTracker::FT_MOUTH_STRETCH_RIGHT,
+ { "mouthstretchright", "lipstretcherr" } },
+ { XRFaceTracker::FT_MOUTH_STRETCH_LEFT,
+ { "mouthstretchleft", "lipstretcherl" } },
+ { XRFaceTracker::FT_MOUTH_DIMPLE_RIGHT,
+ { "mouthdimplerright", "mouthdimpleright", "dimplerr" } },
+ { XRFaceTracker::FT_MOUTH_DIMPLE_LEFT,
+ { "mouthdimplerleft", "mouthdimpleleft", "dimplerl" } },
+ { XRFaceTracker::FT_MOUTH_RAISER_UPPER,
+ { "mouthraiserupper", "mouthshrugupper", "chinraisert" } },
+ { XRFaceTracker::FT_MOUTH_RAISER_LOWER,
+ { "mouthraiserlower", "mouthshruglower", "mouthloweroverlay", "chinraiserb" } },
+ { XRFaceTracker::FT_MOUTH_PRESS_RIGHT,
+ { "mouthpressright", "lippressorr" } },
+ { XRFaceTracker::FT_MOUTH_PRESS_LEFT,
+ { "mouthpressleft", "lippressorl" } },
+ { XRFaceTracker::FT_MOUTH_TIGHTENER_RIGHT,
+ { "mouthtightenerright", "liptightenerr" } },
+ { XRFaceTracker::FT_MOUTH_TIGHTENER_LEFT,
+ { "mouthtightenerleft", "liptightenerl" } },
+ { XRFaceTracker::FT_TONGUE_OUT,
+ { "tongueout", "tonguelongstep2" } },
+ { XRFaceTracker::FT_TONGUE_UP,
+ { "tongueup" } },
+ { XRFaceTracker::FT_TONGUE_DOWN,
+ { "tonguedown" } },
+ { XRFaceTracker::FT_TONGUE_RIGHT,
+ { "tongueright" } },
+ { XRFaceTracker::FT_TONGUE_LEFT,
+ { "tongueleft" } },
+ { XRFaceTracker::FT_TONGUE_ROLL,
+ { "tongueroll" } },
+ { XRFaceTracker::FT_TONGUE_BLEND_DOWN,
+ { "tongueblenddown" } },
+ { XRFaceTracker::FT_TONGUE_CURL_UP,
+ { "tonguecurlup" } },
+ { XRFaceTracker::FT_TONGUE_SQUISH,
+ { "tonguesquish" } },
+ { XRFaceTracker::FT_TONGUE_FLAT,
+ { "tongueflat" } },
+ { XRFaceTracker::FT_TONGUE_TWIST_RIGHT,
+ { "tonguetwistright" } },
+ { XRFaceTracker::FT_TONGUE_TWIST_LEFT,
+ { "tonguetwistleft" } },
+ { XRFaceTracker::FT_SOFT_PALATE_CLOSE,
+ { "softpalateclose" } },
+ { XRFaceTracker::FT_THROAT_SWALLOW,
+ { "throatswallow" } },
+ { XRFaceTracker::FT_NECK_FLEX_RIGHT,
+ { "neckflexright" } },
+ { XRFaceTracker::FT_NECK_FLEX_LEFT,
+ { "neckflexleft" } },
+ { XRFaceTracker::FT_EYE_CLOSED,
+ { "eyeclosed" } },
+ { XRFaceTracker::FT_EYE_WIDE,
+ { "eyewide" } },
+ { XRFaceTracker::FT_EYE_SQUINT,
+ { "eyesquint" } },
+ { XRFaceTracker::FT_EYE_DILATION,
+ { "eyedilation" } },
+ { XRFaceTracker::FT_EYE_CONSTRICT,
+ { "eyeconstrict" } },
+ { XRFaceTracker::FT_BROW_DOWN_RIGHT,
+ { "browdownright", "browlowererr" } },
+ { XRFaceTracker::FT_BROW_DOWN_LEFT,
+ { "browdownleft", "browlowererl" } },
+ { XRFaceTracker::FT_BROW_DOWN,
+ { "browdown" } },
+ { XRFaceTracker::FT_BROW_UP_RIGHT,
+ { "browupright" } },
+ { XRFaceTracker::FT_BROW_UP_LEFT,
+ { "browupleft" } },
+ { XRFaceTracker::FT_BROW_UP,
+ { "browup" } },
+ { XRFaceTracker::FT_NOSE_SNEER,
+ { "nosesneer" } },
+ { XRFaceTracker::FT_NASAL_DILATION,
+ { "nasaldilation" } },
+ { XRFaceTracker::FT_NASAL_CONSTRICT,
+ { "nasalconstrict" } },
+ { XRFaceTracker::FT_CHEEK_PUFF,
+ { "cheekpuff" } },
+ { XRFaceTracker::FT_CHEEK_SUCK,
+ { "cheeksuck" } },
+ { XRFaceTracker::FT_CHEEK_SQUINT,
+ { "cheeksquint" } },
+ { XRFaceTracker::FT_LIP_SUCK_UPPER,
+ { "lipsuckupper", "mouthrollupper", "mouthupperinside" } },
+ { XRFaceTracker::FT_LIP_SUCK_LOWER,
+ { "lipsucklower", "mouthrolllower", "mouthlowerinside" } },
+ { XRFaceTracker::FT_LIP_SUCK,
+ { "lipsuck" } },
+ { XRFaceTracker::FT_LIP_FUNNEL_UPPER,
+ { "lipfunnelupper", "mouthupperoverturn" } },
+ { XRFaceTracker::FT_LIP_FUNNEL_LOWER,
+ { "lipfunnellower", "mouthloweroverturn" } },
+ { XRFaceTracker::FT_LIP_FUNNEL,
+ { "lipfunnel", "mouthfunnel" } },
+ { XRFaceTracker::FT_LIP_PUCKER_UPPER,
+ { "lippuckerupper" } },
+ { XRFaceTracker::FT_LIP_PUCKER_LOWER,
+ { "lippuckerlower" } },
+ { XRFaceTracker::FT_LIP_PUCKER,
+ { "lippucker", "mouthpucker", "mouthpout" } },
+ { XRFaceTracker::FT_MOUTH_UPPER_UP,
+ { "mouthupperup" } },
+ { XRFaceTracker::FT_MOUTH_LOWER_DOWN,
+ { "mouthlowerdown" } },
+ { XRFaceTracker::FT_MOUTH_OPEN,
+ { "mouthopen" } },
+ { XRFaceTracker::FT_MOUTH_RIGHT,
+ { "mouthright" } },
+ { XRFaceTracker::FT_MOUTH_LEFT,
+ { "mouthleft" } },
+ { XRFaceTracker::FT_MOUTH_SMILE_RIGHT,
+ { "mouthsmileright", "lipcornerpullerr" } },
+ { XRFaceTracker::FT_MOUTH_SMILE_LEFT,
+ { "mouthsmileleft", "lipcornerpullerl" } },
+ { XRFaceTracker::FT_MOUTH_SMILE,
+ { "mouthsmile" } },
+ { XRFaceTracker::FT_MOUTH_SAD_RIGHT,
+ { "mouthsadright" } },
+ { XRFaceTracker::FT_MOUTH_SAD_LEFT,
+ { "mouthsadleft" } },
+ { XRFaceTracker::FT_MOUTH_SAD,
+ { "mouthsad" } },
+ { XRFaceTracker::FT_MOUTH_STRETCH,
+ { "mouthstretch" } },
+ { XRFaceTracker::FT_MOUTH_DIMPLE,
+ { "mouthdimple" } },
+ { XRFaceTracker::FT_MOUTH_TIGHTENER,
+ { "mouthtightener" } },
+ { XRFaceTracker::FT_MOUTH_PRESS,
+ { "mouthpress" } }
+ };
+
+ // Convert the name to lower-case and strip non-alphanumeric characters.
+ const String name = String(p_name).to_lower().replace("_", "");
+
+ // Iterate through the blend map.
+ for (const blend_map_entry &entry : blend_map) {
+ for (const char *n : entry.name) {
+ if (n == nullptr) {
+ break;
+ }
+
+ if (name == n) {
+ return entry.blend;
+ }
+ }
+ }
+
+ // Blend shape not found.
+ return -1;
+}
+
+// This method adds all the identified XRFaceTracker blend shapes of
+// the mesh to the p_blend_mapping map. The map is indexed by the
+// XRFaceTracker blend shape, and the value is the index of the mesh
+// blend shape.
+static void identify_face_blend_shapes(RBMap<int, int> &p_blend_mapping, const Ref<Mesh> &mesh) {
+ // Find all blend shapes.
+ const int count = mesh->get_blend_shape_count();
+ for (int i = 0; i < count; i++) {
+ const int blend = find_face_blend_shape(mesh->get_blend_shape_name(i));
+ if (blend >= 0) {
+ p_blend_mapping[blend] = i;
+ }
+ }
+}
+
+// This method removes any unified blend shapes from the p_blend_mapping map
+// if all the individual blend shapes are found and going to be driven.
+static void remove_driven_unified_blend_shapes(RBMap<int, int> &p_blend_mapping) {
+ // Entry for unified blend table.
+ struct unified_blend_entry {
+ int unified;
+ int individual[4];
+ };
+
+ // Table of unified blend shapes.
+ //
+ // This table consists of:
+ // - The XRFaceTracker unified blend shape.
+ // - The individual blend shapes that make up the unified blend shape.
+ static constexpr unified_blend_entry unified_blends[] = {
+ { XRFaceTracker::FT_EYE_CLOSED,
+ { XRFaceTracker::FT_EYE_CLOSED_RIGHT, XRFaceTracker::FT_EYE_CLOSED_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_EYE_WIDE,
+ { XRFaceTracker::FT_EYE_WIDE_RIGHT, XRFaceTracker::FT_EYE_WIDE_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_EYE_SQUINT,
+ { XRFaceTracker::FT_EYE_SQUINT_RIGHT, XRFaceTracker::FT_EYE_SQUINT_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_EYE_DILATION,
+ { XRFaceTracker::FT_EYE_DILATION_RIGHT, XRFaceTracker::FT_EYE_DILATION_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_EYE_CONSTRICT,
+ { XRFaceTracker::FT_EYE_CONSTRICT_RIGHT, XRFaceTracker::FT_EYE_CONSTRICT_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_BROW_DOWN_RIGHT,
+ { XRFaceTracker::FT_BROW_LOWERER_RIGHT, XRFaceTracker::FT_BROW_PINCH_RIGHT, -1, -1 } },
+ { XRFaceTracker::FT_BROW_DOWN_LEFT,
+ { XRFaceTracker::FT_BROW_LOWERER_LEFT, XRFaceTracker::FT_BROW_PINCH_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_BROW_DOWN,
+ { XRFaceTracker::FT_BROW_LOWERER_RIGHT, XRFaceTracker::FT_BROW_PINCH_RIGHT, XRFaceTracker::FT_BROW_LOWERER_LEFT, XRFaceTracker::FT_BROW_PINCH_LEFT } },
+ { XRFaceTracker::FT_BROW_UP_RIGHT,
+ { XRFaceTracker::FT_BROW_INNER_UP_RIGHT, XRFaceTracker::FT_BROW_OUTER_UP_RIGHT, -1, -1 } },
+ { XRFaceTracker::FT_BROW_UP_LEFT,
+ { XRFaceTracker::FT_BROW_INNER_UP_LEFT, XRFaceTracker::FT_BROW_OUTER_UP_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_BROW_UP,
+ { XRFaceTracker::FT_BROW_INNER_UP_RIGHT, XRFaceTracker::FT_BROW_OUTER_UP_RIGHT, XRFaceTracker::FT_BROW_INNER_UP_LEFT, XRFaceTracker::FT_BROW_OUTER_UP_LEFT } },
+ { XRFaceTracker::FT_NOSE_SNEER,
+ { XRFaceTracker::FT_NOSE_SNEER_RIGHT, XRFaceTracker::FT_NOSE_SNEER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_NASAL_DILATION,
+ { XRFaceTracker::FT_NASAL_DILATION_RIGHT, XRFaceTracker::FT_NASAL_DILATION_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_NASAL_CONSTRICT,
+ { XRFaceTracker::FT_NASAL_CONSTRICT_RIGHT, XRFaceTracker::FT_NASAL_CONSTRICT_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_CHEEK_PUFF,
+ { XRFaceTracker::FT_CHEEK_PUFF_RIGHT, XRFaceTracker::FT_CHEEK_PUFF_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_CHEEK_SUCK,
+ { XRFaceTracker::FT_CHEEK_SUCK_RIGHT, XRFaceTracker::FT_CHEEK_SUCK_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_CHEEK_SQUINT,
+ { XRFaceTracker::FT_CHEEK_SQUINT_RIGHT, XRFaceTracker::FT_CHEEK_SQUINT_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_SUCK_UPPER,
+ { XRFaceTracker::FT_LIP_SUCK_UPPER_RIGHT, XRFaceTracker::FT_LIP_SUCK_UPPER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_SUCK_LOWER,
+ { XRFaceTracker::FT_LIP_SUCK_LOWER_RIGHT, XRFaceTracker::FT_LIP_SUCK_LOWER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_SUCK,
+ { XRFaceTracker::FT_LIP_SUCK_UPPER_RIGHT, XRFaceTracker::FT_LIP_SUCK_UPPER_LEFT, XRFaceTracker::FT_LIP_SUCK_LOWER_RIGHT, XRFaceTracker::FT_LIP_SUCK_LOWER_LEFT } },
+ { XRFaceTracker::FT_LIP_FUNNEL_UPPER,
+ { XRFaceTracker::FT_LIP_FUNNEL_UPPER_RIGHT, XRFaceTracker::FT_LIP_FUNNEL_UPPER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_FUNNEL_LOWER,
+ { XRFaceTracker::FT_LIP_FUNNEL_LOWER_RIGHT, XRFaceTracker::FT_LIP_FUNNEL_LOWER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_FUNNEL,
+ { XRFaceTracker::FT_LIP_FUNNEL_UPPER_RIGHT, XRFaceTracker::FT_LIP_FUNNEL_UPPER_LEFT, XRFaceTracker::FT_LIP_FUNNEL_LOWER_RIGHT, XRFaceTracker::FT_LIP_FUNNEL_LOWER_LEFT } },
+ { XRFaceTracker::FT_LIP_PUCKER_UPPER,
+ { XRFaceTracker::FT_LIP_PUCKER_UPPER_RIGHT, XRFaceTracker::FT_LIP_PUCKER_UPPER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_PUCKER_LOWER,
+ { XRFaceTracker::FT_LIP_PUCKER_LOWER_RIGHT, XRFaceTracker::FT_LIP_PUCKER_LOWER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_LIP_PUCKER,
+ { XRFaceTracker::FT_LIP_PUCKER_UPPER_RIGHT, XRFaceTracker::FT_LIP_PUCKER_UPPER_LEFT, XRFaceTracker::FT_LIP_PUCKER_LOWER_RIGHT, XRFaceTracker::FT_LIP_PUCKER_LOWER_LEFT } },
+ { XRFaceTracker::FT_MOUTH_UPPER_UP,
+ { XRFaceTracker::FT_MOUTH_UPPER_UP_RIGHT, XRFaceTracker::FT_MOUTH_UPPER_UP_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_LOWER_DOWN,
+ { XRFaceTracker::FT_MOUTH_LOWER_DOWN_RIGHT, XRFaceTracker::FT_MOUTH_LOWER_DOWN_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_OPEN,
+ { XRFaceTracker::FT_MOUTH_UPPER_UP_RIGHT, XRFaceTracker::FT_MOUTH_UPPER_UP_LEFT, XRFaceTracker::FT_MOUTH_LOWER_DOWN_RIGHT, XRFaceTracker::FT_MOUTH_LOWER_DOWN_LEFT } },
+ { XRFaceTracker::FT_MOUTH_RIGHT,
+ { XRFaceTracker::FT_MOUTH_UPPER_RIGHT, XRFaceTracker::FT_MOUTH_LOWER_RIGHT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_LEFT,
+ { XRFaceTracker::FT_MOUTH_UPPER_LEFT, XRFaceTracker::FT_MOUTH_LOWER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_SMILE_RIGHT,
+ { XRFaceTracker::FT_MOUTH_CORNER_PULL_RIGHT, XRFaceTracker::FT_MOUTH_CORNER_SLANT_RIGHT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_SMILE_LEFT,
+ { XRFaceTracker::FT_MOUTH_CORNER_PULL_LEFT, XRFaceTracker::FT_MOUTH_CORNER_SLANT_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_SMILE,
+ { XRFaceTracker::FT_MOUTH_CORNER_PULL_RIGHT, XRFaceTracker::FT_MOUTH_CORNER_SLANT_RIGHT, XRFaceTracker::FT_MOUTH_CORNER_PULL_LEFT, XRFaceTracker::FT_MOUTH_CORNER_SLANT_LEFT } },
+ { XRFaceTracker::FT_MOUTH_SAD_RIGHT,
+ { XRFaceTracker::FT_MOUTH_FROWN_RIGHT, XRFaceTracker::FT_MOUTH_STRETCH_RIGHT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_SAD_LEFT,
+ { XRFaceTracker::FT_MOUTH_FROWN_LEFT, XRFaceTracker::FT_MOUTH_STRETCH_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_SAD,
+ { XRFaceTracker::FT_MOUTH_FROWN_RIGHT, XRFaceTracker::FT_MOUTH_STRETCH_RIGHT, XRFaceTracker::FT_MOUTH_FROWN_LEFT, XRFaceTracker::FT_MOUTH_STRETCH_LEFT } },
+ { XRFaceTracker::FT_MOUTH_STRETCH,
+ { XRFaceTracker::FT_MOUTH_STRETCH_RIGHT, XRFaceTracker::FT_MOUTH_STRETCH_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_DIMPLE,
+ { XRFaceTracker::FT_MOUTH_DIMPLE_RIGHT, XRFaceTracker::FT_MOUTH_DIMPLE_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_TIGHTENER,
+ { XRFaceTracker::FT_MOUTH_TIGHTENER_RIGHT, XRFaceTracker::FT_MOUTH_TIGHTENER_LEFT, -1, -1 } },
+ { XRFaceTracker::FT_MOUTH_PRESS,
+ { XRFaceTracker::FT_MOUTH_PRESS_RIGHT, XRFaceTracker::FT_MOUTH_PRESS_LEFT, -1, -1 } }
+ };
+
+ // Remove unified blend shapes if individual blend shapes are found.
+ for (const unified_blend_entry &entry : unified_blends) {
+ // Check if all individual blend shapes are found.
+ bool found = true;
+ for (const int i : entry.individual) {
+ if (i >= 0 && !p_blend_mapping.find(i)) {
+ found = false;
+ break;
+ }
+ }
+
+ // If all individual blend shapes are found then remove the unified blend shape.
+ if (found) {
+ p_blend_mapping.erase(entry.unified);
+ }
+ }
+}
+
+void XRFaceModifier3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_face_tracker", "tracker_name"), &XRFaceModifier3D::set_face_tracker);
+ ClassDB::bind_method(D_METHOD("get_face_tracker"), &XRFaceModifier3D::get_face_tracker);
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "face_tracker", PROPERTY_HINT_ENUM_SUGGESTION, "/user/head"), "set_face_tracker", "get_face_tracker");
+
+ ClassDB::bind_method(D_METHOD("set_target", "target"), &XRFaceModifier3D::set_target);
+ ClassDB::bind_method(D_METHOD("get_target"), &XRFaceModifier3D::get_target);
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "MeshInstance3D"), "set_target", "get_target");
+}
+
+void XRFaceModifier3D::set_face_tracker(const StringName &p_tracker_name) {
+ tracker_name = p_tracker_name;
+}
+
+StringName XRFaceModifier3D::get_face_tracker() const {
+ return tracker_name;
+}
+
+void XRFaceModifier3D::set_target(const NodePath &p_target) {
+ target = p_target;
+
+ if (is_inside_tree()) {
+ _get_blend_data();
+ }
+}
+
+NodePath XRFaceModifier3D::get_target() const {
+ return target;
+}
+
+MeshInstance3D *XRFaceModifier3D::get_mesh_instance() const {
+ if (!has_node(target)) {
+ return nullptr;
+ }
+
+ Node *node = get_node(target);
+ if (!node) {
+ return nullptr;
+ }
+
+ return Object::cast_to<MeshInstance3D>(node);
+}
+
+void XRFaceModifier3D::_get_blend_data() {
+ // This method constructs the blend mapping from the XRFaceTracker
+ // blend shapes to the available blend shapes of the target mesh. It does this
+ // by:
+ //
+ // 1. Identifying the blend shapes of the target mesh and identifying what
+ // XRFaceTracker blend shape they correspond to. The results are
+ // placed in the blend_mapping map.
+ // 2. Prevent over-driving facial blend-shapes by removing any unified blend
+ // shapes from the map if all the individual blend shapes are already
+ // found and going to be driven.
+
+ blend_mapping.clear();
+
+ // Get the target MeshInstance3D.
+ const MeshInstance3D *mesh_instance = get_mesh_instance();
+ if (!mesh_instance) {
+ return;
+ }
+
+ // Get the mesh.
+ const Ref<Mesh> mesh = mesh_instance->get_mesh();
+ if (mesh.is_null()) {
+ return;
+ }
+
+ // Identify all face blend shapes and populate the map.
+ identify_face_blend_shapes(blend_mapping, mesh);
+
+ // Remove the unified blend shapes if all the individual blend shapes are found.
+ remove_driven_unified_blend_shapes(blend_mapping);
+}
+
+void XRFaceModifier3D::_update_face_blends() const {
+ // Get the XR Server.
+ const XRServer *xr_server = XRServer::get_singleton();
+ if (!xr_server) {
+ return;
+ }
+
+ // Get the face tracker.
+ const Ref<XRFaceTracker> p = xr_server->get_face_tracker(tracker_name);
+ if (!p.is_valid()) {
+ return;
+ }
+
+ // Get the face mesh.
+ MeshInstance3D *mesh_instance = get_mesh_instance();
+ if (!mesh_instance) {
+ return;
+ }
+
+ // Get the blend weights.
+ const PackedFloat32Array weights = p->get_blend_shapes();
+
+ // Apply all the face blend weights to the mesh.
+ for (const KeyValue<int, int> &it : blend_mapping) {
+ mesh_instance->set_blend_shape_value(it.value, weights[it.key]);
+ }
+}
+
+void XRFaceModifier3D::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ _get_blend_data();
+ set_process_internal(true);
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ set_process_internal(false);
+ blend_mapping.clear();
+ } break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ _update_face_blends();
+ } break;
+ default: {
+ } break;
+ }
+}
diff --git a/scene/3d/xr_face_modifier_3d.h b/scene/3d/xr_face_modifier_3d.h
new file mode 100644
index 0000000000..147c374e95
--- /dev/null
+++ b/scene/3d/xr_face_modifier_3d.h
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* xr_face_modifier_3d.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 XR_FACE_MODIFIER_3D_H
+#define XR_FACE_MODIFIER_3D_H
+
+#include "mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+
+/**
+ The XRFaceModifier3D node drives the blend shapes of a MeshInstance3D
+ with facial expressions from an XRFaceTracking instance.
+
+ The blend shapes provided by the mesh are interrogated, and used to
+ deduce an optimal mapping from the Unified Expressions blend shapes
+ provided by the XRFaceTracking instance to drive the face.
+ */
+
+class XRFaceModifier3D : public Node3D {
+ GDCLASS(XRFaceModifier3D, Node3D);
+
+private:
+ StringName tracker_name = "/user/head";
+ NodePath target;
+
+ // Map from XRFaceTracker blend shape index to mesh blend shape index.
+ RBMap<int, int> blend_mapping;
+
+ MeshInstance3D *get_mesh_instance() const;
+ void _get_blend_data();
+ void _update_face_blends() const;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_face_tracker(const StringName &p_tracker_name);
+ StringName get_face_tracker() const;
+
+ void set_target(const NodePath &p_target);
+ NodePath get_target() const;
+
+ void _notification(int p_what);
+};
+
+#endif // XR_FACE_MODIFIER_3D_H
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index 79db9d5560..70e32c1a31 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -76,8 +76,8 @@ void XRCamera3D::_pose_changed(const Ref<XRPose> &p_pose) {
}
}
-Array XRCamera3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray XRCamera3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
// must be child node of XROrigin3D!
@@ -424,8 +424,8 @@ XRNode3D::~XRNode3D() {
xr_server->disconnect("tracker_removed", callable_mp(this, &XRNode3D::_removed_tracker));
}
-Array XRNode3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray XRNode3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
// must be child node of XROrigin!
@@ -603,8 +603,8 @@ Plane XRAnchor3D::get_plane() const {
Vector<XROrigin3D *> XROrigin3D::origin_nodes;
-Array XROrigin3D::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray XROrigin3D::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (is_visible() && is_inside_tree()) {
bool has_camera = false;
diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h
index 7f75e69fbc..ad52cf113d 100644
--- a/scene/3d/xr_nodes.h
+++ b/scene/3d/xr_nodes.h
@@ -55,7 +55,7 @@ protected:
void _pose_changed(const Ref<XRPose> &p_pose);
public:
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const override;
virtual Point2 unproject_position(const Vector3 &p_pos) const override;
@@ -109,7 +109,7 @@ public:
Ref<XRPose> get_pose();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
XRNode3D();
~XRNode3D();
@@ -193,7 +193,7 @@ protected:
static void _bind_methods();
public:
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
real_t get_world_scale() const;
void set_world_scale(real_t p_world_scale);
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 4240df9d27..87ac0bf5c8 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -661,13 +661,6 @@ bool AnimationMixer::_update_caches() {
Ref<Resource> resource;
Vector<StringName> leftover_path;
- if (!parent->has_node_and_resource(path)) {
- if (check_path) {
- WARN_PRINT_ED(mixer_name + ": '" + String(E) + "', couldn't resolve track: '" + String(path) + "'. This warning can be disabled in Project Settings.");
- }
- continue;
- }
-
Node *child = parent->get_node_and_resource(path, resource, leftover_path);
if (!child) {
if (check_path) {
@@ -872,13 +865,12 @@ bool AnimationMixer::_update_caches() {
}
} else if (track_cache_type == Animation::TYPE_VALUE) {
TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track);
- if (track_value->init_value.is_string() && anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE) {
- WARN_PRINT_ONCE_ED(mixer_name + ": '" + String(E) + "', Value Track: '" + String(path) + "' blends String types. This is an experimental algorithm.");
- }
-
// If it has at least one angle interpolation, it also uses angle interpolation for blending.
bool was_using_angle = track_value->is_using_angle;
if (track_src_type == Animation::TYPE_VALUE) {
+ 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;
}
if (check_angle_interpolation && (was_using_angle != track_value->is_using_angle)) {
@@ -1419,10 +1411,11 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue; // Nothing to blend.
}
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
- bool is_discrete = a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE;
+ bool is_value = ttype == Animation::TYPE_VALUE;
+ bool is_discrete = is_value && a->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE;
bool force_continuous = callback_mode_discrete == ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS;
if (!is_discrete || force_continuous) {
- Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time, is_discrete && force_continuous ? backward : false) : Variant(a->bezier_track_interpolate(i, time));
+ Variant value = is_value ? a->value_track_interpolate(i, time, is_discrete && force_continuous ? backward : false) : Variant(a->bezier_track_interpolate(i, time));
value = post_process_key_value(a, i, value, t->object_id);
if (value == Variant()) {
continue;
@@ -1974,7 +1967,13 @@ void AnimationMixer::_build_backup_track_cache() {
TrackCacheValue *t = static_cast<TrackCacheValue *>(track);
Object *t_obj = ObjectDB::get_instance(t->object_id);
if (t_obj) {
- t->value = t_obj->get_indexed(t->subpath);
+ t->value = Animation::cast_to_blendwise(t_obj->get_indexed(t->subpath));
+ }
+ t->use_discrete = false;
+ if (t->init_value.is_array()) {
+ t->element_size = MAX(t->element_size.operator int(), (t->value.operator Array()).size());
+ } else if (t->init_value.is_string()) {
+ t->element_size = (real_t)(t->value.operator Array()).size();
}
} break;
case Animation::TYPE_AUDIO: {
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index 5447a00ee3..7808ec788c 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -126,7 +126,7 @@ protected:
/* ---- General settings for animation ---- */
AnimationCallbackModeProcess callback_mode_process = ANIMATION_CALLBACK_MODE_PROCESS_IDLE;
AnimationCallbackModeMethod callback_mode_method = ANIMATION_CALLBACK_MODE_METHOD_DEFERRED;
- AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE;
+ AnimationCallbackModeDiscrete callback_mode_discrete = ANIMATION_CALLBACK_MODE_DISCRETE_DOMINANT;
int audio_max_polyphony = 32;
NodePath root_node;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 9694e855b5..2b7c47c869 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -568,10 +568,11 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const
pi.seeked = true;
root_animation_node->_pre_process(&process_state, pi, false);
started = false;
+ } else {
+ pi.seeked = false;
+ pi.time = p_delta;
+ root_animation_node->_pre_process(&process_state, pi, false);
}
- pi.seeked = false;
- pi.time = p_delta;
- root_animation_node->_pre_process(&process_state, pi, false);
}
if (!process_state.valid) {
@@ -606,8 +607,8 @@ uint64_t AnimationTree::get_last_process_pass() const {
return process_pass;
}
-Array AnimationTree::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray AnimationTree::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!root_animation_node.is_valid()) {
warnings.push_back(RTR("No root AnimationNode for the graph is set."));
}
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 7d153b2736..87928e4d20 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -261,7 +261,7 @@ public:
void set_advance_expression_base_node(const NodePath &p_path);
NodePath get_advance_expression_base_node() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
bool is_state_invalid() const;
String get_invalid_state_reason() const;
diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp
index d5f6f91c88..19b3ec481b 100644
--- a/scene/audio/audio_stream_player_internal.cpp
+++ b/scene/audio/audio_stream_player_internal.cpp
@@ -38,7 +38,7 @@ void AudioStreamPlayerInternal::_set_process(bool p_enabled) {
if (physical) {
node->set_physics_process_internal(p_enabled);
} else {
- node->set_process(p_enabled);
+ node->set_process_internal(p_enabled);
}
}
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 49616f87cc..66b14dc967 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -430,8 +430,8 @@ bool BaseButton::_was_pressed_by_mouse() const {
return was_mouse_pressed;
}
-Array BaseButton::get_configuration_warnings() const {
- Array warnings = Control::get_configuration_warnings();
+PackedStringArray BaseButton::get_configuration_warnings() const {
+ PackedStringArray warnings = Control::get_configuration_warnings();
if (get_button_group().is_valid() && !is_toggle_mode()) {
warnings.push_back(RTR("ButtonGroup is intended to be used only with buttons that have toggle_mode set to true."));
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index b5fbf11c8d..a8d5cee44c 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -139,7 +139,7 @@ public:
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
BaseButton();
~BaseButton();
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 594ae3eee8..c6e66c95c6 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -189,8 +189,8 @@ void Container::_notification(int p_what) {
}
}
-Array Container::get_configuration_warnings() const {
- Array warnings = Control::get_configuration_warnings();
+PackedStringArray Container::get_configuration_warnings() const {
+ PackedStringArray warnings = Control::get_configuration_warnings();
if (get_class() == "Container" && get_script().is_null()) {
warnings.push_back(RTR("Container by itself serves no purpose unless a script configures its children placement behavior.\nIf you don't intend to add a script, use a plain Control node instead."));
diff --git a/scene/gui/container.h b/scene/gui/container.h
index d04a71fe23..94c3c540d7 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -63,7 +63,7 @@ public:
virtual Vector<int> get_allowed_size_flags_horizontal() const;
virtual Vector<int> get_allowed_size_flags_vertical() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
Container();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 2124ffb806..b351d1ee4f 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -228,9 +228,9 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
}
}
-Array Control::get_configuration_warnings() const {
- ERR_READ_THREAD_GUARD_V(Array());
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray Control::get_configuration_warnings() const {
+ ERR_READ_THREAD_GUARD_V(PackedStringArray());
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 8bcd955457..a900a593dd 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -407,7 +407,7 @@ public:
static void set_root_layout_direction(int p_root_dir);
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
virtual bool is_text_field() const;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 7ea3aa344a..c23d21775f 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -251,8 +251,8 @@ Control::CursorShape GraphEdit::get_cursor_shape(const Point2 &p_pos) const {
return Control::get_cursor_shape(p_pos);
}
-Array GraphEdit::get_configuration_warnings() const {
- Array warnings = Control::get_configuration_warnings();
+PackedStringArray GraphEdit::get_configuration_warnings() const {
+ PackedStringArray warnings = Control::get_configuration_warnings();
warnings.push_back(RTR("Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future 4.x version involving compatibility-breaking API changes."));
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index dd99651d60..e24f039e84 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -375,7 +375,7 @@ public:
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index f847971015..56da1332e7 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -330,8 +330,8 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
}
}
-Array Label::get_configuration_warnings() const {
- Array warnings = Control::get_configuration_warnings();
+PackedStringArray Label::get_configuration_warnings() const {
+ PackedStringArray warnings = Control::get_configuration_warnings();
// FIXME: This is not ideal and the sizing model should be fixed,
// but for now we have to warn about this impossible to resolve combination.
diff --git a/scene/gui/label.h b/scene/gui/label.h
index fe90bd06bb..4bd0e53605 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -97,7 +97,7 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
void set_horizontal_alignment(HorizontalAlignment p_alignment);
HorizontalAlignment get_horizontal_alignment() const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 72a84e4884..d707a98e14 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -138,8 +138,16 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
return;
}
+ if (selection.enabled) {
+ selection_delete();
+ return;
+ }
+
+ if (caret_column == 0) {
+ return; // Nothing to do.
+ }
+
if (p_all_to_left) {
- deselect();
text = text.substr(caret_column);
_shape();
set_caret_column(0);
@@ -147,11 +155,6 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
return;
}
- if (selection.enabled) {
- selection_delete();
- return;
- }
-
if (p_word) {
int cc = caret_column;
@@ -176,25 +179,22 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
return;
}
- if (p_all_to_right) {
- deselect();
- text = text.substr(0, caret_column);
- _shape();
- _text_changed();
- return;
- }
-
if (selection.enabled) {
selection_delete();
return;
}
- int text_len = text.length();
-
- if (caret_column == text_len) {
+ if (caret_column == text.length()) {
return; // Nothing to do.
}
+ if (p_all_to_right) {
+ text = text.substr(0, caret_column);
+ _shape();
+ _text_changed();
+ return;
+ }
+
if (p_word) {
int cc = caret_column;
PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
@@ -2133,6 +2133,9 @@ PopupMenu *LineEdit::get_menu() const {
void LineEdit::_editor_settings_changed() {
#ifdef TOOLS_ENABLED
+ if (!EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/appearance/caret")) {
+ return;
+ }
set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
set_caret_blink_interval(EDITOR_GET("text_editor/appearance/caret/caret_blink_interval"));
#endif
@@ -2279,8 +2282,8 @@ void LineEdit::_emit_text_change() {
emit_signal(SNAME("text_changed"), text);
text_changed_dirty = false;
}
-Array LineEdit::get_configuration_warnings() const {
- Array warnings = Control::get_configuration_warnings();
+PackedStringArray LineEdit::get_configuration_warnings() const {
+ PackedStringArray warnings = Control::get_configuration_warnings();
if (secret_character.length() > 1) {
warnings.push_back("Secret Character property supports only one character. Extra characters will be ignored.");
}
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index b2582e0eb5..993bc727e4 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -385,7 +385,7 @@ public:
virtual bool is_text_field() const override;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void show_virtual_keyboard();
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 90d962ccfd..0da5093ab8 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -456,9 +456,6 @@ void PopupMenu::_input_from_window(const Ref<InputEvent> &p_event) {
}
void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
- Ref<InputEventMouseButton> b = p_event;
- Ref<InputEventMouseMotion> m = p_event;
-
if (!items.is_empty()) {
Input *input = Input::get_singleton();
Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
@@ -587,58 +584,53 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
}
}
- if (m.is_valid() && drag_to_press) {
- BitField<MouseButtonMask> initial_button_mask = m->get_button_mask();
- if (!initial_button_mask.has_flag(mouse_button_to_mask(MouseButton::LEFT)) && !initial_button_mask.has_flag(mouse_button_to_mask(MouseButton::RIGHT))) {
- mouse_is_pressed = false;
- }
-
- if (!item_clickable_area.has_point(m->get_position()) && !mouse_is_pressed) {
- drag_to_press = false;
- }
- }
+ Ref<InputEventMouseButton> b = p_event;
- if ((b.is_valid() && b->is_pressed()) || (!mouse_is_pressed && drag_to_press)) {
- if (b.is_valid()) {
- MouseButton button_idx = b->get_button_index();
- if (button_idx != MouseButton::LEFT && button_idx != MouseButton::RIGHT) {
- return;
- }
- } else {
- uint64_t now = OS::get_singleton()->get_ticks_msec();
- uint64_t diff = now - popup_time_msec;
- if (diff < 250) {
- drag_to_press = false;
- return;
- }
+ if (b.is_valid()) {
+ if (!item_clickable_area.has_point(b->get_position())) {
+ return;
}
- drag_to_press = false;
-
- int over = -1;
-
- if (m.is_valid()) {
- over = _get_mouse_over(m->get_position());
- } else if (b.is_valid()) {
- over = _get_mouse_over(b->get_position());
- }
+ MouseButton button_idx = b->get_button_index();
+ if (!b->is_pressed()) {
+ // Activate the item on release of either the left mouse button or
+ // any mouse button held down when the popup was opened.
+ // This allows for opening the popup and triggering an action in a single mouse click.
+ if (button_idx == MouseButton::LEFT || initial_button_mask.has_flag(mouse_button_to_mask(button_idx))) {
+ bool was_during_grabbed_click = during_grabbed_click;
+ during_grabbed_click = false;
+ initial_button_mask.clear();
+
+ // Disable clicks under a time threshold to avoid selection right when opening the popup.
+ uint64_t now = OS::get_singleton()->get_ticks_msec();
+ uint64_t diff = now - popup_time_msec;
+ if (diff < 150) {
+ return;
+ }
- if (over < 0) {
- hide();
- return;
- }
+ int over = _get_mouse_over(b->get_position());
+ if (over < 0) {
+ if (!was_during_grabbed_click) {
+ hide();
+ }
+ return;
+ }
- if (items[over].separator || items[over].disabled) {
- return;
- }
+ if (items[over].separator || items[over].disabled) {
+ return;
+ }
- if (!items[over].submenu.is_empty()) {
- _activate_submenu(over);
- return;
+ if (!items[over].submenu.is_empty()) {
+ _activate_submenu(over);
+ return;
+ }
+ activate_item(over);
+ }
}
- activate_item(over);
}
+ Ref<InputEventMouseMotion> m = p_event;
+
if (m.is_valid()) {
if (m->get_velocity().is_zero_approx()) {
return;
@@ -1070,6 +1062,11 @@ void PopupMenu::_notification(int p_what) {
}
} break;
+ case NOTIFICATION_POST_POPUP: {
+ initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
+ during_grabbed_click = (bool)initial_button_mask;
+ } break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
Input *input = Input::get_singleton();
@@ -2805,8 +2802,6 @@ void PopupMenu::popup(const Rect2i &p_bounds) {
moved = Vector2();
popup_time_msec = OS::get_singleton()->get_ticks_msec();
Popup::popup(p_bounds);
- drag_to_press = true;
- mouse_is_pressed = true;
}
PopupMenu::PopupMenu() {
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index abe852a993..d068418059 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -107,8 +107,8 @@ class PopupMenu : public Popup {
Timer *submenu_timer = nullptr;
List<Rect2> autohide_areas;
Vector<Item> items;
- bool mouse_is_pressed = true;
- bool drag_to_press = true;
+ BitField<MouseButtonMask> initial_button_mask;
+ bool during_grabbed_click = false;
int mouse_over = -1;
int submenu_over = -1;
String _get_accel_text(const Item &p_item) const;
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index da40c7f3e2..236dfcc864 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -30,8 +30,8 @@
#include "range.h"
-Array Range::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray Range::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (shared->exp_ratio && shared->min <= 0) {
warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
diff --git a/scene/gui/range.h b/scene/gui/range.h
index e08a77f0f7..b1c2446ded 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -103,7 +103,7 @@ public:
void share(Range *p_range);
void unshare();
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
Range();
~Range();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index b6dd953d50..4f25fed335 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -5650,10 +5650,15 @@ void RichTextLabel::set_text(const String &p_bbcode) {
}
void RichTextLabel::_apply_translation() {
+ // If `text` is empty, it could mean that the tag stack is being used instead. Leave it be.
+ if (text.is_empty()) {
+ return;
+ }
+
String xl_text = atr(text);
if (use_bbcode) {
parse_bbcode(xl_text);
- } else { // raw text
+ } else { // Raw text.
clear();
add_text(xl_text);
}
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index fd3a1f4a45..89d308de3f 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -537,8 +537,8 @@ void ScrollContainer::set_follow_focus(bool p_follow) {
follow_focus = p_follow;
}
-Array ScrollContainer::get_configuration_warnings() const {
- Array warnings = Container::get_configuration_warnings();
+PackedStringArray ScrollContainer::get_configuration_warnings() const {
+ PackedStringArray warnings = Container::get_configuration_warnings();
int found = 0;
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 69ff7ccc14..02146618cd 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -119,7 +119,7 @@ public:
VScrollBar *get_v_scroll_bar();
void ensure_control_visible(Control *p_control);
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
ScrollContainer();
};
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 4ed136005a..0d33774e20 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -259,8 +259,8 @@ void SubViewportContainer::remove_child_notify(Node *p_child) {
}
}
-Array SubViewportContainer::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray SubViewportContainer::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
bool has_viewport = false;
for (int i = 0; i < get_child_count(); i++) {
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index e172f2f040..06420de730 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -68,7 +68,7 @@ public:
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
virtual Vector<int> get_allowed_size_flags_vertical() const override;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
SubViewportContainer();
};
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index bb787a742a..f87bccdfe7 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -635,6 +635,13 @@ void TabBar::set_tab_count(int p_count) {
}
}
+ if (!initialized) {
+ if (queued_current != current) {
+ current = queued_current;
+ }
+ initialized = true;
+ }
+
queue_redraw();
update_minimum_size();
notify_property_list_changed();
@@ -649,6 +656,10 @@ void TabBar::set_current_tab(int p_current) {
// An index of -1 is only valid if deselecting is enabled or there are no valid tabs.
ERR_FAIL_COND_MSG(!_can_deselect(), "Cannot deselect tabs, deselection is not enabled.");
} else {
+ if (!initialized && p_current >= get_tab_count()) {
+ queued_current = p_current;
+ return;
+ }
ERR_FAIL_INDEX(p_current, get_tab_count());
}
@@ -1825,9 +1836,6 @@ void TabBar::_bind_methods() {
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
- // "current_tab" property must come after "tab_count", otherwise the property isn't loaded correctly.
- ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
-
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1"), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
@@ -1840,6 +1848,8 @@ void TabBar::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb"), "set_select_with_rmb", "get_select_with_rmb");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_enabled"), "set_deselect_enabled", "get_deselect_enabled");
+ ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
+
BIND_ENUM_CONSTANT(ALIGNMENT_LEFT);
BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT);
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index 444c737220..65a1d5bd4f 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -108,6 +108,9 @@ private:
bool scroll_to_selected = true;
int tabs_rearrange_group = -1;
+ bool initialized = false;
+ int queued_current = -1;
+
const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 0.5;
const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 20;
float gamepad_event_delay_ms = DEFAULT_GAMEPAD_EVENT_DELAY_MS;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 093b52b650..2f435cafee 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -1169,6 +1169,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_local_mouse_position"), &CanvasItem::get_local_mouse_position);
ClassDB::bind_method(D_METHOD("get_global_mouse_position"), &CanvasItem::get_global_mouse_position);
ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasItem::get_canvas);
+ ClassDB::bind_method(D_METHOD("get_canvas_layer_node"), &CanvasItem::get_canvas_layer_node);
ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasItem::get_world_2d);
//ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasItem::get_viewport);
@@ -1325,6 +1326,11 @@ int CanvasItem::get_canvas_layer() const {
}
}
+CanvasLayer *CanvasItem::get_canvas_layer_node() const {
+ ERR_READ_THREAD_GUARD_V(nullptr);
+ return canvas_layer;
+}
+
void CanvasItem::set_visibility_layer(uint32_t p_visibility_layer) {
ERR_THREAD_GUARD;
visibility_layer = p_visibility_layer;
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index ce1dbce6c3..03b01f7ef7 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -363,6 +363,7 @@ public:
virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); };
int get_canvas_layer() const;
+ CanvasLayer *get_canvas_layer_node() const;
CanvasItem();
~CanvasItem();
diff --git a/scene/main/missing_node.cpp b/scene/main/missing_node.cpp
index 02e079892c..83672ae5e0 100644
--- a/scene/main/missing_node.cpp
+++ b/scene/main/missing_node.cpp
@@ -82,9 +82,9 @@ bool MissingNode::is_recording_properties() const {
return recording_properties;
}
-Array MissingNode::get_configuration_warnings() const {
+PackedStringArray MissingNode::get_configuration_warnings() const {
// The mere existence of this node is warning.
- Array ret;
+ PackedStringArray ret;
if (!original_scene.is_empty()) {
ret.push_back(vformat(RTR("This node was an instance of scene '%s', which was no longer available when this scene was loaded."), original_scene));
ret.push_back(vformat(RTR("Saving current scene will discard instance and all its properties, including editable children edits (if existing).")));
diff --git a/scene/main/missing_node.h b/scene/main/missing_node.h
index ccaf1e471e..fb1c957988 100644
--- a/scene/main/missing_node.h
+++ b/scene/main/missing_node.h
@@ -59,7 +59,7 @@ public:
void set_recording_properties(bool p_enable);
bool is_recording_properties() const;
- virtual Array get_configuration_warnings() const override;
+ virtual PackedStringArray get_configuration_warnings() const override;
MissingNode();
};
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 77e00e4ab4..f827f68def 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -110,14 +110,12 @@ void Node::_notification(int p_notification) {
// Don't translate UI elements when they're being edited.
if (is_part_of_edited_scene()) {
set_message_translation(false);
- } else if (data.auto_translate_mode != AUTO_TRANSLATE_MODE_DISABLED) {
- notification(NOTIFICATION_TRANSLATION_CHANGED);
}
-#else
+#endif
+
if (data.auto_translate_mode != AUTO_TRANSLATE_MODE_DISABLED) {
notification(NOTIFICATION_TRANSLATION_CHANGED);
}
-#endif
if (data.input) {
add_to_group("_vp_input" + itos(get_viewport()->get_instance_id()));
@@ -2506,7 +2504,7 @@ StringName Node::get_property_store_alias(const StringName &p_property) const {
bool Node::is_part_of_edited_scene() const {
return Engine::get_singleton()->is_editor_hint() && is_inside_tree() && get_tree()->get_edited_scene_root() &&
- (get_tree()->get_edited_scene_root() == this || get_tree()->get_edited_scene_root()->is_ancestor_of(this));
+ get_tree()->get_edited_scene_root()->get_parent()->is_ancestor_of(this);
}
#endif
@@ -3027,9 +3025,9 @@ Array Node::_get_node_and_resource(const NodePath &p_path) {
Node *Node::get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property) const {
ERR_THREAD_GUARD_V(nullptr);
- Node *node = get_node(p_path);
r_res = Ref<Resource>();
r_leftover_subpath = Vector<StringName>();
+ Node *node = get_node_or_null(p_path);
if (!node) {
return nullptr;
}
@@ -3204,91 +3202,16 @@ void Node::clear_internal_tree_resource_paths() {
}
}
-Array Node::get_configuration_warnings() const {
- ERR_THREAD_GUARD_V(Array());
- Array warnings;
- GDVIRTUAL_CALL(_get_configuration_warnings, warnings);
- return warnings;
-}
+PackedStringArray Node::get_configuration_warnings() const {
+ ERR_THREAD_GUARD_V(PackedStringArray());
+ PackedStringArray ret;
-Dictionary Node::configuration_warning_to_dict(const Variant &p_warning) const {
- switch (p_warning.get_type()) {
- case Variant::Type::DICTIONARY:
- return p_warning;
- case Variant::Type::STRING: {
- // Convert string to dictionary.
- Dictionary warning;
- warning["message"] = p_warning;
- return warning;
- }
- default: {
- ERR_FAIL_V_MSG(Dictionary(), "Node::get_configuration_warnings returned a value which is neither a string nor a dictionary, but a " + Variant::get_type_name(p_warning.get_type()));
- }
- }
-}
-
-Vector<Dictionary> Node::get_configuration_warnings_as_dicts() const {
- Vector<Dictionary> ret;
- Array mixed = get_configuration_warnings();
- for (int i = 0; i < mixed.size(); i++) {
- ret.append(configuration_warning_to_dict(mixed[i]));
- }
- return ret;
-}
-
-Vector<Dictionary> Node::get_configuration_warnings_of_property(const String &p_property) const {
- Vector<Dictionary> ret;
- Vector<Dictionary> warnings = get_configuration_warnings_as_dicts();
- if (p_property.is_empty()) {
+ Vector<String> warnings;
+ if (GDVIRTUAL_CALL(_get_configuration_warnings, warnings)) {
ret.append_array(warnings);
- } else {
- // Filter by property path.
- for (int i = 0; i < warnings.size(); i++) {
- Dictionary warning = warnings[i];
- String warning_property = warning.get("property", String());
- if (p_property == warning_property) {
- ret.append(warning);
- }
- }
}
- return ret;
-}
-
-PackedStringArray Node::get_configuration_warnings_as_strings(bool p_wrap_lines, const String &p_property) const {
- Vector<Dictionary> warnings = get_configuration_warnings_of_property(p_property);
- const String bullet_point = U"• ";
- PackedStringArray all_warnings;
- for (const Dictionary &warning : warnings) {
- if (!warning.has("message")) {
- continue;
- }
-
- // Prefix with property name if we are showing all warnings.
- String text;
- if (warning.has("property") && p_property.is_empty()) {
- text = bullet_point + vformat("[%s] %s", warning["property"], warning["message"]);
- } else {
- text = bullet_point + static_cast<String>(warning["message"]);
- }
-
- if (p_wrap_lines) {
- // Limit the line width while keeping some padding.
- // It is not efficient, but it does not have to be.
- const PackedInt32Array boundaries = TS->string_get_word_breaks(text, "", 80);
- PackedStringArray lines;
- for (int i = 0; i < boundaries.size(); i += 2) {
- const int start = boundaries[i];
- const int end = boundaries[i + 1];
- String line = text.substr(start, end - start);
- lines.append(line);
- }
- text = String("\n").join(lines);
- }
- text = text.replace("\n", "\n ");
- all_warnings.append(text);
- }
- return all_warnings;
+ return ret;
}
void Node::update_configuration_warnings() {
@@ -3774,16 +3697,6 @@ String Node::_get_name_num_separator() {
return " ";
}
-StringName Node::get_configuration_warning_icon(int p_count) {
- if (p_count == 1) {
- return SNAME("NodeWarning");
- } else if (p_count <= 3) {
- return vformat("NodeWarnings%d", p_count);
- } else {
- return SNAME("NodeWarnings4Plus");
- }
-}
-
Node::Node() {
orphan_node_count++;
}
diff --git a/scene/main/node.h b/scene/main/node.h
index bbbdb87a32..b936cdd375 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -304,7 +304,6 @@ protected:
static void _bind_methods();
static String _get_name_num_separator();
- static StringName get_configuration_warning_icon(int p_count);
friend class SceneState;
@@ -331,7 +330,7 @@ protected:
GDVIRTUAL0(_enter_tree)
GDVIRTUAL0(_exit_tree)
GDVIRTUAL0(_ready)
- GDVIRTUAL0RC(Array, _get_configuration_warnings)
+ GDVIRTUAL0RC(Vector<String>, _get_configuration_warnings)
GDVIRTUAL1(_input, Ref<InputEvent>)
GDVIRTUAL1(_shortcut_input, Ref<InputEvent>)
@@ -648,11 +647,7 @@ public:
_FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; }
- virtual Array get_configuration_warnings() const;
- Dictionary configuration_warning_to_dict(const Variant &p_warning) const;
- Vector<Dictionary> get_configuration_warnings_as_dicts() const;
- Vector<Dictionary> get_configuration_warnings_of_property(const String &p_property = String()) const;
- PackedStringArray get_configuration_warnings_as_strings(bool p_wrap_lines, const String &p_property = String()) const;
+ virtual PackedStringArray get_configuration_warnings() const;
void update_configuration_warnings();
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 2cbcc8e33e..6b3b2d6260 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1803,10 +1803,10 @@ SceneTree::SceneTree() {
float mesh_lod_threshold = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/mesh_lod/lod_change/threshold_pixels", PROPERTY_HINT_RANGE, "0,1024,0.1"), 1.0);
root->set_mesh_lod_threshold(mesh_lod_threshold);
- bool snap_2d_transforms = GLOBAL_DEF("rendering/2d/snap/snap_2d_transforms_to_pixel", false);
+ bool snap_2d_transforms = GLOBAL_DEF_BASIC("rendering/2d/snap/snap_2d_transforms_to_pixel", false);
root->set_snap_2d_transforms_to_pixel(snap_2d_transforms);
- bool snap_2d_vertices = GLOBAL_DEF("rendering/2d/snap/snap_2d_vertices_to_pixel", false);
+ bool snap_2d_vertices = GLOBAL_DEF_BASIC("rendering/2d/snap/snap_2d_vertices_to_pixel", false);
root->set_snap_2d_vertices_to_pixel(snap_2d_vertices);
// We setup VRS for the main viewport here, in the editor this will have little effect.
diff --git a/scene/main/shader_globals_override.cpp b/scene/main/shader_globals_override.cpp
index b3d3659c5b..e3c26c30e2 100644
--- a/scene/main/shader_globals_override.cpp
+++ b/scene/main/shader_globals_override.cpp
@@ -271,8 +271,8 @@ void ShaderGlobalsOverride::_notification(int p_what) {
}
}
-Array ShaderGlobalsOverride::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (!active) {
warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
diff --git a/scene/main/shader_globals_override.h b/scene/main/shader_globals_override.h
index 72226c0cb3..d8557ecf6a 100644
--- a/scene/main/shader_globals_override.h
+++ b/scene/main/shader_globals_override.h
@@ -58,7 +58,7 @@ protected:
static void _bind_methods();
public:
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
ShaderGlobalsOverride();
};
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index acb788f76f..0f4f18b495 100644
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -180,8 +180,8 @@ void Timer::_set_process(bool p_process, bool p_force) {
processing = p_process;
}
-Array Timer::get_configuration_warnings() const {
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray Timer::get_configuration_warnings() const {
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (wait_time < 0.05 - CMP_EPSILON) {
warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
diff --git a/scene/main/timer.h b/scene/main/timer.h
index add61ef5f4..d16e49793d 100644
--- a/scene/main/timer.h
+++ b/scene/main/timer.h
@@ -73,7 +73,7 @@ public:
double get_time_left() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_timer_process_callback(TimerProcessCallback p_callback);
TimerProcessCallback get_timer_process_callback() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 5952af60ee..a86a685fc3 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1383,6 +1383,13 @@ Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
}
Transform2D ai = get_final_transform().affine_inverse();
+ Ref<InputEventMouse> me = ev;
+ if (me.is_valid()) {
+ me = me->xformed_by(ai);
+ // For InputEventMouse, the global position is not adjusted by ev->xformed_by() and needs to be set separately.
+ me->set_global_position(me->get_position());
+ return me;
+ }
return ev->xformed_by(ai);
}
@@ -3510,9 +3517,9 @@ Variant Viewport::gui_get_drag_data() const {
return gui.drag_data;
}
-Array Viewport::get_configuration_warnings() const {
- ERR_MAIN_THREAD_GUARD_V(Array());
- Array warnings = Node::get_configuration_warnings();
+PackedStringArray Viewport::get_configuration_warnings() const {
+ ERR_MAIN_THREAD_GUARD_V(PackedStringArray());
+ PackedStringArray warnings = Node::get_configuration_warnings();
if (size.x <= 1 || size.y <= 1) {
warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything."));
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 68f0e8b655..03db0d4023 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -614,7 +614,7 @@ public:
Control *gui_get_focus_owner() const;
Control *gui_get_hovered_control() const;
- Array get_configuration_warnings() const override;
+ PackedStringArray get_configuration_warnings() const override;
void set_debug_draw(DebugDraw p_debug_draw);
DebugDraw get_debug_draw() const;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 3ec36f731c..c7cd52124a 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1770,7 +1770,10 @@ void Window::popup(const Rect2i &p_screen_rect) {
if (p_screen_rect != Rect2i()) {
set_position(p_screen_rect.position);
- set_size(p_screen_rect.size);
+ int screen_id = DisplayServer::get_singleton()->get_screen_from_rect(p_screen_rect);
+ Size2i screen_size = DisplayServer::get_singleton()->screen_get_usable_rect(screen_id).size;
+ Size2i new_size = p_screen_rect.size.min(screen_size);
+ set_size(new_size);
}
Rect2i adjust = _popup_adjust_rect();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index d7e5eb4698..837237ac50 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -272,6 +272,7 @@
#include "scene/3d/visible_on_screen_notifier_3d.h"
#include "scene/3d/voxel_gi.h"
#include "scene/3d/world_environment.h"
+#include "scene/3d/xr_face_modifier_3d.h"
#include "scene/3d/xr_nodes.h"
#include "scene/animation/root_motion_view.h"
#include "scene/resources/environment.h"
@@ -519,6 +520,7 @@ void register_scene_types() {
GDREGISTER_CLASS(XRController3D);
GDREGISTER_CLASS(XRAnchor3D);
GDREGISTER_CLASS(XROrigin3D);
+ GDREGISTER_CLASS(XRFaceModifier3D);
GDREGISTER_CLASS(MeshInstance3D);
GDREGISTER_CLASS(OccluderInstance3D);
GDREGISTER_ABSTRACT_CLASS(Occluder3D);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index b936c2decf..910a6e303d 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -766,6 +766,21 @@ bool Environment::is_fog_enabled() const {
return fog_enabled;
}
+void Environment::set_fog_mode(FogMode p_mode) {
+ if (fog_mode != p_mode && p_mode == FogMode::FOG_MODE_EXPONENTIAL) {
+ set_fog_density(0.01);
+ } else {
+ set_fog_density(1.0);
+ }
+ fog_mode = p_mode;
+ _update_fog();
+ notify_property_list_changed();
+}
+
+Environment::FogMode Environment::get_fog_mode() const {
+ return fog_mode;
+}
+
void Environment::set_fog_light_color(const Color &p_light_color) {
fog_light_color = p_light_color;
_update_fog();
@@ -837,7 +852,51 @@ void Environment::_update_fog() {
fog_height,
fog_height_density,
fog_aerial_perspective,
- fog_sky_affect);
+ fog_sky_affect,
+ RS::EnvironmentFogMode(fog_mode));
+}
+
+// Depth Fog
+
+void Environment::set_fog_depth_curve(float p_curve) {
+ fog_depth_curve = p_curve;
+ _update_fog_depth();
+}
+
+float Environment::get_fog_depth_curve() const {
+ return fog_depth_curve;
+}
+
+void Environment::set_fog_depth_begin(float p_begin) {
+ fog_depth_begin = p_begin;
+ if (fog_depth_begin > fog_depth_end) {
+ set_fog_depth_end(fog_depth_begin);
+ }
+ _update_fog_depth();
+}
+
+float Environment::get_fog_depth_begin() const {
+ return fog_depth_begin;
+}
+
+void Environment::set_fog_depth_end(float p_end) {
+ fog_depth_end = p_end;
+ if (fog_depth_end < fog_depth_begin) {
+ set_fog_depth_begin(fog_depth_end);
+ }
+ _update_fog_depth();
+}
+
+float Environment::get_fog_depth_end() const {
+ return fog_depth_end;
+}
+
+void Environment::_update_fog_depth() {
+ RS::get_singleton()->environment_set_fog_depth(
+ environment,
+ fog_depth_curve,
+ fog_depth_begin,
+ fog_depth_end);
}
// Volumetric Fog
@@ -1040,6 +1099,12 @@ void Environment::_validate_property(PropertyInfo &p_property) const {
}
}
+ if (p_property.name == "fog_depth_curve" || p_property.name == "fog_depth_begin" || p_property.name == "fog_depth_end") {
+ if (fog_mode == FOG_MODE_EXPONENTIAL) {
+ p_property.usage = PROPERTY_USAGE_NO_EDITOR;
+ }
+ }
+
if (p_property.name == "ambient_light_color" || p_property.name == "ambient_light_energy") {
if (ambient_source == AMBIENT_SOURCE_DISABLED) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
@@ -1377,6 +1442,8 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fog_enabled", "enabled"), &Environment::set_fog_enabled);
ClassDB::bind_method(D_METHOD("is_fog_enabled"), &Environment::is_fog_enabled);
+ ClassDB::bind_method(D_METHOD("set_fog_mode", "mode"), &Environment::set_fog_mode);
+ ClassDB::bind_method(D_METHOD("get_fog_mode"), &Environment::get_fog_mode);
ClassDB::bind_method(D_METHOD("set_fog_light_color", "light_color"), &Environment::set_fog_light_color);
ClassDB::bind_method(D_METHOD("get_fog_light_color"), &Environment::get_fog_light_color);
ClassDB::bind_method(D_METHOD("set_fog_light_energy", "light_energy"), &Environment::set_fog_light_energy);
@@ -1399,8 +1466,16 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fog_sky_affect", "sky_affect"), &Environment::set_fog_sky_affect);
ClassDB::bind_method(D_METHOD("get_fog_sky_affect"), &Environment::get_fog_sky_affect);
+ ClassDB::bind_method(D_METHOD("set_fog_depth_curve", "curve"), &Environment::set_fog_depth_curve);
+ ClassDB::bind_method(D_METHOD("get_fog_depth_curve"), &Environment::get_fog_depth_curve);
+ ClassDB::bind_method(D_METHOD("set_fog_depth_begin", "begin"), &Environment::set_fog_depth_begin);
+ ClassDB::bind_method(D_METHOD("get_fog_depth_begin"), &Environment::get_fog_depth_begin);
+ ClassDB::bind_method(D_METHOD("set_fog_depth_end", "end"), &Environment::set_fog_depth_end);
+ ClassDB::bind_method(D_METHOD("get_fog_depth_end"), &Environment::get_fog_depth_end);
+
ADD_GROUP("Fog", "fog_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fog_mode", PROPERTY_HINT_ENUM, "Exponential,Depth"), "set_fog_mode", "get_fog_mode");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_fog_light_color", "get_fog_light_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_fog_light_energy", "get_fog_light_energy");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_sun_scatter", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_fog_sun_scatter", "get_fog_sun_scatter");
@@ -1411,6 +1486,10 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_less,or_greater,suffix:m"), "set_fog_height", "get_fog_height");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_less,or_greater"), "set_fog_height_density", "get_fog_height_density");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_curve", PROPERTY_HINT_EXP_EASING), "set_fog_depth_curve", "get_fog_depth_curve");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_begin", PROPERTY_HINT_RANGE, "0,4000,0.1,or_greater,or_less,suffix:m"), "set_fog_depth_begin", "get_fog_depth_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_depth_end", PROPERTY_HINT_RANGE, "0,4000,0.1,or_greater,or_less,suffix:m"), "set_fog_depth_end", "get_fog_depth_end");
+
ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled);
ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled);
ClassDB::bind_method(D_METHOD("set_volumetric_fog_emission", "color"), &Environment::set_volumetric_fog_emission);
@@ -1504,6 +1583,9 @@ void Environment::_bind_methods() {
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE);
BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_MIX);
+ BIND_ENUM_CONSTANT(FOG_MODE_EXPONENTIAL);
+ BIND_ENUM_CONSTANT(FOG_MODE_DEPTH);
+
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT);
BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_100_PERCENT);
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index b01c328b50..68b49f38d7 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -75,6 +75,11 @@ public:
SDFGI_Y_SCALE_100_PERCENT,
};
+ enum FogMode {
+ FOG_MODE_EXPONENTIAL,
+ FOG_MODE_DEPTH,
+ };
+
enum GlowBlendMode {
GLOW_BLEND_MODE_ADDITIVE,
GLOW_BLEND_MODE_SCREEN,
@@ -172,6 +177,7 @@ private:
// Fog
bool fog_enabled = false;
+ FogMode fog_mode = FOG_MODE_EXPONENTIAL;
Color fog_light_color = Color(0.518, 0.553, 0.608);
float fog_light_energy = 1.0;
float fog_sun_scatter = 0.0;
@@ -183,6 +189,13 @@ private:
void _update_fog();
+ // Depth Fog
+ float fog_depth_curve = 1.0;
+ float fog_depth_begin = 10.0;
+ float fog_depth_end = 100.0;
+
+ void _update_fog_depth();
+
// Volumetric Fog
bool volumetric_fog_enabled = false;
float volumetric_fog_density = 0.05;
@@ -361,6 +374,8 @@ public:
void set_fog_enabled(bool p_enabled);
bool is_fog_enabled() const;
+ void set_fog_mode(FogMode p_mode);
+ FogMode get_fog_mode() const;
void set_fog_light_color(const Color &p_light_color);
Color get_fog_light_color() const;
void set_fog_light_energy(float p_amount);
@@ -379,6 +394,14 @@ public:
void set_fog_sky_affect(float p_sky_affect);
float get_fog_sky_affect() const;
+ // Depth Fog
+ void set_fog_depth_curve(float p_curve);
+ float get_fog_depth_curve() const;
+ void set_fog_depth_begin(float p_begin);
+ float get_fog_depth_begin() const;
+ void set_fog_depth_end(float p_end);
+ float get_fog_depth_end() const;
+
// Volumetric Fog
void set_volumetric_fog_enabled(bool p_enable);
bool is_volumetric_fog_enabled() const;
@@ -429,5 +452,6 @@ VARIANT_ENUM_CAST(Environment::ReflectionSource)
VARIANT_ENUM_CAST(Environment::ToneMapper)
VARIANT_ENUM_CAST(Environment::SDFGIYScale)
VARIANT_ENUM_CAST(Environment::GlowBlendMode)
+VARIANT_ENUM_CAST(Environment::FogMode)
#endif // ENVIRONMENT_H
diff --git a/servers/audio/effects/audio_effect_pitch_shift.cpp b/servers/audio/effects/audio_effect_pitch_shift.cpp
index 11c1d44472..ddb17e050a 100644
--- a/servers/audio/effects/audio_effect_pitch_shift.cpp
+++ b/servers/audio/effects/audio_effect_pitch_shift.cpp
@@ -87,8 +87,10 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff
double magn, phase, tmp, window, real, imag;
double freqPerBin, expct;
long i,k, qpd, index, inFifoLatency, stepSize, fftFrameSize2;
+ unsigned long fftFrameBufferSize;
/* set up some handy variables */
+ fftFrameBufferSize = (unsigned long)fftFrameSize*sizeof(float);
fftFrameSize2 = fftFrameSize/2;
stepSize = fftFrameSize/osamp;
freqPerBin = sampleRate/(double)fftFrameSize;
@@ -160,8 +162,8 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff
/* ***************** PROCESSING ******************* */
/* this does the actual pitch shifting */
- memset(gSynMagn, 0, fftFrameSize*sizeof(float));
- memset(gSynFreq, 0, fftFrameSize*sizeof(float));
+ memset(gSynMagn, 0, fftFrameBufferSize);
+ memset(gSynFreq, 0, fftFrameBufferSize);
for (k = 0; k <= fftFrameSize2; k++) {
index = k*pitchShift;
if (index <= fftFrameSize2) {
@@ -214,7 +216,7 @@ void SMBPitchShift::PitchShift(float pitchShift, long numSampsToProcess, long ff
}
/* shift accumulator */
- memmove(gOutputAccum, gOutputAccum+stepSize, fftFrameSize*sizeof(float));
+ memmove(gOutputAccum, gOutputAccum+stepSize, fftFrameBufferSize);
/* move input FIFO */
for (k = 0; k < inFifoLatency; k++) { gInFIFO[k] = gInFIFO[k+stepSize];
diff --git a/servers/debugger/servers_debugger.cpp b/servers/debugger/servers_debugger.cpp
index 8da3a10ce9..06be73acc5 100644
--- a/servers/debugger/servers_debugger.cpp
+++ b/servers/debugger/servers_debugger.cpp
@@ -198,7 +198,7 @@ class ServersDebugger::ScriptsProfiler : public EngineProfiler {
typedef ServersDebugger::ScriptFunctionInfo FunctionInfo;
struct ProfileInfoSort {
bool operator()(ScriptLanguage::ProfilingInfo *A, ScriptLanguage::ProfilingInfo *B) const {
- return A->total_time < B->total_time;
+ return A->total_time > B->total_time;
}
};
Vector<ScriptLanguage::ProfilingInfo> info;
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 0ee2984a8c..b42e7bf9bb 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -80,6 +80,7 @@
#include "text/text_server_dummy.h"
#include "text/text_server_extension.h"
#include "text_server.h"
+#include "xr/xr_face_tracker.h"
#include "xr/xr_interface.h"
#include "xr/xr_interface_extension.h"
#include "xr/xr_positional_tracker.h"
@@ -189,6 +190,7 @@ void register_server_types() {
GDREGISTER_CLASS(XRInterfaceExtension); // can't register this as virtual because we need a creation function for our extensions.
GDREGISTER_CLASS(XRPose);
GDREGISTER_CLASS(XRPositionalTracker);
+ GDREGISTER_CLASS(XRFaceTracker);
GDREGISTER_CLASS(AudioStream);
GDREGISTER_CLASS(AudioStreamPlayback);
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index e972d91072..c454d35d28 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -374,7 +374,7 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
SceneShaderForwardClustered::PipelineVersion pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_MAX; // Assigned to silence wrong -Wmaybe-initialized.
uint32_t pipeline_color_pass_flags = 0;
- uint32_t pipeline_specialization = 0;
+ uint32_t pipeline_specialization = p_params->spec_constant_base_flags;
if constexpr (p_pass_mode == PASS_MODE_COLOR) {
if (element_info.uses_softshadow) {
@@ -1884,6 +1884,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES;
bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid();
+ uint32_t spec_constant_base_flags = 0;
+ {
+ if (p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) {
+ spec_constant_base_flags |= 1 << SPEC_CONSTANT_USE_DEPTH_FOG;
+ }
+ }
+
bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment);
if (depth_pre_pass) { //depth pre pass
@@ -1906,7 +1913,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RID rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_OPAQUE, nullptr, RID(), samplers);
bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi;
- RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags);
_render_list_with_draw_list(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear);
RD::get_singleton()->draw_command_end_label();
@@ -1972,7 +1979,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags;
RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer;
- RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags);
_render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 1.0, 0);
}
@@ -1992,7 +1999,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, samplers, true);
- RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags);
_render_list_with_draw_list(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
RD::get_singleton()->draw_command_end_label();
@@ -2125,7 +2132,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer;
- RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count);
+ RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, spec_constant_base_flags);
_render_list_with_draw_list(&render_list_params, alpha_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
}
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index be55341102..51fd0aaffb 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -72,6 +72,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
SPEC_CONSTANT_DIRECTIONAL_PENUMBRA_SHADOW_SAMPLES = 9,
SPEC_CONSTANT_DECAL_FILTER = 10,
SPEC_CONSTANT_PROJECTOR_FILTER = 11,
+ SPEC_CONSTANT_USE_DEPTH_FOG = 12,
};
enum {
@@ -210,8 +211,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {
RD::FramebufferFormatID framebuffer_format = 0;
uint32_t element_offset = 0;
bool use_directional_soft_shadow = false;
+ uint32_t spec_constant_base_flags = 0;
- RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0) {
+ RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_spec_constant_base_flags = 0) {
elements = p_elements;
element_info = p_element_info;
element_count = p_element_count;
@@ -227,6 +229,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
screen_mesh_lod_threshold = p_screen_mesh_lod_threshold;
element_offset = p_element_offset;
use_directional_soft_shadow = p_use_directional_soft_shadows;
+ spec_constant_base_flags = p_spec_constant_base_flags;
}
};
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 29c5deb6c2..fd81430983 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -899,6 +899,10 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (!is_environment(p_render_data->environment) || !environment_get_fog_enabled(p_render_data->environment)) {
spec_constant_base_flags |= 1 << SPEC_CONSTANT_DISABLE_FOG;
}
+
+ if (p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) {
+ spec_constant_base_flags |= 1 << SPEC_CONSTANT_USE_DEPTH_FOG;
+ }
}
{
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index ba5fefc83f..5c02204627 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -80,6 +80,7 @@ private:
SPEC_CONSTANT_DISABLE_DECALS = 13,
SPEC_CONSTANT_DISABLE_FOG = 14,
+ SPEC_CONSTANT_USE_DEPTH_FOG = 16,
};
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index cb95621219..6eae64c04e 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -656,6 +656,7 @@ layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4;
layout(constant_id = 10) const bool sc_decal_use_mipmaps = true;
layout(constant_id = 11) const bool sc_projector_use_mipmaps = true;
+layout(constant_id = 12) const bool sc_use_depth_fog = false;
// not used in clustered renderer but we share some code with the mobile renderer that requires this.
const float sc_luminance_multiplier = 1.0;
@@ -848,7 +849,15 @@ vec4 fog_process(vec3 vertex) {
}
}
- float fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data_block.data.fog_density));
+ float fog_amount = 0.0;
+
+ if (sc_use_depth_fog) {
+ float fog_z = smoothstep(scene_data_block.data.fog_depth_begin, scene_data_block.data.fog_depth_end, length(vertex));
+ float fog_quad_amount = pow(fog_z, scene_data_block.data.fog_depth_curve) * scene_data_block.data.fog_density;
+ fog_amount = fog_quad_amount;
+ } else {
+ fog_amount = 1 - exp(min(0.0, -length(vertex) * scene_data_block.data.fog_density));
+ }
if (abs(scene_data_block.data.fog_height_density) >= 0.0001) {
float y = (scene_data_block.data.inv_view_matrix * vec4(vertex, 1.0)).y;
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index e9c69058f2..259edc63a0 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -525,6 +525,7 @@ layout(constant_id = 12) const bool sc_disable_directional_lights = false;
layout(constant_id = 7) const bool sc_decal_use_mipmaps = true;
layout(constant_id = 13) const bool sc_disable_decals = false;
layout(constant_id = 14) const bool sc_disable_fog = false;
+layout(constant_id = 16) const bool sc_use_depth_fog = false;
#endif //!MODE_RENDER_DEPTH
@@ -690,7 +691,15 @@ vec4 fog_process(vec3 vertex) {
}
}
- float fog_amount = 1.0 - exp(min(0.0, -length(vertex) * scene_data_block.data.fog_density));
+ float fog_amount = 0.0;
+
+ if (sc_use_depth_fog) {
+ float fog_z = smoothstep(scene_data_block.data.fog_depth_begin, scene_data_block.data.fog_depth_end, length(vertex));
+ float fog_quad_amount = pow(fog_z, scene_data_block.data.fog_depth_curve) * scene_data_block.data.fog_density;
+ fog_amount = fog_quad_amount;
+ } else {
+ fog_amount = 1 - exp(min(0.0, -length(vertex) * scene_data_block.data.fog_density));
+ }
if (abs(scene_data_block.data.fog_height_density) >= 0.0001) {
float y = (scene_data_block.data.inv_view_matrix * vec4(vertex, 1.0)).y;
diff --git a/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl
index 70e670c3f7..67542d61fd 100644
--- a/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_data_inc.glsl
@@ -49,24 +49,29 @@ struct SceneData {
mediump float opaque_prepass_threshold;
bool fog_enabled;
+ uint fog_mode;
highp float fog_density;
highp float fog_height;
highp float fog_height_density;
+ highp float fog_depth_curve;
+ highp float pad;
+ highp float fog_depth_begin;
+
mediump vec3 fog_light_color;
- mediump float fog_sun_scatter;
+ highp float fog_depth_end;
+ mediump float fog_sun_scatter;
mediump float fog_aerial_perspective;
highp float time;
mediump float reflection_multiplier; // one normally, zero when rendering reflections
- bool material_uv2_mode;
vec2 taa_jitter;
+ bool material_uv2_mode;
float emissive_exposure_normalization;
- float IBL_exposure_normalization;
+ float IBL_exposure_normalization;
bool pancake_shadows;
uint camera_visible_layers;
float pass_alpha_multiplier;
- uint pad3;
};
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 21787b3fcf..1f362ffd21 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1963,7 +1963,7 @@ void MeshStorage::multimesh_set_buffer(RID p_multimesh, const Vector<float> &p_b
//if we have a mesh set, we need to re-generate the AABB from the new data
const float *data = p_buffer.ptr();
- if (multimesh->custom_aabb != AABB()) {
+ if (multimesh->custom_aabb == AABB()) {
_multimesh_re_create_aabb(multimesh, data, multimesh->instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
@@ -2087,7 +2087,7 @@ void MeshStorage::_update_dirty_multimeshes() {
if (multimesh->aabb_dirty) {
//aabb is dirty..
multimesh->aabb_dirty = false;
- if (multimesh->custom_aabb != AABB()) {
+ if (multimesh->custom_aabb == AABB()) {
_multimesh_re_create_aabb(multimesh, data, visible_instances);
multimesh->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_AABB);
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
index 506e78bae9..86f0f5acf2 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
@@ -168,11 +168,16 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p
}
ubo.fog_enabled = render_scene_render->environment_get_fog_enabled(p_env);
+ ubo.fog_mode = render_scene_render->environment_get_fog_mode(p_env);
ubo.fog_density = render_scene_render->environment_get_fog_density(p_env);
ubo.fog_height = render_scene_render->environment_get_fog_height(p_env);
ubo.fog_height_density = render_scene_render->environment_get_fog_height_density(p_env);
ubo.fog_aerial_perspective = render_scene_render->environment_get_fog_aerial_perspective(p_env);
+ ubo.fog_depth_curve = render_scene_render->environment_get_fog_depth_curve(p_env);
+ ubo.fog_depth_end = render_scene_render->environment_get_fog_depth_end(p_env) > 0.0 ? render_scene_render->environment_get_fog_depth_end(p_env) : ubo.z_far;
+ ubo.fog_depth_begin = MIN(render_scene_render->environment_get_fog_depth_begin(p_env), ubo.fog_depth_end - 0.001);
+
Color fog_color = render_scene_render->environment_get_fog_light_color(p_env).srgb_to_linear();
float fog_energy = render_scene_render->environment_get_fog_light_energy(p_env);
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
index f93f22816b..9453966a86 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
@@ -131,26 +131,31 @@ private:
// Fog
uint32_t fog_enabled;
+ uint32_t fog_mode;
float fog_density;
float fog_height;
+
float fog_height_density;
+ float fog_depth_curve;
+ float pad;
+ float fog_depth_begin;
float fog_light_color[3];
- float fog_sun_scatter;
+ float fog_depth_end;
+ float fog_sun_scatter;
float fog_aerial_perspective;
float time;
float reflection_multiplier;
- uint32_t material_uv2_mode;
float taa_jitter[2];
+ uint32_t material_uv2_mode;
float emissive_exposure_normalization; // Needed to normalize emissive when using physical units.
- float IBL_exposure_normalization; // Adjusts for baked exposure.
+ float IBL_exposure_normalization; // Adjusts for baked exposure.
uint32_t pancake_shadows;
uint32_t camera_visible_layers;
float pass_alpha_multiplier;
- uint32_t pad3;
};
struct UBODATA {
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index a09823b008..f48902a4ef 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1203,7 +1203,7 @@ public:
PASS1RC(float, environment_get_white, RID)
// Fog
- PASS10(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float)
+ PASS11(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float, RS::EnvironmentFogMode)
PASS1RC(bool, environment_get_fog_enabled, RID)
PASS1RC(Color, environment_get_fog_light_color, RID)
@@ -1214,10 +1214,17 @@ public:
PASS1RC(float, environment_get_fog_height, RID)
PASS1RC(float, environment_get_fog_height_density, RID)
PASS1RC(float, environment_get_fog_aerial_perspective, RID)
+ PASS1RC(RS::EnvironmentFogMode, environment_get_fog_mode, RID)
PASS2(environment_set_volumetric_fog_volume_size, int, int)
PASS1(environment_set_volumetric_fog_filter_active, bool)
+ // Depth Fog
+ PASS4(environment_set_fog_depth, RID, float, float, float)
+ PASS1RC(float, environment_get_fog_depth_curve, RID)
+ PASS1RC(float, environment_get_fog_depth_begin, RID)
+ PASS1RC(float, environment_get_fog_depth_end, RID)
+
// Volumentric Fog
PASS14(environment_set_volumetric_fog, RID, bool, float, const Color &, const Color &, float, float, float, float, float, bool, float, float, float)
diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp
index dce301d87b..95eb29a891 100644
--- a/servers/rendering/renderer_scene_render.cpp
+++ b/servers/rendering/renderer_scene_render.cpp
@@ -310,14 +310,18 @@ float RendererSceneRender::environment_get_white(RID p_env) const {
// Fog
-void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) {
- environment_storage.environment_set_fog(p_env, p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_aerial_perspective, p_sky_affect);
+void RendererSceneRender::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) {
+ environment_storage.environment_set_fog(p_env, p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_aerial_perspective, p_sky_affect, p_mode);
}
bool RendererSceneRender::environment_get_fog_enabled(RID p_env) const {
return environment_storage.environment_get_fog_enabled(p_env);
}
+RS::EnvironmentFogMode RendererSceneRender::environment_get_fog_mode(RID p_env) const {
+ return environment_storage.environment_get_fog_mode(p_env);
+}
+
Color RendererSceneRender::environment_get_fog_light_color(RID p_env) const {
return environment_storage.environment_get_fog_light_color(p_env);
}
@@ -350,6 +354,24 @@ float RendererSceneRender::environment_get_fog_aerial_perspective(RID p_env) con
return environment_storage.environment_get_fog_aerial_perspective(p_env);
}
+// Depth Fog
+
+void RendererSceneRender::environment_set_fog_depth(RID p_env, float p_curve, float p_begin, float p_end) {
+ environment_storage.environment_set_fog_depth(p_env, p_curve, p_begin, p_end);
+}
+
+float RendererSceneRender::environment_get_fog_depth_curve(RID p_env) const {
+ return environment_storage.environment_get_fog_depth_curve(p_env);
+}
+
+float RendererSceneRender::environment_get_fog_depth_begin(RID p_env) const {
+ return environment_storage.environment_get_fog_depth_begin(p_env);
+}
+
+float RendererSceneRender::environment_get_fog_depth_end(RID p_env) const {
+ return environment_storage.environment_get_fog_depth_end(p_env);
+}
+
// Volumetric Fog
void RendererSceneRender::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) {
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 9007383641..c6e2c23e91 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -116,8 +116,9 @@ public:
float environment_get_white(RID p_env) const;
// Fog
- void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect);
+ void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode);
bool environment_get_fog_enabled(RID p_env) const;
+ RS::EnvironmentFogMode environment_get_fog_mode(RID p_env) const;
Color environment_get_fog_light_color(RID p_env) const;
float environment_get_fog_light_energy(RID p_env) const;
float environment_get_fog_sun_scatter(RID p_env) const;
@@ -127,6 +128,12 @@ public:
float environment_get_fog_height_density(RID p_env) const;
float environment_get_fog_aerial_perspective(RID p_env) const;
+ // Depth Fog
+ void environment_set_fog_depth(RID p_env, float p_curve, float p_begin, float p_end);
+ float environment_get_fog_depth_curve(RID p_env) const;
+ float environment_get_fog_depth_begin(RID p_env) const;
+ float environment_get_fog_depth_end(RID p_env) const;
+
// Volumetric Fog
void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect);
bool environment_get_volumetric_fog_enabled(RID p_env) const;
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index d1c6c1cbf9..be14a50eec 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -154,9 +154,10 @@ public:
virtual float environment_get_white(RID p_env) const = 0;
// Fog
- virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) = 0;
+ virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode = RS::EnvironmentFogMode::ENV_FOG_MODE_EXPONENTIAL) = 0;
virtual bool environment_get_fog_enabled(RID p_env) const = 0;
+ virtual RS::EnvironmentFogMode environment_get_fog_mode(RID p_env) const = 0;
virtual Color environment_get_fog_light_color(RID p_env) const = 0;
virtual float environment_get_fog_light_energy(RID p_env) const = 0;
virtual float environment_get_fog_sun_scatter(RID p_env) const = 0;
@@ -166,6 +167,13 @@ public:
virtual float environment_get_fog_aerial_perspective(RID p_env) const = 0;
virtual float environment_get_fog_sky_affect(RID p_env) const = 0;
+ // Depth Fog
+ virtual void environment_set_fog_depth(RID p_env, float p_curve, float p_begin, float p_end) = 0;
+
+ virtual float environment_get_fog_depth_curve(RID p_env) const = 0;
+ virtual float environment_get_fog_depth_begin(RID p_env) const = 0;
+ virtual float environment_get_fog_depth_end(RID p_env) const = 0;
+
// Volumetric Fog
virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 577e9accc0..c218007a78 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -725,7 +725,9 @@ public:
FUNC7(environment_set_adjustment, RID, bool, float, float, float, bool, RID)
- FUNC10(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float)
+ FUNC11(environment_set_fog, RID, bool, const Color &, float, float, float, float, float, float, float, EnvironmentFogMode)
+
+ FUNC4(environment_set_fog_depth, RID, float, float, float)
FUNC14(environment_set_volumetric_fog, RID, bool, float, const Color &, const Color &, float, float, float, float, float, bool, float, float, float)
FUNC2(environment_set_volumetric_fog_volume_size, int, int)
diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp
index 0234f52ca1..ec26e36509 100644
--- a/servers/rendering/storage/environment_storage.cpp
+++ b/servers/rendering/storage/environment_storage.cpp
@@ -205,10 +205,11 @@ float RendererEnvironmentStorage::environment_get_white(RID p_env) const {
// Fog
-void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective, float p_sky_affect) {
+void RendererEnvironmentStorage::environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode) {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL(env);
env->fog_enabled = p_enable;
+ env->fog_mode = p_mode;
env->fog_light_color = p_light_color;
env->fog_light_energy = p_light_energy;
env->fog_sun_scatter = p_sun_scatter;
@@ -225,6 +226,12 @@ bool RendererEnvironmentStorage::environment_get_fog_enabled(RID p_env) const {
return env->fog_enabled;
}
+RS::EnvironmentFogMode RendererEnvironmentStorage::environment_get_fog_mode(RID p_env) const {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL_V(env, RS::ENV_FOG_MODE_EXPONENTIAL);
+ return env->fog_mode;
+}
+
Color RendererEnvironmentStorage::environment_get_fog_light_color(RID p_env) const {
Environment *env = environment_owner.get_or_null(p_env);
ERR_FAIL_NULL_V(env, Color(0.5, 0.6, 0.7));
@@ -273,6 +280,34 @@ float RendererEnvironmentStorage::environment_get_fog_sky_affect(RID p_env) cons
return env->fog_sky_affect;
}
+// Depth Fog
+
+void RendererEnvironmentStorage::environment_set_fog_depth(RID p_env, float p_curve, float p_begin, float p_end) {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL(env);
+ env->fog_depth_curve = p_curve;
+ env->fog_depth_begin = p_begin;
+ env->fog_depth_end = p_end;
+}
+
+float RendererEnvironmentStorage::environment_get_fog_depth_curve(RID p_env) const {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL_V(env, 0.0);
+ return env->fog_depth_curve;
+}
+
+float RendererEnvironmentStorage::environment_get_fog_depth_begin(RID p_env) const {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL_V(env, 0.0);
+ return env->fog_depth_begin;
+}
+
+float RendererEnvironmentStorage::environment_get_fog_depth_end(RID p_env) const {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL_V(env, 0.0);
+ return env->fog_depth_end;
+}
+
// Volumetric Fog
void RendererEnvironmentStorage::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) {
diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h
index d677dfc57b..c077e093da 100644
--- a/servers/rendering/storage/environment_storage.h
+++ b/servers/rendering/storage/environment_storage.h
@@ -62,6 +62,7 @@ private:
// Fog
bool fog_enabled = false;
+ RS::EnvironmentFogMode fog_mode = RS::EnvironmentFogMode::ENV_FOG_MODE_EXPONENTIAL;
Color fog_light_color = Color(0.518, 0.553, 0.608);
float fog_light_energy = 1.0;
float fog_sun_scatter = 0.0;
@@ -71,6 +72,11 @@ private:
float fog_height_density = 0.0; //can be negative to invert effect
float fog_aerial_perspective = 0.0;
+ // Depth Fog
+ float fog_depth_curve = 1.0;
+ float fog_depth_begin = 10.0;
+ float fog_depth_end = 100.0;
+
// Volumetric Fog
bool volumetric_fog_enabled = false;
float volumetric_fog_density = 0.01;
@@ -192,8 +198,9 @@ public:
float environment_get_white(RID p_env) const;
// Fog
- void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect);
+ void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, RS::EnvironmentFogMode p_mode);
bool environment_get_fog_enabled(RID p_env) const;
+ RS::EnvironmentFogMode environment_get_fog_mode(RID p_env) const;
Color environment_get_fog_light_color(RID p_env) const;
float environment_get_fog_light_energy(RID p_env) const;
float environment_get_fog_sun_scatter(RID p_env) const;
@@ -203,6 +210,12 @@ public:
float environment_get_fog_height_density(RID p_env) const;
float environment_get_fog_aerial_perspective(RID p_env) const;
+ // Depth Fog
+ void environment_set_fog_depth(RID p_env, float p_curve, float p_begin, float p_end);
+ float environment_get_fog_depth_curve(RID p_env) const;
+ float environment_get_fog_depth_begin(RID p_env) const;
+ float environment_get_fog_depth_end(RID p_env) const;
+
// Volumetric Fog
void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect);
bool environment_get_volumetric_fog_enabled(RID p_env) const;
diff --git a/servers/rendering_server.compat.inc b/servers/rendering_server.compat.inc
new file mode 100644
index 0000000000..0cef3c906c
--- /dev/null
+++ b/servers/rendering_server.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* rendering_server.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+void RenderingServer::_environment_set_fog_bind_compat_84792(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) {
+ environment_set_fog(p_env, p_enable, p_light_color, p_light_energy, p_sun_scatter, p_density, p_height, p_height_density, p_aerial_perspective, p_sky_affect, RS::EnvironmentFogMode::ENV_FOG_MODE_EXPONENTIAL);
+}
+
+void RenderingServer::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect"), &RenderingServer::_environment_set_fog_bind_compat_84792);
+}
+
+#endif
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index d03f8113f8..655b748d3f 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "rendering_server.h"
+#include "rendering_server.compat.inc"
#include "core/config/project_settings.h"
#include "core/object/worker_thread_pool.h"
@@ -2943,7 +2944,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "use_1d_color_correction", "color_correction"), &RenderingServer::environment_set_adjustment);
ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance"), &RenderingServer::environment_set_ssr);
ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "power", "detail", "horizon", "sharpness", "light_affect", "ao_channel_affect"), &RenderingServer::environment_set_ssao);
- ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect"), &RenderingServer::environment_set_fog);
+ ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "light_color", "light_energy", "sun_scatter", "density", "height", "height_density", "aerial_perspective", "sky_affect", "fog_mode"), &RenderingServer::environment_set_fog, DEFVAL(RS::ENV_FOG_MODE_EXPONENTIAL));
ClassDB::bind_method(D_METHOD("environment_set_sdfgi", "env", "enable", "cascades", "min_cell_size", "y_scale", "use_occlusion", "bounce_feedback", "read_sky", "energy", "normal_bias", "probe_bias"), &RenderingServer::environment_set_sdfgi);
ClassDB::bind_method(D_METHOD("environment_set_volumetric_fog", "env", "enable", "density", "albedo", "emission", "emission_energy", "anisotropy", "length", "p_detail_spread", "gi_inject", "temporal_reprojection", "temporal_reprojection_amount", "ambient_inject", "sky_affect"), &RenderingServer::environment_set_volumetric_fog);
@@ -2986,6 +2987,9 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(ENV_GLOW_BLEND_MODE_REPLACE);
BIND_ENUM_CONSTANT(ENV_GLOW_BLEND_MODE_MIX);
+ BIND_ENUM_CONSTANT(ENV_FOG_MODE_EXPONENTIAL);
+ BIND_ENUM_CONSTANT(ENV_FOG_MODE_DEPTH);
+
BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_LINEAR);
BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_REINHARD);
BIND_ENUM_CONSTANT(ENV_TONE_MAPPER_FILMIC);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 19b6c35339..6b00213440 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -71,6 +71,11 @@ protected:
static RenderingServer *(*create_func)();
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _environment_set_fog_bind_compat_84792(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect);
+ static void _bind_compatibility_methods();
+#endif
+
public:
static RenderingServer *get_singleton();
static RenderingServer *create();
@@ -1178,7 +1183,13 @@ public:
virtual void environment_set_sdfgi_frames_to_update_light(EnvironmentSDFGIFramesToUpdateLight p_update) = 0;
- virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect) = 0;
+ enum EnvironmentFogMode {
+ ENV_FOG_MODE_EXPONENTIAL,
+ ENV_FOG_MODE_DEPTH,
+ };
+
+ virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_aerial_perspective, float p_sky_affect, EnvironmentFogMode p_mode = EnvironmentFogMode::ENV_FOG_MODE_EXPONENTIAL) = 0;
+ virtual void environment_set_fog_depth(RID p_env, float p_curve, float p_begin, float p_end) = 0;
virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_albedo, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject, float p_sky_affect) = 0;
virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0;
@@ -1730,6 +1741,7 @@ VARIANT_ENUM_CAST(RenderingServer::EnvironmentBG);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentAmbientSource);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentReflectionSource);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentGlowBlendMode);
+VARIANT_ENUM_CAST(RenderingServer::EnvironmentFogMode);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentToneMapper);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSRRoughnessQuality);
VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSAOQuality);
diff --git a/servers/xr/xr_face_tracker.cpp b/servers/xr/xr_face_tracker.cpp
new file mode 100644
index 0000000000..a38ccfd527
--- /dev/null
+++ b/servers/xr/xr_face_tracker.cpp
@@ -0,0 +1,222 @@
+/**************************************************************************/
+/* xr_face_tracker.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 "xr_face_tracker.h"
+
+void XRFaceTracker::_bind_methods() {
+ // Base Shapes
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_OUT_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_IN_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_UP_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_DOWN_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_OUT_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_IN_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_UP_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_LOOK_DOWN_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_CLOSED_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_CLOSED_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_SQUINT_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_SQUINT_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_WIDE_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_WIDE_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_DILATION_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_DILATION_LEFT);
+ BIND_ENUM_CONSTANT(FT_EYE_CONSTRICT_RIGHT);
+ BIND_ENUM_CONSTANT(FT_EYE_CONSTRICT_LEFT);
+ BIND_ENUM_CONSTANT(FT_BROW_PINCH_RIGHT);
+ BIND_ENUM_CONSTANT(FT_BROW_PINCH_LEFT);
+ BIND_ENUM_CONSTANT(FT_BROW_LOWERER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_BROW_LOWERER_LEFT);
+ BIND_ENUM_CONSTANT(FT_BROW_INNER_UP_RIGHT);
+ BIND_ENUM_CONSTANT(FT_BROW_INNER_UP_LEFT);
+ BIND_ENUM_CONSTANT(FT_BROW_OUTER_UP_RIGHT);
+ BIND_ENUM_CONSTANT(FT_BROW_OUTER_UP_LEFT);
+ BIND_ENUM_CONSTANT(FT_NOSE_SNEER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_NOSE_SNEER_LEFT);
+ BIND_ENUM_CONSTANT(FT_NASAL_DILATION_RIGHT);
+ BIND_ENUM_CONSTANT(FT_NASAL_DILATION_LEFT);
+ BIND_ENUM_CONSTANT(FT_NASAL_CONSTRICT_RIGHT);
+ BIND_ENUM_CONSTANT(FT_NASAL_CONSTRICT_LEFT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_SQUINT_RIGHT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_SQUINT_LEFT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_PUFF_RIGHT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_PUFF_LEFT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_SUCK_RIGHT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_SUCK_LEFT);
+ BIND_ENUM_CONSTANT(FT_JAW_OPEN);
+ BIND_ENUM_CONSTANT(FT_MOUTH_CLOSED);
+ BIND_ENUM_CONSTANT(FT_JAW_RIGHT);
+ BIND_ENUM_CONSTANT(FT_JAW_LEFT);
+ BIND_ENUM_CONSTANT(FT_JAW_FORWARD);
+ BIND_ENUM_CONSTANT(FT_JAW_BACKWARD);
+ BIND_ENUM_CONSTANT(FT_JAW_CLENCH);
+ BIND_ENUM_CONSTANT(FT_JAW_MANDIBLE_RAISE);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_UPPER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_UPPER_LEFT);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_LOWER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_LOWER_LEFT);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_CORNER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_CORNER_LEFT);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL_UPPER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL_UPPER_LEFT);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL_LOWER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL_LOWER_LEFT);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER_UPPER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER_UPPER_LEFT);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER_LOWER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER_LOWER_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_UP_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_UP_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_LOWER_DOWN_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_LOWER_DOWN_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_DEEPEN_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_DEEPEN_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_LOWER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_LOWER_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_CORNER_PULL_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_CORNER_PULL_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_CORNER_SLANT_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_CORNER_SLANT_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_FROWN_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_FROWN_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_STRETCH_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_STRETCH_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_DIMPLE_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_DIMPLE_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_RAISER_UPPER);
+ BIND_ENUM_CONSTANT(FT_MOUTH_RAISER_LOWER);
+ BIND_ENUM_CONSTANT(FT_MOUTH_PRESS_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_PRESS_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_TIGHTENER_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_TIGHTENER_LEFT);
+ BIND_ENUM_CONSTANT(FT_TONGUE_OUT);
+ BIND_ENUM_CONSTANT(FT_TONGUE_UP);
+ BIND_ENUM_CONSTANT(FT_TONGUE_DOWN);
+ BIND_ENUM_CONSTANT(FT_TONGUE_RIGHT);
+ BIND_ENUM_CONSTANT(FT_TONGUE_LEFT);
+ BIND_ENUM_CONSTANT(FT_TONGUE_ROLL);
+ BIND_ENUM_CONSTANT(FT_TONGUE_BLEND_DOWN);
+ BIND_ENUM_CONSTANT(FT_TONGUE_CURL_UP);
+ BIND_ENUM_CONSTANT(FT_TONGUE_SQUISH);
+ BIND_ENUM_CONSTANT(FT_TONGUE_FLAT);
+ BIND_ENUM_CONSTANT(FT_TONGUE_TWIST_RIGHT);
+ BIND_ENUM_CONSTANT(FT_TONGUE_TWIST_LEFT);
+ BIND_ENUM_CONSTANT(FT_SOFT_PALATE_CLOSE);
+ BIND_ENUM_CONSTANT(FT_THROAT_SWALLOW);
+ BIND_ENUM_CONSTANT(FT_NECK_FLEX_RIGHT);
+ BIND_ENUM_CONSTANT(FT_NECK_FLEX_LEFT);
+ // Blended Shapes
+ BIND_ENUM_CONSTANT(FT_EYE_CLOSED);
+ BIND_ENUM_CONSTANT(FT_EYE_WIDE);
+ BIND_ENUM_CONSTANT(FT_EYE_SQUINT);
+ BIND_ENUM_CONSTANT(FT_EYE_DILATION);
+ BIND_ENUM_CONSTANT(FT_EYE_CONSTRICT);
+ BIND_ENUM_CONSTANT(FT_BROW_DOWN_RIGHT);
+ BIND_ENUM_CONSTANT(FT_BROW_DOWN_LEFT);
+ BIND_ENUM_CONSTANT(FT_BROW_DOWN);
+ BIND_ENUM_CONSTANT(FT_BROW_UP_RIGHT);
+ BIND_ENUM_CONSTANT(FT_BROW_UP_LEFT);
+ BIND_ENUM_CONSTANT(FT_BROW_UP);
+ BIND_ENUM_CONSTANT(FT_NOSE_SNEER);
+ BIND_ENUM_CONSTANT(FT_NASAL_DILATION);
+ BIND_ENUM_CONSTANT(FT_NASAL_CONSTRICT);
+ BIND_ENUM_CONSTANT(FT_CHEEK_PUFF);
+ BIND_ENUM_CONSTANT(FT_CHEEK_SUCK);
+ BIND_ENUM_CONSTANT(FT_CHEEK_SQUINT);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_UPPER);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK_LOWER);
+ BIND_ENUM_CONSTANT(FT_LIP_SUCK);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL_UPPER);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL_LOWER);
+ BIND_ENUM_CONSTANT(FT_LIP_FUNNEL);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER_UPPER);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER_LOWER);
+ BIND_ENUM_CONSTANT(FT_LIP_PUCKER);
+ BIND_ENUM_CONSTANT(FT_MOUTH_UPPER_UP);
+ BIND_ENUM_CONSTANT(FT_MOUTH_LOWER_DOWN);
+ BIND_ENUM_CONSTANT(FT_MOUTH_OPEN);
+ BIND_ENUM_CONSTANT(FT_MOUTH_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_SMILE_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_SMILE_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_SMILE);
+ BIND_ENUM_CONSTANT(FT_MOUTH_SAD_RIGHT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_SAD_LEFT);
+ BIND_ENUM_CONSTANT(FT_MOUTH_SAD);
+ BIND_ENUM_CONSTANT(FT_MOUTH_STRETCH);
+ BIND_ENUM_CONSTANT(FT_MOUTH_DIMPLE);
+ BIND_ENUM_CONSTANT(FT_MOUTH_TIGHTENER);
+ BIND_ENUM_CONSTANT(FT_MOUTH_PRESS);
+ BIND_ENUM_CONSTANT(FT_MAX);
+
+ ClassDB::bind_method(D_METHOD("get_blend_shape", "blend_shape"), &XRFaceTracker::get_blend_shape);
+ ClassDB::bind_method(D_METHOD("set_blend_shape", "blend_shape", "weight"), &XRFaceTracker::set_blend_shape);
+
+ ClassDB::bind_method(D_METHOD("get_blend_shapes"), &XRFaceTracker::get_blend_shapes);
+ ClassDB::bind_method(D_METHOD("set_blend_shapes", "weights"), &XRFaceTracker::set_blend_shapes);
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "blend_shapes"), "set_blend_shapes", "get_blend_shapes");
+ ADD_PROPERTY_DEFAULT("blend_shapes", PackedFloat32Array()); // To prevent ludicrously large default values.
+}
+
+float XRFaceTracker::get_blend_shape(BlendShapeEntry p_blend_shape) const {
+ // Fail if the blend shape index is out of range.
+ ERR_FAIL_INDEX_V(p_blend_shape, FT_MAX, 0.0f);
+
+ // Return the blend shape value.
+ return blend_shape_values[p_blend_shape];
+}
+
+void XRFaceTracker::set_blend_shape(BlendShapeEntry p_blend_shape, float p_value) {
+ // Fail if the blend shape index is out of range.
+ ERR_FAIL_INDEX(p_blend_shape, FT_MAX);
+
+ // Save the new blend shape value.
+ blend_shape_values[p_blend_shape] = p_value;
+}
+
+PackedFloat32Array XRFaceTracker::get_blend_shapes() const {
+ // Create a packed float32 array and copy the blend shape values into it.
+ PackedFloat32Array data;
+ data.resize(FT_MAX);
+ memcpy(data.ptrw(), blend_shape_values, sizeof(blend_shape_values));
+
+ // Return the blend shape array.
+ return data;
+}
+
+void XRFaceTracker::set_blend_shapes(const PackedFloat32Array &p_blend_shapes) {
+ // Fail if the blend shape array is not the correct size.
+ ERR_FAIL_COND(p_blend_shapes.size() != FT_MAX);
+
+ // Copy the blend shape values into the blend shape array.
+ memcpy(blend_shape_values, p_blend_shapes.ptr(), sizeof(blend_shape_values));
+}
diff --git a/servers/xr/xr_face_tracker.h b/servers/xr/xr_face_tracker.h
new file mode 100644
index 0000000000..b9f553cba6
--- /dev/null
+++ b/servers/xr/xr_face_tracker.h
@@ -0,0 +1,213 @@
+/**************************************************************************/
+/* xr_face_tracker.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 XR_FACE_TRACKER_H
+#define XR_FACE_TRACKER_H
+
+#include "core/object/ref_counted.h"
+
+/**
+ The XRFaceTracker class provides face blend shape weights.
+
+ The supported blend shapes are based on the Unified Expressions
+ standard, and as such have a well defined mapping to ARKit, SRanipal,
+ and Meta Movement standards.
+ */
+
+class XRFaceTracker : public RefCounted {
+ GDCLASS(XRFaceTracker, RefCounted);
+ _THREAD_SAFE_CLASS_
+
+public:
+ enum BlendShapeEntry {
+ // Base Shapes
+ FT_EYE_LOOK_OUT_RIGHT, // Right eye looks outwards.
+ FT_EYE_LOOK_IN_RIGHT, // Right eye looks inwards.
+ FT_EYE_LOOK_UP_RIGHT, // Right eye looks upwards.
+ FT_EYE_LOOK_DOWN_RIGHT, // Right eye looks downwards.
+ FT_EYE_LOOK_OUT_LEFT, // Left eye looks outwards.
+ FT_EYE_LOOK_IN_LEFT, // Left eye looks inwards.
+ FT_EYE_LOOK_UP_LEFT, // Left eye looks upwards.
+ FT_EYE_LOOK_DOWN_LEFT, // Left eye looks downwards.
+ FT_EYE_CLOSED_RIGHT, // Closes the right eyelid.
+ FT_EYE_CLOSED_LEFT, // Closes the left eyelid.
+ FT_EYE_SQUINT_RIGHT, // Squeezes the right eye socket muscles.
+ FT_EYE_SQUINT_LEFT, // Squeezes the left eye socket muscles.
+ FT_EYE_WIDE_RIGHT, // Right eyelid widens beyond relaxed.
+ FT_EYE_WIDE_LEFT, // Left eyelid widens beyond relaxed.
+ FT_EYE_DILATION_RIGHT, // Dilates the right eye pupil.
+ FT_EYE_DILATION_LEFT, // Dilates the left eye pupil.
+ FT_EYE_CONSTRICT_RIGHT, // Constricts the right eye pupil.
+ FT_EYE_CONSTRICT_LEFT, // Constricts the left eye pupil.
+ FT_BROW_PINCH_RIGHT, // Right eyebrow pinches in.
+ FT_BROW_PINCH_LEFT, // Left eyebrow pinches in.
+ FT_BROW_LOWERER_RIGHT, // Outer right eyebrow pulls down.
+ FT_BROW_LOWERER_LEFT, // Outer left eyebrow pulls down.
+ FT_BROW_INNER_UP_RIGHT, // Inner right eyebrow pulls up.
+ FT_BROW_INNER_UP_LEFT, // Inner left eyebrow pulls up.
+ FT_BROW_OUTER_UP_RIGHT, // Outer right eyebrow pulls up.
+ FT_BROW_OUTER_UP_LEFT, // Outer left eyebrow pulls up.
+ FT_NOSE_SNEER_RIGHT, // Right side face sneers.
+ FT_NOSE_SNEER_LEFT, // Left side face sneers.
+ FT_NASAL_DILATION_RIGHT, // Right side nose canal dilates.
+ FT_NASAL_DILATION_LEFT, // Left side nose canal dilates.
+ FT_NASAL_CONSTRICT_RIGHT, // Right side nose canal constricts.
+ FT_NASAL_CONSTRICT_LEFT, // Left side nose canal constricts.
+ FT_CHEEK_SQUINT_RIGHT, // Raises the right side cheek.
+ FT_CHEEK_SQUINT_LEFT, // Raises the left side cheek.
+ FT_CHEEK_PUFF_RIGHT, // Puffs the right side cheek.
+ FT_CHEEK_PUFF_LEFT, // Puffs the left side cheek.
+ FT_CHEEK_SUCK_RIGHT, // Sucks in the right side cheek.
+ FT_CHEEK_SUCK_LEFT, // Sucks in the left side cheek.
+ FT_JAW_OPEN, // Opens jawbone.
+ FT_MOUTH_CLOSED, // Closes the mouth.
+ FT_JAW_RIGHT, // Pushes jawbone right.
+ FT_JAW_LEFT, // Pushes jawbone left.
+ FT_JAW_FORWARD, // Pushes jawbone forward.
+ FT_JAW_BACKWARD, // Pushes jawbone backward.
+ FT_JAW_CLENCH, // Flexes jaw muscles.
+ FT_JAW_MANDIBLE_RAISE, // Raises the jawbone.
+ FT_LIP_SUCK_UPPER_RIGHT, // Upper right lip part tucks in the mouth.
+ FT_LIP_SUCK_UPPER_LEFT, // Upper left lip part tucks in the mouth.
+ FT_LIP_SUCK_LOWER_RIGHT, // Lower right lip part tucks in the mouth.
+ FT_LIP_SUCK_LOWER_LEFT, // Lower left lip part tucks in the mouth.
+ FT_LIP_SUCK_CORNER_RIGHT, // Right lip corner folds into the mouth.
+ FT_LIP_SUCK_CORNER_LEFT, // Left lip corner folds into the mouth.
+ FT_LIP_FUNNEL_UPPER_RIGHT, // Upper right lip part pushes into a funnel.
+ FT_LIP_FUNNEL_UPPER_LEFT, // Upper left lip part pushes into a funnel.
+ FT_LIP_FUNNEL_LOWER_RIGHT, // Lower right lip part pushes into a funnel.
+ FT_LIP_FUNNEL_LOWER_LEFT, // Lower left lip part pushes into a funnel.
+ FT_LIP_PUCKER_UPPER_RIGHT, // Upper right lip part pushes outwards.
+ FT_LIP_PUCKER_UPPER_LEFT, // Upper left lip part pushes outwards.
+ FT_LIP_PUCKER_LOWER_RIGHT, // Lower right lip part pushes outwards.
+ FT_LIP_PUCKER_LOWER_LEFT, // Lower left lip part pushes outwards.
+ FT_MOUTH_UPPER_UP_RIGHT, // Upper right part of the lip pulls up.
+ FT_MOUTH_UPPER_UP_LEFT, // Upper left part of the lip pulls up.
+ FT_MOUTH_LOWER_DOWN_RIGHT, // Lower right part of the lip pulls up.
+ FT_MOUTH_LOWER_DOWN_LEFT, // Lower left part of the lip pulls up.
+ FT_MOUTH_UPPER_DEEPEN_RIGHT, // Upper right lip part pushes in the cheek.
+ FT_MOUTH_UPPER_DEEPEN_LEFT, // Upper left lip part pushes in the cheek.
+ FT_MOUTH_UPPER_RIGHT, // Moves upper lip right.
+ FT_MOUTH_UPPER_LEFT, // Moves upper lip left.
+ FT_MOUTH_LOWER_RIGHT, // Moves lower lip right.
+ FT_MOUTH_LOWER_LEFT, // Moves lower lip left.
+ FT_MOUTH_CORNER_PULL_RIGHT, // Right lip corner pulls diagonally up and out.
+ FT_MOUTH_CORNER_PULL_LEFT, // Left lip corner pulls diagonally up and out.
+ FT_MOUTH_CORNER_SLANT_RIGHT, // Right corner lip slants up.
+ FT_MOUTH_CORNER_SLANT_LEFT, // Left corner lip slants up.
+ FT_MOUTH_FROWN_RIGHT, // Right corner lip pulls down.
+ FT_MOUTH_FROWN_LEFT, // Left corner lip pulls down.
+ FT_MOUTH_STRETCH_RIGHT, // Mouth corner lip pulls out and down.
+ FT_MOUTH_STRETCH_LEFT, // Mouth corner lip pulls out and down.
+ FT_MOUTH_DIMPLE_RIGHT, // Right lip corner is pushed backwards.
+ FT_MOUTH_DIMPLE_LEFT, // Left lip corner is pushed backwards.
+ FT_MOUTH_RAISER_UPPER, // Raises and slightly pushes out the upper mouth.
+ FT_MOUTH_RAISER_LOWER, // Raises and slightly pushes out the lower mouth.
+ FT_MOUTH_PRESS_RIGHT, // Right side lips press and flatten together vertically.
+ FT_MOUTH_PRESS_LEFT, // Left side lips press and flatten together vertically.
+ FT_MOUTH_TIGHTENER_RIGHT, // Right side lips squeeze together horizontally.
+ FT_MOUTH_TIGHTENER_LEFT, // Left side lips squeeze together horizontally.
+ FT_TONGUE_OUT, // Tongue visibly sticks out of the mouth.
+ FT_TONGUE_UP, // Tongue points upwards.
+ FT_TONGUE_DOWN, // Tongue points downwards.
+ FT_TONGUE_RIGHT, // Tongue points right.
+ FT_TONGUE_LEFT, // Tongue points left.
+ FT_TONGUE_ROLL, // Sides of the tongue funnel, creating a roll.
+ FT_TONGUE_BLEND_DOWN, // Tongue arches up then down inside the mouth.
+ FT_TONGUE_CURL_UP, // Tongue arches down then up inside the mouth.
+ FT_TONGUE_SQUISH, // Tongue squishes together and thickens.
+ FT_TONGUE_FLAT, // Tongue flattens and thins out.
+ FT_TONGUE_TWIST_RIGHT, // Tongue tip rotates clockwise, with the rest following gradually.
+ FT_TONGUE_TWIST_LEFT, // Tongue tip rotates counter-clockwise, with the rest following gradually.
+ FT_SOFT_PALATE_CLOSE, // Inner mouth throat closes.
+ FT_THROAT_SWALLOW, // The Adam's apple visibly swallows.
+ FT_NECK_FLEX_RIGHT, // Right side neck visibly flexes.
+ FT_NECK_FLEX_LEFT, // Left side neck visibly flexes.
+ // Blended Shapes
+ FT_EYE_CLOSED, // Closes both eye lids.
+ FT_EYE_WIDE, // Widens both eye lids.
+ FT_EYE_SQUINT, // Squints both eye lids.
+ FT_EYE_DILATION, // Dilates both pupils.
+ FT_EYE_CONSTRICT, // Constricts both pupils.
+ FT_BROW_DOWN_RIGHT, // Pulls the right eyebrow down and in.
+ FT_BROW_DOWN_LEFT, // Pulls the left eyebrow down and in.
+ FT_BROW_DOWN, // Pulls both eyebrows down and in.
+ FT_BROW_UP_RIGHT, // Right brow appears worried.
+ FT_BROW_UP_LEFT, // Left brow appears worried.
+ FT_BROW_UP, // Both brows appear worried.
+ FT_NOSE_SNEER, // Entire face sneers.
+ FT_NASAL_DILATION, // Both nose canals dilate.
+ FT_NASAL_CONSTRICT, // Both nose canals constrict.
+ FT_CHEEK_PUFF, // Puffs both cheeks.
+ FT_CHEEK_SUCK, // Sucks in both cheeks.
+ FT_CHEEK_SQUINT, // Raises both cheeks.
+ FT_LIP_SUCK_UPPER, // Tucks in the upper lips.
+ FT_LIP_SUCK_LOWER, // Tucks in the lower lips.
+ FT_LIP_SUCK, // Tucks in both lips.
+ FT_LIP_FUNNEL_UPPER, // Funnels in the upper lips.
+ FT_LIP_FUNNEL_LOWER, // Funnels in the lower lips.
+ FT_LIP_FUNNEL, // Funnels in both lips.
+ FT_LIP_PUCKER_UPPER, // Upper lip part pushes outwards.
+ FT_LIP_PUCKER_LOWER, // Lower lip part pushes outwards.
+ FT_LIP_PUCKER, // Lips push outwards.
+ FT_MOUTH_UPPER_UP, // Raises the upper lips.
+ FT_MOUTH_LOWER_DOWN, // Lowers the lower lips.
+ FT_MOUTH_OPEN, // Mouth opens, revealing teeth.
+ FT_MOUTH_RIGHT, // Moves mouth right.
+ FT_MOUTH_LEFT, // Moves mouth left.
+ FT_MOUTH_SMILE_RIGHT, // Right side of the mouth smiles.
+ FT_MOUTH_SMILE_LEFT, // Left side of the mouth smiles.
+ FT_MOUTH_SMILE, // Mouth expresses a smile.
+ FT_MOUTH_SAD_RIGHT, // Right side of the mouth expresses sadness.
+ FT_MOUTH_SAD_LEFT, // Left side of the mouth expresses sadness.
+ FT_MOUTH_SAD, // Mouth expresses sadness.
+ FT_MOUTH_STRETCH, // Mouth stretches.
+ FT_MOUTH_DIMPLE, // Lip corners dimple.
+ FT_MOUTH_TIGHTENER, // Mouth tightens.
+ FT_MOUTH_PRESS, // Mouth presses together.
+ FT_MAX // Maximum blend shape.
+ };
+
+ float get_blend_shape(BlendShapeEntry p_blend_shape) const;
+ void set_blend_shape(BlendShapeEntry p_blend_shape, float p_value);
+
+ PackedFloat32Array get_blend_shapes() const;
+ void set_blend_shapes(const PackedFloat32Array &p_blend_shapes);
+
+protected:
+ static void _bind_methods();
+
+private:
+ float blend_shape_values[FT_MAX] = {};
+};
+
+VARIANT_ENUM_CAST(XRFaceTracker::BlendShapeEntry);
+
+#endif // XR_FACE_TRACKER_H
diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp
index e7f644d53f..b3bb0a3702 100644
--- a/servers/xr_server.cpp
+++ b/servers/xr_server.cpp
@@ -30,6 +30,7 @@
#include "xr_server.h"
#include "core/config/project_settings.h"
+#include "xr/xr_face_tracker.h"
#include "xr/xr_interface.h"
#include "xr/xr_positional_tracker.h"
@@ -74,6 +75,11 @@ void XRServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_trackers", "tracker_types"), &XRServer::get_trackers);
ClassDB::bind_method(D_METHOD("get_tracker", "tracker_name"), &XRServer::get_tracker);
+ ClassDB::bind_method(D_METHOD("add_face_tracker", "tracker_name", "face_tracker"), &XRServer::add_face_tracker);
+ ClassDB::bind_method(D_METHOD("remove_face_tracker", "tracker_name"), &XRServer::remove_face_tracker);
+ ClassDB::bind_method(D_METHOD("get_face_trackers"), &XRServer::get_face_trackers);
+ ClassDB::bind_method(D_METHOD("get_face_tracker", "tracker_name"), &XRServer::get_face_tracker);
+
ClassDB::bind_method(D_METHOD("get_primary_interface"), &XRServer::get_primary_interface);
ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &XRServer::set_primary_interface);
@@ -97,6 +103,10 @@ void XRServer::_bind_methods() {
ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
ADD_SIGNAL(MethodInfo("tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type")));
+
+ ADD_SIGNAL(MethodInfo("face_tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::OBJECT, "face_tracker", PROPERTY_HINT_RESOURCE_TYPE, "XRFaceTracker")));
+ ADD_SIGNAL(MethodInfo("face_tracker_updated", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::OBJECT, "face_tracker", PROPERTY_HINT_RESOURCE_TYPE, "XRFaceTracker")));
+ ADD_SIGNAL(MethodInfo("face_tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name")));
};
double XRServer::get_world_scale() const {
@@ -352,6 +362,44 @@ PackedStringArray XRServer::get_suggested_pose_names(const StringName &p_tracker
return arr;
}
+void XRServer::add_face_tracker(const StringName &p_tracker_name, Ref<XRFaceTracker> p_face_tracker) {
+ ERR_FAIL_COND(p_face_tracker.is_null());
+
+ if (!face_trackers.has(p_tracker_name)) {
+ // We don't have a tracker with this name, we're going to add it.
+ face_trackers[p_tracker_name] = p_face_tracker;
+ emit_signal(SNAME("face_tracker_added"), p_tracker_name, p_face_tracker);
+ } else if (face_trackers[p_tracker_name] != p_face_tracker) {
+ // We already have a tracker with this name, we're going to replace it.
+ face_trackers[p_tracker_name] = p_face_tracker;
+ emit_signal(SNAME("face_tracker_updated"), p_tracker_name, p_face_tracker);
+ }
+}
+
+void XRServer::remove_face_tracker(const StringName &p_tracker_name) {
+ // Skip if no face tracker is found.
+ if (!face_trackers.has(p_tracker_name)) {
+ return;
+ }
+
+ // Send the removed signal, then remove the face tracker.
+ emit_signal(SNAME("face_tracker_removed"), p_tracker_name);
+ face_trackers.erase(p_tracker_name);
+}
+
+Dictionary XRServer::get_face_trackers() const {
+ return face_trackers;
+}
+
+Ref<XRFaceTracker> XRServer::get_face_tracker(const StringName &p_tracker_name) const {
+ // Skip if no tracker is found.
+ if (!face_trackers.has(p_tracker_name)) {
+ return Ref<XRFaceTracker>();
+ }
+
+ return face_trackers[p_tracker_name];
+}
+
void XRServer::_process() {
// called from our main game loop before we handle physics and game logic
// note that we can have multiple interfaces active if we have interfaces that purely handle tracking
diff --git a/servers/xr_server.h b/servers/xr_server.h
index fe59fc22cb..0a4e020a1f 100644
--- a/servers/xr_server.h
+++ b/servers/xr_server.h
@@ -39,6 +39,7 @@
class XRInterface;
class XRPositionalTracker;
+class XRFaceTracker;
/**
The XR server is a singleton object that gives access to the various
@@ -86,6 +87,8 @@ private:
Vector<Ref<XRInterface>> interfaces;
Dictionary trackers;
+ Dictionary face_trackers;
+
Ref<XRInterface> primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */
double world_scale; /* scale by which we multiply our tracker positions */
@@ -183,6 +186,14 @@ public:
PackedStringArray get_suggested_pose_names(const StringName &p_tracker_name) const;
// Q: Should we add get_suggested_input_names and get_suggested_haptic_names even though we don't use them for the IDE?
+ /*
+ Face trackers are objects that expose the tracked blend shapes of a face.
+ */
+ void add_face_tracker(const StringName &p_tracker_name, Ref<XRFaceTracker> p_face_tracker);
+ void remove_face_tracker(const StringName &p_tracker_name);
+ Dictionary get_face_trackers() const;
+ Ref<XRFaceTracker> get_face_tracker(const StringName &p_tracker_name) const;
+
// Process is called before we handle our physics process and game process. This is where our interfaces will update controller data and such.
void _process();
diff --git a/tests/scene/test_image_texture.h b/tests/scene/test_image_texture.h
new file mode 100644
index 0000000000..c9282165a6
--- /dev/null
+++ b/tests/scene/test_image_texture.h
@@ -0,0 +1,111 @@
+/**************************************************************************/
+/* test_image_texture.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 TEST_IMAGE_TEXTURE_H
+#define TEST_IMAGE_TEXTURE_H
+
+#include "core/io/image.h"
+#include "scene/resources/image_texture.h"
+
+#include "tests/test_macros.h"
+#include "tests/test_utils.h"
+
+namespace TestImageTexture {
+
+// [SceneTree] in a test case name enables initializing a mock render server,
+// which ImageTexture is dependent on.
+TEST_CASE("[SceneTree][ImageTexture] constructor") {
+ Ref<ImageTexture> image_texture = memnew(ImageTexture);
+ CHECK(image_texture->get_width() == 0);
+ CHECK(image_texture->get_height() == 0);
+ CHECK(image_texture->get_format() == 0);
+ CHECK(image_texture->has_alpha() == false);
+ CHECK(image_texture->get_image() == Ref<Image>());
+}
+
+TEST_CASE("[SceneTree][ImageTexture] create_from_image") {
+ Ref<Image> image = memnew(Image(16, 8, true, Image::FORMAT_RGBA8));
+ Ref<ImageTexture> image_texture = ImageTexture::create_from_image(image);
+ CHECK(image_texture->get_width() == 16);
+ CHECK(image_texture->get_height() == 8);
+ CHECK(image_texture->get_format() == Image::FORMAT_RGBA8);
+ CHECK(image_texture->has_alpha() == true);
+ CHECK(image_texture->get_rid().is_valid() == true);
+}
+
+TEST_CASE("[SceneTree][ImageTexture] set_image") {
+ Ref<ImageTexture> image_texture = memnew(ImageTexture);
+ Ref<Image> image = memnew(Image(8, 4, false, Image::FORMAT_RGB8));
+ image_texture->set_image(image);
+ CHECK(image_texture->get_width() == 8);
+ CHECK(image_texture->get_height() == 4);
+ CHECK(image_texture->get_format() == Image::FORMAT_RGB8);
+ CHECK(image_texture->has_alpha() == false);
+ CHECK(image_texture->get_width() == image_texture->get_image()->get_width());
+ CHECK(image_texture->get_height() == image_texture->get_image()->get_height());
+ CHECK(image_texture->get_format() == image_texture->get_image()->get_format());
+}
+
+TEST_CASE("[SceneTree][ImageTexture] set_size_override") {
+ Ref<Image> image = memnew(Image(16, 8, false, Image::FORMAT_RGB8));
+ Ref<ImageTexture> image_texture = ImageTexture::create_from_image(image);
+ CHECK(image_texture->get_width() == 16);
+ CHECK(image_texture->get_height() == 8);
+ image_texture->set_size_override(Size2i(32, 16));
+ CHECK(image_texture->get_width() == 32);
+ CHECK(image_texture->get_height() == 16);
+}
+
+TEST_CASE("[SceneTree][ImageTexture] is_pixel_opaque") {
+ Ref<Image> image = memnew(Image(8, 8, false, Image::FORMAT_RGBA8));
+ image->set_pixel(0, 0, Color(0.0, 0.0, 0.0, 0.0)); // not opaque
+ image->set_pixel(0, 1, Color(0.0, 0.0, 0.0, 0.1)); // not opaque
+ image->set_pixel(0, 2, Color(0.0, 0.0, 0.0, 0.5)); // opaque
+ image->set_pixel(0, 3, Color(0.0, 0.0, 0.0, 0.9)); // opaque
+ image->set_pixel(0, 4, Color(0.0, 0.0, 0.0, 1.0)); // opaque
+
+ Ref<ImageTexture> image_texture = ImageTexture::create_from_image(image);
+ CHECK(image_texture->is_pixel_opaque(0, 0) == false);
+ CHECK(image_texture->is_pixel_opaque(0, 1) == false);
+ CHECK(image_texture->is_pixel_opaque(0, 2) == true);
+ CHECK(image_texture->is_pixel_opaque(0, 3) == true);
+ CHECK(image_texture->is_pixel_opaque(0, 4) == true);
+}
+
+TEST_CASE("[SceneTree][ImageTexture] set_path") {
+ Ref<ImageTexture> image_texture = memnew(ImageTexture);
+ String path = TestUtils::get_data_path("images/icon.png");
+ image_texture->set_path(path, true);
+ CHECK(image_texture->get_path() == path);
+}
+
+} //namespace TestImageTexture
+
+#endif // TEST_IMAGE_TEXTURE_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 7f49805274..bac5c3dd7c 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -30,8 +30,11 @@
#include "test_main.h"
+#ifdef TOOLS_ENABLED
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
+#endif // TOOLS_ENABLED
+
#include "tests/core/config/test_project_settings.h"
#include "tests/core/input/test_input_event.h"
#include "tests/core/input/test_input_event_key.h"
@@ -103,6 +106,7 @@
#include "tests/scene/test_curve_2d.h"
#include "tests/scene/test_curve_3d.h"
#include "tests/scene/test_gradient.h"
+#include "tests/scene/test_image_texture.h"
#include "tests/scene/test_navigation_agent_2d.h"
#include "tests/scene/test_navigation_obstacle_2d.h"
#include "tests/scene/test_navigation_region_2d.h"
@@ -270,11 +274,13 @@ struct GodotTestCaseListener : public doctest::IReporter {
SceneTree::get_singleton()->get_root()->set_embedding_subwindows(true);
}
+#ifdef TOOLS_ENABLED
if (name.find("[Editor]") != -1) {
Engine::get_singleton()->set_editor_hint(true);
EditorPaths::create();
EditorSettings::create();
}
+#endif // TOOLS_ENABLED
return;
}
@@ -298,9 +304,11 @@ struct GodotTestCaseListener : public doctest::IReporter {
}
void test_case_end(const doctest::CurrentTestCaseStats &) override {
+#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
EditorSettings::destroy();
}
+#endif // TOOLS_ENABLED
Engine::get_singleton()->set_editor_hint(false);
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 107c2b2589..2e99abc622 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -64,7 +64,7 @@ Files extracted from upstream source:
Files extracted from upstream source:
-- `encoder/` and `transcoder/` folders
+- `encoder/` and `transcoder/` folders, minus `jpgd.{cpp,h}`
- `LICENSE`
Applied upstream PR https://github.com/BinomialLLC/basis_universal/pull/344 to
@@ -697,7 +697,7 @@ Files extracted from the upstream source:
## noise
- Upstream: https://github.com/Auburn/FastNoiseLite
-- Version: git (6be3d6bf7fb408de341285f9ee8a29b67fd953f1, 2022)
+- Version: 1.1.0 (f7af54b56518aa659e1cf9fb103c0b6e36a833d9, 2023)
- License: MIT
Files extracted from the upstream source:
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index c431ceaf12..e87dd636a2 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -409,7 +409,7 @@ namespace basisu
bool load_jpg(const char *pFilename, image& img)
{
int width = 0, height = 0, actual_comps = 0;
- uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagLinearChromaFiltering);
+ uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagBoxChromaFiltering);
if (!pImage_data)
return false;
diff --git a/thirdparty/basis_universal/encoder/jpgd.cpp b/thirdparty/basis_universal/encoder/jpgd.cpp
deleted file mode 100644
index fec8b71439..0000000000
--- a/thirdparty/basis_universal/encoder/jpgd.cpp
+++ /dev/null
@@ -1,3230 +0,0 @@
-// jpgd.cpp - C++ class for JPEG decompression. Written by Richard Geldreich <richgel99@gmail.com> between 1994-2020.
-// Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
-// Supports box and linear chroma upsampling.
-//
-// Released under two licenses. You are free to choose which license you want:
-// License 1:
-// Public Domain
-//
-// License 2:
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// Alex Evans: Linear memory allocator (taken from jpge.h).
-// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings
-// v2.00, March 20, 2020: Fuzzed with zzuf and afl. Fixed several issues, converted most assert()'s to run-time checks. Added chroma upsampling. Removed freq. domain upsampling. gcc/clang warnings.
-//
-
-#include "jpgd.h"
-#include <string.h>
-#include <algorithm>
-#include <assert.h>
-
-#ifdef _MSC_VER
-#pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
-#endif
-
-#define JPGD_TRUE (1)
-#define JPGD_FALSE (0)
-
-#define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
-#define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
-
-namespace jpgd {
-
- static inline void* jpgd_malloc(size_t nSize) { return malloc(nSize); }
- static inline void jpgd_free(void* p) { free(p); }
-
- // DCT coefficients are stored in this sequence.
- static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
-
- enum JPEG_MARKER
- {
- M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8,
- M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC,
- M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7,
- M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF,
- M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0
- };
-
- enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 };
-
-#define CONST_BITS 13
-#define PASS1_BITS 2
-#define SCALEDONE ((int32)1)
-
-#define FIX_0_298631336 ((int32)2446) /* FIX(0.298631336) */
-#define FIX_0_390180644 ((int32)3196) /* FIX(0.390180644) */
-#define FIX_0_541196100 ((int32)4433) /* FIX(0.541196100) */
-#define FIX_0_765366865 ((int32)6270) /* FIX(0.765366865) */
-#define FIX_0_899976223 ((int32)7373) /* FIX(0.899976223) */
-#define FIX_1_175875602 ((int32)9633) /* FIX(1.175875602) */
-#define FIX_1_501321110 ((int32)12299) /* FIX(1.501321110) */
-#define FIX_1_847759065 ((int32)15137) /* FIX(1.847759065) */
-#define FIX_1_961570560 ((int32)16069) /* FIX(1.961570560) */
-#define FIX_2_053119869 ((int32)16819) /* FIX(2.053119869) */
-#define FIX_2_562915447 ((int32)20995) /* FIX(2.562915447) */
-#define FIX_3_072711026 ((int32)25172) /* FIX(3.072711026) */
-
-#define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n))
-#define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n))
-
-#define MULTIPLY(var, cnst) ((var) * (cnst))
-
-#define CLAMP(i) ((static_cast<uint>(i) > 255) ? (((~i) >> 31) & 0xFF) : (i))
-
- static inline int left_shifti(int val, uint32_t bits)
- {
- return static_cast<int>(static_cast<uint32_t>(val) << bits);
- }
-
- // Compiler creates a fast path 1D IDCT for X non-zero columns
- template <int NONZERO_COLS>
- struct Row
- {
- static void idct(int* pTemp, const jpgd_block_t* pSrc)
- {
- // ACCESS_COL() will be optimized at compile time to either an array access, or 0. Good compilers will then optimize out muls against 0.
-#define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
-
- const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6);
-
- const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
- const int tmp2 = z1 + MULTIPLY(z3, -FIX_1_847759065);
- const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
-
- const int tmp0 = left_shifti(ACCESS_COL(0) + ACCESS_COL(4), CONST_BITS);
- const int tmp1 = left_shifti(ACCESS_COL(0) - ACCESS_COL(4), CONST_BITS);
-
- const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
-
- const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1);
-
- const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
- const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
-
- const int az1 = MULTIPLY(bz1, -FIX_0_899976223);
- const int az2 = MULTIPLY(bz2, -FIX_2_562915447);
- const int az3 = MULTIPLY(bz3, -FIX_1_961570560) + bz5;
- const int az4 = MULTIPLY(bz4, -FIX_0_390180644) + bz5;
-
- const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
- const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
- const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
- const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
-
- pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS - PASS1_BITS);
- pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS - PASS1_BITS);
- pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS - PASS1_BITS);
- pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS - PASS1_BITS);
- pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS - PASS1_BITS);
- pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS - PASS1_BITS);
- pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS - PASS1_BITS);
- pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS - PASS1_BITS);
- }
- };
-
- template <>
- struct Row<0>
- {
- static void idct(int* pTemp, const jpgd_block_t* pSrc)
- {
- (void)pTemp;
- (void)pSrc;
- }
- };
-
- template <>
- struct Row<1>
- {
- static void idct(int* pTemp, const jpgd_block_t* pSrc)
- {
- const int dcval = left_shifti(pSrc[0], PASS1_BITS);
-
- pTemp[0] = dcval;
- pTemp[1] = dcval;
- pTemp[2] = dcval;
- pTemp[3] = dcval;
- pTemp[4] = dcval;
- pTemp[5] = dcval;
- pTemp[6] = dcval;
- pTemp[7] = dcval;
- }
- };
-
- // Compiler creates a fast path 1D IDCT for X non-zero rows
- template <int NONZERO_ROWS>
- struct Col
- {
- static void idct(uint8* pDst_ptr, const int* pTemp)
- {
- // ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
-#define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
-
- const int z2 = ACCESS_ROW(2);
- const int z3 = ACCESS_ROW(6);
-
- const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
- const int tmp2 = z1 + MULTIPLY(z3, -FIX_1_847759065);
- const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
-
- const int tmp0 = left_shifti(ACCESS_ROW(0) + ACCESS_ROW(4), CONST_BITS);
- const int tmp1 = left_shifti(ACCESS_ROW(0) - ACCESS_ROW(4), CONST_BITS);
-
- const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
-
- const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1);
-
- const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
- const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
-
- const int az1 = MULTIPLY(bz1, -FIX_0_899976223);
- const int az2 = MULTIPLY(bz2, -FIX_2_562915447);
- const int az3 = MULTIPLY(bz3, -FIX_1_961570560) + bz5;
- const int az4 = MULTIPLY(bz4, -FIX_0_390180644) + bz5;
-
- const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
- const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
- const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
- const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
-
- int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 0] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 7] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 1] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 6] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 2] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 5] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 3] = (uint8)CLAMP(i);
-
- i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS + PASS1_BITS + 3);
- pDst_ptr[8 * 4] = (uint8)CLAMP(i);
- }
- };
-
- template <>
- struct Col<1>
- {
- static void idct(uint8* pDst_ptr, const int* pTemp)
- {
- int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS + 3);
- const uint8 dcval_clamped = (uint8)CLAMP(dcval);
- pDst_ptr[0 * 8] = dcval_clamped;
- pDst_ptr[1 * 8] = dcval_clamped;
- pDst_ptr[2 * 8] = dcval_clamped;
- pDst_ptr[3 * 8] = dcval_clamped;
- pDst_ptr[4 * 8] = dcval_clamped;
- pDst_ptr[5 * 8] = dcval_clamped;
- pDst_ptr[6 * 8] = dcval_clamped;
- pDst_ptr[7 * 8] = dcval_clamped;
- }
- };
-
- static const uint8 s_idct_row_table[] =
- {
- 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
- 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
- 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
- 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
- 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
- 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
- 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
- 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
- };
-
- static const uint8 s_idct_col_table[] =
- {
- 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
- };
-
- // Scalar "fast pathing" IDCT.
- static void idct(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr, int block_max_zag)
- {
- assert(block_max_zag >= 1);
- assert(block_max_zag <= 64);
-
- if (block_max_zag <= 1)
- {
- int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
- k = CLAMP(k);
- k = k | (k << 8);
- k = k | (k << 16);
-
- for (int i = 8; i > 0; i--)
- {
- *(int*)&pDst_ptr[0] = k;
- *(int*)&pDst_ptr[4] = k;
- pDst_ptr += 8;
- }
- return;
- }
-
- int temp[64];
-
- const jpgd_block_t* pSrc = pSrc_ptr;
- int* pTemp = temp;
-
- const uint8* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8];
- int i;
- for (i = 8; i > 0; i--, pRow_tab++)
- {
- switch (*pRow_tab)
- {
- case 0: Row<0>::idct(pTemp, pSrc); break;
- case 1: Row<1>::idct(pTemp, pSrc); break;
- case 2: Row<2>::idct(pTemp, pSrc); break;
- case 3: Row<3>::idct(pTemp, pSrc); break;
- case 4: Row<4>::idct(pTemp, pSrc); break;
- case 5: Row<5>::idct(pTemp, pSrc); break;
- case 6: Row<6>::idct(pTemp, pSrc); break;
- case 7: Row<7>::idct(pTemp, pSrc); break;
- case 8: Row<8>::idct(pTemp, pSrc); break;
- }
-
- pSrc += 8;
- pTemp += 8;
- }
-
- pTemp = temp;
-
- const int nonzero_rows = s_idct_col_table[block_max_zag - 1];
- for (i = 8; i > 0; i--)
- {
- switch (nonzero_rows)
- {
- case 1: Col<1>::idct(pDst_ptr, pTemp); break;
- case 2: Col<2>::idct(pDst_ptr, pTemp); break;
- case 3: Col<3>::idct(pDst_ptr, pTemp); break;
- case 4: Col<4>::idct(pDst_ptr, pTemp); break;
- case 5: Col<5>::idct(pDst_ptr, pTemp); break;
- case 6: Col<6>::idct(pDst_ptr, pTemp); break;
- case 7: Col<7>::idct(pDst_ptr, pTemp); break;
- case 8: Col<8>::idct(pDst_ptr, pTemp); break;
- }
-
- pTemp++;
- pDst_ptr++;
- }
- }
-
- // Retrieve one character from the input stream.
- inline uint jpeg_decoder::get_char()
- {
- // Any bytes remaining in buffer?
- if (!m_in_buf_left)
- {
- // Try to get more bytes.
- prep_in_buffer();
- // Still nothing to get?
- if (!m_in_buf_left)
- {
- // Pad the end of the stream with 0xFF 0xD9 (EOI marker)
- int t = m_tem_flag;
- m_tem_flag ^= 1;
- if (t)
- return 0xD9;
- else
- return 0xFF;
- }
- }
-
- uint c = *m_pIn_buf_ofs++;
- m_in_buf_left--;
-
- return c;
- }
-
- // Same as previous method, except can indicate if the character is a pad character or not.
- inline uint jpeg_decoder::get_char(bool* pPadding_flag)
- {
- if (!m_in_buf_left)
- {
- prep_in_buffer();
- if (!m_in_buf_left)
- {
- *pPadding_flag = true;
- int t = m_tem_flag;
- m_tem_flag ^= 1;
- if (t)
- return 0xD9;
- else
- return 0xFF;
- }
- }
-
- *pPadding_flag = false;
-
- uint c = *m_pIn_buf_ofs++;
- m_in_buf_left--;
-
- return c;
- }
-
- // Inserts a previously retrieved character back into the input buffer.
- inline void jpeg_decoder::stuff_char(uint8 q)
- {
- // This could write before the input buffer, but we've placed another array there.
- *(--m_pIn_buf_ofs) = q;
- m_in_buf_left++;
- }
-
- // Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
- inline uint8 jpeg_decoder::get_octet()
- {
- bool padding_flag;
- int c = get_char(&padding_flag);
-
- if (c == 0xFF)
- {
- if (padding_flag)
- return 0xFF;
-
- c = get_char(&padding_flag);
- if (padding_flag)
- {
- stuff_char(0xFF);
- return 0xFF;
- }
-
- if (c == 0x00)
- return 0xFF;
- else
- {
- stuff_char(static_cast<uint8>(c));
- stuff_char(0xFF);
- return 0xFF;
- }
- }
-
- return static_cast<uint8>(c);
- }
-
- // Retrieves a variable number of bits from the input stream. Does not recognize markers.
- inline uint jpeg_decoder::get_bits(int num_bits)
- {
- if (!num_bits)
- return 0;
-
- uint i = m_bit_buf >> (32 - num_bits);
-
- if ((m_bits_left -= num_bits) <= 0)
- {
- m_bit_buf <<= (num_bits += m_bits_left);
-
- uint c1 = get_char();
- uint c2 = get_char();
- m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
-
- m_bit_buf <<= -m_bits_left;
-
- m_bits_left += 16;
-
- assert(m_bits_left >= 0);
- }
- else
- m_bit_buf <<= num_bits;
-
- return i;
- }
-
- // Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
- inline uint jpeg_decoder::get_bits_no_markers(int num_bits)
- {
- if (!num_bits)
- return 0;
-
- assert(num_bits <= 16);
-
- uint i = m_bit_buf >> (32 - num_bits);
-
- if ((m_bits_left -= num_bits) <= 0)
- {
- m_bit_buf <<= (num_bits += m_bits_left);
-
- if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF))
- {
- uint c1 = get_octet();
- uint c2 = get_octet();
- m_bit_buf |= (c1 << 8) | c2;
- }
- else
- {
- m_bit_buf |= ((uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
- m_in_buf_left -= 2;
- m_pIn_buf_ofs += 2;
- }
-
- m_bit_buf <<= -m_bits_left;
-
- m_bits_left += 16;
-
- assert(m_bits_left >= 0);
- }
- else
- m_bit_buf <<= num_bits;
-
- return i;
- }
-
- // Decodes a Huffman encoded symbol.
- inline int jpeg_decoder::huff_decode(huff_tables* pH)
- {
- if (!pH)
- stop_decoding(JPGD_DECODE_ERROR);
-
- int symbol;
- // Check first 8-bits: do we have a complete symbol?
- if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0)
- {
- // Decode more bits, use a tree traversal to find symbol.
- int ofs = 23;
- do
- {
- unsigned int idx = -(int)(symbol + ((m_bit_buf >> ofs) & 1));
-
- // This should never happen, but to be safe I'm turning these asserts into a run-time check.
- if ((idx >= JPGD_HUFF_TREE_MAX_LENGTH) || (ofs < 0))
- stop_decoding(JPGD_DECODE_ERROR);
-
- symbol = pH->tree[idx];
- ofs--;
- } while (symbol < 0);
-
- get_bits_no_markers(8 + (23 - ofs));
- }
- else
- {
- assert(symbol < JPGD_HUFF_CODE_SIZE_MAX_LENGTH);
- get_bits_no_markers(pH->code_size[symbol]);
- }
-
- return symbol;
- }
-
- // Decodes a Huffman encoded symbol.
- inline int jpeg_decoder::huff_decode(huff_tables* pH, int& extra_bits)
- {
- int symbol;
-
- if (!pH)
- stop_decoding(JPGD_DECODE_ERROR);
-
- // Check first 8-bits: do we have a complete symbol?
- if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0)
- {
- // Use a tree traversal to find symbol.
- int ofs = 23;
- do
- {
- unsigned int idx = -(int)(symbol + ((m_bit_buf >> ofs) & 1));
-
- // This should never happen, but to be safe I'm turning these asserts into a run-time check.
- if ((idx >= JPGD_HUFF_TREE_MAX_LENGTH) || (ofs < 0))
- stop_decoding(JPGD_DECODE_ERROR);
-
- symbol = pH->tree[idx];
- ofs--;
- } while (symbol < 0);
-
- get_bits_no_markers(8 + (23 - ofs));
-
- extra_bits = get_bits_no_markers(symbol & 0xF);
- }
- else
- {
- if (symbol & 0x8000)
- {
- //get_bits_no_markers((symbol >> 8) & 31);
- assert(((symbol >> 8) & 31) <= 15);
- get_bits_no_markers((symbol >> 8) & 15);
- extra_bits = symbol >> 16;
- }
- else
- {
- int code_size = (symbol >> 8) & 31;
- int num_extra_bits = symbol & 0xF;
- int bits = code_size + num_extra_bits;
-
- if (bits <= 16)
- extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
- else
- {
- get_bits_no_markers(code_size);
- extra_bits = get_bits_no_markers(num_extra_bits);
- }
- }
-
- symbol &= 0xFF;
- }
-
- return symbol;
- }
-
- // Tables and macro used to fully decode the DPCM differences.
- static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
- static const int s_extend_offset[16] = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
- //static const int s_extend_mask[] = { 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15), (1 << 16) };
-
-#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x))
-
- // Unconditionally frees all allocated m_blocks.
- void jpeg_decoder::free_all_blocks()
- {
- m_pStream = nullptr;
- for (mem_block* b = m_pMem_blocks; b; )
- {
- mem_block* n = b->m_pNext;
- jpgd_free(b);
- b = n;
- }
- m_pMem_blocks = nullptr;
- }
-
- // This method handles all errors. It will never return.
- // It could easily be changed to use C++ exceptions.
- JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
- {
- m_error_code = status;
- free_all_blocks();
- longjmp(m_jmp_state, status);
- }
-
- void* jpeg_decoder::alloc(size_t nSize, bool zero)
- {
- nSize = (JPGD_MAX(nSize, 1) + 3) & ~3;
- char* rv = nullptr;
- for (mem_block* b = m_pMem_blocks; b; b = b->m_pNext)
- {
- if ((b->m_used_count + nSize) <= b->m_size)
- {
- rv = b->m_data + b->m_used_count;
- b->m_used_count += nSize;
- break;
- }
- }
- if (!rv)
- {
- int capacity = JPGD_MAX(32768 - 256, ((int)nSize + 2047) & ~2047);
- mem_block* b = (mem_block*)jpgd_malloc(sizeof(mem_block) + capacity);
- if (!b)
- {
- stop_decoding(JPGD_NOTENOUGHMEM);
- }
-
- b->m_pNext = m_pMem_blocks;
- m_pMem_blocks = b;
- b->m_used_count = nSize;
- b->m_size = capacity;
- rv = b->m_data;
- }
- if (zero) memset(rv, 0, nSize);
- return rv;
- }
-
- void jpeg_decoder::word_clear(void* p, uint16 c, uint n)
- {
- uint8* pD = (uint8*)p;
- const uint8 l = c & 0xFF, h = (c >> 8) & 0xFF;
- while (n)
- {
- pD[0] = l;
- pD[1] = h;
- pD += 2;
- n--;
- }
- }
-
- // Refill the input buffer.
- // This method will sit in a loop until (A) the buffer is full or (B)
- // the stream's read() method reports and end of file condition.
- void jpeg_decoder::prep_in_buffer()
- {
- m_in_buf_left = 0;
- m_pIn_buf_ofs = m_in_buf;
-
- if (m_eof_flag)
- return;
-
- do
- {
- int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
- if (bytes_read == -1)
- stop_decoding(JPGD_STREAM_READ);
-
- m_in_buf_left += bytes_read;
- } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
-
- m_total_bytes_read += m_in_buf_left;
-
- // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
- // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
- word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
- }
-
- // Read a Huffman code table.
- void jpeg_decoder::read_dht_marker()
- {
- int i, index, count;
- uint8 huff_num[17];
- uint8 huff_val[256];
-
- uint num_left = get_bits(16);
-
- if (num_left < 2)
- stop_decoding(JPGD_BAD_DHT_MARKER);
-
- num_left -= 2;
-
- while (num_left)
- {
- index = get_bits(8);
-
- huff_num[0] = 0;
-
- count = 0;
-
- for (i = 1; i <= 16; i++)
- {
- huff_num[i] = static_cast<uint8>(get_bits(8));
- count += huff_num[i];
- }
-
- if (count > 255)
- stop_decoding(JPGD_BAD_DHT_COUNTS);
-
- bool symbol_present[256];
- memset(symbol_present, 0, sizeof(symbol_present));
-
- for (i = 0; i < count; i++)
- {
- const int s = get_bits(8);
-
- // Check for obviously bogus tables.
- if (symbol_present[s])
- stop_decoding(JPGD_BAD_DHT_COUNTS);
-
- huff_val[i] = static_cast<uint8_t>(s);
- symbol_present[s] = true;
- }
-
- i = 1 + 16 + count;
-
- if (num_left < (uint)i)
- stop_decoding(JPGD_BAD_DHT_MARKER);
-
- num_left -= i;
-
- if ((index & 0x10) > 0x10)
- stop_decoding(JPGD_BAD_DHT_INDEX);
-
- index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
-
- if (index >= JPGD_MAX_HUFF_TABLES)
- stop_decoding(JPGD_BAD_DHT_INDEX);
-
- if (!m_huff_num[index])
- m_huff_num[index] = (uint8*)alloc(17);
-
- if (!m_huff_val[index])
- m_huff_val[index] = (uint8*)alloc(256);
-
- m_huff_ac[index] = (index & 0x10) != 0;
- memcpy(m_huff_num[index], huff_num, 17);
- memcpy(m_huff_val[index], huff_val, 256);
- }
- }
-
- // Read a quantization table.
- void jpeg_decoder::read_dqt_marker()
- {
- int n, i, prec;
- uint num_left;
- uint temp;
-
- num_left = get_bits(16);
-
- if (num_left < 2)
- stop_decoding(JPGD_BAD_DQT_MARKER);
-
- num_left -= 2;
-
- while (num_left)
- {
- n = get_bits(8);
- prec = n >> 4;
- n &= 0x0F;
-
- if (n >= JPGD_MAX_QUANT_TABLES)
- stop_decoding(JPGD_BAD_DQT_TABLE);
-
- if (!m_quant[n])
- m_quant[n] = (jpgd_quant_t*)alloc(64 * sizeof(jpgd_quant_t));
-
- // read quantization entries, in zag order
- for (i = 0; i < 64; i++)
- {
- temp = get_bits(8);
-
- if (prec)
- temp = (temp << 8) + get_bits(8);
-
- m_quant[n][i] = static_cast<jpgd_quant_t>(temp);
- }
-
- i = 64 + 1;
-
- if (prec)
- i += 64;
-
- if (num_left < (uint)i)
- stop_decoding(JPGD_BAD_DQT_LENGTH);
-
- num_left -= i;
- }
- }
-
- // Read the start of frame (SOF) marker.
- void jpeg_decoder::read_sof_marker()
- {
- int i;
- uint num_left;
-
- num_left = get_bits(16);
-
- /* precision: sorry, only 8-bit precision is supported */
- if (get_bits(8) != 8)
- stop_decoding(JPGD_BAD_PRECISION);
-
- m_image_y_size = get_bits(16);
-
- if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT))
- stop_decoding(JPGD_BAD_HEIGHT);
-
- m_image_x_size = get_bits(16);
-
- if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH))
- stop_decoding(JPGD_BAD_WIDTH);
-
- m_comps_in_frame = get_bits(8);
-
- if (m_comps_in_frame > JPGD_MAX_COMPONENTS)
- stop_decoding(JPGD_TOO_MANY_COMPONENTS);
-
- if (num_left != (uint)(m_comps_in_frame * 3 + 8))
- stop_decoding(JPGD_BAD_SOF_LENGTH);
-
- for (i = 0; i < m_comps_in_frame; i++)
- {
- m_comp_ident[i] = get_bits(8);
- m_comp_h_samp[i] = get_bits(4);
- m_comp_v_samp[i] = get_bits(4);
-
- if (!m_comp_h_samp[i] || !m_comp_v_samp[i] || (m_comp_h_samp[i] > 2) || (m_comp_v_samp[i] > 2))
- stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
-
- m_comp_quant[i] = get_bits(8);
- if (m_comp_quant[i] >= JPGD_MAX_QUANT_TABLES)
- stop_decoding(JPGD_DECODE_ERROR);
- }
- }
-
- // Used to skip unrecognized markers.
- void jpeg_decoder::skip_variable_marker()
- {
- uint num_left;
-
- num_left = get_bits(16);
-
- if (num_left < 2)
- stop_decoding(JPGD_BAD_VARIABLE_MARKER);
-
- num_left -= 2;
-
- while (num_left)
- {
- get_bits(8);
- num_left--;
- }
- }
-
- // Read a define restart interval (DRI) marker.
- void jpeg_decoder::read_dri_marker()
- {
- if (get_bits(16) != 4)
- stop_decoding(JPGD_BAD_DRI_LENGTH);
-
- m_restart_interval = get_bits(16);
- }
-
- // Read a start of scan (SOS) marker.
- void jpeg_decoder::read_sos_marker()
- {
- uint num_left;
- int i, ci, n, c, cc;
-
- num_left = get_bits(16);
-
- n = get_bits(8);
-
- m_comps_in_scan = n;
-
- num_left -= 3;
-
- if ((num_left != (uint)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN))
- stop_decoding(JPGD_BAD_SOS_LENGTH);
-
- for (i = 0; i < n; i++)
- {
- cc = get_bits(8);
- c = get_bits(8);
- num_left -= 2;
-
- for (ci = 0; ci < m_comps_in_frame; ci++)
- if (cc == m_comp_ident[ci])
- break;
-
- if (ci >= m_comps_in_frame)
- stop_decoding(JPGD_BAD_SOS_COMP_ID);
-
- if (ci >= JPGD_MAX_COMPONENTS)
- stop_decoding(JPGD_DECODE_ERROR);
-
- m_comp_list[i] = ci;
-
- m_comp_dc_tab[ci] = (c >> 4) & 15;
- m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1);
-
- if (m_comp_dc_tab[ci] >= JPGD_MAX_HUFF_TABLES)
- stop_decoding(JPGD_DECODE_ERROR);
-
- if (m_comp_ac_tab[ci] >= JPGD_MAX_HUFF_TABLES)
- stop_decoding(JPGD_DECODE_ERROR);
- }
-
- m_spectral_start = get_bits(8);
- m_spectral_end = get_bits(8);
- m_successive_high = get_bits(4);
- m_successive_low = get_bits(4);
-
- if (!m_progressive_flag)
- {
- m_spectral_start = 0;
- m_spectral_end = 63;
- }
-
- num_left -= 3;
-
- /* read past whatever is num_left */
- while (num_left)
- {
- get_bits(8);
- num_left--;
- }
- }
-
- // Finds the next marker.
- int jpeg_decoder::next_marker()
- {
- uint c, bytes;
-
- bytes = 0;
-
- do
- {
- do
- {
- bytes++;
- c = get_bits(8);
- } while (c != 0xFF);
-
- do
- {
- c = get_bits(8);
- } while (c == 0xFF);
-
- } while (c == 0);
-
- // If bytes > 0 here, there where extra bytes before the marker (not good).
-
- return c;
- }
-
- // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
- // encountered.
- int jpeg_decoder::process_markers()
- {
- int c;
-
- for (; ; )
- {
- c = next_marker();
-
- switch (c)
- {
- case M_SOF0:
- case M_SOF1:
- case M_SOF2:
- case M_SOF3:
- case M_SOF5:
- case M_SOF6:
- case M_SOF7:
- // case M_JPG:
- case M_SOF9:
- case M_SOF10:
- case M_SOF11:
- case M_SOF13:
- case M_SOF14:
- case M_SOF15:
- case M_SOI:
- case M_EOI:
- case M_SOS:
- {
- return c;
- }
- case M_DHT:
- {
- read_dht_marker();
- break;
- }
- // No arithmitic support - dumb patents!
- case M_DAC:
- {
- stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
- break;
- }
- case M_DQT:
- {
- read_dqt_marker();
- break;
- }
- case M_DRI:
- {
- read_dri_marker();
- break;
- }
- //case M_APP0: /* no need to read the JFIF marker */
- case M_JPG:
- case M_RST0: /* no parameters */
- case M_RST1:
- case M_RST2:
- case M_RST3:
- case M_RST4:
- case M_RST5:
- case M_RST6:
- case M_RST7:
- case M_TEM:
- {
- stop_decoding(JPGD_UNEXPECTED_MARKER);
- break;
- }
- default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
- {
- skip_variable_marker();
- break;
- }
- }
- }
- }
-
- // Finds the start of image (SOI) marker.
- void jpeg_decoder::locate_soi_marker()
- {
- uint lastchar, thischar;
- uint bytesleft;
-
- lastchar = get_bits(8);
-
- thischar = get_bits(8);
-
- /* ok if it's a normal JPEG file without a special header */
-
- if ((lastchar == 0xFF) && (thischar == M_SOI))
- return;
-
- bytesleft = 4096;
-
- for (; ; )
- {
- if (--bytesleft == 0)
- stop_decoding(JPGD_NOT_JPEG);
-
- lastchar = thischar;
-
- thischar = get_bits(8);
-
- if (lastchar == 0xFF)
- {
- if (thischar == M_SOI)
- break;
- else if (thischar == M_EOI) // get_bits will keep returning M_EOI if we read past the end
- stop_decoding(JPGD_NOT_JPEG);
- }
- }
-
- // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
- thischar = (m_bit_buf >> 24) & 0xFF;
-
- if (thischar != 0xFF)
- stop_decoding(JPGD_NOT_JPEG);
- }
-
- // Find a start of frame (SOF) marker.
- void jpeg_decoder::locate_sof_marker()
- {
- locate_soi_marker();
-
- int c = process_markers();
-
- switch (c)
- {
- case M_SOF2:
- {
- m_progressive_flag = JPGD_TRUE;
- read_sof_marker();
- break;
- }
- case M_SOF0: /* baseline DCT */
- case M_SOF1: /* extended sequential DCT */
- {
- read_sof_marker();
- break;
- }
- case M_SOF9: /* Arithmitic coding */
- {
- stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
- break;
- }
- default:
- {
- stop_decoding(JPGD_UNSUPPORTED_MARKER);
- break;
- }
- }
- }
-
- // Find a start of scan (SOS) marker.
- int jpeg_decoder::locate_sos_marker()
- {
- int c;
-
- c = process_markers();
-
- if (c == M_EOI)
- return JPGD_FALSE;
- else if (c != M_SOS)
- stop_decoding(JPGD_UNEXPECTED_MARKER);
-
- read_sos_marker();
-
- return JPGD_TRUE;
- }
-
- // Reset everything to default/uninitialized state.
- void jpeg_decoder::init(jpeg_decoder_stream* pStream, uint32_t flags)
- {
- m_flags = flags;
- m_pMem_blocks = nullptr;
- m_error_code = JPGD_SUCCESS;
- m_ready_flag = false;
- m_image_x_size = m_image_y_size = 0;
- m_pStream = pStream;
- m_progressive_flag = JPGD_FALSE;
-
- memset(m_huff_ac, 0, sizeof(m_huff_ac));
- memset(m_huff_num, 0, sizeof(m_huff_num));
- memset(m_huff_val, 0, sizeof(m_huff_val));
- memset(m_quant, 0, sizeof(m_quant));
-
- m_scan_type = 0;
- m_comps_in_frame = 0;
-
- memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp));
- memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp));
- memset(m_comp_quant, 0, sizeof(m_comp_quant));
- memset(m_comp_ident, 0, sizeof(m_comp_ident));
- memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks));
- memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks));
-
- m_comps_in_scan = 0;
- memset(m_comp_list, 0, sizeof(m_comp_list));
- memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab));
- memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab));
-
- m_spectral_start = 0;
- m_spectral_end = 0;
- m_successive_low = 0;
- m_successive_high = 0;
- m_max_mcu_x_size = 0;
- m_max_mcu_y_size = 0;
- m_blocks_per_mcu = 0;
- m_max_blocks_per_row = 0;
- m_mcus_per_row = 0;
- m_mcus_per_col = 0;
-
- memset(m_mcu_org, 0, sizeof(m_mcu_org));
-
- m_total_lines_left = 0;
- m_mcu_lines_left = 0;
- m_num_buffered_scanlines = 0;
- m_real_dest_bytes_per_scan_line = 0;
- m_dest_bytes_per_scan_line = 0;
- m_dest_bytes_per_pixel = 0;
-
- memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs));
-
- memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs));
- memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs));
- memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
-
- m_eob_run = 0;
-
- m_pIn_buf_ofs = m_in_buf;
- m_in_buf_left = 0;
- m_eof_flag = false;
- m_tem_flag = 0;
-
- memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start));
- memset(m_in_buf, 0, sizeof(m_in_buf));
- memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end));
-
- m_restart_interval = 0;
- m_restarts_left = 0;
- m_next_restart_num = 0;
-
- m_max_mcus_per_row = 0;
- m_max_blocks_per_mcu = 0;
- m_max_mcus_per_col = 0;
-
- memset(m_last_dc_val, 0, sizeof(m_last_dc_val));
- m_pMCU_coefficients = nullptr;
- m_pSample_buf = nullptr;
- m_pSample_buf_prev = nullptr;
- m_sample_buf_prev_valid = false;
-
- m_total_bytes_read = 0;
-
- m_pScan_line_0 = nullptr;
- m_pScan_line_1 = nullptr;
-
- // Ready the input buffer.
- prep_in_buffer();
-
- // Prime the bit buffer.
- m_bits_left = 16;
- m_bit_buf = 0;
-
- get_bits(16);
- get_bits(16);
-
- for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++)
- m_mcu_block_max_zag[i] = 64;
- }
-
-#define SCALEBITS 16
-#define ONE_HALF ((int) 1 << (SCALEBITS-1))
-#define FIX(x) ((int) ((x) * (1L<<SCALEBITS) + 0.5f))
-
- // Create a few tables that allow us to quickly convert YCbCr to RGB.
- void jpeg_decoder::create_look_ups()
- {
- for (int i = 0; i <= 255; i++)
- {
- int k = i - 128;
- m_crr[i] = (FIX(1.40200f) * k + ONE_HALF) >> SCALEBITS;
- m_cbb[i] = (FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS;
- m_crg[i] = (-FIX(0.71414f)) * k;
- m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF;
- }
- }
-
- // This method throws back into the stream any bytes that where read
- // into the bit buffer during initial marker scanning.
- void jpeg_decoder::fix_in_buffer()
- {
- // In case any 0xFF's where pulled into the buffer during marker scanning.
- assert((m_bits_left & 7) == 0);
-
- if (m_bits_left == 16)
- stuff_char((uint8)(m_bit_buf & 0xFF));
-
- if (m_bits_left >= 8)
- stuff_char((uint8)((m_bit_buf >> 8) & 0xFF));
-
- stuff_char((uint8)((m_bit_buf >> 16) & 0xFF));
- stuff_char((uint8)((m_bit_buf >> 24) & 0xFF));
-
- m_bits_left = 16;
- get_bits_no_markers(16);
- get_bits_no_markers(16);
- }
-
- void jpeg_decoder::transform_mcu(int mcu_row)
- {
- jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
- if (mcu_row * m_blocks_per_mcu >= m_max_blocks_per_row)
- stop_decoding(JPGD_DECODE_ERROR);
-
- uint8* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
-
- for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
- {
- idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
- pSrc_ptr += 64;
- pDst_ptr += 64;
- }
- }
-
- // Loads and dequantizes the next row of (already decoded) coefficients.
- // Progressive images only.
- void jpeg_decoder::load_next_row()
- {
- int i;
- jpgd_block_t* p;
- jpgd_quant_t* q;
- int mcu_row, mcu_block, row_block = 0;
- int component_num, component_id;
- int block_x_mcu[JPGD_MAX_COMPONENTS];
-
- memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int));
-
- for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
- {
- int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
-
- for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
- {
- component_id = m_mcu_org[mcu_block];
- if (m_comp_quant[component_id] >= JPGD_MAX_QUANT_TABLES)
- stop_decoding(JPGD_DECODE_ERROR);
-
- q = m_quant[m_comp_quant[component_id]];
-
- p = m_pMCU_coefficients + 64 * mcu_block;
-
- jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
- jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
- p[0] = pDC[0];
- memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
-
- for (i = 63; i > 0; i--)
- if (p[g_ZAG[i]])
- break;
-
- m_mcu_block_max_zag[mcu_block] = i + 1;
-
- for (; i >= 0; i--)
- if (p[g_ZAG[i]])
- p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
-
- row_block++;
-
- if (m_comps_in_scan == 1)
- block_x_mcu[component_id]++;
- else
- {
- if (++block_x_mcu_ofs == m_comp_h_samp[component_id])
- {
- block_x_mcu_ofs = 0;
-
- if (++block_y_mcu_ofs == m_comp_v_samp[component_id])
- {
- block_y_mcu_ofs = 0;
-
- block_x_mcu[component_id] += m_comp_h_samp[component_id];
- }
- }
- }
- }
-
- transform_mcu(mcu_row);
- }
-
- if (m_comps_in_scan == 1)
- m_block_y_mcu[m_comp_list[0]]++;
- else
- {
- for (component_num = 0; component_num < m_comps_in_scan; component_num++)
- {
- component_id = m_comp_list[component_num];
-
- m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
- }
- }
- }
-
- // Restart interval processing.
- void jpeg_decoder::process_restart()
- {
- int i;
- int c = 0;
-
- // Align to a byte boundry
- // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
- //get_bits_no_markers(m_bits_left & 7);
-
- // Let's scan a little bit to find the marker, but not _too_ far.
- // 1536 is a "fudge factor" that determines how much to scan.
- for (i = 1536; i > 0; i--)
- if (get_char() == 0xFF)
- break;
-
- if (i == 0)
- stop_decoding(JPGD_BAD_RESTART_MARKER);
-
- for (; i > 0; i--)
- if ((c = get_char()) != 0xFF)
- break;
-
- if (i == 0)
- stop_decoding(JPGD_BAD_RESTART_MARKER);
-
- // Is it the expected marker? If not, something bad happened.
- if (c != (m_next_restart_num + M_RST0))
- stop_decoding(JPGD_BAD_RESTART_MARKER);
-
- // Reset each component's DC prediction values.
- memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint));
-
- m_eob_run = 0;
-
- m_restarts_left = m_restart_interval;
-
- m_next_restart_num = (m_next_restart_num + 1) & 7;
-
- // Get the bit buffer going again...
-
- m_bits_left = 16;
- get_bits_no_markers(16);
- get_bits_no_markers(16);
- }
-
- static inline int dequantize_ac(int c, int q) { c *= q; return c; }
-
- // Decodes and dequantizes the next row of coefficients.
- void jpeg_decoder::decode_next_row()
- {
- int row_block = 0;
-
- for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
- {
- if ((m_restart_interval) && (m_restarts_left == 0))
- process_restart();
-
- jpgd_block_t* p = m_pMCU_coefficients;
- for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64)
- {
- int component_id = m_mcu_org[mcu_block];
- if (m_comp_quant[component_id] >= JPGD_MAX_QUANT_TABLES)
- stop_decoding(JPGD_DECODE_ERROR);
-
- jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
-
- int r, s;
- s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r);
- if (s >= 16)
- stop_decoding(JPGD_DECODE_ERROR);
-
- s = JPGD_HUFF_EXTEND(r, s);
-
- m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]);
-
- p[0] = static_cast<jpgd_block_t>(s * q[0]);
-
- int prev_num_set = m_mcu_block_max_zag[mcu_block];
-
- huff_tables* pH = m_pHuff_tabs[m_comp_ac_tab[component_id]];
-
- int k;
- for (k = 1; k < 64; k++)
- {
- int extra_bits;
- s = huff_decode(pH, extra_bits);
-
- r = s >> 4;
- s &= 15;
-
- if (s)
- {
- if (r)
- {
- if ((k + r) > 63)
- stop_decoding(JPGD_DECODE_ERROR);
-
- if (k < prev_num_set)
- {
- int n = JPGD_MIN(r, prev_num_set - k);
- int kt = k;
- while (n--)
- p[g_ZAG[kt++]] = 0;
- }
-
- k += r;
- }
-
- s = JPGD_HUFF_EXTEND(extra_bits, s);
-
- if (k >= 64)
- stop_decoding(JPGD_DECODE_ERROR);
-
- p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
- }
- else
- {
- if (r == 15)
- {
- if ((k + 16) > 64)
- stop_decoding(JPGD_DECODE_ERROR);
-
- if (k < prev_num_set)
- {
- int n = JPGD_MIN(16, prev_num_set - k);
- int kt = k;
- while (n--)
- {
- if (kt > 63)
- stop_decoding(JPGD_DECODE_ERROR);
- p[g_ZAG[kt++]] = 0;
- }
- }
-
- k += 16 - 1; // - 1 because the loop counter is k
-
- if (p[g_ZAG[k & 63]] != 0)
- stop_decoding(JPGD_DECODE_ERROR);
- }
- else
- break;
- }
- }
-
- if (k < prev_num_set)
- {
- int kt = k;
- while (kt < prev_num_set)
- p[g_ZAG[kt++]] = 0;
- }
-
- m_mcu_block_max_zag[mcu_block] = k;
-
- row_block++;
- }
-
- transform_mcu(mcu_row);
-
- m_restarts_left--;
- }
- }
-
- // YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB
- void jpeg_decoder::H1V1Convert()
- {
- int row = m_max_mcu_y_size - m_mcu_lines_left;
- uint8* d = m_pScan_line_0;
- uint8* s = m_pSample_buf + row * 8;
-
- for (int i = m_max_mcus_per_row; i > 0; i--)
- {
- for (int j = 0; j < 8; j++)
- {
- int y = s[j];
- int cb = s[64 + j];
- int cr = s[128 + j];
-
- d[0] = clamp(y + m_crr[cr]);
- d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
- d[2] = clamp(y + m_cbb[cb]);
- d[3] = 255;
-
- d += 4;
- }
-
- s += 64 * 3;
- }
- }
-
- // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
- void jpeg_decoder::H2V1Convert()
- {
- int row = m_max_mcu_y_size - m_mcu_lines_left;
- uint8* d0 = m_pScan_line_0;
- uint8* y = m_pSample_buf + row * 8;
- uint8* c = m_pSample_buf + 2 * 64 + row * 8;
-
- for (int i = m_max_mcus_per_row; i > 0; i--)
- {
- for (int l = 0; l < 2; l++)
- {
- for (int j = 0; j < 4; j++)
- {
- int cb = c[0];
- int cr = c[64];
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- int yy = y[j << 1];
- d0[0] = clamp(yy + rc);
- d0[1] = clamp(yy + gc);
- d0[2] = clamp(yy + bc);
- d0[3] = 255;
-
- yy = y[(j << 1) + 1];
- d0[4] = clamp(yy + rc);
- d0[5] = clamp(yy + gc);
- d0[6] = clamp(yy + bc);
- d0[7] = 255;
-
- d0 += 8;
-
- c++;
- }
- y += 64;
- }
-
- y += 64 * 4 - 64 * 2;
- c += 64 * 4 - 8;
- }
- }
-
- // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
- void jpeg_decoder::H2V1ConvertFiltered()
- {
- const uint BLOCKS_PER_MCU = 4;
- int row = m_max_mcu_y_size - m_mcu_lines_left;
- uint8* d0 = m_pScan_line_0;
-
- const int half_image_x_size = (m_image_x_size >> 1) - 1;
- const int row_x8 = row * 8;
-
- for (int x = 0; x < m_image_x_size; x++)
- {
- int y = m_pSample_buf[check_sample_buf_ofs((x >> 4) * BLOCKS_PER_MCU * 64 + ((x & 8) ? 64 : 0) + (x & 7) + row_x8)];
-
- int c_x0 = (x - 1) >> 1;
- int c_x1 = JPGD_MIN(c_x0 + 1, half_image_x_size);
- c_x0 = JPGD_MAX(c_x0, 0);
-
- int a = (c_x0 >> 3) * BLOCKS_PER_MCU * 64 + (c_x0 & 7) + row_x8 + 128;
- int cb0 = m_pSample_buf[check_sample_buf_ofs(a)];
- int cr0 = m_pSample_buf[check_sample_buf_ofs(a + 64)];
-
- int b = (c_x1 >> 3) * BLOCKS_PER_MCU * 64 + (c_x1 & 7) + row_x8 + 128;
- int cb1 = m_pSample_buf[check_sample_buf_ofs(b)];
- int cr1 = m_pSample_buf[check_sample_buf_ofs(b + 64)];
-
- int w0 = (x & 1) ? 3 : 1;
- int w1 = (x & 1) ? 1 : 3;
-
- int cb = (cb0 * w0 + cb1 * w1 + 2) >> 2;
- int cr = (cr0 * w0 + cr1 * w1 + 2) >> 2;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d0[0] = clamp(y + rc);
- d0[1] = clamp(y + gc);
- d0[2] = clamp(y + bc);
- d0[3] = 255;
-
- d0 += 4;
- }
- }
-
- // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
- void jpeg_decoder::H1V2Convert()
- {
- int row = m_max_mcu_y_size - m_mcu_lines_left;
- uint8* d0 = m_pScan_line_0;
- uint8* d1 = m_pScan_line_1;
- uint8* y;
- uint8* c;
-
- if (row < 8)
- y = m_pSample_buf + row * 8;
- else
- y = m_pSample_buf + 64 * 1 + (row & 7) * 8;
-
- c = m_pSample_buf + 64 * 2 + (row >> 1) * 8;
-
- for (int i = m_max_mcus_per_row; i > 0; i--)
- {
- for (int j = 0; j < 8; j++)
- {
- int cb = c[0 + j];
- int cr = c[64 + j];
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- int yy = y[j];
- d0[0] = clamp(yy + rc);
- d0[1] = clamp(yy + gc);
- d0[2] = clamp(yy + bc);
- d0[3] = 255;
-
- yy = y[8 + j];
- d1[0] = clamp(yy + rc);
- d1[1] = clamp(yy + gc);
- d1[2] = clamp(yy + bc);
- d1[3] = 255;
-
- d0 += 4;
- d1 += 4;
- }
-
- y += 64 * 4;
- c += 64 * 4;
- }
- }
-
- // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
- void jpeg_decoder::H1V2ConvertFiltered()
- {
- const uint BLOCKS_PER_MCU = 4;
- int y = m_image_y_size - m_total_lines_left;
- int row = y & 15;
-
- const int half_image_y_size = (m_image_y_size >> 1) - 1;
-
- uint8* d0 = m_pScan_line_0;
-
- const int w0 = (row & 1) ? 3 : 1;
- const int w1 = (row & 1) ? 1 : 3;
-
- int c_y0 = (y - 1) >> 1;
- int c_y1 = JPGD_MIN(c_y0 + 1, half_image_y_size);
-
- const uint8_t* p_YSamples = m_pSample_buf;
- const uint8_t* p_C0Samples = m_pSample_buf;
- if ((c_y0 >= 0) && (((row & 15) == 0) || ((row & 15) == 15)) && (m_total_lines_left > 1))
- {
- assert(y > 0);
- assert(m_sample_buf_prev_valid);
-
- if ((row & 15) == 15)
- p_YSamples = m_pSample_buf_prev;
-
- p_C0Samples = m_pSample_buf_prev;
- }
-
- const int y_sample_base_ofs = ((row & 8) ? 64 : 0) + (row & 7) * 8;
- const int y0_base = (c_y0 & 7) * 8 + 128;
- const int y1_base = (c_y1 & 7) * 8 + 128;
-
- for (int x = 0; x < m_image_x_size; x++)
- {
- const int base_ofs = (x >> 3) * BLOCKS_PER_MCU * 64 + (x & 7);
-
- int y_sample = p_YSamples[check_sample_buf_ofs(base_ofs + y_sample_base_ofs)];
-
- int a = base_ofs + y0_base;
- int cb0_sample = p_C0Samples[check_sample_buf_ofs(a)];
- int cr0_sample = p_C0Samples[check_sample_buf_ofs(a + 64)];
-
- int b = base_ofs + y1_base;
- int cb1_sample = m_pSample_buf[check_sample_buf_ofs(b)];
- int cr1_sample = m_pSample_buf[check_sample_buf_ofs(b + 64)];
-
- int cb = (cb0_sample * w0 + cb1_sample * w1 + 2) >> 2;
- int cr = (cr0_sample * w0 + cr1_sample * w1 + 2) >> 2;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d0[0] = clamp(y_sample + rc);
- d0[1] = clamp(y_sample + gc);
- d0[2] = clamp(y_sample + bc);
- d0[3] = 255;
-
- d0 += 4;
- }
- }
-
- // YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB
- void jpeg_decoder::H2V2Convert()
- {
- int row = m_max_mcu_y_size - m_mcu_lines_left;
- uint8* d0 = m_pScan_line_0;
- uint8* d1 = m_pScan_line_1;
- uint8* y;
- uint8* c;
-
- if (row < 8)
- y = m_pSample_buf + row * 8;
- else
- y = m_pSample_buf + 64 * 2 + (row & 7) * 8;
-
- c = m_pSample_buf + 64 * 4 + (row >> 1) * 8;
-
- for (int i = m_max_mcus_per_row; i > 0; i--)
- {
- for (int l = 0; l < 2; l++)
- {
- for (int j = 0; j < 8; j += 2)
- {
- int cb = c[0];
- int cr = c[64];
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- int yy = y[j];
- d0[0] = clamp(yy + rc);
- d0[1] = clamp(yy + gc);
- d0[2] = clamp(yy + bc);
- d0[3] = 255;
-
- yy = y[j + 1];
- d0[4] = clamp(yy + rc);
- d0[5] = clamp(yy + gc);
- d0[6] = clamp(yy + bc);
- d0[7] = 255;
-
- yy = y[j + 8];
- d1[0] = clamp(yy + rc);
- d1[1] = clamp(yy + gc);
- d1[2] = clamp(yy + bc);
- d1[3] = 255;
-
- yy = y[j + 8 + 1];
- d1[4] = clamp(yy + rc);
- d1[5] = clamp(yy + gc);
- d1[6] = clamp(yy + bc);
- d1[7] = 255;
-
- d0 += 8;
- d1 += 8;
-
- c++;
- }
- y += 64;
- }
-
- y += 64 * 6 - 64 * 2;
- c += 64 * 6 - 8;
- }
- }
-
- uint32_t jpeg_decoder::H2V2ConvertFiltered()
- {
- const uint BLOCKS_PER_MCU = 6;
- int y = m_image_y_size - m_total_lines_left;
- int row = y & 15;
-
- const int half_image_y_size = (m_image_y_size >> 1) - 1;
-
- uint8* d0 = m_pScan_line_0;
-
- int c_y0 = (y - 1) >> 1;
- int c_y1 = JPGD_MIN(c_y0 + 1, half_image_y_size);
-
- const uint8_t* p_YSamples = m_pSample_buf;
- const uint8_t* p_C0Samples = m_pSample_buf;
- if ((c_y0 >= 0) && (((row & 15) == 0) || ((row & 15) == 15)) && (m_total_lines_left > 1))
- {
- assert(y > 0);
- assert(m_sample_buf_prev_valid);
-
- if ((row & 15) == 15)
- p_YSamples = m_pSample_buf_prev;
-
- p_C0Samples = m_pSample_buf_prev;
- }
-
- const int y_sample_base_ofs = ((row & 8) ? 128 : 0) + (row & 7) * 8;
- const int y0_base = (c_y0 & 7) * 8 + 256;
- const int y1_base = (c_y1 & 7) * 8 + 256;
-
- const int half_image_x_size = (m_image_x_size >> 1) - 1;
-
- static const uint8_t s_muls[2][2][4] =
- {
- { { 1, 3, 3, 9 }, { 3, 9, 1, 3 }, },
- { { 3, 1, 9, 3 }, { 9, 3, 3, 1 } }
- };
-
- if (((row & 15) >= 1) && ((row & 15) <= 14))
- {
- assert((row & 1) == 1);
- assert(((y + 1 - 1) >> 1) == c_y0);
-
- assert(p_YSamples == m_pSample_buf);
- assert(p_C0Samples == m_pSample_buf);
-
- uint8* d1 = m_pScan_line_1;
- const int y_sample_base_ofs1 = (((row + 1) & 8) ? 128 : 0) + ((row + 1) & 7) * 8;
-
- for (int x = 0; x < m_image_x_size; x++)
- {
- int k = (x >> 4) * BLOCKS_PER_MCU * 64 + ((x & 8) ? 64 : 0) + (x & 7);
- int y_sample0 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs)];
- int y_sample1 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs1)];
-
- int c_x0 = (x - 1) >> 1;
- int c_x1 = JPGD_MIN(c_x0 + 1, half_image_x_size);
- c_x0 = JPGD_MAX(c_x0, 0);
-
- int a = (c_x0 >> 3) * BLOCKS_PER_MCU * 64 + (c_x0 & 7);
- int cb00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base)];
- int cr00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base + 64)];
-
- int cb01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base)];
- int cr01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base + 64)];
-
- int b = (c_x1 >> 3) * BLOCKS_PER_MCU * 64 + (c_x1 & 7);
- int cb10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base)];
- int cr10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base + 64)];
-
- int cb11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base)];
- int cr11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base + 64)];
-
- {
- const uint8_t* pMuls = &s_muls[row & 1][x & 1][0];
- int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4;
- int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d0[0] = clamp(y_sample0 + rc);
- d0[1] = clamp(y_sample0 + gc);
- d0[2] = clamp(y_sample0 + bc);
- d0[3] = 255;
-
- d0 += 4;
- }
-
- {
- const uint8_t* pMuls = &s_muls[(row + 1) & 1][x & 1][0];
- int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4;
- int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d1[0] = clamp(y_sample1 + rc);
- d1[1] = clamp(y_sample1 + gc);
- d1[2] = clamp(y_sample1 + bc);
- d1[3] = 255;
-
- d1 += 4;
- }
-
- if (((x & 1) == 1) && (x < m_image_x_size - 1))
- {
- const int nx = x + 1;
- assert(c_x0 == (nx - 1) >> 1);
-
- k = (nx >> 4) * BLOCKS_PER_MCU * 64 + ((nx & 8) ? 64 : 0) + (nx & 7);
- y_sample0 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs)];
- y_sample1 = p_YSamples[check_sample_buf_ofs(k + y_sample_base_ofs1)];
-
- {
- const uint8_t* pMuls = &s_muls[row & 1][nx & 1][0];
- int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4;
- int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d0[0] = clamp(y_sample0 + rc);
- d0[1] = clamp(y_sample0 + gc);
- d0[2] = clamp(y_sample0 + bc);
- d0[3] = 255;
-
- d0 += 4;
- }
-
- {
- const uint8_t* pMuls = &s_muls[(row + 1) & 1][nx & 1][0];
- int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4;
- int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d1[0] = clamp(y_sample1 + rc);
- d1[1] = clamp(y_sample1 + gc);
- d1[2] = clamp(y_sample1 + bc);
- d1[3] = 255;
-
- d1 += 4;
- }
-
- ++x;
- }
- }
-
- return 2;
- }
- else
- {
- for (int x = 0; x < m_image_x_size; x++)
- {
- int y_sample = p_YSamples[check_sample_buf_ofs((x >> 4) * BLOCKS_PER_MCU * 64 + ((x & 8) ? 64 : 0) + (x & 7) + y_sample_base_ofs)];
-
- int c_x0 = (x - 1) >> 1;
- int c_x1 = JPGD_MIN(c_x0 + 1, half_image_x_size);
- c_x0 = JPGD_MAX(c_x0, 0);
-
- int a = (c_x0 >> 3) * BLOCKS_PER_MCU * 64 + (c_x0 & 7);
- int cb00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base)];
- int cr00_sample = p_C0Samples[check_sample_buf_ofs(a + y0_base + 64)];
-
- int cb01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base)];
- int cr01_sample = m_pSample_buf[check_sample_buf_ofs(a + y1_base + 64)];
-
- int b = (c_x1 >> 3) * BLOCKS_PER_MCU * 64 + (c_x1 & 7);
- int cb10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base)];
- int cr10_sample = p_C0Samples[check_sample_buf_ofs(b + y0_base + 64)];
-
- int cb11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base)];
- int cr11_sample = m_pSample_buf[check_sample_buf_ofs(b + y1_base + 64)];
-
- const uint8_t* pMuls = &s_muls[row & 1][x & 1][0];
- int cb = (cb00_sample * pMuls[0] + cb01_sample * pMuls[1] + cb10_sample * pMuls[2] + cb11_sample * pMuls[3] + 8) >> 4;
- int cr = (cr00_sample * pMuls[0] + cr01_sample * pMuls[1] + cr10_sample * pMuls[2] + cr11_sample * pMuls[3] + 8) >> 4;
-
- int rc = m_crr[cr];
- int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
- int bc = m_cbb[cb];
-
- d0[0] = clamp(y_sample + rc);
- d0[1] = clamp(y_sample + gc);
- d0[2] = clamp(y_sample + bc);
- d0[3] = 255;
-
- d0 += 4;
- }
-
- return 1;
- }
- }
-
- // Y (1 block per MCU) to 8-bit grayscale
- void jpeg_decoder::gray_convert()
- {
- int row = m_max_mcu_y_size - m_mcu_lines_left;
- uint8* d = m_pScan_line_0;
- uint8* s = m_pSample_buf + row * 8;
-
- for (int i = m_max_mcus_per_row; i > 0; i--)
- {
- *(uint*)d = *(uint*)s;
- *(uint*)(&d[4]) = *(uint*)(&s[4]);
-
- s += 64;
- d += 8;
- }
- }
-
- // Find end of image (EOI) marker, so we can return to the user the exact size of the input stream.
- void jpeg_decoder::find_eoi()
- {
- if (!m_progressive_flag)
- {
- // Attempt to read the EOI marker.
- //get_bits_no_markers(m_bits_left & 7);
-
- // Prime the bit buffer
- m_bits_left = 16;
- get_bits(16);
- get_bits(16);
-
- // The next marker _should_ be EOI
- process_markers();
- }
-
- m_total_bytes_read -= m_in_buf_left;
- }
-
- int jpeg_decoder::decode_next_mcu_row()
- {
- if (setjmp(m_jmp_state))
- return JPGD_FAILED;
-
- const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)) && (m_image_x_size >= 2) && (m_image_y_size >= 2);
- if (chroma_y_filtering)
- {
- std::swap(m_pSample_buf, m_pSample_buf_prev);
-
- m_sample_buf_prev_valid = true;
- }
-
- if (m_progressive_flag)
- load_next_row();
- else
- decode_next_row();
-
- // Find the EOI marker if that was the last row.
- if (m_total_lines_left <= m_max_mcu_y_size)
- find_eoi();
-
- m_mcu_lines_left = m_max_mcu_y_size;
- return 0;
- }
-
- int jpeg_decoder::decode(const void** pScan_line, uint* pScan_line_len)
- {
- if ((m_error_code) || (!m_ready_flag))
- return JPGD_FAILED;
-
- if (m_total_lines_left == 0)
- return JPGD_DONE;
-
- const bool chroma_y_filtering = (m_flags & cFlagLinearChromaFiltering) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2)) && (m_image_x_size >= 2) && (m_image_y_size >= 2);
-
- bool get_another_mcu_row = false;
- bool got_mcu_early = false;
- if (chroma_y_filtering)
- {
- if (m_total_lines_left == m_image_y_size)
- get_another_mcu_row = true;
- else if ((m_mcu_lines_left == 1) && (m_total_lines_left > 1))
- {
- get_another_mcu_row = true;
- got_mcu_early = true;
- }
- }
- else
- {
- get_another_mcu_row = (m_mcu_lines_left == 0);
- }
-
- if (get_another_mcu_row)
- {
- int status = decode_next_mcu_row();
- if (status != 0)
- return status;
- }
-
- switch (m_scan_type)
- {
- case JPGD_YH2V2:
- {
- if ((m_flags & cFlagLinearChromaFiltering) && (m_image_x_size >= 2) && (m_image_y_size >= 2))
- {
- if (m_num_buffered_scanlines == 1)
- {
- *pScan_line = m_pScan_line_1;
- }
- else if (m_num_buffered_scanlines == 0)
- {
- m_num_buffered_scanlines = H2V2ConvertFiltered();
- *pScan_line = m_pScan_line_0;
- }
-
- m_num_buffered_scanlines--;
- }
- else
- {
- if ((m_mcu_lines_left & 1) == 0)
- {
- H2V2Convert();
- *pScan_line = m_pScan_line_0;
- }
- else
- *pScan_line = m_pScan_line_1;
- }
-
- break;
- }
- case JPGD_YH2V1:
- {
- if ((m_flags & cFlagLinearChromaFiltering) && (m_image_x_size >= 2) && (m_image_y_size >= 2))
- H2V1ConvertFiltered();
- else
- H2V1Convert();
- *pScan_line = m_pScan_line_0;
- break;
- }
- case JPGD_YH1V2:
- {
- if (chroma_y_filtering)
- {
- H1V2ConvertFiltered();
- *pScan_line = m_pScan_line_0;
- }
- else
- {
- if ((m_mcu_lines_left & 1) == 0)
- {
- H1V2Convert();
- *pScan_line = m_pScan_line_0;
- }
- else
- *pScan_line = m_pScan_line_1;
- }
-
- break;
- }
- case JPGD_YH1V1:
- {
- H1V1Convert();
- *pScan_line = m_pScan_line_0;
- break;
- }
- case JPGD_GRAYSCALE:
- {
- gray_convert();
- *pScan_line = m_pScan_line_0;
-
- break;
- }
- }
-
- *pScan_line_len = m_real_dest_bytes_per_scan_line;
-
- if (!got_mcu_early)
- {
- m_mcu_lines_left--;
- }
-
- m_total_lines_left--;
-
- return JPGD_SUCCESS;
- }
-
- // Creates the tables needed for efficient Huffman decoding.
- void jpeg_decoder::make_huff_table(int index, huff_tables* pH)
- {
- int p, i, l, si;
- uint8 huffsize[258];
- uint huffcode[258];
- uint code;
- uint subtree;
- int code_size;
- int lastp;
- int nextfreeentry;
- int currententry;
-
- pH->ac_table = m_huff_ac[index] != 0;
-
- p = 0;
-
- for (l = 1; l <= 16; l++)
- {
- for (i = 1; i <= m_huff_num[index][l]; i++)
- {
- if (p >= 257)
- stop_decoding(JPGD_DECODE_ERROR);
- huffsize[p++] = static_cast<uint8>(l);
- }
- }
-
- assert(p < 258);
- huffsize[p] = 0;
-
- lastp = p;
-
- code = 0;
- si = huffsize[0];
- p = 0;
-
- while (huffsize[p])
- {
- while (huffsize[p] == si)
- {
- if (p >= 257)
- stop_decoding(JPGD_DECODE_ERROR);
- huffcode[p++] = code;
- code++;
- }
-
- code <<= 1;
- si++;
- }
-
- memset(pH->look_up, 0, sizeof(pH->look_up));
- memset(pH->look_up2, 0, sizeof(pH->look_up2));
- memset(pH->tree, 0, sizeof(pH->tree));
- memset(pH->code_size, 0, sizeof(pH->code_size));
-
- nextfreeentry = -1;
-
- p = 0;
-
- while (p < lastp)
- {
- i = m_huff_val[index][p];
-
- code = huffcode[p];
- code_size = huffsize[p];
-
- assert(i < JPGD_HUFF_CODE_SIZE_MAX_LENGTH);
- pH->code_size[i] = static_cast<uint8>(code_size);
-
- if (code_size <= 8)
- {
- code <<= (8 - code_size);
-
- for (l = 1 << (8 - code_size); l > 0; l--)
- {
- if (code >= 256)
- stop_decoding(JPGD_DECODE_ERROR);
-
- pH->look_up[code] = i;
-
- bool has_extrabits = false;
- int extra_bits = 0;
- int num_extra_bits = i & 15;
-
- int bits_to_fetch = code_size;
- if (num_extra_bits)
- {
- int total_codesize = code_size + num_extra_bits;
- if (total_codesize <= 8)
- {
- has_extrabits = true;
- extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize));
-
- if (extra_bits > 0x7FFF)
- stop_decoding(JPGD_DECODE_ERROR);
-
- bits_to_fetch += num_extra_bits;
- }
- }
-
- if (!has_extrabits)
- pH->look_up2[code] = i | (bits_to_fetch << 8);
- else
- pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8);
-
- code++;
- }
- }
- else
- {
- subtree = (code >> (code_size - 8)) & 0xFF;
-
- currententry = pH->look_up[subtree];
-
- if (currententry == 0)
- {
- pH->look_up[subtree] = currententry = nextfreeentry;
- pH->look_up2[subtree] = currententry = nextfreeentry;
-
- nextfreeentry -= 2;
- }
-
- code <<= (16 - (code_size - 8));
-
- for (l = code_size; l > 9; l--)
- {
- if ((code & 0x8000) == 0)
- currententry--;
-
- unsigned int idx = -currententry - 1;
-
- if (idx >= JPGD_HUFF_TREE_MAX_LENGTH)
- stop_decoding(JPGD_DECODE_ERROR);
-
- if (pH->tree[idx] == 0)
- {
- pH->tree[idx] = nextfreeentry;
-
- currententry = nextfreeentry;
-
- nextfreeentry -= 2;
- }
- else
- {
- currententry = pH->tree[idx];
- }
-
- code <<= 1;
- }
-
- if ((code & 0x8000) == 0)
- currententry--;
-
- if ((-currententry - 1) >= JPGD_HUFF_TREE_MAX_LENGTH)
- stop_decoding(JPGD_DECODE_ERROR);
-
- pH->tree[-currententry - 1] = i;
- }
-
- p++;
- }
- }
-
- // Verifies the quantization tables needed for this scan are available.
- void jpeg_decoder::check_quant_tables()
- {
- for (int i = 0; i < m_comps_in_scan; i++)
- if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr)
- stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
- }
-
- // Verifies that all the Huffman tables needed for this scan are available.
- void jpeg_decoder::check_huff_tables()
- {
- for (int i = 0; i < m_comps_in_scan; i++)
- {
- if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr))
- stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
-
- if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr))
- stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
- }
-
- for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++)
- if (m_huff_num[i])
- {
- if (!m_pHuff_tabs[i])
- m_pHuff_tabs[i] = (huff_tables*)alloc(sizeof(huff_tables));
-
- make_huff_table(i, m_pHuff_tabs[i]);
- }
- }
-
- // Determines the component order inside each MCU.
- // Also calcs how many MCU's are on each row, etc.
- bool jpeg_decoder::calc_mcu_block_order()
- {
- int component_num, component_id;
- int max_h_samp = 0, max_v_samp = 0;
-
- for (component_id = 0; component_id < m_comps_in_frame; component_id++)
- {
- if (m_comp_h_samp[component_id] > max_h_samp)
- max_h_samp = m_comp_h_samp[component_id];
-
- if (m_comp_v_samp[component_id] > max_v_samp)
- max_v_samp = m_comp_v_samp[component_id];
- }
-
- for (component_id = 0; component_id < m_comps_in_frame; component_id++)
- {
- m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8;
- m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8;
- }
-
- if (m_comps_in_scan == 1)
- {
- m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]];
- m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]];
- }
- else
- {
- m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp;
- m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp;
- }
-
- if (m_comps_in_scan == 1)
- {
- m_mcu_org[0] = m_comp_list[0];
-
- m_blocks_per_mcu = 1;
- }
- else
- {
- m_blocks_per_mcu = 0;
-
- for (component_num = 0; component_num < m_comps_in_scan; component_num++)
- {
- int num_blocks;
-
- component_id = m_comp_list[component_num];
-
- num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id];
-
- while (num_blocks--)
- m_mcu_org[m_blocks_per_mcu++] = component_id;
- }
- }
-
- if (m_blocks_per_mcu > m_max_blocks_per_mcu)
- return false;
-
- for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
- {
- int comp_id = m_mcu_org[mcu_block];
- if (comp_id >= JPGD_MAX_QUANT_TABLES)
- return false;
- }
-
- return true;
- }
-
- // Starts a new scan.
- int jpeg_decoder::init_scan()
- {
- if (!locate_sos_marker())
- return JPGD_FALSE;
-
- if (!calc_mcu_block_order())
- return JPGD_FALSE;
-
- check_huff_tables();
-
- check_quant_tables();
-
- memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint));
-
- m_eob_run = 0;
-
- if (m_restart_interval)
- {
- m_restarts_left = m_restart_interval;
- m_next_restart_num = 0;
- }
-
- fix_in_buffer();
-
- return JPGD_TRUE;
- }
-
- // Starts a frame. Determines if the number of components or sampling factors
- // are supported.
- void jpeg_decoder::init_frame()
- {
- int i;
-
- if (m_comps_in_frame == 1)
- {
- if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1))
- stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
-
- m_scan_type = JPGD_GRAYSCALE;
- m_max_blocks_per_mcu = 1;
- m_max_mcu_x_size = 8;
- m_max_mcu_y_size = 8;
- }
- else if (m_comps_in_frame == 3)
- {
- if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) ||
- ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)))
- stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
-
- if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
- {
- m_scan_type = JPGD_YH1V1;
-
- m_max_blocks_per_mcu = 3;
- m_max_mcu_x_size = 8;
- m_max_mcu_y_size = 8;
- }
- else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
- {
- m_scan_type = JPGD_YH2V1;
- m_max_blocks_per_mcu = 4;
- m_max_mcu_x_size = 16;
- m_max_mcu_y_size = 8;
- }
- else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2))
- {
- m_scan_type = JPGD_YH1V2;
- m_max_blocks_per_mcu = 4;
- m_max_mcu_x_size = 8;
- m_max_mcu_y_size = 16;
- }
- else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
- {
- m_scan_type = JPGD_YH2V2;
- m_max_blocks_per_mcu = 6;
- m_max_mcu_x_size = 16;
- m_max_mcu_y_size = 16;
- }
- else
- stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
- }
- else
- stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
-
- m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
- m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
-
- // These values are for the *destination* pixels: after conversion.
- if (m_scan_type == JPGD_GRAYSCALE)
- m_dest_bytes_per_pixel = 1;
- else
- m_dest_bytes_per_pixel = 4;
-
- m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel;
-
- m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel);
-
- // Initialize two scan line buffers.
- m_pScan_line_0 = (uint8*)alloc(m_dest_bytes_per_scan_line, true);
- if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2))
- m_pScan_line_1 = (uint8*)alloc(m_dest_bytes_per_scan_line, true);
-
- m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
-
- // Should never happen
- if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW)
- stop_decoding(JPGD_DECODE_ERROR);
-
- // Allocate the coefficient buffer, enough for one MCU
- m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
-
- for (i = 0; i < m_max_blocks_per_mcu; i++)
- m_mcu_block_max_zag[i] = 64;
-
- m_pSample_buf = (uint8*)alloc(m_max_blocks_per_row * 64);
- m_pSample_buf_prev = (uint8*)alloc(m_max_blocks_per_row * 64);
-
- m_total_lines_left = m_image_y_size;
-
- m_mcu_lines_left = 0;
-
- create_look_ups();
- }
-
- // The coeff_buf series of methods originally stored the coefficients
- // into a "virtual" file which was located in EMS, XMS, or a disk file. A cache
- // was used to make this process more efficient. Now, we can store the entire
- // thing in RAM.
- jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y)
- {
- coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf));
-
- cb->block_num_x = block_num_x;
- cb->block_num_y = block_num_y;
- cb->block_len_x = block_len_x;
- cb->block_len_y = block_len_y;
- cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t);
- cb->pData = (uint8*)alloc(cb->block_size * block_num_x * block_num_y, true);
- return cb;
- }
-
- inline jpgd_block_t* jpeg_decoder::coeff_buf_getp(coeff_buf* cb, int block_x, int block_y)
- {
- if ((block_x >= cb->block_num_x) || (block_y >= cb->block_num_y))
- stop_decoding(JPGD_DECODE_ERROR);
-
- return (jpgd_block_t*)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x));
- }
-
- // The following methods decode the various types of m_blocks encountered
- // in progressively encoded images.
- void jpeg_decoder::decode_block_dc_first(jpeg_decoder* pD, int component_id, int block_x, int block_y)
- {
- int s, r;
- jpgd_block_t* p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
-
- if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0)
- {
- if (s >= 16)
- pD->stop_decoding(JPGD_DECODE_ERROR);
-
- r = pD->get_bits_no_markers(s);
- s = JPGD_HUFF_EXTEND(r, s);
- }
-
- pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
-
- p[0] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
- }
-
- void jpeg_decoder::decode_block_dc_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y)
- {
- if (pD->get_bits_no_markers(1))
- {
- jpgd_block_t* p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
-
- p[0] |= (1 << pD->m_successive_low);
- }
- }
-
- void jpeg_decoder::decode_block_ac_first(jpeg_decoder* pD, int component_id, int block_x, int block_y)
- {
- int k, s, r;
-
- if (pD->m_eob_run)
- {
- pD->m_eob_run--;
- return;
- }
-
- jpgd_block_t* p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
-
- for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++)
- {
- unsigned int idx = pD->m_comp_ac_tab[component_id];
- if (idx >= JPGD_MAX_HUFF_TABLES)
- pD->stop_decoding(JPGD_DECODE_ERROR);
-
- s = pD->huff_decode(pD->m_pHuff_tabs[idx]);
-
- r = s >> 4;
- s &= 15;
-
- if (s)
- {
- if ((k += r) > 63)
- pD->stop_decoding(JPGD_DECODE_ERROR);
-
- r = pD->get_bits_no_markers(s);
- s = JPGD_HUFF_EXTEND(r, s);
-
- p[g_ZAG[k]] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
- }
- else
- {
- if (r == 15)
- {
- if ((k += 15) > 63)
- pD->stop_decoding(JPGD_DECODE_ERROR);
- }
- else
- {
- pD->m_eob_run = 1 << r;
-
- if (r)
- pD->m_eob_run += pD->get_bits_no_markers(r);
-
- pD->m_eob_run--;
-
- break;
- }
- }
- }
- }
-
- void jpeg_decoder::decode_block_ac_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y)
- {
- int s, k, r;
-
- int p1 = 1 << pD->m_successive_low;
-
- //int m1 = (-1) << pD->m_successive_low;
- int m1 = static_cast<int>((UINT32_MAX << pD->m_successive_low));
-
- jpgd_block_t* p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
- if (pD->m_spectral_end > 63)
- pD->stop_decoding(JPGD_DECODE_ERROR);
-
- k = pD->m_spectral_start;
-
- if (pD->m_eob_run == 0)
- {
- for (; k <= pD->m_spectral_end; k++)
- {
- unsigned int idx = pD->m_comp_ac_tab[component_id];
- if (idx >= JPGD_MAX_HUFF_TABLES)
- pD->stop_decoding(JPGD_DECODE_ERROR);
-
- s = pD->huff_decode(pD->m_pHuff_tabs[idx]);
-
- r = s >> 4;
- s &= 15;
-
- if (s)
- {
- if (s != 1)
- pD->stop_decoding(JPGD_DECODE_ERROR);
-
- if (pD->get_bits_no_markers(1))
- s = p1;
- else
- s = m1;
- }
- else
- {
- if (r != 15)
- {
- pD->m_eob_run = 1 << r;
-
- if (r)
- pD->m_eob_run += pD->get_bits_no_markers(r);
-
- break;
- }
- }
-
- do
- {
- jpgd_block_t* this_coef = p + g_ZAG[k & 63];
-
- if (*this_coef != 0)
- {
- if (pD->get_bits_no_markers(1))
- {
- if ((*this_coef & p1) == 0)
- {
- if (*this_coef >= 0)
- *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
- else
- *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
- }
- }
- }
- else
- {
- if (--r < 0)
- break;
- }
-
- k++;
-
- } while (k <= pD->m_spectral_end);
-
- if ((s) && (k < 64))
- {
- p[g_ZAG[k]] = static_cast<jpgd_block_t>(s);
- }
- }
- }
-
- if (pD->m_eob_run > 0)
- {
- for (; k <= pD->m_spectral_end; k++)
- {
- jpgd_block_t* this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis
-
- if (*this_coef != 0)
- {
- if (pD->get_bits_no_markers(1))
- {
- if ((*this_coef & p1) == 0)
- {
- if (*this_coef >= 0)
- *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
- else
- *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
- }
- }
- }
- }
-
- pD->m_eob_run--;
- }
- }
-
- // Decode a scan in a progressively encoded image.
- void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
- {
- int mcu_row, mcu_col, mcu_block;
- int block_x_mcu[JPGD_MAX_COMPONENTS], block_y_mcu[JPGD_MAX_COMPONENTS];
-
- memset(block_y_mcu, 0, sizeof(block_y_mcu));
-
- for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++)
- {
- int component_num, component_id;
-
- memset(block_x_mcu, 0, sizeof(block_x_mcu));
-
- for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
- {
- int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
-
- if ((m_restart_interval) && (m_restarts_left == 0))
- process_restart();
-
- for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
- {
- component_id = m_mcu_org[mcu_block];
-
- decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, block_y_mcu[component_id] + block_y_mcu_ofs);
-
- if (m_comps_in_scan == 1)
- block_x_mcu[component_id]++;
- else
- {
- if (++block_x_mcu_ofs == m_comp_h_samp[component_id])
- {
- block_x_mcu_ofs = 0;
-
- if (++block_y_mcu_ofs == m_comp_v_samp[component_id])
- {
- block_y_mcu_ofs = 0;
- block_x_mcu[component_id] += m_comp_h_samp[component_id];
- }
- }
- }
- }
-
- m_restarts_left--;
- }
-
- if (m_comps_in_scan == 1)
- block_y_mcu[m_comp_list[0]]++;
- else
- {
- for (component_num = 0; component_num < m_comps_in_scan; component_num++)
- {
- component_id = m_comp_list[component_num];
- block_y_mcu[component_id] += m_comp_v_samp[component_id];
- }
- }
- }
- }
-
- // Decode a progressively encoded image.
- void jpeg_decoder::init_progressive()
- {
- int i;
-
- if (m_comps_in_frame == 4)
- stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
-
- // Allocate the coefficient buffers.
- for (i = 0; i < m_comps_in_frame; i++)
- {
- m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1);
- m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8);
- }
-
- // See https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
- uint32_t total_scans = 0;
- const uint32_t MAX_SCANS_TO_PROCESS = 1000;
-
- for (; ; )
- {
- int dc_only_scan, refinement_scan;
- pDecode_block_func decode_block_func;
-
- if (!init_scan())
- break;
-
- dc_only_scan = (m_spectral_start == 0);
- refinement_scan = (m_successive_high != 0);
-
- if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63))
- stop_decoding(JPGD_BAD_SOS_SPECTRAL);
-
- if (dc_only_scan)
- {
- if (m_spectral_end)
- stop_decoding(JPGD_BAD_SOS_SPECTRAL);
- }
- else if (m_comps_in_scan != 1) /* AC scans can only contain one component */
- stop_decoding(JPGD_BAD_SOS_SPECTRAL);
-
- if ((refinement_scan) && (m_successive_low != m_successive_high - 1))
- stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
-
- if (dc_only_scan)
- {
- if (refinement_scan)
- decode_block_func = decode_block_dc_refine;
- else
- decode_block_func = decode_block_dc_first;
- }
- else
- {
- if (refinement_scan)
- decode_block_func = decode_block_ac_refine;
- else
- decode_block_func = decode_block_ac_first;
- }
-
- decode_scan(decode_block_func);
-
- m_bits_left = 16;
- get_bits(16);
- get_bits(16);
-
- total_scans++;
- if (total_scans > MAX_SCANS_TO_PROCESS)
- stop_decoding(JPGD_TOO_MANY_SCANS);
- }
-
- m_comps_in_scan = m_comps_in_frame;
-
- for (i = 0; i < m_comps_in_frame; i++)
- m_comp_list[i] = i;
-
- if (!calc_mcu_block_order())
- stop_decoding(JPGD_DECODE_ERROR);
- }
-
- void jpeg_decoder::init_sequential()
- {
- if (!init_scan())
- stop_decoding(JPGD_UNEXPECTED_MARKER);
- }
-
- void jpeg_decoder::decode_start()
- {
- init_frame();
-
- if (m_progressive_flag)
- init_progressive();
- else
- init_sequential();
- }
-
- void jpeg_decoder::decode_init(jpeg_decoder_stream* pStream, uint32_t flags)
- {
- init(pStream, flags);
- locate_sof_marker();
- }
-
- jpeg_decoder::jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags)
- {
- if (setjmp(m_jmp_state))
- return;
- decode_init(pStream, flags);
- }
-
- int jpeg_decoder::begin_decoding()
- {
- if (m_ready_flag)
- return JPGD_SUCCESS;
-
- if (m_error_code)
- return JPGD_FAILED;
-
- if (setjmp(m_jmp_state))
- return JPGD_FAILED;
-
- decode_start();
-
- m_ready_flag = true;
-
- return JPGD_SUCCESS;
- }
-
- jpeg_decoder::~jpeg_decoder()
- {
- free_all_blocks();
- }
-
- jpeg_decoder_file_stream::jpeg_decoder_file_stream()
- {
- m_pFile = nullptr;
- m_eof_flag = false;
- m_error_flag = false;
- }
-
- void jpeg_decoder_file_stream::close()
- {
- if (m_pFile)
- {
- fclose(m_pFile);
- m_pFile = nullptr;
- }
-
- m_eof_flag = false;
- m_error_flag = false;
- }
-
- jpeg_decoder_file_stream::~jpeg_decoder_file_stream()
- {
- close();
- }
-
- bool jpeg_decoder_file_stream::open(const char* Pfilename)
- {
- close();
-
- m_eof_flag = false;
- m_error_flag = false;
-
-#if defined(_MSC_VER)
- m_pFile = nullptr;
- fopen_s(&m_pFile, Pfilename, "rb");
-#else
- m_pFile = fopen(Pfilename, "rb");
-#endif
- return m_pFile != nullptr;
- }
-
- int jpeg_decoder_file_stream::read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag)
- {
- if (!m_pFile)
- return -1;
-
- if (m_eof_flag)
- {
- *pEOF_flag = true;
- return 0;
- }
-
- if (m_error_flag)
- return -1;
-
- int bytes_read = static_cast<int>(fread(pBuf, 1, max_bytes_to_read, m_pFile));
- if (bytes_read < max_bytes_to_read)
- {
- if (ferror(m_pFile))
- {
- m_error_flag = true;
- return -1;
- }
-
- m_eof_flag = true;
- *pEOF_flag = true;
- }
-
- return bytes_read;
- }
-
- bool jpeg_decoder_mem_stream::open(const uint8* pSrc_data, uint size)
- {
- close();
- m_pSrc_data = pSrc_data;
- m_ofs = 0;
- m_size = size;
- return true;
- }
-
- int jpeg_decoder_mem_stream::read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag)
- {
- *pEOF_flag = false;
-
- if (!m_pSrc_data)
- return -1;
-
- uint bytes_remaining = m_size - m_ofs;
- if ((uint)max_bytes_to_read > bytes_remaining)
- {
- max_bytes_to_read = bytes_remaining;
- *pEOF_flag = true;
- }
-
- memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read);
- m_ofs += max_bytes_to_read;
-
- return max_bytes_to_read;
- }
-
- unsigned char* decompress_jpeg_image_from_stream(jpeg_decoder_stream* pStream, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags)
- {
- if (!actual_comps)
- return nullptr;
- *actual_comps = 0;
-
- if ((!pStream) || (!width) || (!height) || (!req_comps))
- return nullptr;
-
- if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4))
- return nullptr;
-
- jpeg_decoder decoder(pStream, flags);
- if (decoder.get_error_code() != JPGD_SUCCESS)
- return nullptr;
-
- const int image_width = decoder.get_width(), image_height = decoder.get_height();
- *width = image_width;
- *height = image_height;
- *actual_comps = decoder.get_num_components();
-
- if (decoder.begin_decoding() != JPGD_SUCCESS)
- return nullptr;
-
- const int dst_bpl = image_width * req_comps;
-
- uint8* pImage_data = (uint8*)jpgd_malloc(dst_bpl * image_height);
- if (!pImage_data)
- return nullptr;
-
- for (int y = 0; y < image_height; y++)
- {
- const uint8* pScan_line;
- uint scan_line_len;
- if (decoder.decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS)
- {
- jpgd_free(pImage_data);
- return nullptr;
- }
-
- uint8* pDst = pImage_data + y * dst_bpl;
-
- if (((req_comps == 1) && (decoder.get_num_components() == 1)) || ((req_comps == 4) && (decoder.get_num_components() == 3)))
- memcpy(pDst, pScan_line, dst_bpl);
- else if (decoder.get_num_components() == 1)
- {
- if (req_comps == 3)
- {
- for (int x = 0; x < image_width; x++)
- {
- uint8 luma = pScan_line[x];
- pDst[0] = luma;
- pDst[1] = luma;
- pDst[2] = luma;
- pDst += 3;
- }
- }
- else
- {
- for (int x = 0; x < image_width; x++)
- {
- uint8 luma = pScan_line[x];
- pDst[0] = luma;
- pDst[1] = luma;
- pDst[2] = luma;
- pDst[3] = 255;
- pDst += 4;
- }
- }
- }
- else if (decoder.get_num_components() == 3)
- {
- if (req_comps == 1)
- {
- const int YR = 19595, YG = 38470, YB = 7471;
- for (int x = 0; x < image_width; x++)
- {
- int r = pScan_line[x * 4 + 0];
- int g = pScan_line[x * 4 + 1];
- int b = pScan_line[x * 4 + 2];
- *pDst++ = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
- }
- }
- else
- {
- for (int x = 0; x < image_width; x++)
- {
- pDst[0] = pScan_line[x * 4 + 0];
- pDst[1] = pScan_line[x * 4 + 1];
- pDst[2] = pScan_line[x * 4 + 2];
- pDst += 3;
- }
- }
- }
- }
-
- return pImage_data;
- }
-
- unsigned char* decompress_jpeg_image_from_memory(const unsigned char* pSrc_data, int src_data_size, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags)
- {
- jpgd::jpeg_decoder_mem_stream mem_stream(pSrc_data, src_data_size);
- return decompress_jpeg_image_from_stream(&mem_stream, width, height, actual_comps, req_comps, flags);
- }
-
- unsigned char* decompress_jpeg_image_from_file(const char* pSrc_filename, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags)
- {
- jpgd::jpeg_decoder_file_stream file_stream;
- if (!file_stream.open(pSrc_filename))
- return nullptr;
- return decompress_jpeg_image_from_stream(&file_stream, width, height, actual_comps, req_comps, flags);
- }
-
-} // namespace jpgd
diff --git a/thirdparty/basis_universal/encoder/jpgd.h b/thirdparty/basis_universal/encoder/jpgd.h
deleted file mode 100644
index 86a7814cae..0000000000
--- a/thirdparty/basis_universal/encoder/jpgd.h
+++ /dev/null
@@ -1,347 +0,0 @@
-// jpgd.h - C++ class for JPEG decompression.
-// Public domain, Rich Geldreich <richgel99@gmail.com>
-#ifndef JPEG_DECODER_H
-#define JPEG_DECODER_H
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <setjmp.h>
-#include <assert.h>
-#include <stdint.h>
-
-#ifdef _MSC_VER
-#define JPGD_NORETURN __declspec(noreturn)
-#elif defined(__GNUC__)
-#define JPGD_NORETURN __attribute__ ((noreturn))
-#else
-#define JPGD_NORETURN
-#endif
-
-#define JPGD_HUFF_TREE_MAX_LENGTH 512
-#define JPGD_HUFF_CODE_SIZE_MAX_LENGTH 256
-
-namespace jpgd
-{
- typedef unsigned char uint8;
- typedef signed short int16;
- typedef unsigned short uint16;
- typedef unsigned int uint;
- typedef signed int int32;
-
- // Loads a JPEG image from a memory buffer or a file.
- // req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA).
- // On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB).
- // Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly.
- // Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp.
- unsigned char* decompress_jpeg_image_from_memory(const unsigned char* pSrc_data, int src_data_size, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0);
- unsigned char* decompress_jpeg_image_from_file(const char* pSrc_filename, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0);
-
- // Success/failure error codes.
- enum jpgd_status
- {
- JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
- JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
- JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
- JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
- JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
- JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
- JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
- JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER,
- JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM, JPGD_TOO_MANY_SCANS
- };
-
- // Input stream interface.
- // Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
- // The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
- // It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
- // Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
- class jpeg_decoder_stream
- {
- public:
- jpeg_decoder_stream() { }
- virtual ~jpeg_decoder_stream() { }
-
- // The read() method is called when the internal input buffer is empty.
- // Parameters:
- // pBuf - input buffer
- // max_bytes_to_read - maximum bytes that can be written to pBuf
- // pEOF_flag - set this to true if at end of stream (no more bytes remaining)
- // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
- // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
- virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag) = 0;
- };
-
- // stdio FILE stream class.
- class jpeg_decoder_file_stream : public jpeg_decoder_stream
- {
- jpeg_decoder_file_stream(const jpeg_decoder_file_stream&);
- jpeg_decoder_file_stream& operator =(const jpeg_decoder_file_stream&);
-
- FILE* m_pFile;
- bool m_eof_flag, m_error_flag;
-
- public:
- jpeg_decoder_file_stream();
- virtual ~jpeg_decoder_file_stream();
-
- bool open(const char* Pfilename);
- void close();
-
- virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag);
- };
-
- // Memory stream class.
- class jpeg_decoder_mem_stream : public jpeg_decoder_stream
- {
- const uint8* m_pSrc_data;
- uint m_ofs, m_size;
-
- public:
- jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { }
- jpeg_decoder_mem_stream(const uint8* pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { }
-
- virtual ~jpeg_decoder_mem_stream() { }
-
- bool open(const uint8* pSrc_data, uint size);
- void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; }
-
- virtual int read(uint8* pBuf, int max_bytes_to_read, bool* pEOF_flag);
- };
-
- // Loads JPEG file from a jpeg_decoder_stream.
- unsigned char* decompress_jpeg_image_from_stream(jpeg_decoder_stream* pStream, int* width, int* height, int* actual_comps, int req_comps, uint32_t flags = 0);
-
- enum
- {
- JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
- JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 16384, JPGD_MAX_HEIGHT = 32768, JPGD_MAX_WIDTH = 32768
- };
-
- typedef int16 jpgd_quant_t;
- typedef int16 jpgd_block_t;
-
- class jpeg_decoder
- {
- public:
- enum
- {
- cFlagLinearChromaFiltering = 1
- };
-
- // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
- // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
- jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags = cFlagLinearChromaFiltering);
-
- ~jpeg_decoder();
-
- // Call this method after constructing the object to begin decompression.
- // If JPGD_SUCCESS is returned you may then call decode() on each scanline.
-
- int begin_decoding();
-
- // Returns the next scan line.
- // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
- // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
- // Returns JPGD_SUCCESS if a scan line has been returned.
- // Returns JPGD_DONE if all scan lines have been returned.
- // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
- int decode(const void** pScan_line, uint* pScan_line_len);
-
- inline jpgd_status get_error_code() const { return m_error_code; }
-
- inline int get_width() const { return m_image_x_size; }
- inline int get_height() const { return m_image_y_size; }
-
- inline int get_num_components() const { return m_comps_in_frame; }
-
- inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
- inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
-
- // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
- inline int get_total_bytes_read() const { return m_total_bytes_read; }
-
- private:
- jpeg_decoder(const jpeg_decoder&);
- jpeg_decoder& operator =(const jpeg_decoder&);
-
- typedef void (*pDecode_block_func)(jpeg_decoder*, int, int, int);
-
- struct huff_tables
- {
- bool ac_table;
- uint look_up[256];
- uint look_up2[256];
- uint8 code_size[JPGD_HUFF_CODE_SIZE_MAX_LENGTH];
- uint tree[JPGD_HUFF_TREE_MAX_LENGTH];
- };
-
- struct coeff_buf
- {
- uint8* pData;
- int block_num_x, block_num_y;
- int block_len_x, block_len_y;
- int block_size;
- };
-
- struct mem_block
- {
- mem_block* m_pNext;
- size_t m_used_count;
- size_t m_size;
- char m_data[1];
- };
-
- jmp_buf m_jmp_state;
- uint32_t m_flags;
- mem_block* m_pMem_blocks;
- int m_image_x_size;
- int m_image_y_size;
- jpeg_decoder_stream* m_pStream;
-
- int m_progressive_flag;
-
- uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES];
- uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
- uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
- jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
- int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
- int m_comps_in_frame; // # of components in frame
- int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
- int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
- int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
- int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
- int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
- int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
- int m_comps_in_scan; // # of components in scan
- int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
- int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
- int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
- int m_spectral_start; // spectral selection start
- int m_spectral_end; // spectral selection end
- int m_successive_low; // successive approximation low
- int m_successive_high; // successive approximation high
- int m_max_mcu_x_size; // MCU's max. X size in pixels
- int m_max_mcu_y_size; // MCU's max. Y size in pixels
- int m_blocks_per_mcu;
- int m_max_blocks_per_row;
- int m_mcus_per_row, m_mcus_per_col;
- int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
- int m_total_lines_left; // total # lines left in image
- int m_mcu_lines_left; // total # lines left in this MCU
- int m_num_buffered_scanlines;
- int m_real_dest_bytes_per_scan_line;
- int m_dest_bytes_per_scan_line; // rounded up
- int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
- huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
- coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
- coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
- int m_eob_run;
- int m_block_y_mcu[JPGD_MAX_COMPONENTS];
- uint8* m_pIn_buf_ofs;
- int m_in_buf_left;
- int m_tem_flag;
-
- uint8 m_in_buf_pad_start[64];
- uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128];
- uint8 m_in_buf_pad_end[64];
-
- int m_bits_left;
- uint m_bit_buf;
- int m_restart_interval;
- int m_restarts_left;
- int m_next_restart_num;
- int m_max_mcus_per_row;
- int m_max_blocks_per_mcu;
-
- int m_max_mcus_per_col;
- uint m_last_dc_val[JPGD_MAX_COMPONENTS];
- jpgd_block_t* m_pMCU_coefficients;
- int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
- uint8* m_pSample_buf;
- uint8* m_pSample_buf_prev;
- int m_crr[256];
- int m_cbb[256];
- int m_crg[256];
- int m_cbg[256];
- uint8* m_pScan_line_0;
- uint8* m_pScan_line_1;
- jpgd_status m_error_code;
- int m_total_bytes_read;
-
- bool m_ready_flag;
- bool m_eof_flag;
- bool m_sample_buf_prev_valid;
-
- inline int check_sample_buf_ofs(int ofs) const { assert(ofs >= 0); assert(ofs < m_max_blocks_per_row * 64); return ofs; }
- void free_all_blocks();
- JPGD_NORETURN void stop_decoding(jpgd_status status);
- void* alloc(size_t n, bool zero = false);
- void word_clear(void* p, uint16 c, uint n);
- void prep_in_buffer();
- void read_dht_marker();
- void read_dqt_marker();
- void read_sof_marker();
- void skip_variable_marker();
- void read_dri_marker();
- void read_sos_marker();
- int next_marker();
- int process_markers();
- void locate_soi_marker();
- void locate_sof_marker();
- int locate_sos_marker();
- void init(jpeg_decoder_stream* pStream, uint32_t flags);
- void create_look_ups();
- void fix_in_buffer();
- void transform_mcu(int mcu_row);
- coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
- inline jpgd_block_t* coeff_buf_getp(coeff_buf* cb, int block_x, int block_y);
- void load_next_row();
- void decode_next_row();
- void make_huff_table(int index, huff_tables* pH);
- void check_quant_tables();
- void check_huff_tables();
- bool calc_mcu_block_order();
- int init_scan();
- void init_frame();
- void process_restart();
- void decode_scan(pDecode_block_func decode_block_func);
- void init_progressive();
- void init_sequential();
- void decode_start();
- void decode_init(jpeg_decoder_stream* pStream, uint32_t flags);
- void H2V2Convert();
- uint32_t H2V2ConvertFiltered();
- void H2V1Convert();
- void H2V1ConvertFiltered();
- void H1V2Convert();
- void H1V2ConvertFiltered();
- void H1V1Convert();
- void gray_convert();
- void find_eoi();
- inline uint get_char();
- inline uint get_char(bool* pPadding_flag);
- inline void stuff_char(uint8 q);
- inline uint8 get_octet();
- inline uint get_bits(int num_bits);
- inline uint get_bits_no_markers(int numbits);
- inline int huff_decode(huff_tables* pH);
- inline int huff_decode(huff_tables* pH, int& extrabits);
-
- // Clamps a value between 0-255.
- static inline uint8 clamp(int i)
- {
- if (static_cast<uint>(i) > 255)
- i = (((~i) >> 31) & 0xFF);
- return static_cast<uint8>(i);
- }
- int decode_next_mcu_row();
-
- static void decode_block_dc_first(jpeg_decoder* pD, int component_id, int block_x, int block_y);
- static void decode_block_dc_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y);
- static void decode_block_ac_first(jpeg_decoder* pD, int component_id, int block_x, int block_y);
- static void decode_block_ac_refine(jpeg_decoder* pD, int component_id, int block_x, int block_y);
- };
-
-} // namespace jpgd
-
-#endif // JPEG_DECODER_H
diff --git a/thirdparty/noise/FastNoiseLite.h b/thirdparty/noise/FastNoiseLite.h
index a213f0888e..fb6dbcb92a 100644
--- a/thirdparty/noise/FastNoiseLite.h
+++ b/thirdparty/noise/FastNoiseLite.h
@@ -1,7 +1,7 @@
// MIT License
//
-// Copyright(c) 2020 Jordan Peck (jordan.me2@gmail.com)
-// Copyright(c) 2020 Contributors
+// Copyright(c) 2023 Jordan Peck (jordan.me2@gmail.com)
+// Copyright(c) 2023 Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
@@ -44,15 +44,15 @@
// ....',;:codxkO000OOxdoc:;,''',,,;;;;,''.......',,;:clodkO00000Okxolc::;,,''..',;:ldxOKXNWWWNNK0OkkkkkkkkkkkxxddooooodxxkOOOOO000
// ....',;;clodxkkOOOkkdolc:;,,,,,,,,'..........,;:clodxkO0KKXKK0Okxdolcc::;;,,,;;:codkO0XXNNNNXKK0OOOOOkkkkxxdoollloodxkO0KKKXXXXX
//
-// VERSION: 1.0.1
-// https://github.com/Auburn/FastNoise
+// VERSION: 1.1.0
+// https://github.com/Auburn/FastNoiseLite
#ifndef FASTNOISELITE_H
#define FASTNOISELITE_H
#include <cmath>
-namespace fastnoiselite{
+namespace fastnoiselite {
class FastNoiseLite
{
@@ -2594,5 +2594,6 @@ const T FastNoiseLite::Lookup<T>::RandVecs3D[] =
-0.7870349638f, 0.03447489231f, 0.6159443543f, 0, -0.2015596421f, 0.6859872284f, 0.6991389226f, 0, -0.08581082512f, -0.10920836f, -0.9903080513f, 0, 0.5532693395f, 0.7325250401f, -0.396610771f, 0, -0.1842489331f, -0.9777375055f, -0.1004076743f, 0, 0.0775473789f, -0.9111505856f, 0.4047110257f, 0, 0.1399838409f, 0.7601631212f, -0.6344734459f, 0, 0.4484419361f, -0.845289248f, 0.2904925424f, 0
};
-}
-#endif // namespace fastnoiselite
+} // namespace fastnoiselite
+
+#endif
diff --git a/thirdparty/noise/patches/FastNoiseLite.patch b/thirdparty/noise/patches/FastNoiseLite.patch
deleted file mode 100644
index 3c835c7b06..0000000000
--- a/thirdparty/noise/patches/FastNoiseLite.patch
+++ /dev/null
@@ -1,455 +0,0 @@
---- orig/FastNoiseLite.h 1900-01-00 00:00:00 +0000
-+++ noise/FastNoiseLite.h 1900-01-00 00:00:00 +0000
-@@ -52,6 +52,8 @@
-
- #include <cmath>
-
-+namespace fastnoiselite{
-+
- class FastNoiseLite
- {
- public:
-@@ -2583,4 +2585,5 @@
- -0.7870349638f, 0.03447489231f, 0.6159443543f, 0, -0.2015596421f, 0.6859872284f, 0.6991389226f, 0, -0.08581082512f, -0.10920836f, -0.9903080513f, 0, 0.5532693395f, 0.7325250401f, -0.396610771f, 0, -0.1842489331f, -0.9777375055f, -0.1004076743f, 0, 0.0775473789f, -0.9111505856f, 0.4047110257f, 0, 0.1399838409f, 0.7601631212f, -0.6344734459f, 0, 0.4484419361f, -0.845289248f, 0.2904925424f, 0
- };
-
--#endif
-+}
-+#endif // namespace fastnoiselite
-@@ -295,7 +295,7 @@ public:
- /// Noise output bounded between -1...1
- /// </returns>
- template <typename FNfloat>
-- float GetNoise(FNfloat x, FNfloat y)
-+ float GetNoise(FNfloat x, FNfloat y) const
- {
- Arguments_must_be_floating_point_values<FNfloat>();
-
-@@ -321,7 +321,7 @@ public:
- /// Noise output bounded between -1...1
- /// </returns>
- template <typename FNfloat>
-- float GetNoise(FNfloat x, FNfloat y, FNfloat z)
-+ float GetNoise(FNfloat x, FNfloat y, FNfloat z) const
- {
- Arguments_must_be_floating_point_values<FNfloat>();
-
-@@ -350,7 +350,7 @@ public:
- /// noise = GetNoise(x, y)</code>
- /// </example>
- template <typename FNfloat>
-- void DomainWarp(FNfloat& x, FNfloat& y)
-+ void DomainWarp(FNfloat& x, FNfloat& y) const
- {
- Arguments_must_be_floating_point_values<FNfloat>();
-
-@@ -377,7 +377,7 @@ public:
- /// noise = GetNoise(x, y, z)</code>
- /// </example>
- template <typename FNfloat>
-- void DomainWarp(FNfloat& x, FNfloat& y, FNfloat& z)
-+ void DomainWarp(FNfloat& x, FNfloat& y, FNfloat& z) const
- {
- Arguments_must_be_floating_point_values<FNfloat>();
-
-@@ -528,7 +528,7 @@ private:
- }
-
-
-- float GradCoord(int seed, int xPrimed, int yPrimed, float xd, float yd)
-+ float GradCoord(int seed, int xPrimed, int yPrimed, float xd, float yd) const
- {
- int hash = Hash(seed, xPrimed, yPrimed);
- hash ^= hash >> 15;
-@@ -541,7 +541,7 @@ private:
- }
-
-
-- float GradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd)
-+ float GradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd) const
- {
- int hash = Hash(seed, xPrimed, yPrimed, zPrimed);
- hash ^= hash >> 15;
-@@ -555,7 +555,7 @@ private:
- }
-
-
-- void GradCoordOut(int seed, int xPrimed, int yPrimed, float& xo, float& yo)
-+ void GradCoordOut(int seed, int xPrimed, int yPrimed, float& xo, float& yo) const
- {
- int hash = Hash(seed, xPrimed, yPrimed) & (255 << 1);
-
-@@ -564,7 +564,7 @@ private:
- }
-
-
-- void GradCoordOut(int seed, int xPrimed, int yPrimed, int zPrimed, float& xo, float& yo, float& zo)
-+ void GradCoordOut(int seed, int xPrimed, int yPrimed, int zPrimed, float& xo, float& yo, float& zo) const
- {
- int hash = Hash(seed, xPrimed, yPrimed, zPrimed) & (255 << 2);
-
-@@ -574,7 +574,7 @@ private:
- }
-
-
-- void GradCoordDual(int seed, int xPrimed, int yPrimed, float xd, float yd, float& xo, float& yo)
-+ void GradCoordDual(int seed, int xPrimed, int yPrimed, float xd, float yd, float& xo, float& yo) const
- {
- int hash = Hash(seed, xPrimed, yPrimed);
- int index1 = hash & (127 << 1);
-@@ -592,7 +592,7 @@ private:
- }
-
-
-- void GradCoordDual(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd, float& xo, float& yo, float& zo)
-+ void GradCoordDual(int seed, int xPrimed, int yPrimed, int zPrimed, float xd, float yd, float zd, float& xo, float& yo, float& zo) const
- {
- int hash = Hash(seed, xPrimed, yPrimed, zPrimed);
- int index1 = hash & (63 << 2);
-@@ -616,7 +616,7 @@ private:
- // Generic noise gen
-
- template <typename FNfloat>
-- float GenNoiseSingle(int seed, FNfloat x, FNfloat y)
-+ float GenNoiseSingle(int seed, FNfloat x, FNfloat y) const
- {
- switch (mNoiseType)
- {
-@@ -638,7 +638,7 @@ private:
- }
-
- template <typename FNfloat>
-- float GenNoiseSingle(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float GenNoiseSingle(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- switch (mNoiseType)
- {
-@@ -663,7 +663,7 @@ private:
- // Noise Coordinate Transforms (frequency, and possible skew or rotation)
-
- template <typename FNfloat>
-- void TransformNoiseCoordinate(FNfloat& x, FNfloat& y)
-+ void TransformNoiseCoordinate(FNfloat& x, FNfloat& y) const
- {
- x *= mFrequency;
- y *= mFrequency;
-@@ -686,7 +686,7 @@ private:
- }
-
- template <typename FNfloat>
-- void TransformNoiseCoordinate(FNfloat& x, FNfloat& y, FNfloat& z)
-+ void TransformNoiseCoordinate(FNfloat& x, FNfloat& y, FNfloat& z) const
- {
- x *= mFrequency;
- y *= mFrequency;
-@@ -757,7 +757,7 @@ private:
- // Domain Warp Coordinate Transforms
-
- template <typename FNfloat>
-- void TransformDomainWarpCoordinate(FNfloat& x, FNfloat& y)
-+ void TransformDomainWarpCoordinate(FNfloat& x, FNfloat& y) const
- {
- switch (mDomainWarpType)
- {
-@@ -777,7 +777,7 @@ private:
- }
-
- template <typename FNfloat>
-- void TransformDomainWarpCoordinate(FNfloat& x, FNfloat& y, FNfloat& z)
-+ void TransformDomainWarpCoordinate(FNfloat& x, FNfloat& y, FNfloat& z) const
- {
- switch (mWarpTransformType3D)
- {
-@@ -844,7 +844,7 @@ private:
- // Fractal FBm
-
- template <typename FNfloat>
-- float GenFractalFBm(FNfloat x, FNfloat y)
-+ float GenFractalFBm(FNfloat x, FNfloat y) const
- {
- int seed = mSeed;
- float sum = 0;
-@@ -865,7 +865,7 @@ private:
- }
-
- template <typename FNfloat>
-- float GenFractalFBm(FNfloat x, FNfloat y, FNfloat z)
-+ float GenFractalFBm(FNfloat x, FNfloat y, FNfloat z) const
- {
- int seed = mSeed;
- float sum = 0;
-@@ -890,7 +890,7 @@ private:
- // Fractal Ridged
-
- template <typename FNfloat>
-- float GenFractalRidged(FNfloat x, FNfloat y)
-+ float GenFractalRidged(FNfloat x, FNfloat y) const
- {
- int seed = mSeed;
- float sum = 0;
-@@ -911,7 +911,7 @@ private:
- }
-
- template <typename FNfloat>
-- float GenFractalRidged(FNfloat x, FNfloat y, FNfloat z)
-+ float GenFractalRidged(FNfloat x, FNfloat y, FNfloat z) const
- {
- int seed = mSeed;
- float sum = 0;
-@@ -936,7 +936,7 @@ private:
- // Fractal PingPong
-
- template <typename FNfloat>
-- float GenFractalPingPong(FNfloat x, FNfloat y)
-+ float GenFractalPingPong(FNfloat x, FNfloat y) const
- {
- int seed = mSeed;
- float sum = 0;
-@@ -957,7 +957,7 @@ private:
- }
-
- template <typename FNfloat>
-- float GenFractalPingPong(FNfloat x, FNfloat y, FNfloat z)
-+ float GenFractalPingPong(FNfloat x, FNfloat y, FNfloat z) const
- {
- int seed = mSeed;
- float sum = 0;
-@@ -982,7 +982,7 @@ private:
- // Simplex/OpenSimplex2 Noise
-
- template <typename FNfloat>
-- float SingleSimplex(int seed, FNfloat x, FNfloat y)
-+ float SingleSimplex(int seed, FNfloat x, FNfloat y) const
- {
- // 2D OpenSimplex2 case uses the same algorithm as ordinary Simplex.
-
-@@ -1053,7 +1053,7 @@ private:
- }
-
- template <typename FNfloat>
-- float SingleOpenSimplex2(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float SingleOpenSimplex2(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- // 3D OpenSimplex2 case uses two offset rotated cube grids.
-
-@@ -1155,7 +1155,7 @@ private:
- // OpenSimplex2S Noise
-
- template <typename FNfloat>
-- float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y)
-+ float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y) const
- {
- // 2D OpenSimplex2S case is a modified 2D simplex noise.
-
-@@ -1286,7 +1286,7 @@ private:
- }
-
- template <typename FNfloat>
-- float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float SingleOpenSimplex2S(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- // 3D OpenSimplex2S case uses two offset rotated cube grids.
-
-@@ -1482,7 +1482,7 @@ private:
- // Cellular Noise
-
- template <typename FNfloat>
-- float SingleCellular(int seed, FNfloat x, FNfloat y)
-+ float SingleCellular(int seed, FNfloat x, FNfloat y) const
- {
- int xr = FastRound(x);
- int yr = FastRound(y);
-@@ -1612,7 +1612,7 @@ private:
- }
-
- template <typename FNfloat>
-- float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- int xr = FastRound(x);
- int yr = FastRound(y);
-@@ -1769,7 +1769,7 @@ private:
- // Perlin Noise
-
- template <typename FNfloat>
-- float SinglePerlin(int seed, FNfloat x, FNfloat y)
-+ float SinglePerlin(int seed, FNfloat x, FNfloat y) const
- {
- int x0 = FastFloor(x);
- int y0 = FastFloor(y);
-@@ -1794,7 +1794,7 @@ private:
- }
-
- template <typename FNfloat>
-- float SinglePerlin(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float SinglePerlin(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- int x0 = FastFloor(x);
- int y0 = FastFloor(y);
-@@ -1833,7 +1833,7 @@ private:
- // Value Cubic Noise
-
- template <typename FNfloat>
-- float SingleValueCubic(int seed, FNfloat x, FNfloat y)
-+ float SingleValueCubic(int seed, FNfloat x, FNfloat y) const
- {
- int x1 = FastFloor(x);
- int y1 = FastFloor(y);
-@@ -1863,7 +1863,7 @@ private:
- }
-
- template <typename FNfloat>
-- float SingleValueCubic(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float SingleValueCubic(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- int x1 = FastFloor(x);
- int y1 = FastFloor(y);
-@@ -1920,7 +1920,7 @@ private:
- // Value Noise
-
- template <typename FNfloat>
-- float SingleValue(int seed, FNfloat x, FNfloat y)
-+ float SingleValue(int seed, FNfloat x, FNfloat y) const
- {
- int x0 = FastFloor(x);
- int y0 = FastFloor(y);
-@@ -1940,7 +1940,7 @@ private:
- }
-
- template <typename FNfloat>
-- float SingleValue(int seed, FNfloat x, FNfloat y, FNfloat z)
-+ float SingleValue(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
- int x0 = FastFloor(x);
- int y0 = FastFloor(y);
-@@ -1972,7 +1972,7 @@ private:
- // Domain Warp
-
- template <typename FNfloat>
-- void DoSingleDomainWarp(int seed, float amp, float freq, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr)
-+ void DoSingleDomainWarp(int seed, float amp, float freq, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr) const
- {
- switch (mDomainWarpType)
- {
-@@ -1989,7 +1989,7 @@ private:
- }
-
- template <typename FNfloat>
-- void DoSingleDomainWarp(int seed, float amp, float freq, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr)
-+ void DoSingleDomainWarp(int seed, float amp, float freq, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr) const
- {
- switch (mDomainWarpType)
- {
-@@ -2009,7 +2009,7 @@ private:
- // Domain Warp Single Wrapper
-
- template <typename FNfloat>
-- void DomainWarpSingle(FNfloat& x, FNfloat& y)
-+ void DomainWarpSingle(FNfloat& x, FNfloat& y) const
- {
- int seed = mSeed;
- float amp = mDomainWarpAmp * mFractalBounding;
-@@ -2023,7 +2023,7 @@ private:
- }
-
- template <typename FNfloat>
-- void DomainWarpSingle(FNfloat& x, FNfloat& y, FNfloat& z)
-+ void DomainWarpSingle(FNfloat& x, FNfloat& y, FNfloat& z) const
- {
- int seed = mSeed;
- float amp = mDomainWarpAmp * mFractalBounding;
-@@ -2041,7 +2041,7 @@ private:
- // Domain Warp Fractal Progressive
-
- template <typename FNfloat>
-- void DomainWarpFractalProgressive(FNfloat& x, FNfloat& y)
-+ void DomainWarpFractalProgressive(FNfloat& x, FNfloat& y) const
- {
- int seed = mSeed;
- float amp = mDomainWarpAmp * mFractalBounding;
-@@ -2062,7 +2062,7 @@ private:
- }
-
- template <typename FNfloat>
-- void DomainWarpFractalProgressive(FNfloat& x, FNfloat& y, FNfloat& z)
-+ void DomainWarpFractalProgressive(FNfloat& x, FNfloat& y, FNfloat& z) const
- {
- int seed = mSeed;
- float amp = mDomainWarpAmp * mFractalBounding;
-@@ -2087,7 +2087,7 @@ private:
- // Domain Warp Fractal Independant
-
- template <typename FNfloat>
-- void DomainWarpFractalIndependent(FNfloat& x, FNfloat& y)
-+ void DomainWarpFractalIndependent(FNfloat& x, FNfloat& y) const
- {
- FNfloat xs = x;
- FNfloat ys = y;
-@@ -2108,7 +2108,7 @@ private:
- }
-
- template <typename FNfloat>
-- void DomainWarpFractalIndependent(FNfloat& x, FNfloat& y, FNfloat& z)
-+ void DomainWarpFractalIndependent(FNfloat& x, FNfloat& y, FNfloat& z) const
- {
- FNfloat xs = x;
- FNfloat ys = y;
-@@ -2133,7 +2133,7 @@ private:
- // Domain Warp Basic Grid
-
- template <typename FNfloat>
-- void SingleDomainWarpBasicGrid(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr)
-+ void SingleDomainWarpBasicGrid(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr) const
- {
- FNfloat xf = x * frequency;
- FNfloat yf = y * frequency;
-@@ -2166,7 +2166,7 @@ private:
- }
-
- template <typename FNfloat>
-- void SingleDomainWarpBasicGrid(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr)
-+ void SingleDomainWarpBasicGrid(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr) const
- {
- FNfloat xf = x * frequency;
- FNfloat yf = y * frequency;
-@@ -2228,7 +2228,7 @@ private:
- // Domain Warp Simplex/OpenSimplex2
-
- template <typename FNfloat>
-- void SingleDomainWarpSimplexGradient(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr, bool outGradOnly)
-+ void SingleDomainWarpSimplexGradient(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat& xr, FNfloat& yr, bool outGradOnly) const
- {
- const float SQRT3 = 1.7320508075688772935274463415059f;
- const float G2 = (3 - SQRT3) / 6;
-@@ -2326,7 +2326,7 @@ private:
- }
-
- template <typename FNfloat>
-- void SingleDomainWarpOpenSimplex2Gradient(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr, bool outGradOnly)
-+ void SingleDomainWarpOpenSimplex2Gradient(int seed, float warpAmp, float frequency, FNfloat x, FNfloat y, FNfloat z, FNfloat& xr, FNfloat& yr, FNfloat& zr, bool outGradOnly) const
- {
- x *= frequency;
- y *= frequency;
-@@ -1611,6 +1611,12 @@ private:
- }
- }
-
-+// GCC raises warnings when integer overflows occur, which are needed for hashing here.
-+#if defined(__GNUC__) && !defined(__clang__)
-+#pragma GCC diagnostic push
-+#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations"
-+#endif
-+
- template <typename FNfloat>
- float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z) const
- {
-@@ -1765,6 +1771,9 @@ private:
- }
- }
-
-+#if defined(__GNUC__) && !defined(__clang__)
-+#pragma GCC diagnostic pop
-+#endif
-
- // Perlin Noise
- \ No newline at end of file
diff --git a/thirdparty/noise/patches/namespace-warnings.patch b/thirdparty/noise/patches/namespace-warnings.patch
new file mode 100644
index 0000000000..6348ae1f05
--- /dev/null
+++ b/thirdparty/noise/patches/namespace-warnings.patch
@@ -0,0 +1,43 @@
+diff --git a/thirdparty/noise/FastNoiseLite.h b/thirdparty/noise/FastNoiseLite.h
+index ed97b0fcac..fb6dbcb92a 100644
+--- a/thirdparty/noise/FastNoiseLite.h
++++ b/thirdparty/noise/FastNoiseLite.h
+@@ -52,6 +52,8 @@
+
+ #include <cmath>
+
++namespace fastnoiselite {
++
+ class FastNoiseLite
+ {
+ public:
+@@ -1609,6 +1611,12 @@ private:
+ }
+ }
+
++// GCC raises warnings when integer overflows occur, which are needed for hashing here.
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations"
++#endif
++
+ template <typename FNfloat>
+ float SingleCellular(int seed, FNfloat x, FNfloat y, FNfloat z) const
+ {
+@@ -1763,6 +1771,9 @@ private:
+ }
+ }
+
++#if defined(__GNUC__) && !defined(__clang__)
++#pragma GCC diagnostic pop
++#endif
+
+ // Perlin Noise
+
+@@ -2583,4 +2594,6 @@ const T FastNoiseLite::Lookup<T>::RandVecs3D[] =
+ -0.7870349638f, 0.03447489231f, 0.6159443543f, 0, -0.2015596421f, 0.6859872284f, 0.6991389226f, 0, -0.08581082512f, -0.10920836f, -0.9903080513f, 0, 0.5532693395f, 0.7325250401f, -0.396610771f, 0, -0.1842489331f, -0.9777375055f, -0.1004076743f, 0, 0.0775473789f, -0.9111505856f, 0.4047110257f, 0, 0.1399838409f, 0.7601631212f, -0.6344734459f, 0, 0.4484419361f, -0.845289248f, 0.2904925424f, 0
+ };
+
++} // namespace fastnoiselite
++
+ #endif